roojs-ui-debug.js
[roojs1] / roojs-ui-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11
12
13
14 /*
15  * These classes are derivatives of the similarly named classes in the YUI Library.
16  * The original license:
17  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
18  * Code licensed under the BSD License:
19  * http://developer.yahoo.net/yui/license.txt
20  */
21
22 (function() {
23
24 var Event=Roo.EventManager;
25 var Dom=Roo.lib.Dom;
26
27 /**
28  * @class Roo.dd.DragDrop
29  * @extends Roo.util.Observable
30  * Defines the interface and base operation of items that that can be
31  * dragged or can be drop targets.  It was designed to be extended, overriding
32  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
33  * Up to three html elements can be associated with a DragDrop instance:
34  * <ul>
35  * <li>linked element: the element that is passed into the constructor.
36  * This is the element which defines the boundaries for interaction with
37  * other DragDrop objects.</li>
38  * <li>handle element(s): The drag operation only occurs if the element that
39  * was clicked matches a handle element.  By default this is the linked
40  * element, but there are times that you will want only a portion of the
41  * linked element to initiate the drag operation, and the setHandleElId()
42  * method provides a way to define this.</li>
43  * <li>drag element: this represents the element that would be moved along
44  * with the cursor during a drag operation.  By default, this is the linked
45  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
46  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
47  * </li>
48  * </ul>
49  * This class should not be instantiated until the onload event to ensure that
50  * the associated elements are available.
51  * The following would define a DragDrop obj that would interact with any
52  * other DragDrop obj in the "group1" group:
53  * <pre>
54  *  dd = new Roo.dd.DragDrop("div1", "group1");
55  * </pre>
56  * Since none of the event handlers have been implemented, nothing would
57  * actually happen if you were to run the code above.  Normally you would
58  * override this class or one of the default implementations, but you can
59  * also override the methods you want on an instance of the class...
60  * <pre>
61  *  dd.onDragDrop = function(e, id) {
62  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
63  *  }
64  * </pre>
65  * @constructor
66  * @param {String} id of the element that is linked to this instance
67  * @param {String} sGroup the group of related DragDrop objects
68  * @param {object} config an object containing configurable attributes
69  *                Valid properties for DragDrop:
70  *                    padding, isTarget, maintainOffset, primaryButtonOnly
71  */
72 Roo.dd.DragDrop = function(id, sGroup, config) {
73     if (id) {
74         this.init(id, sGroup, config);
75     }
76     
77 };
78
79 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
80
81     /**
82      * The id of the element associated with this object.  This is what we
83      * refer to as the "linked element" because the size and position of
84      * this element is used to determine when the drag and drop objects have
85      * interacted.
86      * @property id
87      * @type String
88      */
89     id: null,
90
91     /**
92      * Configuration attributes passed into the constructor
93      * @property config
94      * @type object
95      */
96     config: null,
97
98     /**
99      * The id of the element that will be dragged.  By default this is same
100      * as the linked element , but could be changed to another element. Ex:
101      * Roo.dd.DDProxy
102      * @property dragElId
103      * @type String
104      * @private
105      */
106     dragElId: null,
107
108     /**
109      * the id of the element that initiates the drag operation.  By default
110      * this is the linked element, but could be changed to be a child of this
111      * element.  This lets us do things like only starting the drag when the
112      * header element within the linked html element is clicked.
113      * @property handleElId
114      * @type String
115      * @private
116      */
117     handleElId: null,
118
119     /**
120      * An associative array of HTML tags that will be ignored if clicked.
121      * @property invalidHandleTypes
122      * @type {string: string}
123      */
124     invalidHandleTypes: null,
125
126     /**
127      * An associative array of ids for elements that will be ignored if clicked
128      * @property invalidHandleIds
129      * @type {string: string}
130      */
131     invalidHandleIds: null,
132
133     /**
134      * An indexted array of css class names for elements that will be ignored
135      * if clicked.
136      * @property invalidHandleClasses
137      * @type string[]
138      */
139     invalidHandleClasses: null,
140
141     /**
142      * The linked element's absolute X position at the time the drag was
143      * started
144      * @property startPageX
145      * @type int
146      * @private
147      */
148     startPageX: 0,
149
150     /**
151      * The linked element's absolute X position at the time the drag was
152      * started
153      * @property startPageY
154      * @type int
155      * @private
156      */
157     startPageY: 0,
158
159     /**
160      * The group defines a logical collection of DragDrop objects that are
161      * related.  Instances only get events when interacting with other
162      * DragDrop object in the same group.  This lets us define multiple
163      * groups using a single DragDrop subclass if we want.
164      * @property groups
165      * @type {string: string}
166      */
167     groups: null,
168
169     /**
170      * Individual drag/drop instances can be locked.  This will prevent
171      * onmousedown start drag.
172      * @property locked
173      * @type boolean
174      * @private
175      */
176     locked: false,
177
178     /**
179      * Lock this instance
180      * @method lock
181      */
182     lock: function() { this.locked = true; },
183
184     /**
185      * Unlock this instace
186      * @method unlock
187      */
188     unlock: function() { this.locked = false; },
189
190     /**
191      * By default, all insances can be a drop target.  This can be disabled by
192      * setting isTarget to false.
193      * @method isTarget
194      * @type boolean
195      */
196     isTarget: true,
197
198     /**
199      * The padding configured for this drag and drop object for calculating
200      * the drop zone intersection with this object.
201      * @method padding
202      * @type int[]
203      */
204     padding: null,
205
206     /**
207      * Cached reference to the linked element
208      * @property _domRef
209      * @private
210      */
211     _domRef: null,
212
213     /**
214      * Internal typeof flag
215      * @property __ygDragDrop
216      * @private
217      */
218     __ygDragDrop: true,
219
220     /**
221      * Set to true when horizontal contraints are applied
222      * @property constrainX
223      * @type boolean
224      * @private
225      */
226     constrainX: false,
227
228     /**
229      * Set to true when vertical contraints are applied
230      * @property constrainY
231      * @type boolean
232      * @private
233      */
234     constrainY: false,
235
236     /**
237      * The left constraint
238      * @property minX
239      * @type int
240      * @private
241      */
242     minX: 0,
243
244     /**
245      * The right constraint
246      * @property maxX
247      * @type int
248      * @private
249      */
250     maxX: 0,
251
252     /**
253      * The up constraint
254      * @property minY
255      * @type int
256      * @type int
257      * @private
258      */
259     minY: 0,
260
261     /**
262      * The down constraint
263      * @property maxY
264      * @type int
265      * @private
266      */
267     maxY: 0,
268
269     /**
270      * Maintain offsets when we resetconstraints.  Set to true when you want
271      * the position of the element relative to its parent to stay the same
272      * when the page changes
273      *
274      * @property maintainOffset
275      * @type boolean
276      */
277     maintainOffset: false,
278
279     /**
280      * Array of pixel locations the element will snap to if we specified a
281      * horizontal graduation/interval.  This array is generated automatically
282      * when you define a tick interval.
283      * @property xTicks
284      * @type int[]
285      */
286     xTicks: null,
287
288     /**
289      * Array of pixel locations the element will snap to if we specified a
290      * vertical graduation/interval.  This array is generated automatically
291      * when you define a tick interval.
292      * @property yTicks
293      * @type int[]
294      */
295     yTicks: null,
296
297     /**
298      * By default the drag and drop instance will only respond to the primary
299      * button click (left button for a right-handed mouse).  Set to true to
300      * allow drag and drop to start with any mouse click that is propogated
301      * by the browser
302      * @property primaryButtonOnly
303      * @type boolean
304      */
305     primaryButtonOnly: true,
306
307     /**
308      * The availabe property is false until the linked dom element is accessible.
309      * @property available
310      * @type boolean
311      */
312     available: false,
313
314     /**
315      * By default, drags can only be initiated if the mousedown occurs in the
316      * region the linked element is.  This is done in part to work around a
317      * bug in some browsers that mis-report the mousedown if the previous
318      * mouseup happened outside of the window.  This property is set to true
319      * if outer handles are defined.
320      *
321      * @property hasOuterHandles
322      * @type boolean
323      * @default false
324      */
325     hasOuterHandles: false,
326
327     /**
328      * Code that executes immediately before the startDrag event
329      * @method b4StartDrag
330      * @private
331      */
332     b4StartDrag: function(x, y) { },
333
334     /**
335      * Abstract method called after a drag/drop object is clicked
336      * and the drag or mousedown time thresholds have beeen met.
337      * @method startDrag
338      * @param {int} X click location
339      * @param {int} Y click location
340      */
341     startDrag: function(x, y) { /* override this */ },
342
343     /**
344      * Code that executes immediately before the onDrag event
345      * @method b4Drag
346      * @private
347      */
348     b4Drag: function(e) { },
349
350     /**
351      * Abstract method called during the onMouseMove event while dragging an
352      * object.
353      * @method onDrag
354      * @param {Event} e the mousemove event
355      */
356     onDrag: function(e) { /* override this */ },
357
358     /**
359      * Abstract method called when this element fist begins hovering over
360      * another DragDrop obj
361      * @method onDragEnter
362      * @param {Event} e the mousemove event
363      * @param {String|DragDrop[]} id In POINT mode, the element
364      * id this is hovering over.  In INTERSECT mode, an array of one or more
365      * dragdrop items being hovered over.
366      */
367     onDragEnter: function(e, id) { /* override this */ },
368
369     /**
370      * Code that executes immediately before the onDragOver event
371      * @method b4DragOver
372      * @private
373      */
374     b4DragOver: function(e) { },
375
376     /**
377      * Abstract method called when this element is hovering over another
378      * DragDrop obj
379      * @method onDragOver
380      * @param {Event} e the mousemove event
381      * @param {String|DragDrop[]} id In POINT mode, the element
382      * id this is hovering over.  In INTERSECT mode, an array of dd items
383      * being hovered over.
384      */
385     onDragOver: function(e, id) { /* override this */ },
386
387     /**
388      * Code that executes immediately before the onDragOut event
389      * @method b4DragOut
390      * @private
391      */
392     b4DragOut: function(e) { },
393
394     /**
395      * Abstract method called when we are no longer hovering over an element
396      * @method onDragOut
397      * @param {Event} e the mousemove event
398      * @param {String|DragDrop[]} id In POINT mode, the element
399      * id this was hovering over.  In INTERSECT mode, an array of dd items
400      * that the mouse is no longer over.
401      */
402     onDragOut: function(e, id) { /* override this */ },
403
404     /**
405      * Code that executes immediately before the onDragDrop event
406      * @method b4DragDrop
407      * @private
408      */
409     b4DragDrop: function(e) { },
410
411     /**
412      * Abstract method called when this item is dropped on another DragDrop
413      * obj
414      * @method onDragDrop
415      * @param {Event} e the mouseup event
416      * @param {String|DragDrop[]} id In POINT mode, the element
417      * id this was dropped on.  In INTERSECT mode, an array of dd items this
418      * was dropped on.
419      */
420     onDragDrop: function(e, id) { /* override this */ },
421
422     /**
423      * Abstract method called when this item is dropped on an area with no
424      * drop target
425      * @method onInvalidDrop
426      * @param {Event} e the mouseup event
427      */
428     onInvalidDrop: function(e) { /* override this */ },
429
430     /**
431      * Code that executes immediately before the endDrag event
432      * @method b4EndDrag
433      * @private
434      */
435     b4EndDrag: function(e) { },
436
437     /**
438      * Fired when we are done dragging the object
439      * @method endDrag
440      * @param {Event} e the mouseup event
441      */
442     endDrag: function(e) { /* override this */ },
443
444     /**
445      * Code executed immediately before the onMouseDown event
446      * @method b4MouseDown
447      * @param {Event} e the mousedown event
448      * @private
449      */
450     b4MouseDown: function(e) {  },
451
452     /**
453      * Event handler that fires when a drag/drop obj gets a mousedown
454      * @method onMouseDown
455      * @param {Event} e the mousedown event
456      */
457     onMouseDown: function(e) { /* override this */ },
458
459     /**
460      * Event handler that fires when a drag/drop obj gets a mouseup
461      * @method onMouseUp
462      * @param {Event} e the mouseup event
463      */
464     onMouseUp: function(e) { /* override this */ },
465
466     /**
467      * Override the onAvailable method to do what is needed after the initial
468      * position was determined.
469      * @method onAvailable
470      */
471     onAvailable: function () {
472     },
473
474     /*
475      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
476      * @type Object
477      */
478     defaultPadding : {left:0, right:0, top:0, bottom:0},
479
480     /*
481      * Initializes the drag drop object's constraints to restrict movement to a certain element.
482  *
483  * Usage:
484  <pre><code>
485  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
486                 { dragElId: "existingProxyDiv" });
487  dd.startDrag = function(){
488      this.constrainTo("parent-id");
489  };
490  </code></pre>
491  * Or you can initalize it using the {@link Roo.Element} object:
492  <pre><code>
493  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
494      startDrag : function(){
495          this.constrainTo("parent-id");
496      }
497  });
498  </code></pre>
499      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
500      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
501      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
502      * an object containing the sides to pad. For example: {right:10, bottom:10}
503      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
504      */
505     constrainTo : function(constrainTo, pad, inContent){
506         if(typeof pad == "number"){
507             pad = {left: pad, right:pad, top:pad, bottom:pad};
508         }
509         pad = pad || this.defaultPadding;
510         var b = Roo.get(this.getEl()).getBox();
511         var ce = Roo.get(constrainTo);
512         var s = ce.getScroll();
513         var c, cd = ce.dom;
514         if(cd == document.body){
515             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
516         }else{
517             xy = ce.getXY();
518             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
519         }
520
521
522         var topSpace = b.y - c.y;
523         var leftSpace = b.x - c.x;
524
525         this.resetConstraints();
526         this.setXConstraint(leftSpace - (pad.left||0), // left
527                 c.width - leftSpace - b.width - (pad.right||0) //right
528         );
529         this.setYConstraint(topSpace - (pad.top||0), //top
530                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
531         );
532     },
533
534     /**
535      * Returns a reference to the linked element
536      * @method getEl
537      * @return {HTMLElement} the html element
538      */
539     getEl: function() {
540         if (!this._domRef) {
541             this._domRef = Roo.getDom(this.id);
542         }
543
544         return this._domRef;
545     },
546
547     /**
548      * Returns a reference to the actual element to drag.  By default this is
549      * the same as the html element, but it can be assigned to another
550      * element. An example of this can be found in Roo.dd.DDProxy
551      * @method getDragEl
552      * @return {HTMLElement} the html element
553      */
554     getDragEl: function() {
555         return Roo.getDom(this.dragElId);
556     },
557
558     /**
559      * Sets up the DragDrop object.  Must be called in the constructor of any
560      * Roo.dd.DragDrop subclass
561      * @method init
562      * @param id the id of the linked element
563      * @param {String} sGroup the group of related items
564      * @param {object} config configuration attributes
565      */
566     init: function(id, sGroup, config) {
567         this.initTarget(id, sGroup, config);
568         if (!Roo.isTouch) {
569             Event.on(this.id, "mousedown", this.handleMouseDown, this);
570         }
571         Event.on(this.id, "touchstart", this.handleMouseDown, this);
572         // Event.on(this.id, "selectstart", Event.preventDefault);
573     },
574
575     /**
576      * Initializes Targeting functionality only... the object does not
577      * get a mousedown handler.
578      * @method initTarget
579      * @param id the id of the linked element
580      * @param {String} sGroup the group of related items
581      * @param {object} config configuration attributes
582      */
583     initTarget: function(id, sGroup, config) {
584
585         // configuration attributes
586         this.config = config || {};
587
588         // create a local reference to the drag and drop manager
589         this.DDM = Roo.dd.DDM;
590         // initialize the groups array
591         this.groups = {};
592
593         // assume that we have an element reference instead of an id if the
594         // parameter is not a string
595         if (typeof id !== "string") {
596             id = Roo.id(id);
597         }
598
599         // set the id
600         this.id = id;
601
602         // add to an interaction group
603         this.addToGroup((sGroup) ? sGroup : "default");
604
605         // We don't want to register this as the handle with the manager
606         // so we just set the id rather than calling the setter.
607         this.handleElId = id;
608
609         // the linked element is the element that gets dragged by default
610         this.setDragElId(id);
611
612         // by default, clicked anchors will not start drag operations.
613         this.invalidHandleTypes = { A: "A" };
614         this.invalidHandleIds = {};
615         this.invalidHandleClasses = [];
616
617         this.applyConfig();
618
619         this.handleOnAvailable();
620     },
621
622     /**
623      * Applies the configuration parameters that were passed into the constructor.
624      * This is supposed to happen at each level through the inheritance chain.  So
625      * a DDProxy implentation will execute apply config on DDProxy, DD, and
626      * DragDrop in order to get all of the parameters that are available in
627      * each object.
628      * @method applyConfig
629      */
630     applyConfig: function() {
631
632         // configurable properties:
633         //    padding, isTarget, maintainOffset, primaryButtonOnly
634         this.padding           = this.config.padding || [0, 0, 0, 0];
635         this.isTarget          = (this.config.isTarget !== false);
636         this.maintainOffset    = (this.config.maintainOffset);
637         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
638
639     },
640
641     /**
642      * Executed when the linked element is available
643      * @method handleOnAvailable
644      * @private
645      */
646     handleOnAvailable: function() {
647         this.available = true;
648         this.resetConstraints();
649         this.onAvailable();
650     },
651
652      /**
653      * Configures the padding for the target zone in px.  Effectively expands
654      * (or reduces) the virtual object size for targeting calculations.
655      * Supports css-style shorthand; if only one parameter is passed, all sides
656      * will have that padding, and if only two are passed, the top and bottom
657      * will have the first param, the left and right the second.
658      * @method setPadding
659      * @param {int} iTop    Top pad
660      * @param {int} iRight  Right pad
661      * @param {int} iBot    Bot pad
662      * @param {int} iLeft   Left pad
663      */
664     setPadding: function(iTop, iRight, iBot, iLeft) {
665         // this.padding = [iLeft, iRight, iTop, iBot];
666         if (!iRight && 0 !== iRight) {
667             this.padding = [iTop, iTop, iTop, iTop];
668         } else if (!iBot && 0 !== iBot) {
669             this.padding = [iTop, iRight, iTop, iRight];
670         } else {
671             this.padding = [iTop, iRight, iBot, iLeft];
672         }
673     },
674
675     /**
676      * Stores the initial placement of the linked element.
677      * @method setInitialPosition
678      * @param {int} diffX   the X offset, default 0
679      * @param {int} diffY   the Y offset, default 0
680      */
681     setInitPosition: function(diffX, diffY) {
682         var el = this.getEl();
683
684         if (!this.DDM.verifyEl(el)) {
685             return;
686         }
687
688         var dx = diffX || 0;
689         var dy = diffY || 0;
690
691         var p = Dom.getXY( el );
692
693         this.initPageX = p[0] - dx;
694         this.initPageY = p[1] - dy;
695
696         this.lastPageX = p[0];
697         this.lastPageY = p[1];
698
699
700         this.setStartPosition(p);
701     },
702
703     /**
704      * Sets the start position of the element.  This is set when the obj
705      * is initialized, the reset when a drag is started.
706      * @method setStartPosition
707      * @param pos current position (from previous lookup)
708      * @private
709      */
710     setStartPosition: function(pos) {
711         var p = pos || Dom.getXY( this.getEl() );
712         this.deltaSetXY = null;
713
714         this.startPageX = p[0];
715         this.startPageY = p[1];
716     },
717
718     /**
719      * Add this instance to a group of related drag/drop objects.  All
720      * instances belong to at least one group, and can belong to as many
721      * groups as needed.
722      * @method addToGroup
723      * @param sGroup {string} the name of the group
724      */
725     addToGroup: function(sGroup) {
726         this.groups[sGroup] = true;
727         this.DDM.regDragDrop(this, sGroup);
728     },
729
730     /**
731      * Remove's this instance from the supplied interaction group
732      * @method removeFromGroup
733      * @param {string}  sGroup  The group to drop
734      */
735     removeFromGroup: function(sGroup) {
736         if (this.groups[sGroup]) {
737             delete this.groups[sGroup];
738         }
739
740         this.DDM.removeDDFromGroup(this, sGroup);
741     },
742
743     /**
744      * Allows you to specify that an element other than the linked element
745      * will be moved with the cursor during a drag
746      * @method setDragElId
747      * @param id {string} the id of the element that will be used to initiate the drag
748      */
749     setDragElId: function(id) {
750         this.dragElId = id;
751     },
752
753     /**
754      * Allows you to specify a child of the linked element that should be
755      * used to initiate the drag operation.  An example of this would be if
756      * you have a content div with text and links.  Clicking anywhere in the
757      * content area would normally start the drag operation.  Use this method
758      * to specify that an element inside of the content div is the element
759      * that starts the drag operation.
760      * @method setHandleElId
761      * @param id {string} the id of the element that will be used to
762      * initiate the drag.
763      */
764     setHandleElId: function(id) {
765         if (typeof id !== "string") {
766             id = Roo.id(id);
767         }
768         this.handleElId = id;
769         this.DDM.regHandle(this.id, id);
770     },
771
772     /**
773      * Allows you to set an element outside of the linked element as a drag
774      * handle
775      * @method setOuterHandleElId
776      * @param id the id of the element that will be used to initiate the drag
777      */
778     setOuterHandleElId: function(id) {
779         if (typeof id !== "string") {
780             id = Roo.id(id);
781         }
782         Event.on(id, "mousedown",
783                 this.handleMouseDown, this);
784         this.setHandleElId(id);
785
786         this.hasOuterHandles = true;
787     },
788
789     /**
790      * Remove all drag and drop hooks for this element
791      * @method unreg
792      */
793     unreg: function() {
794         Event.un(this.id, "mousedown",
795                 this.handleMouseDown);
796         Event.un(this.id, "touchstart",
797                 this.handleMouseDown);
798         this._domRef = null;
799         this.DDM._remove(this);
800     },
801
802     destroy : function(){
803         this.unreg();
804     },
805
806     /**
807      * Returns true if this instance is locked, or the drag drop mgr is locked
808      * (meaning that all drag/drop is disabled on the page.)
809      * @method isLocked
810      * @return {boolean} true if this obj or all drag/drop is locked, else
811      * false
812      */
813     isLocked: function() {
814         return (this.DDM.isLocked() || this.locked);
815     },
816
817     /**
818      * Fired when this object is clicked
819      * @method handleMouseDown
820      * @param {Event} e
821      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
822      * @private
823      */
824     handleMouseDown: function(e, oDD){
825         Roo.log(this);
826         Roo.log(e);
827         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
828             //Roo.log('not touch/ button !=0');
829             return;
830         }
831         if (ev.browserEvent.touches && ev.browserEvent.touches.length != 1) {
832             return; // double touch..
833         }
834         
835
836         if (this.isLocked()) {
837             //Roo.log('locked');
838             return;
839         }
840
841         this.DDM.refreshCache(this.groups);
842         Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
843         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
844         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
845             //Roo.log('no outer handes or not over target');
846                 // do nothing.
847         } else {
848             Roo.log('check validator');
849             if (this.clickValidator(e)) {
850                 Roo.log('validate success');
851                 // set the initial element position
852                 this.setStartPosition();
853
854
855                 this.b4MouseDown(e);
856                 this.onMouseDown(e);
857
858                 this.DDM.handleMouseDown(e, this);
859
860                 this.DDM.stopEvent(e);
861             } else {
862
863
864             }
865         }
866     },
867
868     clickValidator: function(e) {
869         var target = e.getTarget();
870         return ( this.isValidHandleChild(target) &&
871                     (this.id == this.handleElId ||
872                         this.DDM.handleWasClicked(target, this.id)) );
873     },
874
875     /**
876      * Allows you to specify a tag name that should not start a drag operation
877      * when clicked.  This is designed to facilitate embedding links within a
878      * drag handle that do something other than start the drag.
879      * @method addInvalidHandleType
880      * @param {string} tagName the type of element to exclude
881      */
882     addInvalidHandleType: function(tagName) {
883         var type = tagName.toUpperCase();
884         this.invalidHandleTypes[type] = type;
885     },
886
887     /**
888      * Lets you to specify an element id for a child of a drag handle
889      * that should not initiate a drag
890      * @method addInvalidHandleId
891      * @param {string} id the element id of the element you wish to ignore
892      */
893     addInvalidHandleId: function(id) {
894         if (typeof id !== "string") {
895             id = Roo.id(id);
896         }
897         this.invalidHandleIds[id] = id;
898     },
899
900     /**
901      * Lets you specify a css class of elements that will not initiate a drag
902      * @method addInvalidHandleClass
903      * @param {string} cssClass the class of the elements you wish to ignore
904      */
905     addInvalidHandleClass: function(cssClass) {
906         this.invalidHandleClasses.push(cssClass);
907     },
908
909     /**
910      * Unsets an excluded tag name set by addInvalidHandleType
911      * @method removeInvalidHandleType
912      * @param {string} tagName the type of element to unexclude
913      */
914     removeInvalidHandleType: function(tagName) {
915         var type = tagName.toUpperCase();
916         // this.invalidHandleTypes[type] = null;
917         delete this.invalidHandleTypes[type];
918     },
919
920     /**
921      * Unsets an invalid handle id
922      * @method removeInvalidHandleId
923      * @param {string} id the id of the element to re-enable
924      */
925     removeInvalidHandleId: function(id) {
926         if (typeof id !== "string") {
927             id = Roo.id(id);
928         }
929         delete this.invalidHandleIds[id];
930     },
931
932     /**
933      * Unsets an invalid css class
934      * @method removeInvalidHandleClass
935      * @param {string} cssClass the class of the element(s) you wish to
936      * re-enable
937      */
938     removeInvalidHandleClass: function(cssClass) {
939         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
940             if (this.invalidHandleClasses[i] == cssClass) {
941                 delete this.invalidHandleClasses[i];
942             }
943         }
944     },
945
946     /**
947      * Checks the tag exclusion list to see if this click should be ignored
948      * @method isValidHandleChild
949      * @param {HTMLElement} node the HTMLElement to evaluate
950      * @return {boolean} true if this is a valid tag type, false if not
951      */
952     isValidHandleChild: function(node) {
953
954         var valid = true;
955         // var n = (node.nodeName == "#text") ? node.parentNode : node;
956         var nodeName;
957         try {
958             nodeName = node.nodeName.toUpperCase();
959         } catch(e) {
960             nodeName = node.nodeName;
961         }
962         valid = valid && !this.invalidHandleTypes[nodeName];
963         valid = valid && !this.invalidHandleIds[node.id];
964
965         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
966             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
967         }
968
969
970         return valid;
971
972     },
973
974     /**
975      * Create the array of horizontal tick marks if an interval was specified
976      * in setXConstraint().
977      * @method setXTicks
978      * @private
979      */
980     setXTicks: function(iStartX, iTickSize) {
981         this.xTicks = [];
982         this.xTickSize = iTickSize;
983
984         var tickMap = {};
985
986         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
987             if (!tickMap[i]) {
988                 this.xTicks[this.xTicks.length] = i;
989                 tickMap[i] = true;
990             }
991         }
992
993         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
994             if (!tickMap[i]) {
995                 this.xTicks[this.xTicks.length] = i;
996                 tickMap[i] = true;
997             }
998         }
999
1000         this.xTicks.sort(this.DDM.numericSort) ;
1001     },
1002
1003     /**
1004      * Create the array of vertical tick marks if an interval was specified in
1005      * setYConstraint().
1006      * @method setYTicks
1007      * @private
1008      */
1009     setYTicks: function(iStartY, iTickSize) {
1010         this.yTicks = [];
1011         this.yTickSize = iTickSize;
1012
1013         var tickMap = {};
1014
1015         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
1016             if (!tickMap[i]) {
1017                 this.yTicks[this.yTicks.length] = i;
1018                 tickMap[i] = true;
1019             }
1020         }
1021
1022         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
1023             if (!tickMap[i]) {
1024                 this.yTicks[this.yTicks.length] = i;
1025                 tickMap[i] = true;
1026             }
1027         }
1028
1029         this.yTicks.sort(this.DDM.numericSort) ;
1030     },
1031
1032     /**
1033      * By default, the element can be dragged any place on the screen.  Use
1034      * this method to limit the horizontal travel of the element.  Pass in
1035      * 0,0 for the parameters if you want to lock the drag to the y axis.
1036      * @method setXConstraint
1037      * @param {int} iLeft the number of pixels the element can move to the left
1038      * @param {int} iRight the number of pixels the element can move to the
1039      * right
1040      * @param {int} iTickSize optional parameter for specifying that the
1041      * element
1042      * should move iTickSize pixels at a time.
1043      */
1044     setXConstraint: function(iLeft, iRight, iTickSize) {
1045         this.leftConstraint = iLeft;
1046         this.rightConstraint = iRight;
1047
1048         this.minX = this.initPageX - iLeft;
1049         this.maxX = this.initPageX + iRight;
1050         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
1051
1052         this.constrainX = true;
1053     },
1054
1055     /**
1056      * Clears any constraints applied to this instance.  Also clears ticks
1057      * since they can't exist independent of a constraint at this time.
1058      * @method clearConstraints
1059      */
1060     clearConstraints: function() {
1061         this.constrainX = false;
1062         this.constrainY = false;
1063         this.clearTicks();
1064     },
1065
1066     /**
1067      * Clears any tick interval defined for this instance
1068      * @method clearTicks
1069      */
1070     clearTicks: function() {
1071         this.xTicks = null;
1072         this.yTicks = null;
1073         this.xTickSize = 0;
1074         this.yTickSize = 0;
1075     },
1076
1077     /**
1078      * By default, the element can be dragged any place on the screen.  Set
1079      * this to limit the vertical travel of the element.  Pass in 0,0 for the
1080      * parameters if you want to lock the drag to the x axis.
1081      * @method setYConstraint
1082      * @param {int} iUp the number of pixels the element can move up
1083      * @param {int} iDown the number of pixels the element can move down
1084      * @param {int} iTickSize optional parameter for specifying that the
1085      * element should move iTickSize pixels at a time.
1086      */
1087     setYConstraint: function(iUp, iDown, iTickSize) {
1088         this.topConstraint = iUp;
1089         this.bottomConstraint = iDown;
1090
1091         this.minY = this.initPageY - iUp;
1092         this.maxY = this.initPageY + iDown;
1093         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
1094
1095         this.constrainY = true;
1096
1097     },
1098
1099     /**
1100      * resetConstraints must be called if you manually reposition a dd element.
1101      * @method resetConstraints
1102      * @param {boolean} maintainOffset
1103      */
1104     resetConstraints: function() {
1105
1106
1107         // Maintain offsets if necessary
1108         if (this.initPageX || this.initPageX === 0) {
1109             // figure out how much this thing has moved
1110             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
1111             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
1112
1113             this.setInitPosition(dx, dy);
1114
1115         // This is the first time we have detected the element's position
1116         } else {
1117             this.setInitPosition();
1118         }
1119
1120         if (this.constrainX) {
1121             this.setXConstraint( this.leftConstraint,
1122                                  this.rightConstraint,
1123                                  this.xTickSize        );
1124         }
1125
1126         if (this.constrainY) {
1127             this.setYConstraint( this.topConstraint,
1128                                  this.bottomConstraint,
1129                                  this.yTickSize         );
1130         }
1131     },
1132
1133     /**
1134      * Normally the drag element is moved pixel by pixel, but we can specify
1135      * that it move a number of pixels at a time.  This method resolves the
1136      * location when we have it set up like this.
1137      * @method getTick
1138      * @param {int} val where we want to place the object
1139      * @param {int[]} tickArray sorted array of valid points
1140      * @return {int} the closest tick
1141      * @private
1142      */
1143     getTick: function(val, tickArray) {
1144
1145         if (!tickArray) {
1146             // If tick interval is not defined, it is effectively 1 pixel,
1147             // so we return the value passed to us.
1148             return val;
1149         } else if (tickArray[0] >= val) {
1150             // The value is lower than the first tick, so we return the first
1151             // tick.
1152             return tickArray[0];
1153         } else {
1154             for (var i=0, len=tickArray.length; i<len; ++i) {
1155                 var next = i + 1;
1156                 if (tickArray[next] && tickArray[next] >= val) {
1157                     var diff1 = val - tickArray[i];
1158                     var diff2 = tickArray[next] - val;
1159                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
1160                 }
1161             }
1162
1163             // The value is larger than the last tick, so we return the last
1164             // tick.
1165             return tickArray[tickArray.length - 1];
1166         }
1167     },
1168
1169     /**
1170      * toString method
1171      * @method toString
1172      * @return {string} string representation of the dd obj
1173      */
1174     toString: function() {
1175         return ("DragDrop " + this.id);
1176     }
1177
1178 });
1179
1180 })();
1181 /*
1182  * Based on:
1183  * Ext JS Library 1.1.1
1184  * Copyright(c) 2006-2007, Ext JS, LLC.
1185  *
1186  * Originally Released Under LGPL - original licence link has changed is not relivant.
1187  *
1188  * Fork - LGPL
1189  * <script type="text/javascript">
1190  */
1191
1192
1193 /**
1194  * The drag and drop utility provides a framework for building drag and drop
1195  * applications.  In addition to enabling drag and drop for specific elements,
1196  * the drag and drop elements are tracked by the manager class, and the
1197  * interactions between the various elements are tracked during the drag and
1198  * the implementing code is notified about these important moments.
1199  */
1200
1201 // Only load the library once.  Rewriting the manager class would orphan
1202 // existing drag and drop instances.
1203 if (!Roo.dd.DragDropMgr) {
1204
1205 /**
1206  * @class Roo.dd.DragDropMgr
1207  * DragDropMgr is a singleton that tracks the element interaction for
1208  * all DragDrop items in the window.  Generally, you will not call
1209  * this class directly, but it does have helper methods that could
1210  * be useful in your DragDrop implementations.
1211  * @singleton
1212  */
1213 Roo.dd.DragDropMgr = function() {
1214
1215     var Event = Roo.EventManager;
1216
1217     return {
1218
1219         /**
1220          * Two dimensional Array of registered DragDrop objects.  The first
1221          * dimension is the DragDrop item group, the second the DragDrop
1222          * object.
1223          * @property ids
1224          * @type {string: string}
1225          * @private
1226          * @static
1227          */
1228         ids: {},
1229
1230         /**
1231          * Array of element ids defined as drag handles.  Used to determine
1232          * if the element that generated the mousedown event is actually the
1233          * handle and not the html element itself.
1234          * @property handleIds
1235          * @type {string: string}
1236          * @private
1237          * @static
1238          */
1239         handleIds: {},
1240
1241         /**
1242          * the DragDrop object that is currently being dragged
1243          * @property dragCurrent
1244          * @type DragDrop
1245          * @private
1246          * @static
1247          **/
1248         dragCurrent: null,
1249
1250         /**
1251          * the DragDrop object(s) that are being hovered over
1252          * @property dragOvers
1253          * @type Array
1254          * @private
1255          * @static
1256          */
1257         dragOvers: {},
1258
1259         /**
1260          * the X distance between the cursor and the object being dragged
1261          * @property deltaX
1262          * @type int
1263          * @private
1264          * @static
1265          */
1266         deltaX: 0,
1267
1268         /**
1269          * the Y distance between the cursor and the object being dragged
1270          * @property deltaY
1271          * @type int
1272          * @private
1273          * @static
1274          */
1275         deltaY: 0,
1276
1277         /**
1278          * Flag to determine if we should prevent the default behavior of the
1279          * events we define. By default this is true, but this can be set to
1280          * false if you need the default behavior (not recommended)
1281          * @property preventDefault
1282          * @type boolean
1283          * @static
1284          */
1285         preventDefault: true,
1286
1287         /**
1288          * Flag to determine if we should stop the propagation of the events
1289          * we generate. This is true by default but you may want to set it to
1290          * false if the html element contains other features that require the
1291          * mouse click.
1292          * @property stopPropagation
1293          * @type boolean
1294          * @static
1295          */
1296         stopPropagation: true,
1297
1298         /**
1299          * Internal flag that is set to true when drag and drop has been
1300          * intialized
1301          * @property initialized
1302          * @private
1303          * @static
1304          */
1305         initalized: false,
1306
1307         /**
1308          * All drag and drop can be disabled.
1309          * @property locked
1310          * @private
1311          * @static
1312          */
1313         locked: false,
1314
1315         /**
1316          * Called the first time an element is registered.
1317          * @method init
1318          * @private
1319          * @static
1320          */
1321         init: function() {
1322             this.initialized = true;
1323         },
1324
1325         /**
1326          * In point mode, drag and drop interaction is defined by the
1327          * location of the cursor during the drag/drop
1328          * @property POINT
1329          * @type int
1330          * @static
1331          */
1332         POINT: 0,
1333
1334         /**
1335          * In intersect mode, drag and drop interactio nis defined by the
1336          * overlap of two or more drag and drop objects.
1337          * @property INTERSECT
1338          * @type int
1339          * @static
1340          */
1341         INTERSECT: 1,
1342
1343         /**
1344          * The current drag and drop mode.  Default: POINT
1345          * @property mode
1346          * @type int
1347          * @static
1348          */
1349         mode: 0,
1350
1351         /**
1352          * Runs method on all drag and drop objects
1353          * @method _execOnAll
1354          * @private
1355          * @static
1356          */
1357         _execOnAll: function(sMethod, args) {
1358             for (var i in this.ids) {
1359                 for (var j in this.ids[i]) {
1360                     var oDD = this.ids[i][j];
1361                     if (! this.isTypeOfDD(oDD)) {
1362                         continue;
1363                     }
1364                     oDD[sMethod].apply(oDD, args);
1365                 }
1366             }
1367         },
1368
1369         /**
1370          * Drag and drop initialization.  Sets up the global event handlers
1371          * @method _onLoad
1372          * @private
1373          * @static
1374          */
1375         _onLoad: function() {
1376
1377             this.init();
1378
1379             if (!Roo.isTouch) {
1380                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
1381                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
1382             }
1383             Event.on(document, "touchend",   this.handleMouseUp, this, true);
1384             Event.on(document, "touchmove", this.handleMouseMove, this, true);
1385             
1386             Event.on(window,   "unload",    this._onUnload, this, true);
1387             Event.on(window,   "resize",    this._onResize, this, true);
1388             // Event.on(window,   "mouseout",    this._test);
1389
1390         },
1391
1392         /**
1393          * Reset constraints on all drag and drop objs
1394          * @method _onResize
1395          * @private
1396          * @static
1397          */
1398         _onResize: function(e) {
1399             this._execOnAll("resetConstraints", []);
1400         },
1401
1402         /**
1403          * Lock all drag and drop functionality
1404          * @method lock
1405          * @static
1406          */
1407         lock: function() { this.locked = true; },
1408
1409         /**
1410          * Unlock all drag and drop functionality
1411          * @method unlock
1412          * @static
1413          */
1414         unlock: function() { this.locked = false; },
1415
1416         /**
1417          * Is drag and drop locked?
1418          * @method isLocked
1419          * @return {boolean} True if drag and drop is locked, false otherwise.
1420          * @static
1421          */
1422         isLocked: function() { return this.locked; },
1423
1424         /**
1425          * Location cache that is set for all drag drop objects when a drag is
1426          * initiated, cleared when the drag is finished.
1427          * @property locationCache
1428          * @private
1429          * @static
1430          */
1431         locationCache: {},
1432
1433         /**
1434          * Set useCache to false if you want to force object the lookup of each
1435          * drag and drop linked element constantly during a drag.
1436          * @property useCache
1437          * @type boolean
1438          * @static
1439          */
1440         useCache: true,
1441
1442         /**
1443          * The number of pixels that the mouse needs to move after the
1444          * mousedown before the drag is initiated.  Default=3;
1445          * @property clickPixelThresh
1446          * @type int
1447          * @static
1448          */
1449         clickPixelThresh: 3,
1450
1451         /**
1452          * The number of milliseconds after the mousedown event to initiate the
1453          * drag if we don't get a mouseup event. Default=1000
1454          * @property clickTimeThresh
1455          * @type int
1456          * @static
1457          */
1458         clickTimeThresh: 350,
1459
1460         /**
1461          * Flag that indicates that either the drag pixel threshold or the
1462          * mousdown time threshold has been met
1463          * @property dragThreshMet
1464          * @type boolean
1465          * @private
1466          * @static
1467          */
1468         dragThreshMet: false,
1469
1470         /**
1471          * Timeout used for the click time threshold
1472          * @property clickTimeout
1473          * @type Object
1474          * @private
1475          * @static
1476          */
1477         clickTimeout: null,
1478
1479         /**
1480          * The X position of the mousedown event stored for later use when a
1481          * drag threshold is met.
1482          * @property startX
1483          * @type int
1484          * @private
1485          * @static
1486          */
1487         startX: 0,
1488
1489         /**
1490          * The Y position of the mousedown event stored for later use when a
1491          * drag threshold is met.
1492          * @property startY
1493          * @type int
1494          * @private
1495          * @static
1496          */
1497         startY: 0,
1498
1499         /**
1500          * Each DragDrop instance must be registered with the DragDropMgr.
1501          * This is executed in DragDrop.init()
1502          * @method regDragDrop
1503          * @param {DragDrop} oDD the DragDrop object to register
1504          * @param {String} sGroup the name of the group this element belongs to
1505          * @static
1506          */
1507         regDragDrop: function(oDD, sGroup) {
1508             if (!this.initialized) { this.init(); }
1509
1510             if (!this.ids[sGroup]) {
1511                 this.ids[sGroup] = {};
1512             }
1513             this.ids[sGroup][oDD.id] = oDD;
1514         },
1515
1516         /**
1517          * Removes the supplied dd instance from the supplied group. Executed
1518          * by DragDrop.removeFromGroup, so don't call this function directly.
1519          * @method removeDDFromGroup
1520          * @private
1521          * @static
1522          */
1523         removeDDFromGroup: function(oDD, sGroup) {
1524             if (!this.ids[sGroup]) {
1525                 this.ids[sGroup] = {};
1526             }
1527
1528             var obj = this.ids[sGroup];
1529             if (obj && obj[oDD.id]) {
1530                 delete obj[oDD.id];
1531             }
1532         },
1533
1534         /**
1535          * Unregisters a drag and drop item.  This is executed in
1536          * DragDrop.unreg, use that method instead of calling this directly.
1537          * @method _remove
1538          * @private
1539          * @static
1540          */
1541         _remove: function(oDD) {
1542             for (var g in oDD.groups) {
1543                 if (g && this.ids[g][oDD.id]) {
1544                     delete this.ids[g][oDD.id];
1545                 }
1546             }
1547             delete this.handleIds[oDD.id];
1548         },
1549
1550         /**
1551          * Each DragDrop handle element must be registered.  This is done
1552          * automatically when executing DragDrop.setHandleElId()
1553          * @method regHandle
1554          * @param {String} sDDId the DragDrop id this element is a handle for
1555          * @param {String} sHandleId the id of the element that is the drag
1556          * handle
1557          * @static
1558          */
1559         regHandle: function(sDDId, sHandleId) {
1560             if (!this.handleIds[sDDId]) {
1561                 this.handleIds[sDDId] = {};
1562             }
1563             this.handleIds[sDDId][sHandleId] = sHandleId;
1564         },
1565
1566         /**
1567          * Utility function to determine if a given element has been
1568          * registered as a drag drop item.
1569          * @method isDragDrop
1570          * @param {String} id the element id to check
1571          * @return {boolean} true if this element is a DragDrop item,
1572          * false otherwise
1573          * @static
1574          */
1575         isDragDrop: function(id) {
1576             return ( this.getDDById(id) ) ? true : false;
1577         },
1578
1579         /**
1580          * Returns the drag and drop instances that are in all groups the
1581          * passed in instance belongs to.
1582          * @method getRelated
1583          * @param {DragDrop} p_oDD the obj to get related data for
1584          * @param {boolean} bTargetsOnly if true, only return targetable objs
1585          * @return {DragDrop[]} the related instances
1586          * @static
1587          */
1588         getRelated: function(p_oDD, bTargetsOnly) {
1589             var oDDs = [];
1590             for (var i in p_oDD.groups) {
1591                 for (j in this.ids[i]) {
1592                     var dd = this.ids[i][j];
1593                     if (! this.isTypeOfDD(dd)) {
1594                         continue;
1595                     }
1596                     if (!bTargetsOnly || dd.isTarget) {
1597                         oDDs[oDDs.length] = dd;
1598                     }
1599                 }
1600             }
1601
1602             return oDDs;
1603         },
1604
1605         /**
1606          * Returns true if the specified dd target is a legal target for
1607          * the specifice drag obj
1608          * @method isLegalTarget
1609          * @param {DragDrop} the drag obj
1610          * @param {DragDrop} the target
1611          * @return {boolean} true if the target is a legal target for the
1612          * dd obj
1613          * @static
1614          */
1615         isLegalTarget: function (oDD, oTargetDD) {
1616             var targets = this.getRelated(oDD, true);
1617             for (var i=0, len=targets.length;i<len;++i) {
1618                 if (targets[i].id == oTargetDD.id) {
1619                     return true;
1620                 }
1621             }
1622
1623             return false;
1624         },
1625
1626         /**
1627          * My goal is to be able to transparently determine if an object is
1628          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
1629          * returns "object", oDD.constructor.toString() always returns
1630          * "DragDrop" and not the name of the subclass.  So for now it just
1631          * evaluates a well-known variable in DragDrop.
1632          * @method isTypeOfDD
1633          * @param {Object} the object to evaluate
1634          * @return {boolean} true if typeof oDD = DragDrop
1635          * @static
1636          */
1637         isTypeOfDD: function (oDD) {
1638             return (oDD && oDD.__ygDragDrop);
1639         },
1640
1641         /**
1642          * Utility function to determine if a given element has been
1643          * registered as a drag drop handle for the given Drag Drop object.
1644          * @method isHandle
1645          * @param {String} id the element id to check
1646          * @return {boolean} true if this element is a DragDrop handle, false
1647          * otherwise
1648          * @static
1649          */
1650         isHandle: function(sDDId, sHandleId) {
1651             return ( this.handleIds[sDDId] &&
1652                             this.handleIds[sDDId][sHandleId] );
1653         },
1654
1655         /**
1656          * Returns the DragDrop instance for a given id
1657          * @method getDDById
1658          * @param {String} id the id of the DragDrop object
1659          * @return {DragDrop} the drag drop object, null if it is not found
1660          * @static
1661          */
1662         getDDById: function(id) {
1663             for (var i in this.ids) {
1664                 if (this.ids[i][id]) {
1665                     return this.ids[i][id];
1666                 }
1667             }
1668             return null;
1669         },
1670
1671         /**
1672          * Fired after a registered DragDrop object gets the mousedown event.
1673          * Sets up the events required to track the object being dragged
1674          * @method handleMouseDown
1675          * @param {Event} e the event
1676          * @param oDD the DragDrop object being dragged
1677          * @private
1678          * @static
1679          */
1680         handleMouseDown: function(e, oDD) {
1681             if(Roo.QuickTips){
1682                 Roo.QuickTips.disable();
1683             }
1684             this.currentTarget = e.getTarget();
1685
1686             this.dragCurrent = oDD;
1687
1688             var el = oDD.getEl();
1689
1690             // track start position
1691             this.startX = e.getPageX();
1692             this.startY = e.getPageY();
1693
1694             this.deltaX = this.startX - el.offsetLeft;
1695             this.deltaY = this.startY - el.offsetTop;
1696
1697             this.dragThreshMet = false;
1698
1699             this.clickTimeout = setTimeout(
1700                     function() {
1701                         var DDM = Roo.dd.DDM;
1702                         DDM.startDrag(DDM.startX, DDM.startY);
1703                     },
1704                     this.clickTimeThresh );
1705         },
1706
1707         /**
1708          * Fired when either the drag pixel threshol or the mousedown hold
1709          * time threshold has been met.
1710          * @method startDrag
1711          * @param x {int} the X position of the original mousedown
1712          * @param y {int} the Y position of the original mousedown
1713          * @static
1714          */
1715         startDrag: function(x, y) {
1716             clearTimeout(this.clickTimeout);
1717             if (this.dragCurrent) {
1718                 this.dragCurrent.b4StartDrag(x, y);
1719                 this.dragCurrent.startDrag(x, y);
1720             }
1721             this.dragThreshMet = true;
1722         },
1723
1724         /**
1725          * Internal function to handle the mouseup event.  Will be invoked
1726          * from the context of the document.
1727          * @method handleMouseUp
1728          * @param {Event} e the event
1729          * @private
1730          * @static
1731          */
1732         handleMouseUp: function(e) {
1733
1734             if(Roo.QuickTips){
1735                 Roo.QuickTips.enable();
1736             }
1737             if (! this.dragCurrent) {
1738                 return;
1739             }
1740
1741             clearTimeout(this.clickTimeout);
1742
1743             if (this.dragThreshMet) {
1744                 this.fireEvents(e, true);
1745             } else {
1746             }
1747
1748             this.stopDrag(e);
1749
1750             this.stopEvent(e);
1751         },
1752
1753         /**
1754          * Utility to stop event propagation and event default, if these
1755          * features are turned on.
1756          * @method stopEvent
1757          * @param {Event} e the event as returned by this.getEvent()
1758          * @static
1759          */
1760         stopEvent: function(e){
1761             if(this.stopPropagation) {
1762                 e.stopPropagation();
1763             }
1764
1765             if (this.preventDefault) {
1766                 e.preventDefault();
1767             }
1768         },
1769
1770         /**
1771          * Internal function to clean up event handlers after the drag
1772          * operation is complete
1773          * @method stopDrag
1774          * @param {Event} e the event
1775          * @private
1776          * @static
1777          */
1778         stopDrag: function(e) {
1779             // Fire the drag end event for the item that was dragged
1780             if (this.dragCurrent) {
1781                 if (this.dragThreshMet) {
1782                     this.dragCurrent.b4EndDrag(e);
1783                     this.dragCurrent.endDrag(e);
1784                 }
1785
1786                 this.dragCurrent.onMouseUp(e);
1787             }
1788
1789             this.dragCurrent = null;
1790             this.dragOvers = {};
1791         },
1792
1793         /**
1794          * Internal function to handle the mousemove event.  Will be invoked
1795          * from the context of the html element.
1796          *
1797          * @TODO figure out what we can do about mouse events lost when the
1798          * user drags objects beyond the window boundary.  Currently we can
1799          * detect this in internet explorer by verifying that the mouse is
1800          * down during the mousemove event.  Firefox doesn't give us the
1801          * button state on the mousemove event.
1802          * @method handleMouseMove
1803          * @param {Event} e the event
1804          * @private
1805          * @static
1806          */
1807         handleMouseMove: function(e) {
1808             if (! this.dragCurrent) {
1809                 return true;
1810             }
1811
1812             // var button = e.which || e.button;
1813
1814             // check for IE mouseup outside of page boundary
1815             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
1816                 this.stopEvent(e);
1817                 return this.handleMouseUp(e);
1818             }
1819
1820             if (!this.dragThreshMet) {
1821                 var diffX = Math.abs(this.startX - e.getPageX());
1822                 var diffY = Math.abs(this.startY - e.getPageY());
1823                 if (diffX > this.clickPixelThresh ||
1824                             diffY > this.clickPixelThresh) {
1825                     this.startDrag(this.startX, this.startY);
1826                 }
1827             }
1828
1829             if (this.dragThreshMet) {
1830                 this.dragCurrent.b4Drag(e);
1831                 this.dragCurrent.onDrag(e);
1832                 if(!this.dragCurrent.moveOnly){
1833                     this.fireEvents(e, false);
1834                 }
1835             }
1836
1837             this.stopEvent(e);
1838
1839             return true;
1840         },
1841
1842         /**
1843          * Iterates over all of the DragDrop elements to find ones we are
1844          * hovering over or dropping on
1845          * @method fireEvents
1846          * @param {Event} e the event
1847          * @param {boolean} isDrop is this a drop op or a mouseover op?
1848          * @private
1849          * @static
1850          */
1851         fireEvents: function(e, isDrop) {
1852             var dc = this.dragCurrent;
1853
1854             // If the user did the mouse up outside of the window, we could
1855             // get here even though we have ended the drag.
1856             if (!dc || dc.isLocked()) {
1857                 return;
1858             }
1859
1860             var pt = e.getPoint();
1861
1862             // cache the previous dragOver array
1863             var oldOvers = [];
1864
1865             var outEvts   = [];
1866             var overEvts  = [];
1867             var dropEvts  = [];
1868             var enterEvts = [];
1869
1870             // Check to see if the object(s) we were hovering over is no longer
1871             // being hovered over so we can fire the onDragOut event
1872             for (var i in this.dragOvers) {
1873
1874                 var ddo = this.dragOvers[i];
1875
1876                 if (! this.isTypeOfDD(ddo)) {
1877                     continue;
1878                 }
1879
1880                 if (! this.isOverTarget(pt, ddo, this.mode)) {
1881                     outEvts.push( ddo );
1882                 }
1883
1884                 oldOvers[i] = true;
1885                 delete this.dragOvers[i];
1886             }
1887
1888             for (var sGroup in dc.groups) {
1889
1890                 if ("string" != typeof sGroup) {
1891                     continue;
1892                 }
1893
1894                 for (i in this.ids[sGroup]) {
1895                     var oDD = this.ids[sGroup][i];
1896                     if (! this.isTypeOfDD(oDD)) {
1897                         continue;
1898                     }
1899
1900                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
1901                         if (this.isOverTarget(pt, oDD, this.mode)) {
1902                             // look for drop interactions
1903                             if (isDrop) {
1904                                 dropEvts.push( oDD );
1905                             // look for drag enter and drag over interactions
1906                             } else {
1907
1908                                 // initial drag over: dragEnter fires
1909                                 if (!oldOvers[oDD.id]) {
1910                                     enterEvts.push( oDD );
1911                                 // subsequent drag overs: dragOver fires
1912                                 } else {
1913                                     overEvts.push( oDD );
1914                                 }
1915
1916                                 this.dragOvers[oDD.id] = oDD;
1917                             }
1918                         }
1919                     }
1920                 }
1921             }
1922
1923             if (this.mode) {
1924                 if (outEvts.length) {
1925                     dc.b4DragOut(e, outEvts);
1926                     dc.onDragOut(e, outEvts);
1927                 }
1928
1929                 if (enterEvts.length) {
1930                     dc.onDragEnter(e, enterEvts);
1931                 }
1932
1933                 if (overEvts.length) {
1934                     dc.b4DragOver(e, overEvts);
1935                     dc.onDragOver(e, overEvts);
1936                 }
1937
1938                 if (dropEvts.length) {
1939                     dc.b4DragDrop(e, dropEvts);
1940                     dc.onDragDrop(e, dropEvts);
1941                 }
1942
1943             } else {
1944                 // fire dragout events
1945                 var len = 0;
1946                 for (i=0, len=outEvts.length; i<len; ++i) {
1947                     dc.b4DragOut(e, outEvts[i].id);
1948                     dc.onDragOut(e, outEvts[i].id);
1949                 }
1950
1951                 // fire enter events
1952                 for (i=0,len=enterEvts.length; i<len; ++i) {
1953                     // dc.b4DragEnter(e, oDD.id);
1954                     dc.onDragEnter(e, enterEvts[i].id);
1955                 }
1956
1957                 // fire over events
1958                 for (i=0,len=overEvts.length; i<len; ++i) {
1959                     dc.b4DragOver(e, overEvts[i].id);
1960                     dc.onDragOver(e, overEvts[i].id);
1961                 }
1962
1963                 // fire drop events
1964                 for (i=0, len=dropEvts.length; i<len; ++i) {
1965                     dc.b4DragDrop(e, dropEvts[i].id);
1966                     dc.onDragDrop(e, dropEvts[i].id);
1967                 }
1968
1969             }
1970
1971             // notify about a drop that did not find a target
1972             if (isDrop && !dropEvts.length) {
1973                 dc.onInvalidDrop(e);
1974             }
1975
1976         },
1977
1978         /**
1979          * Helper function for getting the best match from the list of drag
1980          * and drop objects returned by the drag and drop events when we are
1981          * in INTERSECT mode.  It returns either the first object that the
1982          * cursor is over, or the object that has the greatest overlap with
1983          * the dragged element.
1984          * @method getBestMatch
1985          * @param  {DragDrop[]} dds The array of drag and drop objects
1986          * targeted
1987          * @return {DragDrop}       The best single match
1988          * @static
1989          */
1990         getBestMatch: function(dds) {
1991             var winner = null;
1992             // Return null if the input is not what we expect
1993             //if (!dds || !dds.length || dds.length == 0) {
1994                // winner = null;
1995             // If there is only one item, it wins
1996             //} else if (dds.length == 1) {
1997
1998             var len = dds.length;
1999
2000             if (len == 1) {
2001                 winner = dds[0];
2002             } else {
2003                 // Loop through the targeted items
2004                 for (var i=0; i<len; ++i) {
2005                     var dd = dds[i];
2006                     // If the cursor is over the object, it wins.  If the
2007                     // cursor is over multiple matches, the first one we come
2008                     // to wins.
2009                     if (dd.cursorIsOver) {
2010                         winner = dd;
2011                         break;
2012                     // Otherwise the object with the most overlap wins
2013                     } else {
2014                         if (!winner ||
2015                             winner.overlap.getArea() < dd.overlap.getArea()) {
2016                             winner = dd;
2017                         }
2018                     }
2019                 }
2020             }
2021
2022             return winner;
2023         },
2024
2025         /**
2026          * Refreshes the cache of the top-left and bottom-right points of the
2027          * drag and drop objects in the specified group(s).  This is in the
2028          * format that is stored in the drag and drop instance, so typical
2029          * usage is:
2030          * <code>
2031          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
2032          * </code>
2033          * Alternatively:
2034          * <code>
2035          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
2036          * </code>
2037          * @TODO this really should be an indexed array.  Alternatively this
2038          * method could accept both.
2039          * @method refreshCache
2040          * @param {Object} groups an associative array of groups to refresh
2041          * @static
2042          */
2043         refreshCache: function(groups) {
2044             for (var sGroup in groups) {
2045                 if ("string" != typeof sGroup) {
2046                     continue;
2047                 }
2048                 for (var i in this.ids[sGroup]) {
2049                     var oDD = this.ids[sGroup][i];
2050
2051                     if (this.isTypeOfDD(oDD)) {
2052                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
2053                         var loc = this.getLocation(oDD);
2054                         if (loc) {
2055                             this.locationCache[oDD.id] = loc;
2056                         } else {
2057                             delete this.locationCache[oDD.id];
2058                             // this will unregister the drag and drop object if
2059                             // the element is not in a usable state
2060                             // oDD.unreg();
2061                         }
2062                     }
2063                 }
2064             }
2065         },
2066
2067         /**
2068          * This checks to make sure an element exists and is in the DOM.  The
2069          * main purpose is to handle cases where innerHTML is used to remove
2070          * drag and drop objects from the DOM.  IE provides an 'unspecified
2071          * error' when trying to access the offsetParent of such an element
2072          * @method verifyEl
2073          * @param {HTMLElement} el the element to check
2074          * @return {boolean} true if the element looks usable
2075          * @static
2076          */
2077         verifyEl: function(el) {
2078             if (el) {
2079                 var parent;
2080                 if(Roo.isIE){
2081                     try{
2082                         parent = el.offsetParent;
2083                     }catch(e){}
2084                 }else{
2085                     parent = el.offsetParent;
2086                 }
2087                 if (parent) {
2088                     return true;
2089                 }
2090             }
2091
2092             return false;
2093         },
2094
2095         /**
2096          * Returns a Region object containing the drag and drop element's position
2097          * and size, including the padding configured for it
2098          * @method getLocation
2099          * @param {DragDrop} oDD the drag and drop object to get the
2100          *                       location for
2101          * @return {Roo.lib.Region} a Region object representing the total area
2102          *                             the element occupies, including any padding
2103          *                             the instance is configured for.
2104          * @static
2105          */
2106         getLocation: function(oDD) {
2107             if (! this.isTypeOfDD(oDD)) {
2108                 return null;
2109             }
2110
2111             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
2112
2113             try {
2114                 pos= Roo.lib.Dom.getXY(el);
2115             } catch (e) { }
2116
2117             if (!pos) {
2118                 return null;
2119             }
2120
2121             x1 = pos[0];
2122             x2 = x1 + el.offsetWidth;
2123             y1 = pos[1];
2124             y2 = y1 + el.offsetHeight;
2125
2126             t = y1 - oDD.padding[0];
2127             r = x2 + oDD.padding[1];
2128             b = y2 + oDD.padding[2];
2129             l = x1 - oDD.padding[3];
2130
2131             return new Roo.lib.Region( t, r, b, l );
2132         },
2133
2134         /**
2135          * Checks the cursor location to see if it over the target
2136          * @method isOverTarget
2137          * @param {Roo.lib.Point} pt The point to evaluate
2138          * @param {DragDrop} oTarget the DragDrop object we are inspecting
2139          * @return {boolean} true if the mouse is over the target
2140          * @private
2141          * @static
2142          */
2143         isOverTarget: function(pt, oTarget, intersect) {
2144             // use cache if available
2145             var loc = this.locationCache[oTarget.id];
2146             if (!loc || !this.useCache) {
2147                 loc = this.getLocation(oTarget);
2148                 this.locationCache[oTarget.id] = loc;
2149
2150             }
2151
2152             if (!loc) {
2153                 return false;
2154             }
2155
2156             oTarget.cursorIsOver = loc.contains( pt );
2157
2158             // DragDrop is using this as a sanity check for the initial mousedown
2159             // in this case we are done.  In POINT mode, if the drag obj has no
2160             // contraints, we are also done. Otherwise we need to evaluate the
2161             // location of the target as related to the actual location of the
2162             // dragged element.
2163             var dc = this.dragCurrent;
2164             if (!dc || !dc.getTargetCoord ||
2165                     (!intersect && !dc.constrainX && !dc.constrainY)) {
2166                 return oTarget.cursorIsOver;
2167             }
2168
2169             oTarget.overlap = null;
2170
2171             // Get the current location of the drag element, this is the
2172             // location of the mouse event less the delta that represents
2173             // where the original mousedown happened on the element.  We
2174             // need to consider constraints and ticks as well.
2175             var pos = dc.getTargetCoord(pt.x, pt.y);
2176
2177             var el = dc.getDragEl();
2178             var curRegion = new Roo.lib.Region( pos.y,
2179                                                    pos.x + el.offsetWidth,
2180                                                    pos.y + el.offsetHeight,
2181                                                    pos.x );
2182
2183             var overlap = curRegion.intersect(loc);
2184
2185             if (overlap) {
2186                 oTarget.overlap = overlap;
2187                 return (intersect) ? true : oTarget.cursorIsOver;
2188             } else {
2189                 return false;
2190             }
2191         },
2192
2193         /**
2194          * unload event handler
2195          * @method _onUnload
2196          * @private
2197          * @static
2198          */
2199         _onUnload: function(e, me) {
2200             Roo.dd.DragDropMgr.unregAll();
2201         },
2202
2203         /**
2204          * Cleans up the drag and drop events and objects.
2205          * @method unregAll
2206          * @private
2207          * @static
2208          */
2209         unregAll: function() {
2210
2211             if (this.dragCurrent) {
2212                 this.stopDrag();
2213                 this.dragCurrent = null;
2214             }
2215
2216             this._execOnAll("unreg", []);
2217
2218             for (i in this.elementCache) {
2219                 delete this.elementCache[i];
2220             }
2221
2222             this.elementCache = {};
2223             this.ids = {};
2224         },
2225
2226         /**
2227          * A cache of DOM elements
2228          * @property elementCache
2229          * @private
2230          * @static
2231          */
2232         elementCache: {},
2233
2234         /**
2235          * Get the wrapper for the DOM element specified
2236          * @method getElWrapper
2237          * @param {String} id the id of the element to get
2238          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
2239          * @private
2240          * @deprecated This wrapper isn't that useful
2241          * @static
2242          */
2243         getElWrapper: function(id) {
2244             var oWrapper = this.elementCache[id];
2245             if (!oWrapper || !oWrapper.el) {
2246                 oWrapper = this.elementCache[id] =
2247                     new this.ElementWrapper(Roo.getDom(id));
2248             }
2249             return oWrapper;
2250         },
2251
2252         /**
2253          * Returns the actual DOM element
2254          * @method getElement
2255          * @param {String} id the id of the elment to get
2256          * @return {Object} The element
2257          * @deprecated use Roo.getDom instead
2258          * @static
2259          */
2260         getElement: function(id) {
2261             return Roo.getDom(id);
2262         },
2263
2264         /**
2265          * Returns the style property for the DOM element (i.e.,
2266          * document.getElById(id).style)
2267          * @method getCss
2268          * @param {String} id the id of the elment to get
2269          * @return {Object} The style property of the element
2270          * @deprecated use Roo.getDom instead
2271          * @static
2272          */
2273         getCss: function(id) {
2274             var el = Roo.getDom(id);
2275             return (el) ? el.style : null;
2276         },
2277
2278         /**
2279          * Inner class for cached elements
2280          * @class DragDropMgr.ElementWrapper
2281          * @for DragDropMgr
2282          * @private
2283          * @deprecated
2284          */
2285         ElementWrapper: function(el) {
2286                 /**
2287                  * The element
2288                  * @property el
2289                  */
2290                 this.el = el || null;
2291                 /**
2292                  * The element id
2293                  * @property id
2294                  */
2295                 this.id = this.el && el.id;
2296                 /**
2297                  * A reference to the style property
2298                  * @property css
2299                  */
2300                 this.css = this.el && el.style;
2301             },
2302
2303         /**
2304          * Returns the X position of an html element
2305          * @method getPosX
2306          * @param el the element for which to get the position
2307          * @return {int} the X coordinate
2308          * @for DragDropMgr
2309          * @deprecated use Roo.lib.Dom.getX instead
2310          * @static
2311          */
2312         getPosX: function(el) {
2313             return Roo.lib.Dom.getX(el);
2314         },
2315
2316         /**
2317          * Returns the Y position of an html element
2318          * @method getPosY
2319          * @param el the element for which to get the position
2320          * @return {int} the Y coordinate
2321          * @deprecated use Roo.lib.Dom.getY instead
2322          * @static
2323          */
2324         getPosY: function(el) {
2325             return Roo.lib.Dom.getY(el);
2326         },
2327
2328         /**
2329          * Swap two nodes.  In IE, we use the native method, for others we
2330          * emulate the IE behavior
2331          * @method swapNode
2332          * @param n1 the first node to swap
2333          * @param n2 the other node to swap
2334          * @static
2335          */
2336         swapNode: function(n1, n2) {
2337             if (n1.swapNode) {
2338                 n1.swapNode(n2);
2339             } else {
2340                 var p = n2.parentNode;
2341                 var s = n2.nextSibling;
2342
2343                 if (s == n1) {
2344                     p.insertBefore(n1, n2);
2345                 } else if (n2 == n1.nextSibling) {
2346                     p.insertBefore(n2, n1);
2347                 } else {
2348                     n1.parentNode.replaceChild(n2, n1);
2349                     p.insertBefore(n1, s);
2350                 }
2351             }
2352         },
2353
2354         /**
2355          * Returns the current scroll position
2356          * @method getScroll
2357          * @private
2358          * @static
2359          */
2360         getScroll: function () {
2361             var t, l, dde=document.documentElement, db=document.body;
2362             if (dde && (dde.scrollTop || dde.scrollLeft)) {
2363                 t = dde.scrollTop;
2364                 l = dde.scrollLeft;
2365             } else if (db) {
2366                 t = db.scrollTop;
2367                 l = db.scrollLeft;
2368             } else {
2369
2370             }
2371             return { top: t, left: l };
2372         },
2373
2374         /**
2375          * Returns the specified element style property
2376          * @method getStyle
2377          * @param {HTMLElement} el          the element
2378          * @param {string}      styleProp   the style property
2379          * @return {string} The value of the style property
2380          * @deprecated use Roo.lib.Dom.getStyle
2381          * @static
2382          */
2383         getStyle: function(el, styleProp) {
2384             return Roo.fly(el).getStyle(styleProp);
2385         },
2386
2387         /**
2388          * Gets the scrollTop
2389          * @method getScrollTop
2390          * @return {int} the document's scrollTop
2391          * @static
2392          */
2393         getScrollTop: function () { return this.getScroll().top; },
2394
2395         /**
2396          * Gets the scrollLeft
2397          * @method getScrollLeft
2398          * @return {int} the document's scrollTop
2399          * @static
2400          */
2401         getScrollLeft: function () { return this.getScroll().left; },
2402
2403         /**
2404          * Sets the x/y position of an element to the location of the
2405          * target element.
2406          * @method moveToEl
2407          * @param {HTMLElement} moveEl      The element to move
2408          * @param {HTMLElement} targetEl    The position reference element
2409          * @static
2410          */
2411         moveToEl: function (moveEl, targetEl) {
2412             var aCoord = Roo.lib.Dom.getXY(targetEl);
2413             Roo.lib.Dom.setXY(moveEl, aCoord);
2414         },
2415
2416         /**
2417          * Numeric array sort function
2418          * @method numericSort
2419          * @static
2420          */
2421         numericSort: function(a, b) { return (a - b); },
2422
2423         /**
2424          * Internal counter
2425          * @property _timeoutCount
2426          * @private
2427          * @static
2428          */
2429         _timeoutCount: 0,
2430
2431         /**
2432          * Trying to make the load order less important.  Without this we get
2433          * an error if this file is loaded before the Event Utility.
2434          * @method _addListeners
2435          * @private
2436          * @static
2437          */
2438         _addListeners: function() {
2439             var DDM = Roo.dd.DDM;
2440             if ( Roo.lib.Event && document ) {
2441                 DDM._onLoad();
2442             } else {
2443                 if (DDM._timeoutCount > 2000) {
2444                 } else {
2445                     setTimeout(DDM._addListeners, 10);
2446                     if (document && document.body) {
2447                         DDM._timeoutCount += 1;
2448                     }
2449                 }
2450             }
2451         },
2452
2453         /**
2454          * Recursively searches the immediate parent and all child nodes for
2455          * the handle element in order to determine wheter or not it was
2456          * clicked.
2457          * @method handleWasClicked
2458          * @param node the html element to inspect
2459          * @static
2460          */
2461         handleWasClicked: function(node, id) {
2462             if (this.isHandle(id, node.id)) {
2463                 return true;
2464             } else {
2465                 // check to see if this is a text node child of the one we want
2466                 var p = node.parentNode;
2467
2468                 while (p) {
2469                     if (this.isHandle(id, p.id)) {
2470                         return true;
2471                     } else {
2472                         p = p.parentNode;
2473                     }
2474                 }
2475             }
2476
2477             return false;
2478         }
2479
2480     };
2481
2482 }();
2483
2484 // shorter alias, save a few bytes
2485 Roo.dd.DDM = Roo.dd.DragDropMgr;
2486 Roo.dd.DDM._addListeners();
2487
2488 }/*
2489  * Based on:
2490  * Ext JS Library 1.1.1
2491  * Copyright(c) 2006-2007, Ext JS, LLC.
2492  *
2493  * Originally Released Under LGPL - original licence link has changed is not relivant.
2494  *
2495  * Fork - LGPL
2496  * <script type="text/javascript">
2497  */
2498
2499 /**
2500  * @class Roo.dd.DD
2501  * A DragDrop implementation where the linked element follows the
2502  * mouse cursor during a drag.
2503  * @extends Roo.dd.DragDrop
2504  * @constructor
2505  * @param {String} id the id of the linked element
2506  * @param {String} sGroup the group of related DragDrop items
2507  * @param {object} config an object containing configurable attributes
2508  *                Valid properties for DD:
2509  *                    scroll
2510  */
2511 Roo.dd.DD = function(id, sGroup, config) {
2512     if (id) {
2513         this.init(id, sGroup, config);
2514     }
2515 };
2516
2517 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
2518
2519     /**
2520      * When set to true, the utility automatically tries to scroll the browser
2521      * window wehn a drag and drop element is dragged near the viewport boundary.
2522      * Defaults to true.
2523      * @property scroll
2524      * @type boolean
2525      */
2526     scroll: true,
2527
2528     /**
2529      * Sets the pointer offset to the distance between the linked element's top
2530      * left corner and the location the element was clicked
2531      * @method autoOffset
2532      * @param {int} iPageX the X coordinate of the click
2533      * @param {int} iPageY the Y coordinate of the click
2534      */
2535     autoOffset: function(iPageX, iPageY) {
2536         var x = iPageX - this.startPageX;
2537         var y = iPageY - this.startPageY;
2538         this.setDelta(x, y);
2539     },
2540
2541     /**
2542      * Sets the pointer offset.  You can call this directly to force the
2543      * offset to be in a particular location (e.g., pass in 0,0 to set it
2544      * to the center of the object)
2545      * @method setDelta
2546      * @param {int} iDeltaX the distance from the left
2547      * @param {int} iDeltaY the distance from the top
2548      */
2549     setDelta: function(iDeltaX, iDeltaY) {
2550         this.deltaX = iDeltaX;
2551         this.deltaY = iDeltaY;
2552     },
2553
2554     /**
2555      * Sets the drag element to the location of the mousedown or click event,
2556      * maintaining the cursor location relative to the location on the element
2557      * that was clicked.  Override this if you want to place the element in a
2558      * location other than where the cursor is.
2559      * @method setDragElPos
2560      * @param {int} iPageX the X coordinate of the mousedown or drag event
2561      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2562      */
2563     setDragElPos: function(iPageX, iPageY) {
2564         // the first time we do this, we are going to check to make sure
2565         // the element has css positioning
2566
2567         var el = this.getDragEl();
2568         this.alignElWithMouse(el, iPageX, iPageY);
2569     },
2570
2571     /**
2572      * Sets the element to the location of the mousedown or click event,
2573      * maintaining the cursor location relative to the location on the element
2574      * that was clicked.  Override this if you want to place the element in a
2575      * location other than where the cursor is.
2576      * @method alignElWithMouse
2577      * @param {HTMLElement} el the element to move
2578      * @param {int} iPageX the X coordinate of the mousedown or drag event
2579      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2580      */
2581     alignElWithMouse: function(el, iPageX, iPageY) {
2582         var oCoord = this.getTargetCoord(iPageX, iPageY);
2583         var fly = el.dom ? el : Roo.fly(el);
2584         if (!this.deltaSetXY) {
2585             var aCoord = [oCoord.x, oCoord.y];
2586             fly.setXY(aCoord);
2587             var newLeft = fly.getLeft(true);
2588             var newTop  = fly.getTop(true);
2589             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
2590         } else {
2591             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
2592         }
2593
2594         this.cachePosition(oCoord.x, oCoord.y);
2595         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
2596         return oCoord;
2597     },
2598
2599     /**
2600      * Saves the most recent position so that we can reset the constraints and
2601      * tick marks on-demand.  We need to know this so that we can calculate the
2602      * number of pixels the element is offset from its original position.
2603      * @method cachePosition
2604      * @param iPageX the current x position (optional, this just makes it so we
2605      * don't have to look it up again)
2606      * @param iPageY the current y position (optional, this just makes it so we
2607      * don't have to look it up again)
2608      */
2609     cachePosition: function(iPageX, iPageY) {
2610         if (iPageX) {
2611             this.lastPageX = iPageX;
2612             this.lastPageY = iPageY;
2613         } else {
2614             var aCoord = Roo.lib.Dom.getXY(this.getEl());
2615             this.lastPageX = aCoord[0];
2616             this.lastPageY = aCoord[1];
2617         }
2618     },
2619
2620     /**
2621      * Auto-scroll the window if the dragged object has been moved beyond the
2622      * visible window boundary.
2623      * @method autoScroll
2624      * @param {int} x the drag element's x position
2625      * @param {int} y the drag element's y position
2626      * @param {int} h the height of the drag element
2627      * @param {int} w the width of the drag element
2628      * @private
2629      */
2630     autoScroll: function(x, y, h, w) {
2631
2632         if (this.scroll) {
2633             // The client height
2634             var clientH = Roo.lib.Dom.getViewWidth();
2635
2636             // The client width
2637             var clientW = Roo.lib.Dom.getViewHeight();
2638
2639             // The amt scrolled down
2640             var st = this.DDM.getScrollTop();
2641
2642             // The amt scrolled right
2643             var sl = this.DDM.getScrollLeft();
2644
2645             // Location of the bottom of the element
2646             var bot = h + y;
2647
2648             // Location of the right of the element
2649             var right = w + x;
2650
2651             // The distance from the cursor to the bottom of the visible area,
2652             // adjusted so that we don't scroll if the cursor is beyond the
2653             // element drag constraints
2654             var toBot = (clientH + st - y - this.deltaY);
2655
2656             // The distance from the cursor to the right of the visible area
2657             var toRight = (clientW + sl - x - this.deltaX);
2658
2659
2660             // How close to the edge the cursor must be before we scroll
2661             // var thresh = (document.all) ? 100 : 40;
2662             var thresh = 40;
2663
2664             // How many pixels to scroll per autoscroll op.  This helps to reduce
2665             // clunky scrolling. IE is more sensitive about this ... it needs this
2666             // value to be higher.
2667             var scrAmt = (document.all) ? 80 : 30;
2668
2669             // Scroll down if we are near the bottom of the visible page and the
2670             // obj extends below the crease
2671             if ( bot > clientH && toBot < thresh ) {
2672                 window.scrollTo(sl, st + scrAmt);
2673             }
2674
2675             // Scroll up if the window is scrolled down and the top of the object
2676             // goes above the top border
2677             if ( y < st && st > 0 && y - st < thresh ) {
2678                 window.scrollTo(sl, st - scrAmt);
2679             }
2680
2681             // Scroll right if the obj is beyond the right border and the cursor is
2682             // near the border.
2683             if ( right > clientW && toRight < thresh ) {
2684                 window.scrollTo(sl + scrAmt, st);
2685             }
2686
2687             // Scroll left if the window has been scrolled to the right and the obj
2688             // extends past the left border
2689             if ( x < sl && sl > 0 && x - sl < thresh ) {
2690                 window.scrollTo(sl - scrAmt, st);
2691             }
2692         }
2693     },
2694
2695     /**
2696      * Finds the location the element should be placed if we want to move
2697      * it to where the mouse location less the click offset would place us.
2698      * @method getTargetCoord
2699      * @param {int} iPageX the X coordinate of the click
2700      * @param {int} iPageY the Y coordinate of the click
2701      * @return an object that contains the coordinates (Object.x and Object.y)
2702      * @private
2703      */
2704     getTargetCoord: function(iPageX, iPageY) {
2705
2706
2707         var x = iPageX - this.deltaX;
2708         var y = iPageY - this.deltaY;
2709
2710         if (this.constrainX) {
2711             if (x < this.minX) { x = this.minX; }
2712             if (x > this.maxX) { x = this.maxX; }
2713         }
2714
2715         if (this.constrainY) {
2716             if (y < this.minY) { y = this.minY; }
2717             if (y > this.maxY) { y = this.maxY; }
2718         }
2719
2720         x = this.getTick(x, this.xTicks);
2721         y = this.getTick(y, this.yTicks);
2722
2723
2724         return {x:x, y:y};
2725     },
2726
2727     /*
2728      * Sets up config options specific to this class. Overrides
2729      * Roo.dd.DragDrop, but all versions of this method through the
2730      * inheritance chain are called
2731      */
2732     applyConfig: function() {
2733         Roo.dd.DD.superclass.applyConfig.call(this);
2734         this.scroll = (this.config.scroll !== false);
2735     },
2736
2737     /*
2738      * Event that fires prior to the onMouseDown event.  Overrides
2739      * Roo.dd.DragDrop.
2740      */
2741     b4MouseDown: function(e) {
2742         // this.resetConstraints();
2743         this.autoOffset(e.getPageX(),
2744                             e.getPageY());
2745     },
2746
2747     /*
2748      * Event that fires prior to the onDrag event.  Overrides
2749      * Roo.dd.DragDrop.
2750      */
2751     b4Drag: function(e) {
2752         this.setDragElPos(e.getPageX(),
2753                             e.getPageY());
2754     },
2755
2756     toString: function() {
2757         return ("DD " + this.id);
2758     }
2759
2760     //////////////////////////////////////////////////////////////////////////
2761     // Debugging ygDragDrop events that can be overridden
2762     //////////////////////////////////////////////////////////////////////////
2763     /*
2764     startDrag: function(x, y) {
2765     },
2766
2767     onDrag: function(e) {
2768     },
2769
2770     onDragEnter: function(e, id) {
2771     },
2772
2773     onDragOver: function(e, id) {
2774     },
2775
2776     onDragOut: function(e, id) {
2777     },
2778
2779     onDragDrop: function(e, id) {
2780     },
2781
2782     endDrag: function(e) {
2783     }
2784
2785     */
2786
2787 });/*
2788  * Based on:
2789  * Ext JS Library 1.1.1
2790  * Copyright(c) 2006-2007, Ext JS, LLC.
2791  *
2792  * Originally Released Under LGPL - original licence link has changed is not relivant.
2793  *
2794  * Fork - LGPL
2795  * <script type="text/javascript">
2796  */
2797
2798 /**
2799  * @class Roo.dd.DDProxy
2800  * A DragDrop implementation that inserts an empty, bordered div into
2801  * the document that follows the cursor during drag operations.  At the time of
2802  * the click, the frame div is resized to the dimensions of the linked html
2803  * element, and moved to the exact location of the linked element.
2804  *
2805  * References to the "frame" element refer to the single proxy element that
2806  * was created to be dragged in place of all DDProxy elements on the
2807  * page.
2808  *
2809  * @extends Roo.dd.DD
2810  * @constructor
2811  * @param {String} id the id of the linked html element
2812  * @param {String} sGroup the group of related DragDrop objects
2813  * @param {object} config an object containing configurable attributes
2814  *                Valid properties for DDProxy in addition to those in DragDrop:
2815  *                   resizeFrame, centerFrame, dragElId
2816  */
2817 Roo.dd.DDProxy = function(id, sGroup, config) {
2818     if (id) {
2819         this.init(id, sGroup, config);
2820         this.initFrame();
2821     }
2822 };
2823
2824 /**
2825  * The default drag frame div id
2826  * @property Roo.dd.DDProxy.dragElId
2827  * @type String
2828  * @static
2829  */
2830 Roo.dd.DDProxy.dragElId = "ygddfdiv";
2831
2832 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
2833
2834     /**
2835      * By default we resize the drag frame to be the same size as the element
2836      * we want to drag (this is to get the frame effect).  We can turn it off
2837      * if we want a different behavior.
2838      * @property resizeFrame
2839      * @type boolean
2840      */
2841     resizeFrame: true,
2842
2843     /**
2844      * By default the frame is positioned exactly where the drag element is, so
2845      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
2846      * you do not have constraints on the obj is to have the drag frame centered
2847      * around the cursor.  Set centerFrame to true for this effect.
2848      * @property centerFrame
2849      * @type boolean
2850      */
2851     centerFrame: false,
2852
2853     /**
2854      * Creates the proxy element if it does not yet exist
2855      * @method createFrame
2856      */
2857     createFrame: function() {
2858         var self = this;
2859         var body = document.body;
2860
2861         if (!body || !body.firstChild) {
2862             setTimeout( function() { self.createFrame(); }, 50 );
2863             return;
2864         }
2865
2866         var div = this.getDragEl();
2867
2868         if (!div) {
2869             div    = document.createElement("div");
2870             div.id = this.dragElId;
2871             var s  = div.style;
2872
2873             s.position   = "absolute";
2874             s.visibility = "hidden";
2875             s.cursor     = "move";
2876             s.border     = "2px solid #aaa";
2877             s.zIndex     = 999;
2878
2879             // appendChild can blow up IE if invoked prior to the window load event
2880             // while rendering a table.  It is possible there are other scenarios
2881             // that would cause this to happen as well.
2882             body.insertBefore(div, body.firstChild);
2883         }
2884     },
2885
2886     /**
2887      * Initialization for the drag frame element.  Must be called in the
2888      * constructor of all subclasses
2889      * @method initFrame
2890      */
2891     initFrame: function() {
2892         this.createFrame();
2893     },
2894
2895     applyConfig: function() {
2896         Roo.dd.DDProxy.superclass.applyConfig.call(this);
2897
2898         this.resizeFrame = (this.config.resizeFrame !== false);
2899         this.centerFrame = (this.config.centerFrame);
2900         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
2901     },
2902
2903     /**
2904      * Resizes the drag frame to the dimensions of the clicked object, positions
2905      * it over the object, and finally displays it
2906      * @method showFrame
2907      * @param {int} iPageX X click position
2908      * @param {int} iPageY Y click position
2909      * @private
2910      */
2911     showFrame: function(iPageX, iPageY) {
2912         var el = this.getEl();
2913         var dragEl = this.getDragEl();
2914         var s = dragEl.style;
2915
2916         this._resizeProxy();
2917
2918         if (this.centerFrame) {
2919             this.setDelta( Math.round(parseInt(s.width,  10)/2),
2920                            Math.round(parseInt(s.height, 10)/2) );
2921         }
2922
2923         this.setDragElPos(iPageX, iPageY);
2924
2925         Roo.fly(dragEl).show();
2926     },
2927
2928     /**
2929      * The proxy is automatically resized to the dimensions of the linked
2930      * element when a drag is initiated, unless resizeFrame is set to false
2931      * @method _resizeProxy
2932      * @private
2933      */
2934     _resizeProxy: function() {
2935         if (this.resizeFrame) {
2936             var el = this.getEl();
2937             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
2938         }
2939     },
2940
2941     // overrides Roo.dd.DragDrop
2942     b4MouseDown: function(e) {
2943         var x = e.getPageX();
2944         var y = e.getPageY();
2945         this.autoOffset(x, y);
2946         this.setDragElPos(x, y);
2947     },
2948
2949     // overrides Roo.dd.DragDrop
2950     b4StartDrag: function(x, y) {
2951         // show the drag frame
2952         this.showFrame(x, y);
2953     },
2954
2955     // overrides Roo.dd.DragDrop
2956     b4EndDrag: function(e) {
2957         Roo.fly(this.getDragEl()).hide();
2958     },
2959
2960     // overrides Roo.dd.DragDrop
2961     // By default we try to move the element to the last location of the frame.
2962     // This is so that the default behavior mirrors that of Roo.dd.DD.
2963     endDrag: function(e) {
2964
2965         var lel = this.getEl();
2966         var del = this.getDragEl();
2967
2968         // Show the drag frame briefly so we can get its position
2969         del.style.visibility = "";
2970
2971         this.beforeMove();
2972         // Hide the linked element before the move to get around a Safari
2973         // rendering bug.
2974         lel.style.visibility = "hidden";
2975         Roo.dd.DDM.moveToEl(lel, del);
2976         del.style.visibility = "hidden";
2977         lel.style.visibility = "";
2978
2979         this.afterDrag();
2980     },
2981
2982     beforeMove : function(){
2983
2984     },
2985
2986     afterDrag : function(){
2987
2988     },
2989
2990     toString: function() {
2991         return ("DDProxy " + this.id);
2992     }
2993
2994 });
2995 /*
2996  * Based on:
2997  * Ext JS Library 1.1.1
2998  * Copyright(c) 2006-2007, Ext JS, LLC.
2999  *
3000  * Originally Released Under LGPL - original licence link has changed is not relivant.
3001  *
3002  * Fork - LGPL
3003  * <script type="text/javascript">
3004  */
3005
3006  /**
3007  * @class Roo.dd.DDTarget
3008  * A DragDrop implementation that does not move, but can be a drop
3009  * target.  You would get the same result by simply omitting implementation
3010  * for the event callbacks, but this way we reduce the processing cost of the
3011  * event listener and the callbacks.
3012  * @extends Roo.dd.DragDrop
3013  * @constructor
3014  * @param {String} id the id of the element that is a drop target
3015  * @param {String} sGroup the group of related DragDrop objects
3016  * @param {object} config an object containing configurable attributes
3017  *                 Valid properties for DDTarget in addition to those in
3018  *                 DragDrop:
3019  *                    none
3020  */
3021 Roo.dd.DDTarget = function(id, sGroup, config) {
3022     if (id) {
3023         this.initTarget(id, sGroup, config);
3024     }
3025     if (config.listeners || config.events) { 
3026        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
3027             listeners : config.listeners || {}, 
3028             events : config.events || {} 
3029         });    
3030     }
3031 };
3032
3033 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
3034 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
3035     toString: function() {
3036         return ("DDTarget " + this.id);
3037     }
3038 });
3039 /*
3040  * Based on:
3041  * Ext JS Library 1.1.1
3042  * Copyright(c) 2006-2007, Ext JS, LLC.
3043  *
3044  * Originally Released Under LGPL - original licence link has changed is not relivant.
3045  *
3046  * Fork - LGPL
3047  * <script type="text/javascript">
3048  */
3049  
3050
3051 /**
3052  * @class Roo.dd.ScrollManager
3053  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
3054  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
3055  * @singleton
3056  */
3057 Roo.dd.ScrollManager = function(){
3058     var ddm = Roo.dd.DragDropMgr;
3059     var els = {};
3060     var dragEl = null;
3061     var proc = {};
3062     
3063     
3064     
3065     var onStop = function(e){
3066         dragEl = null;
3067         clearProc();
3068     };
3069     
3070     var triggerRefresh = function(){
3071         if(ddm.dragCurrent){
3072              ddm.refreshCache(ddm.dragCurrent.groups);
3073         }
3074     };
3075     
3076     var doScroll = function(){
3077         if(ddm.dragCurrent){
3078             var dds = Roo.dd.ScrollManager;
3079             if(!dds.animate){
3080                 if(proc.el.scroll(proc.dir, dds.increment)){
3081                     triggerRefresh();
3082                 }
3083             }else{
3084                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
3085             }
3086         }
3087     };
3088     
3089     var clearProc = function(){
3090         if(proc.id){
3091             clearInterval(proc.id);
3092         }
3093         proc.id = 0;
3094         proc.el = null;
3095         proc.dir = "";
3096     };
3097     
3098     var startProc = function(el, dir){
3099          Roo.log('scroll startproc');
3100         clearProc();
3101         proc.el = el;
3102         proc.dir = dir;
3103         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
3104     };
3105     
3106     var onFire = function(e, isDrop){
3107        
3108         if(isDrop || !ddm.dragCurrent){ return; }
3109         var dds = Roo.dd.ScrollManager;
3110         if(!dragEl || dragEl != ddm.dragCurrent){
3111             dragEl = ddm.dragCurrent;
3112             // refresh regions on drag start
3113             dds.refreshCache();
3114         }
3115         
3116         var xy = Roo.lib.Event.getXY(e);
3117         var pt = new Roo.lib.Point(xy[0], xy[1]);
3118         for(var id in els){
3119             var el = els[id], r = el._region;
3120             if(r && r.contains(pt) && el.isScrollable()){
3121                 if(r.bottom - pt.y <= dds.thresh){
3122                     if(proc.el != el){
3123                         startProc(el, "down");
3124                     }
3125                     return;
3126                 }else if(r.right - pt.x <= dds.thresh){
3127                     if(proc.el != el){
3128                         startProc(el, "left");
3129                     }
3130                     return;
3131                 }else if(pt.y - r.top <= dds.thresh){
3132                     if(proc.el != el){
3133                         startProc(el, "up");
3134                     }
3135                     return;
3136                 }else if(pt.x - r.left <= dds.thresh){
3137                     if(proc.el != el){
3138                         startProc(el, "right");
3139                     }
3140                     return;
3141                 }
3142             }
3143         }
3144         clearProc();
3145     };
3146     
3147     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
3148     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
3149     
3150     return {
3151         /**
3152          * Registers new overflow element(s) to auto scroll
3153          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
3154          */
3155         register : function(el){
3156             if(el instanceof Array){
3157                 for(var i = 0, len = el.length; i < len; i++) {
3158                         this.register(el[i]);
3159                 }
3160             }else{
3161                 el = Roo.get(el);
3162                 els[el.id] = el;
3163             }
3164             Roo.dd.ScrollManager.els = els;
3165         },
3166         
3167         /**
3168          * Unregisters overflow element(s) so they are no longer scrolled
3169          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
3170          */
3171         unregister : function(el){
3172             if(el instanceof Array){
3173                 for(var i = 0, len = el.length; i < len; i++) {
3174                         this.unregister(el[i]);
3175                 }
3176             }else{
3177                 el = Roo.get(el);
3178                 delete els[el.id];
3179             }
3180         },
3181         
3182         /**
3183          * The number of pixels from the edge of a container the pointer needs to be to 
3184          * trigger scrolling (defaults to 25)
3185          * @type Number
3186          */
3187         thresh : 25,
3188         
3189         /**
3190          * The number of pixels to scroll in each scroll increment (defaults to 50)
3191          * @type Number
3192          */
3193         increment : 100,
3194         
3195         /**
3196          * The frequency of scrolls in milliseconds (defaults to 500)
3197          * @type Number
3198          */
3199         frequency : 500,
3200         
3201         /**
3202          * True to animate the scroll (defaults to true)
3203          * @type Boolean
3204          */
3205         animate: true,
3206         
3207         /**
3208          * The animation duration in seconds - 
3209          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
3210          * @type Number
3211          */
3212         animDuration: .4,
3213         
3214         /**
3215          * Manually trigger a cache refresh.
3216          */
3217         refreshCache : function(){
3218             for(var id in els){
3219                 if(typeof els[id] == 'object'){ // for people extending the object prototype
3220                     els[id]._region = els[id].getRegion();
3221                 }
3222             }
3223         }
3224     };
3225 }();/*
3226  * Based on:
3227  * Ext JS Library 1.1.1
3228  * Copyright(c) 2006-2007, Ext JS, LLC.
3229  *
3230  * Originally Released Under LGPL - original licence link has changed is not relivant.
3231  *
3232  * Fork - LGPL
3233  * <script type="text/javascript">
3234  */
3235  
3236
3237 /**
3238  * @class Roo.dd.Registry
3239  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
3240  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
3241  * @singleton
3242  */
3243 Roo.dd.Registry = function(){
3244     var elements = {}; 
3245     var handles = {}; 
3246     var autoIdSeed = 0;
3247
3248     var getId = function(el, autogen){
3249         if(typeof el == "string"){
3250             return el;
3251         }
3252         var id = el.id;
3253         if(!id && autogen !== false){
3254             id = "roodd-" + (++autoIdSeed);
3255             el.id = id;
3256         }
3257         return id;
3258     };
3259     
3260     return {
3261     /**
3262      * Register a drag drop element
3263      * @param {String|HTMLElement} element The id or DOM node to register
3264      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
3265      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
3266      * knows how to interpret, plus there are some specific properties known to the Registry that should be
3267      * populated in the data object (if applicable):
3268      * <pre>
3269 Value      Description<br />
3270 ---------  ------------------------------------------<br />
3271 handles    Array of DOM nodes that trigger dragging<br />
3272            for the element being registered<br />
3273 isHandle   True if the element passed in triggers<br />
3274            dragging itself, else false
3275 </pre>
3276      */
3277         register : function(el, data){
3278             data = data || {};
3279             if(typeof el == "string"){
3280                 el = document.getElementById(el);
3281             }
3282             data.ddel = el;
3283             elements[getId(el)] = data;
3284             if(data.isHandle !== false){
3285                 handles[data.ddel.id] = data;
3286             }
3287             if(data.handles){
3288                 var hs = data.handles;
3289                 for(var i = 0, len = hs.length; i < len; i++){
3290                         handles[getId(hs[i])] = data;
3291                 }
3292             }
3293         },
3294
3295     /**
3296      * Unregister a drag drop element
3297      * @param {String|HTMLElement}  element The id or DOM node to unregister
3298      */
3299         unregister : function(el){
3300             var id = getId(el, false);
3301             var data = elements[id];
3302             if(data){
3303                 delete elements[id];
3304                 if(data.handles){
3305                     var hs = data.handles;
3306                     for(var i = 0, len = hs.length; i < len; i++){
3307                         delete handles[getId(hs[i], false)];
3308                     }
3309                 }
3310             }
3311         },
3312
3313     /**
3314      * Returns the handle registered for a DOM Node by id
3315      * @param {String|HTMLElement} id The DOM node or id to look up
3316      * @return {Object} handle The custom handle data
3317      */
3318         getHandle : function(id){
3319             if(typeof id != "string"){ // must be element?
3320                 id = id.id;
3321             }
3322             return handles[id];
3323         },
3324
3325     /**
3326      * Returns the handle that is registered for the DOM node that is the target of the event
3327      * @param {Event} e The event
3328      * @return {Object} handle The custom handle data
3329      */
3330         getHandleFromEvent : function(e){
3331             var t = Roo.lib.Event.getTarget(e);
3332             return t ? handles[t.id] : null;
3333         },
3334
3335     /**
3336      * Returns a custom data object that is registered for a DOM node by id
3337      * @param {String|HTMLElement} id The DOM node or id to look up
3338      * @return {Object} data The custom data
3339      */
3340         getTarget : function(id){
3341             if(typeof id != "string"){ // must be element?
3342                 id = id.id;
3343             }
3344             return elements[id];
3345         },
3346
3347     /**
3348      * Returns a custom data object that is registered for the DOM node that is the target of the event
3349      * @param {Event} e The event
3350      * @return {Object} data The custom data
3351      */
3352         getTargetFromEvent : function(e){
3353             var t = Roo.lib.Event.getTarget(e);
3354             return t ? elements[t.id] || handles[t.id] : null;
3355         }
3356     };
3357 }();/*
3358  * Based on:
3359  * Ext JS Library 1.1.1
3360  * Copyright(c) 2006-2007, Ext JS, LLC.
3361  *
3362  * Originally Released Under LGPL - original licence link has changed is not relivant.
3363  *
3364  * Fork - LGPL
3365  * <script type="text/javascript">
3366  */
3367  
3368
3369 /**
3370  * @class Roo.dd.StatusProxy
3371  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
3372  * default drag proxy used by all Roo.dd components.
3373  * @constructor
3374  * @param {Object} config
3375  */
3376 Roo.dd.StatusProxy = function(config){
3377     Roo.apply(this, config);
3378     this.id = this.id || Roo.id();
3379     this.el = new Roo.Layer({
3380         dh: {
3381             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
3382                 {tag: "div", cls: "x-dd-drop-icon"},
3383                 {tag: "div", cls: "x-dd-drag-ghost"}
3384             ]
3385         }, 
3386         shadow: !config || config.shadow !== false
3387     });
3388     this.ghost = Roo.get(this.el.dom.childNodes[1]);
3389     this.dropStatus = this.dropNotAllowed;
3390 };
3391
3392 Roo.dd.StatusProxy.prototype = {
3393     /**
3394      * @cfg {String} dropAllowed
3395      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
3396      */
3397     dropAllowed : "x-dd-drop-ok",
3398     /**
3399      * @cfg {String} dropNotAllowed
3400      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
3401      */
3402     dropNotAllowed : "x-dd-drop-nodrop",
3403
3404     /**
3405      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
3406      * over the current target element.
3407      * @param {String} cssClass The css class for the new drop status indicator image
3408      */
3409     setStatus : function(cssClass){
3410         cssClass = cssClass || this.dropNotAllowed;
3411         if(this.dropStatus != cssClass){
3412             this.el.replaceClass(this.dropStatus, cssClass);
3413             this.dropStatus = cssClass;
3414         }
3415     },
3416
3417     /**
3418      * Resets the status indicator to the default dropNotAllowed value
3419      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
3420      */
3421     reset : function(clearGhost){
3422         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
3423         this.dropStatus = this.dropNotAllowed;
3424         if(clearGhost){
3425             this.ghost.update("");
3426         }
3427     },
3428
3429     /**
3430      * Updates the contents of the ghost element
3431      * @param {String} html The html that will replace the current innerHTML of the ghost element
3432      */
3433     update : function(html){
3434         if(typeof html == "string"){
3435             this.ghost.update(html);
3436         }else{
3437             this.ghost.update("");
3438             html.style.margin = "0";
3439             this.ghost.dom.appendChild(html);
3440         }
3441         // ensure float = none set?? cant remember why though.
3442         var el = this.ghost.dom.firstChild;
3443                 if(el){
3444                         Roo.fly(el).setStyle('float', 'none');
3445                 }
3446     },
3447     
3448     /**
3449      * Returns the underlying proxy {@link Roo.Layer}
3450      * @return {Roo.Layer} el
3451     */
3452     getEl : function(){
3453         return this.el;
3454     },
3455
3456     /**
3457      * Returns the ghost element
3458      * @return {Roo.Element} el
3459      */
3460     getGhost : function(){
3461         return this.ghost;
3462     },
3463
3464     /**
3465      * Hides the proxy
3466      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
3467      */
3468     hide : function(clear){
3469         this.el.hide();
3470         if(clear){
3471             this.reset(true);
3472         }
3473     },
3474
3475     /**
3476      * Stops the repair animation if it's currently running
3477      */
3478     stop : function(){
3479         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
3480             this.anim.stop();
3481         }
3482     },
3483
3484     /**
3485      * Displays this proxy
3486      */
3487     show : function(){
3488         this.el.show();
3489     },
3490
3491     /**
3492      * Force the Layer to sync its shadow and shim positions to the element
3493      */
3494     sync : function(){
3495         this.el.sync();
3496     },
3497
3498     /**
3499      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
3500      * invalid drop operation by the item being dragged.
3501      * @param {Array} xy The XY position of the element ([x, y])
3502      * @param {Function} callback The function to call after the repair is complete
3503      * @param {Object} scope The scope in which to execute the callback
3504      */
3505     repair : function(xy, callback, scope){
3506         this.callback = callback;
3507         this.scope = scope;
3508         if(xy && this.animRepair !== false){
3509             this.el.addClass("x-dd-drag-repair");
3510             this.el.hideUnders(true);
3511             this.anim = this.el.shift({
3512                 duration: this.repairDuration || .5,
3513                 easing: 'easeOut',
3514                 xy: xy,
3515                 stopFx: true,
3516                 callback: this.afterRepair,
3517                 scope: this
3518             });
3519         }else{
3520             this.afterRepair();
3521         }
3522     },
3523
3524     // private
3525     afterRepair : function(){
3526         this.hide(true);
3527         if(typeof this.callback == "function"){
3528             this.callback.call(this.scope || this);
3529         }
3530         this.callback = null;
3531         this.scope = null;
3532     }
3533 };/*
3534  * Based on:
3535  * Ext JS Library 1.1.1
3536  * Copyright(c) 2006-2007, Ext JS, LLC.
3537  *
3538  * Originally Released Under LGPL - original licence link has changed is not relivant.
3539  *
3540  * Fork - LGPL
3541  * <script type="text/javascript">
3542  */
3543
3544 /**
3545  * @class Roo.dd.DragSource
3546  * @extends Roo.dd.DDProxy
3547  * A simple class that provides the basic implementation needed to make any element draggable.
3548  * @constructor
3549  * @param {String/HTMLElement/Element} el The container element
3550  * @param {Object} config
3551  */
3552 Roo.dd.DragSource = function(el, config){
3553     this.el = Roo.get(el);
3554     this.dragData = {};
3555     
3556     Roo.apply(this, config);
3557     
3558     if(!this.proxy){
3559         this.proxy = new Roo.dd.StatusProxy();
3560     }
3561
3562     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
3563           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
3564     
3565     this.dragging = false;
3566 };
3567
3568 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
3569     /**
3570      * @cfg {String} dropAllowed
3571      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3572      */
3573     dropAllowed : "x-dd-drop-ok",
3574     /**
3575      * @cfg {String} dropNotAllowed
3576      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3577      */
3578     dropNotAllowed : "x-dd-drop-nodrop",
3579
3580     /**
3581      * Returns the data object associated with this drag source
3582      * @return {Object} data An object containing arbitrary data
3583      */
3584     getDragData : function(e){
3585         return this.dragData;
3586     },
3587
3588     // private
3589     onDragEnter : function(e, id){
3590         var target = Roo.dd.DragDropMgr.getDDById(id);
3591         this.cachedTarget = target;
3592         if(this.beforeDragEnter(target, e, id) !== false){
3593             if(target.isNotifyTarget){
3594                 var status = target.notifyEnter(this, e, this.dragData);
3595                 this.proxy.setStatus(status);
3596             }else{
3597                 this.proxy.setStatus(this.dropAllowed);
3598             }
3599             
3600             if(this.afterDragEnter){
3601                 /**
3602                  * An empty function by default, but provided so that you can perform a custom action
3603                  * when the dragged item enters the drop target by providing an implementation.
3604                  * @param {Roo.dd.DragDrop} target The drop target
3605                  * @param {Event} e The event object
3606                  * @param {String} id The id of the dragged element
3607                  * @method afterDragEnter
3608                  */
3609                 this.afterDragEnter(target, e, id);
3610             }
3611         }
3612     },
3613
3614     /**
3615      * An empty function by default, but provided so that you can perform a custom action
3616      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
3617      * @param {Roo.dd.DragDrop} target The drop target
3618      * @param {Event} e The event object
3619      * @param {String} id The id of the dragged element
3620      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3621      */
3622     beforeDragEnter : function(target, e, id){
3623         return true;
3624     },
3625
3626     // private
3627     alignElWithMouse: function() {
3628         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
3629         this.proxy.sync();
3630     },
3631
3632     // private
3633     onDragOver : function(e, id){
3634         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3635         if(this.beforeDragOver(target, e, id) !== false){
3636             if(target.isNotifyTarget){
3637                 var status = target.notifyOver(this, e, this.dragData);
3638                 this.proxy.setStatus(status);
3639             }
3640
3641             if(this.afterDragOver){
3642                 /**
3643                  * An empty function by default, but provided so that you can perform a custom action
3644                  * while the dragged item is over the drop target by providing an implementation.
3645                  * @param {Roo.dd.DragDrop} target The drop target
3646                  * @param {Event} e The event object
3647                  * @param {String} id The id of the dragged element
3648                  * @method afterDragOver
3649                  */
3650                 this.afterDragOver(target, e, id);
3651             }
3652         }
3653     },
3654
3655     /**
3656      * An empty function by default, but provided so that you can perform a custom action
3657      * while the dragged item is over the drop target and optionally cancel the onDragOver.
3658      * @param {Roo.dd.DragDrop} target The drop target
3659      * @param {Event} e The event object
3660      * @param {String} id The id of the dragged element
3661      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3662      */
3663     beforeDragOver : function(target, e, id){
3664         return true;
3665     },
3666
3667     // private
3668     onDragOut : function(e, id){
3669         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3670         if(this.beforeDragOut(target, e, id) !== false){
3671             if(target.isNotifyTarget){
3672                 target.notifyOut(this, e, this.dragData);
3673             }
3674             this.proxy.reset();
3675             if(this.afterDragOut){
3676                 /**
3677                  * An empty function by default, but provided so that you can perform a custom action
3678                  * after the dragged item is dragged out of the target without dropping.
3679                  * @param {Roo.dd.DragDrop} target The drop target
3680                  * @param {Event} e The event object
3681                  * @param {String} id The id of the dragged element
3682                  * @method afterDragOut
3683                  */
3684                 this.afterDragOut(target, e, id);
3685             }
3686         }
3687         this.cachedTarget = null;
3688     },
3689
3690     /**
3691      * An empty function by default, but provided so that you can perform a custom action before the dragged
3692      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
3693      * @param {Roo.dd.DragDrop} target The drop target
3694      * @param {Event} e The event object
3695      * @param {String} id The id of the dragged element
3696      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3697      */
3698     beforeDragOut : function(target, e, id){
3699         return true;
3700     },
3701     
3702     // private
3703     onDragDrop : function(e, id){
3704         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3705         if(this.beforeDragDrop(target, e, id) !== false){
3706             if(target.isNotifyTarget){
3707                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
3708                     this.onValidDrop(target, e, id);
3709                 }else{
3710                     this.onInvalidDrop(target, e, id);
3711                 }
3712             }else{
3713                 this.onValidDrop(target, e, id);
3714             }
3715             
3716             if(this.afterDragDrop){
3717                 /**
3718                  * An empty function by default, but provided so that you can perform a custom action
3719                  * after a valid drag drop has occurred by providing an implementation.
3720                  * @param {Roo.dd.DragDrop} target The drop target
3721                  * @param {Event} e The event object
3722                  * @param {String} id The id of the dropped element
3723                  * @method afterDragDrop
3724                  */
3725                 this.afterDragDrop(target, e, id);
3726             }
3727         }
3728         delete this.cachedTarget;
3729     },
3730
3731     /**
3732      * An empty function by default, but provided so that you can perform a custom action before the dragged
3733      * item is dropped onto the target and optionally cancel the onDragDrop.
3734      * @param {Roo.dd.DragDrop} target The drop target
3735      * @param {Event} e The event object
3736      * @param {String} id The id of the dragged element
3737      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
3738      */
3739     beforeDragDrop : function(target, e, id){
3740         return true;
3741     },
3742
3743     // private
3744     onValidDrop : function(target, e, id){
3745         this.hideProxy();
3746         if(this.afterValidDrop){
3747             /**
3748              * An empty function by default, but provided so that you can perform a custom action
3749              * after a valid drop has occurred by providing an implementation.
3750              * @param {Object} target The target DD 
3751              * @param {Event} e The event object
3752              * @param {String} id The id of the dropped element
3753              * @method afterInvalidDrop
3754              */
3755             this.afterValidDrop(target, e, id);
3756         }
3757     },
3758
3759     // private
3760     getRepairXY : function(e, data){
3761         return this.el.getXY();  
3762     },
3763
3764     // private
3765     onInvalidDrop : function(target, e, id){
3766         this.beforeInvalidDrop(target, e, id);
3767         if(this.cachedTarget){
3768             if(this.cachedTarget.isNotifyTarget){
3769                 this.cachedTarget.notifyOut(this, e, this.dragData);
3770             }
3771             this.cacheTarget = null;
3772         }
3773         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
3774
3775         if(this.afterInvalidDrop){
3776             /**
3777              * An empty function by default, but provided so that you can perform a custom action
3778              * after an invalid drop has occurred by providing an implementation.
3779              * @param {Event} e The event object
3780              * @param {String} id The id of the dropped element
3781              * @method afterInvalidDrop
3782              */
3783             this.afterInvalidDrop(e, id);
3784         }
3785     },
3786
3787     // private
3788     afterRepair : function(){
3789         if(Roo.enableFx){
3790             this.el.highlight(this.hlColor || "c3daf9");
3791         }
3792         this.dragging = false;
3793     },
3794
3795     /**
3796      * An empty function by default, but provided so that you can perform a custom action after an invalid
3797      * drop has occurred.
3798      * @param {Roo.dd.DragDrop} target The drop target
3799      * @param {Event} e The event object
3800      * @param {String} id The id of the dragged element
3801      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
3802      */
3803     beforeInvalidDrop : function(target, e, id){
3804         return true;
3805     },
3806
3807     // private
3808     handleMouseDown : function(e){
3809         if(this.dragging) {
3810             return;
3811         }
3812         var data = this.getDragData(e);
3813         if(data && this.onBeforeDrag(data, e) !== false){
3814             this.dragData = data;
3815             this.proxy.stop();
3816             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
3817         } 
3818     },
3819
3820     /**
3821      * An empty function by default, but provided so that you can perform a custom action before the initial
3822      * drag event begins and optionally cancel it.
3823      * @param {Object} data An object containing arbitrary data to be shared with drop targets
3824      * @param {Event} e The event object
3825      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3826      */
3827     onBeforeDrag : function(data, e){
3828         return true;
3829     },
3830
3831     /**
3832      * An empty function by default, but provided so that you can perform a custom action once the initial
3833      * drag event has begun.  The drag cannot be canceled from this function.
3834      * @param {Number} x The x position of the click on the dragged object
3835      * @param {Number} y The y position of the click on the dragged object
3836      */
3837     onStartDrag : Roo.emptyFn,
3838
3839     // private - YUI override
3840     startDrag : function(x, y){
3841         this.proxy.reset();
3842         this.dragging = true;
3843         this.proxy.update("");
3844         this.onInitDrag(x, y);
3845         this.proxy.show();
3846     },
3847
3848     // private
3849     onInitDrag : function(x, y){
3850         var clone = this.el.dom.cloneNode(true);
3851         clone.id = Roo.id(); // prevent duplicate ids
3852         this.proxy.update(clone);
3853         this.onStartDrag(x, y);
3854         return true;
3855     },
3856
3857     /**
3858      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
3859      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
3860      */
3861     getProxy : function(){
3862         return this.proxy;  
3863     },
3864
3865     /**
3866      * Hides the drag source's {@link Roo.dd.StatusProxy}
3867      */
3868     hideProxy : function(){
3869         this.proxy.hide();  
3870         this.proxy.reset(true);
3871         this.dragging = false;
3872     },
3873
3874     // private
3875     triggerCacheRefresh : function(){
3876         Roo.dd.DDM.refreshCache(this.groups);
3877     },
3878
3879     // private - override to prevent hiding
3880     b4EndDrag: function(e) {
3881     },
3882
3883     // private - override to prevent moving
3884     endDrag : function(e){
3885         this.onEndDrag(this.dragData, e);
3886     },
3887
3888     // private
3889     onEndDrag : function(data, e){
3890     },
3891     
3892     // private - pin to cursor
3893     autoOffset : function(x, y) {
3894         this.setDelta(-12, -20);
3895     }    
3896 });/*
3897  * Based on:
3898  * Ext JS Library 1.1.1
3899  * Copyright(c) 2006-2007, Ext JS, LLC.
3900  *
3901  * Originally Released Under LGPL - original licence link has changed is not relivant.
3902  *
3903  * Fork - LGPL
3904  * <script type="text/javascript">
3905  */
3906
3907
3908 /**
3909  * @class Roo.dd.DropTarget
3910  * @extends Roo.dd.DDTarget
3911  * A simple class that provides the basic implementation needed to make any element a drop target that can have
3912  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
3913  * @constructor
3914  * @param {String/HTMLElement/Element} el The container element
3915  * @param {Object} config
3916  */
3917 Roo.dd.DropTarget = function(el, config){
3918     this.el = Roo.get(el);
3919     
3920     var listeners = false; ;
3921     if (config && config.listeners) {
3922         listeners= config.listeners;
3923         delete config.listeners;
3924     }
3925     Roo.apply(this, config);
3926     
3927     if(this.containerScroll){
3928         Roo.dd.ScrollManager.register(this.el);
3929     }
3930     this.addEvents( {
3931          /**
3932          * @scope Roo.dd.DropTarget
3933          */
3934          
3935          /**
3936          * @event enter
3937          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
3938          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
3939          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
3940          * 
3941          * IMPORTANT : it should set this.overClass and this.dropAllowed
3942          * 
3943          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3944          * @param {Event} e The event
3945          * @param {Object} data An object containing arbitrary data supplied by the drag source
3946          */
3947         "enter" : true,
3948         
3949          /**
3950          * @event over
3951          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
3952          * This method will be called on every mouse movement while the drag source is over the drop target.
3953          * This default implementation simply returns the dropAllowed config value.
3954          * 
3955          * IMPORTANT : it should set this.dropAllowed
3956          * 
3957          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3958          * @param {Event} e The event
3959          * @param {Object} data An object containing arbitrary data supplied by the drag source
3960          
3961          */
3962         "over" : true,
3963         /**
3964          * @event out
3965          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
3966          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
3967          * overClass (if any) from the drop element.
3968          * 
3969          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3970          * @param {Event} e The event
3971          * @param {Object} data An object containing arbitrary data supplied by the drag source
3972          */
3973          "out" : true,
3974          
3975         /**
3976          * @event drop
3977          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
3978          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
3979          * implementation that does something to process the drop event and returns true so that the drag source's
3980          * repair action does not run.
3981          * 
3982          * IMPORTANT : it should set this.success
3983          * 
3984          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3985          * @param {Event} e The event
3986          * @param {Object} data An object containing arbitrary data supplied by the drag source
3987         */
3988          "drop" : true
3989     });
3990             
3991      
3992     Roo.dd.DropTarget.superclass.constructor.call(  this, 
3993         this.el.dom, 
3994         this.ddGroup || this.group,
3995         {
3996             isTarget: true,
3997             listeners : listeners || {} 
3998            
3999         
4000         }
4001     );
4002
4003 };
4004
4005 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
4006     /**
4007      * @cfg {String} overClass
4008      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
4009      */
4010      /**
4011      * @cfg {String} ddGroup
4012      * The drag drop group to handle drop events for
4013      */
4014      
4015     /**
4016      * @cfg {String} dropAllowed
4017      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
4018      */
4019     dropAllowed : "x-dd-drop-ok",
4020     /**
4021      * @cfg {String} dropNotAllowed
4022      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
4023      */
4024     dropNotAllowed : "x-dd-drop-nodrop",
4025     /**
4026      * @cfg {boolean} success
4027      * set this after drop listener.. 
4028      */
4029     success : false,
4030     /**
4031      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
4032      * if the drop point is valid for over/enter..
4033      */
4034     valid : false,
4035     // private
4036     isTarget : true,
4037
4038     // private
4039     isNotifyTarget : true,
4040     
4041     /**
4042      * @hide
4043      */
4044     notifyEnter : function(dd, e, data)
4045     {
4046         this.valid = true;
4047         this.fireEvent('enter', dd, e, data);
4048         if(this.overClass){
4049             this.el.addClass(this.overClass);
4050         }
4051         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4052             this.valid ? this.dropAllowed : this.dropNotAllowed
4053         );
4054     },
4055
4056     /**
4057      * @hide
4058      */
4059     notifyOver : function(dd, e, data)
4060     {
4061         this.valid = true;
4062         this.fireEvent('over', dd, e, data);
4063         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4064             this.valid ? this.dropAllowed : this.dropNotAllowed
4065         );
4066     },
4067
4068     /**
4069      * @hide
4070      */
4071     notifyOut : function(dd, e, data)
4072     {
4073         this.fireEvent('out', dd, e, data);
4074         if(this.overClass){
4075             this.el.removeClass(this.overClass);
4076         }
4077     },
4078
4079     /**
4080      * @hide
4081      */
4082     notifyDrop : function(dd, e, data)
4083     {
4084         this.success = false;
4085         this.fireEvent('drop', dd, e, data);
4086         return this.success;
4087     }
4088 });/*
4089  * Based on:
4090  * Ext JS Library 1.1.1
4091  * Copyright(c) 2006-2007, Ext JS, LLC.
4092  *
4093  * Originally Released Under LGPL - original licence link has changed is not relivant.
4094  *
4095  * Fork - LGPL
4096  * <script type="text/javascript">
4097  */
4098
4099
4100 /**
4101  * @class Roo.dd.DragZone
4102  * @extends Roo.dd.DragSource
4103  * This class provides a container DD instance that proxies for multiple child node sources.<br />
4104  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
4105  * @constructor
4106  * @param {String/HTMLElement/Element} el The container element
4107  * @param {Object} config
4108  */
4109 Roo.dd.DragZone = function(el, config){
4110     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
4111     if(this.containerScroll){
4112         Roo.dd.ScrollManager.register(this.el);
4113     }
4114 };
4115
4116 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
4117     /**
4118      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
4119      * for auto scrolling during drag operations.
4120      */
4121     /**
4122      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
4123      * method after a failed drop (defaults to "c3daf9" - light blue)
4124      */
4125
4126     /**
4127      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
4128      * for a valid target to drag based on the mouse down. Override this method
4129      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
4130      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
4131      * @param {EventObject} e The mouse down event
4132      * @return {Object} The dragData
4133      */
4134     getDragData : function(e){
4135         return Roo.dd.Registry.getHandleFromEvent(e);
4136     },
4137     
4138     /**
4139      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
4140      * this.dragData.ddel
4141      * @param {Number} x The x position of the click on the dragged object
4142      * @param {Number} y The y position of the click on the dragged object
4143      * @return {Boolean} true to continue the drag, false to cancel
4144      */
4145     onInitDrag : function(x, y){
4146         this.proxy.update(this.dragData.ddel.cloneNode(true));
4147         this.onStartDrag(x, y);
4148         return true;
4149     },
4150     
4151     /**
4152      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
4153      */
4154     afterRepair : function(){
4155         if(Roo.enableFx){
4156             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
4157         }
4158         this.dragging = false;
4159     },
4160
4161     /**
4162      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
4163      * the XY of this.dragData.ddel
4164      * @param {EventObject} e The mouse up event
4165      * @return {Array} The xy location (e.g. [100, 200])
4166      */
4167     getRepairXY : function(e){
4168         return Roo.Element.fly(this.dragData.ddel).getXY();  
4169     }
4170 });/*
4171  * Based on:
4172  * Ext JS Library 1.1.1
4173  * Copyright(c) 2006-2007, Ext JS, LLC.
4174  *
4175  * Originally Released Under LGPL - original licence link has changed is not relivant.
4176  *
4177  * Fork - LGPL
4178  * <script type="text/javascript">
4179  */
4180 /**
4181  * @class Roo.dd.DropZone
4182  * @extends Roo.dd.DropTarget
4183  * This class provides a container DD instance that proxies for multiple child node targets.<br />
4184  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
4185  * @constructor
4186  * @param {String/HTMLElement/Element} el The container element
4187  * @param {Object} config
4188  */
4189 Roo.dd.DropZone = function(el, config){
4190     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
4191 };
4192
4193 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
4194     /**
4195      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
4196      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
4197      * provide your own custom lookup.
4198      * @param {Event} e The event
4199      * @return {Object} data The custom data
4200      */
4201     getTargetFromEvent : function(e){
4202         return Roo.dd.Registry.getTargetFromEvent(e);
4203     },
4204
4205     /**
4206      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
4207      * that it has registered.  This method has no default implementation and should be overridden to provide
4208      * node-specific processing if necessary.
4209      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
4210      * {@link #getTargetFromEvent} for this node)
4211      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4212      * @param {Event} e The event
4213      * @param {Object} data An object containing arbitrary data supplied by the drag source
4214      */
4215     onNodeEnter : function(n, dd, e, data){
4216         
4217     },
4218
4219     /**
4220      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
4221      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
4222      * overridden to provide the proper feedback.
4223      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4224      * {@link #getTargetFromEvent} for this node)
4225      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4226      * @param {Event} e The event
4227      * @param {Object} data An object containing arbitrary data supplied by the drag source
4228      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4229      * underlying {@link Roo.dd.StatusProxy} can be updated
4230      */
4231     onNodeOver : function(n, dd, e, data){
4232         return this.dropAllowed;
4233     },
4234
4235     /**
4236      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
4237      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
4238      * node-specific processing if necessary.
4239      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4240      * {@link #getTargetFromEvent} for this node)
4241      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4242      * @param {Event} e The event
4243      * @param {Object} data An object containing arbitrary data supplied by the drag source
4244      */
4245     onNodeOut : function(n, dd, e, data){
4246         
4247     },
4248
4249     /**
4250      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
4251      * the drop node.  The default implementation returns false, so it should be overridden to provide the
4252      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
4253      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4254      * {@link #getTargetFromEvent} for this node)
4255      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4256      * @param {Event} e The event
4257      * @param {Object} data An object containing arbitrary data supplied by the drag source
4258      * @return {Boolean} True if the drop was valid, else false
4259      */
4260     onNodeDrop : function(n, dd, e, data){
4261         return false;
4262     },
4263
4264     /**
4265      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
4266      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
4267      * it should be overridden to provide the proper feedback if necessary.
4268      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4269      * @param {Event} e The event
4270      * @param {Object} data An object containing arbitrary data supplied by the drag source
4271      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4272      * underlying {@link Roo.dd.StatusProxy} can be updated
4273      */
4274     onContainerOver : function(dd, e, data){
4275         return this.dropNotAllowed;
4276     },
4277
4278     /**
4279      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
4280      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
4281      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
4282      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
4283      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4284      * @param {Event} e The event
4285      * @param {Object} data An object containing arbitrary data supplied by the drag source
4286      * @return {Boolean} True if the drop was valid, else false
4287      */
4288     onContainerDrop : function(dd, e, data){
4289         return false;
4290     },
4291
4292     /**
4293      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
4294      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
4295      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
4296      * you should override this method and provide a custom implementation.
4297      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4298      * @param {Event} e The event
4299      * @param {Object} data An object containing arbitrary data supplied by the drag source
4300      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4301      * underlying {@link Roo.dd.StatusProxy} can be updated
4302      */
4303     notifyEnter : function(dd, e, data){
4304         return this.dropNotAllowed;
4305     },
4306
4307     /**
4308      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
4309      * This method will be called on every mouse movement while the drag source is over the drop zone.
4310      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
4311      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
4312      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
4313      * registered node, it will call {@link #onContainerOver}.
4314      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4315      * @param {Event} e The event
4316      * @param {Object} data An object containing arbitrary data supplied by the drag source
4317      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4318      * underlying {@link Roo.dd.StatusProxy} can be updated
4319      */
4320     notifyOver : function(dd, e, data){
4321         var n = this.getTargetFromEvent(e);
4322         if(!n){ // not over valid drop target
4323             if(this.lastOverNode){
4324                 this.onNodeOut(this.lastOverNode, dd, e, data);
4325                 this.lastOverNode = null;
4326             }
4327             return this.onContainerOver(dd, e, data);
4328         }
4329         if(this.lastOverNode != n){
4330             if(this.lastOverNode){
4331                 this.onNodeOut(this.lastOverNode, dd, e, data);
4332             }
4333             this.onNodeEnter(n, dd, e, data);
4334             this.lastOverNode = n;
4335         }
4336         return this.onNodeOver(n, dd, e, data);
4337     },
4338
4339     /**
4340      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
4341      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
4342      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
4343      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
4344      * @param {Event} e The event
4345      * @param {Object} data An object containing arbitrary data supplied by the drag zone
4346      */
4347     notifyOut : function(dd, e, data){
4348         if(this.lastOverNode){
4349             this.onNodeOut(this.lastOverNode, dd, e, data);
4350             this.lastOverNode = null;
4351         }
4352     },
4353
4354     /**
4355      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
4356      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
4357      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
4358      * otherwise it will call {@link #onContainerDrop}.
4359      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4360      * @param {Event} e The event
4361      * @param {Object} data An object containing arbitrary data supplied by the drag source
4362      * @return {Boolean} True if the drop was valid, else false
4363      */
4364     notifyDrop : function(dd, e, data){
4365         if(this.lastOverNode){
4366             this.onNodeOut(this.lastOverNode, dd, e, data);
4367             this.lastOverNode = null;
4368         }
4369         var n = this.getTargetFromEvent(e);
4370         return n ?
4371             this.onNodeDrop(n, dd, e, data) :
4372             this.onContainerDrop(dd, e, data);
4373     },
4374
4375     // private
4376     triggerCacheRefresh : function(){
4377         Roo.dd.DDM.refreshCache(this.groups);
4378     }  
4379 });/*
4380  * Based on:
4381  * Ext JS Library 1.1.1
4382  * Copyright(c) 2006-2007, Ext JS, LLC.
4383  *
4384  * Originally Released Under LGPL - original licence link has changed is not relivant.
4385  *
4386  * Fork - LGPL
4387  * <script type="text/javascript">
4388  */
4389
4390
4391 /**
4392  * @class Roo.data.SortTypes
4393  * @singleton
4394  * Defines the default sorting (casting?) comparison functions used when sorting data.
4395  */
4396 Roo.data.SortTypes = {
4397     /**
4398      * Default sort that does nothing
4399      * @param {Mixed} s The value being converted
4400      * @return {Mixed} The comparison value
4401      */
4402     none : function(s){
4403         return s;
4404     },
4405     
4406     /**
4407      * The regular expression used to strip tags
4408      * @type {RegExp}
4409      * @property
4410      */
4411     stripTagsRE : /<\/?[^>]+>/gi,
4412     
4413     /**
4414      * Strips all HTML tags to sort on text only
4415      * @param {Mixed} s The value being converted
4416      * @return {String} The comparison value
4417      */
4418     asText : function(s){
4419         return String(s).replace(this.stripTagsRE, "");
4420     },
4421     
4422     /**
4423      * Strips all HTML tags to sort on text only - Case insensitive
4424      * @param {Mixed} s The value being converted
4425      * @return {String} The comparison value
4426      */
4427     asUCText : function(s){
4428         return String(s).toUpperCase().replace(this.stripTagsRE, "");
4429     },
4430     
4431     /**
4432      * Case insensitive string
4433      * @param {Mixed} s The value being converted
4434      * @return {String} The comparison value
4435      */
4436     asUCString : function(s) {
4437         return String(s).toUpperCase();
4438     },
4439     
4440     /**
4441      * Date sorting
4442      * @param {Mixed} s The value being converted
4443      * @return {Number} The comparison value
4444      */
4445     asDate : function(s) {
4446         if(!s){
4447             return 0;
4448         }
4449         if(s instanceof Date){
4450             return s.getTime();
4451         }
4452         return Date.parse(String(s));
4453     },
4454     
4455     /**
4456      * Float sorting
4457      * @param {Mixed} s The value being converted
4458      * @return {Float} The comparison value
4459      */
4460     asFloat : function(s) {
4461         var val = parseFloat(String(s).replace(/,/g, ""));
4462         if(isNaN(val)) val = 0;
4463         return val;
4464     },
4465     
4466     /**
4467      * Integer sorting
4468      * @param {Mixed} s The value being converted
4469      * @return {Number} The comparison value
4470      */
4471     asInt : function(s) {
4472         var val = parseInt(String(s).replace(/,/g, ""));
4473         if(isNaN(val)) val = 0;
4474         return val;
4475     }
4476 };/*
4477  * Based on:
4478  * Ext JS Library 1.1.1
4479  * Copyright(c) 2006-2007, Ext JS, LLC.
4480  *
4481  * Originally Released Under LGPL - original licence link has changed is not relivant.
4482  *
4483  * Fork - LGPL
4484  * <script type="text/javascript">
4485  */
4486
4487 /**
4488 * @class Roo.data.Record
4489  * Instances of this class encapsulate both record <em>definition</em> information, and record
4490  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
4491  * to access Records cached in an {@link Roo.data.Store} object.<br>
4492  * <p>
4493  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
4494  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
4495  * objects.<br>
4496  * <p>
4497  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
4498  * @constructor
4499  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
4500  * {@link #create}. The parameters are the same.
4501  * @param {Array} data An associative Array of data values keyed by the field name.
4502  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
4503  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
4504  * not specified an integer id is generated.
4505  */
4506 Roo.data.Record = function(data, id){
4507     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
4508     this.data = data;
4509 };
4510
4511 /**
4512  * Generate a constructor for a specific record layout.
4513  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
4514  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
4515  * Each field definition object may contain the following properties: <ul>
4516  * <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,
4517  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
4518  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
4519  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
4520  * is being used, then this is a string containing the javascript expression to reference the data relative to 
4521  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
4522  * to the data item relative to the record element. If the mapping expression is the same as the field name,
4523  * this may be omitted.</p></li>
4524  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
4525  * <ul><li>auto (Default, implies no conversion)</li>
4526  * <li>string</li>
4527  * <li>int</li>
4528  * <li>float</li>
4529  * <li>boolean</li>
4530  * <li>date</li></ul></p></li>
4531  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
4532  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
4533  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
4534  * by the Reader into an object that will be stored in the Record. It is passed the
4535  * following parameters:<ul>
4536  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
4537  * </ul></p></li>
4538  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
4539  * </ul>
4540  * <br>usage:<br><pre><code>
4541 var TopicRecord = Roo.data.Record.create(
4542     {name: 'title', mapping: 'topic_title'},
4543     {name: 'author', mapping: 'username'},
4544     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
4545     {name: 'lastPost', mapping: 'post_time', type: 'date'},
4546     {name: 'lastPoster', mapping: 'user2'},
4547     {name: 'excerpt', mapping: 'post_text'}
4548 );
4549
4550 var myNewRecord = new TopicRecord({
4551     title: 'Do my job please',
4552     author: 'noobie',
4553     totalPosts: 1,
4554     lastPost: new Date(),
4555     lastPoster: 'Animal',
4556     excerpt: 'No way dude!'
4557 });
4558 myStore.add(myNewRecord);
4559 </code></pre>
4560  * @method create
4561  * @static
4562  */
4563 Roo.data.Record.create = function(o){
4564     var f = function(){
4565         f.superclass.constructor.apply(this, arguments);
4566     };
4567     Roo.extend(f, Roo.data.Record);
4568     var p = f.prototype;
4569     p.fields = new Roo.util.MixedCollection(false, function(field){
4570         return field.name;
4571     });
4572     for(var i = 0, len = o.length; i < len; i++){
4573         p.fields.add(new Roo.data.Field(o[i]));
4574     }
4575     f.getField = function(name){
4576         return p.fields.get(name);  
4577     };
4578     return f;
4579 };
4580
4581 Roo.data.Record.AUTO_ID = 1000;
4582 Roo.data.Record.EDIT = 'edit';
4583 Roo.data.Record.REJECT = 'reject';
4584 Roo.data.Record.COMMIT = 'commit';
4585
4586 Roo.data.Record.prototype = {
4587     /**
4588      * Readonly flag - true if this record has been modified.
4589      * @type Boolean
4590      */
4591     dirty : false,
4592     editing : false,
4593     error: null,
4594     modified: null,
4595
4596     // private
4597     join : function(store){
4598         this.store = store;
4599     },
4600
4601     /**
4602      * Set the named field to the specified value.
4603      * @param {String} name The name of the field to set.
4604      * @param {Object} value The value to set the field to.
4605      */
4606     set : function(name, value){
4607         if(this.data[name] == value){
4608             return;
4609         }
4610         this.dirty = true;
4611         if(!this.modified){
4612             this.modified = {};
4613         }
4614         if(typeof this.modified[name] == 'undefined'){
4615             this.modified[name] = this.data[name];
4616         }
4617         this.data[name] = value;
4618         if(!this.editing && this.store){
4619             this.store.afterEdit(this);
4620         }       
4621     },
4622
4623     /**
4624      * Get the value of the named field.
4625      * @param {String} name The name of the field to get the value of.
4626      * @return {Object} The value of the field.
4627      */
4628     get : function(name){
4629         return this.data[name]; 
4630     },
4631
4632     // private
4633     beginEdit : function(){
4634         this.editing = true;
4635         this.modified = {}; 
4636     },
4637
4638     // private
4639     cancelEdit : function(){
4640         this.editing = false;
4641         delete this.modified;
4642     },
4643
4644     // private
4645     endEdit : function(){
4646         this.editing = false;
4647         if(this.dirty && this.store){
4648             this.store.afterEdit(this);
4649         }
4650     },
4651
4652     /**
4653      * Usually called by the {@link Roo.data.Store} which owns the Record.
4654      * Rejects all changes made to the Record since either creation, or the last commit operation.
4655      * Modified fields are reverted to their original values.
4656      * <p>
4657      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4658      * of reject operations.
4659      */
4660     reject : function(){
4661         var m = this.modified;
4662         for(var n in m){
4663             if(typeof m[n] != "function"){
4664                 this.data[n] = m[n];
4665             }
4666         }
4667         this.dirty = false;
4668         delete this.modified;
4669         this.editing = false;
4670         if(this.store){
4671             this.store.afterReject(this);
4672         }
4673     },
4674
4675     /**
4676      * Usually called by the {@link Roo.data.Store} which owns the Record.
4677      * Commits all changes made to the Record since either creation, or the last commit operation.
4678      * <p>
4679      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4680      * of commit operations.
4681      */
4682     commit : function(){
4683         this.dirty = false;
4684         delete this.modified;
4685         this.editing = false;
4686         if(this.store){
4687             this.store.afterCommit(this);
4688         }
4689     },
4690
4691     // private
4692     hasError : function(){
4693         return this.error != null;
4694     },
4695
4696     // private
4697     clearError : function(){
4698         this.error = null;
4699     },
4700
4701     /**
4702      * Creates a copy of this record.
4703      * @param {String} id (optional) A new record id if you don't want to use this record's id
4704      * @return {Record}
4705      */
4706     copy : function(newId) {
4707         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
4708     }
4709 };/*
4710  * Based on:
4711  * Ext JS Library 1.1.1
4712  * Copyright(c) 2006-2007, Ext JS, LLC.
4713  *
4714  * Originally Released Under LGPL - original licence link has changed is not relivant.
4715  *
4716  * Fork - LGPL
4717  * <script type="text/javascript">
4718  */
4719
4720
4721
4722 /**
4723  * @class Roo.data.Store
4724  * @extends Roo.util.Observable
4725  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
4726  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
4727  * <p>
4728  * 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
4729  * has no knowledge of the format of the data returned by the Proxy.<br>
4730  * <p>
4731  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
4732  * instances from the data object. These records are cached and made available through accessor functions.
4733  * @constructor
4734  * Creates a new Store.
4735  * @param {Object} config A config object containing the objects needed for the Store to access data,
4736  * and read the data into Records.
4737  */
4738 Roo.data.Store = function(config){
4739     this.data = new Roo.util.MixedCollection(false);
4740     this.data.getKey = function(o){
4741         return o.id;
4742     };
4743     this.baseParams = {};
4744     // private
4745     this.paramNames = {
4746         "start" : "start",
4747         "limit" : "limit",
4748         "sort" : "sort",
4749         "dir" : "dir",
4750         "multisort" : "_multisort"
4751     };
4752
4753     if(config && config.data){
4754         this.inlineData = config.data;
4755         delete config.data;
4756     }
4757
4758     Roo.apply(this, config);
4759     
4760     if(this.reader){ // reader passed
4761         this.reader = Roo.factory(this.reader, Roo.data);
4762         this.reader.xmodule = this.xmodule || false;
4763         if(!this.recordType){
4764             this.recordType = this.reader.recordType;
4765         }
4766         if(this.reader.onMetaChange){
4767             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
4768         }
4769     }
4770
4771     if(this.recordType){
4772         this.fields = this.recordType.prototype.fields;
4773     }
4774     this.modified = [];
4775
4776     this.addEvents({
4777         /**
4778          * @event datachanged
4779          * Fires when the data cache has changed, and a widget which is using this Store
4780          * as a Record cache should refresh its view.
4781          * @param {Store} this
4782          */
4783         datachanged : true,
4784         /**
4785          * @event metachange
4786          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
4787          * @param {Store} this
4788          * @param {Object} meta The JSON metadata
4789          */
4790         metachange : true,
4791         /**
4792          * @event add
4793          * Fires when Records have been added to the Store
4794          * @param {Store} this
4795          * @param {Roo.data.Record[]} records The array of Records added
4796          * @param {Number} index The index at which the record(s) were added
4797          */
4798         add : true,
4799         /**
4800          * @event remove
4801          * Fires when a Record has been removed from the Store
4802          * @param {Store} this
4803          * @param {Roo.data.Record} record The Record that was removed
4804          * @param {Number} index The index at which the record was removed
4805          */
4806         remove : true,
4807         /**
4808          * @event update
4809          * Fires when a Record has been updated
4810          * @param {Store} this
4811          * @param {Roo.data.Record} record The Record that was updated
4812          * @param {String} operation The update operation being performed.  Value may be one of:
4813          * <pre><code>
4814  Roo.data.Record.EDIT
4815  Roo.data.Record.REJECT
4816  Roo.data.Record.COMMIT
4817          * </code></pre>
4818          */
4819         update : true,
4820         /**
4821          * @event clear
4822          * Fires when the data cache has been cleared.
4823          * @param {Store} this
4824          */
4825         clear : true,
4826         /**
4827          * @event beforeload
4828          * Fires before a request is made for a new data object.  If the beforeload handler returns false
4829          * the load action will be canceled.
4830          * @param {Store} this
4831          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4832          */
4833         beforeload : true,
4834         /**
4835          * @event beforeloadadd
4836          * Fires after a new set of Records has been loaded.
4837          * @param {Store} this
4838          * @param {Roo.data.Record[]} records The Records that were loaded
4839          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4840          */
4841         beforeloadadd : true,
4842         /**
4843          * @event load
4844          * Fires after a new set of Records has been loaded, before they are added to the store.
4845          * @param {Store} this
4846          * @param {Roo.data.Record[]} records The Records that were loaded
4847          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4848          * @params {Object} return from reader
4849          */
4850         load : true,
4851         /**
4852          * @event loadexception
4853          * Fires if an exception occurs in the Proxy during loading.
4854          * Called with the signature of the Proxy's "loadexception" event.
4855          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
4856          * 
4857          * @param {Proxy} 
4858          * @param {Object} return from JsonData.reader() - success, totalRecords, records
4859          * @param {Object} load options 
4860          * @param {Object} jsonData from your request (normally this contains the Exception)
4861          */
4862         loadexception : true
4863     });
4864     
4865     if(this.proxy){
4866         this.proxy = Roo.factory(this.proxy, Roo.data);
4867         this.proxy.xmodule = this.xmodule || false;
4868         this.relayEvents(this.proxy,  ["loadexception"]);
4869     }
4870     this.sortToggle = {};
4871     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
4872
4873     Roo.data.Store.superclass.constructor.call(this);
4874
4875     if(this.inlineData){
4876         this.loadData(this.inlineData);
4877         delete this.inlineData;
4878     }
4879 };
4880
4881 Roo.extend(Roo.data.Store, Roo.util.Observable, {
4882      /**
4883     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
4884     * without a remote query - used by combo/forms at present.
4885     */
4886     
4887     /**
4888     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
4889     */
4890     /**
4891     * @cfg {Array} data Inline data to be loaded when the store is initialized.
4892     */
4893     /**
4894     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
4895     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
4896     */
4897     /**
4898     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
4899     * on any HTTP request
4900     */
4901     /**
4902     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
4903     */
4904     /**
4905     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
4906     */
4907     multiSort: false,
4908     /**
4909     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
4910     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
4911     */
4912     remoteSort : false,
4913
4914     /**
4915     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
4916      * loaded or when a record is removed. (defaults to false).
4917     */
4918     pruneModifiedRecords : false,
4919
4920     // private
4921     lastOptions : null,
4922
4923     /**
4924      * Add Records to the Store and fires the add event.
4925      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4926      */
4927     add : function(records){
4928         records = [].concat(records);
4929         for(var i = 0, len = records.length; i < len; i++){
4930             records[i].join(this);
4931         }
4932         var index = this.data.length;
4933         this.data.addAll(records);
4934         this.fireEvent("add", this, records, index);
4935     },
4936
4937     /**
4938      * Remove a Record from the Store and fires the remove event.
4939      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
4940      */
4941     remove : function(record){
4942         var index = this.data.indexOf(record);
4943         this.data.removeAt(index);
4944         if(this.pruneModifiedRecords){
4945             this.modified.remove(record);
4946         }
4947         this.fireEvent("remove", this, record, index);
4948     },
4949
4950     /**
4951      * Remove all Records from the Store and fires the clear event.
4952      */
4953     removeAll : function(){
4954         this.data.clear();
4955         if(this.pruneModifiedRecords){
4956             this.modified = [];
4957         }
4958         this.fireEvent("clear", this);
4959     },
4960
4961     /**
4962      * Inserts Records to the Store at the given index and fires the add event.
4963      * @param {Number} index The start index at which to insert the passed Records.
4964      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4965      */
4966     insert : function(index, records){
4967         records = [].concat(records);
4968         for(var i = 0, len = records.length; i < len; i++){
4969             this.data.insert(index, records[i]);
4970             records[i].join(this);
4971         }
4972         this.fireEvent("add", this, records, index);
4973     },
4974
4975     /**
4976      * Get the index within the cache of the passed Record.
4977      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
4978      * @return {Number} The index of the passed Record. Returns -1 if not found.
4979      */
4980     indexOf : function(record){
4981         return this.data.indexOf(record);
4982     },
4983
4984     /**
4985      * Get the index within the cache of the Record with the passed id.
4986      * @param {String} id The id of the Record to find.
4987      * @return {Number} The index of the Record. Returns -1 if not found.
4988      */
4989     indexOfId : function(id){
4990         return this.data.indexOfKey(id);
4991     },
4992
4993     /**
4994      * Get the Record with the specified id.
4995      * @param {String} id The id of the Record to find.
4996      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
4997      */
4998     getById : function(id){
4999         return this.data.key(id);
5000     },
5001
5002     /**
5003      * Get the Record at the specified index.
5004      * @param {Number} index The index of the Record to find.
5005      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
5006      */
5007     getAt : function(index){
5008         return this.data.itemAt(index);
5009     },
5010
5011     /**
5012      * Returns a range of Records between specified indices.
5013      * @param {Number} startIndex (optional) The starting index (defaults to 0)
5014      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
5015      * @return {Roo.data.Record[]} An array of Records
5016      */
5017     getRange : function(start, end){
5018         return this.data.getRange(start, end);
5019     },
5020
5021     // private
5022     storeOptions : function(o){
5023         o = Roo.apply({}, o);
5024         delete o.callback;
5025         delete o.scope;
5026         this.lastOptions = o;
5027     },
5028
5029     /**
5030      * Loads the Record cache from the configured Proxy using the configured Reader.
5031      * <p>
5032      * If using remote paging, then the first load call must specify the <em>start</em>
5033      * and <em>limit</em> properties in the options.params property to establish the initial
5034      * position within the dataset, and the number of Records to cache on each read from the Proxy.
5035      * <p>
5036      * <strong>It is important to note that for remote data sources, loading is asynchronous,
5037      * and this call will return before the new data has been loaded. Perform any post-processing
5038      * in a callback function, or in a "load" event handler.</strong>
5039      * <p>
5040      * @param {Object} options An object containing properties which control loading options:<ul>
5041      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
5042      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
5043      * passed the following arguments:<ul>
5044      * <li>r : Roo.data.Record[]</li>
5045      * <li>options: Options object from the load call</li>
5046      * <li>success: Boolean success indicator</li></ul></li>
5047      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
5048      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
5049      * </ul>
5050      */
5051     load : function(options){
5052         options = options || {};
5053         if(this.fireEvent("beforeload", this, options) !== false){
5054             this.storeOptions(options);
5055             var p = Roo.apply(options.params || {}, this.baseParams);
5056             // if meta was not loaded from remote source.. try requesting it.
5057             if (!this.reader.metaFromRemote) {
5058                 p._requestMeta = 1;
5059             }
5060             if(this.sortInfo && this.remoteSort){
5061                 var pn = this.paramNames;
5062                 p[pn["sort"]] = this.sortInfo.field;
5063                 p[pn["dir"]] = this.sortInfo.direction;
5064             }
5065             if (this.multiSort) {
5066                 var pn = this.paramNames;
5067                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
5068             }
5069             
5070             this.proxy.load(p, this.reader, this.loadRecords, this, options);
5071         }
5072     },
5073
5074     /**
5075      * Reloads the Record cache from the configured Proxy using the configured Reader and
5076      * the options from the last load operation performed.
5077      * @param {Object} options (optional) An object containing properties which may override the options
5078      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
5079      * the most recently used options are reused).
5080      */
5081     reload : function(options){
5082         this.load(Roo.applyIf(options||{}, this.lastOptions));
5083     },
5084
5085     // private
5086     // Called as a callback by the Reader during a load operation.
5087     loadRecords : function(o, options, success){
5088         if(!o || success === false){
5089             if(success !== false){
5090                 this.fireEvent("load", this, [], options, o);
5091             }
5092             if(options.callback){
5093                 options.callback.call(options.scope || this, [], options, false);
5094             }
5095             return;
5096         }
5097         // if data returned failure - throw an exception.
5098         if (o.success === false) {
5099             // show a message if no listener is registered.
5100             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
5101                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
5102             }
5103             // loadmask wil be hooked into this..
5104             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
5105             return;
5106         }
5107         var r = o.records, t = o.totalRecords || r.length;
5108         
5109         this.fireEvent("beforeloadadd", this, r, options, o);
5110         
5111         if(!options || options.add !== true){
5112             if(this.pruneModifiedRecords){
5113                 this.modified = [];
5114             }
5115             for(var i = 0, len = r.length; i < len; i++){
5116                 r[i].join(this);
5117             }
5118             if(this.snapshot){
5119                 this.data = this.snapshot;
5120                 delete this.snapshot;
5121             }
5122             this.data.clear();
5123             this.data.addAll(r);
5124             this.totalLength = t;
5125             this.applySort();
5126             this.fireEvent("datachanged", this);
5127         }else{
5128             this.totalLength = Math.max(t, this.data.length+r.length);
5129             this.add(r);
5130         }
5131         this.fireEvent("load", this, r, options, o);
5132         if(options.callback){
5133             options.callback.call(options.scope || this, r, options, true);
5134         }
5135     },
5136
5137
5138     /**
5139      * Loads data from a passed data block. A Reader which understands the format of the data
5140      * must have been configured in the constructor.
5141      * @param {Object} data The data block from which to read the Records.  The format of the data expected
5142      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
5143      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
5144      */
5145     loadData : function(o, append){
5146         var r = this.reader.readRecords(o);
5147         this.loadRecords(r, {add: append}, true);
5148     },
5149
5150     /**
5151      * Gets the number of cached records.
5152      * <p>
5153      * <em>If using paging, this may not be the total size of the dataset. If the data object
5154      * used by the Reader contains the dataset size, then the getTotalCount() function returns
5155      * the data set size</em>
5156      */
5157     getCount : function(){
5158         return this.data.length || 0;
5159     },
5160
5161     /**
5162      * Gets the total number of records in the dataset as returned by the server.
5163      * <p>
5164      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
5165      * the dataset size</em>
5166      */
5167     getTotalCount : function(){
5168         return this.totalLength || 0;
5169     },
5170
5171     /**
5172      * Returns the sort state of the Store as an object with two properties:
5173      * <pre><code>
5174  field {String} The name of the field by which the Records are sorted
5175  direction {String} The sort order, "ASC" or "DESC"
5176      * </code></pre>
5177      */
5178     getSortState : function(){
5179         return this.sortInfo;
5180     },
5181
5182     // private
5183     applySort : function(){
5184         if(this.sortInfo && !this.remoteSort){
5185             var s = this.sortInfo, f = s.field;
5186             var st = this.fields.get(f).sortType;
5187             var fn = function(r1, r2){
5188                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
5189                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
5190             };
5191             this.data.sort(s.direction, fn);
5192             if(this.snapshot && this.snapshot != this.data){
5193                 this.snapshot.sort(s.direction, fn);
5194             }
5195         }
5196     },
5197
5198     /**
5199      * Sets the default sort column and order to be used by the next load operation.
5200      * @param {String} fieldName The name of the field to sort by.
5201      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5202      */
5203     setDefaultSort : function(field, dir){
5204         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
5205     },
5206
5207     /**
5208      * Sort the Records.
5209      * If remote sorting is used, the sort is performed on the server, and the cache is
5210      * reloaded. If local sorting is used, the cache is sorted internally.
5211      * @param {String} fieldName The name of the field to sort by.
5212      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5213      */
5214     sort : function(fieldName, dir){
5215         var f = this.fields.get(fieldName);
5216         if(!dir){
5217             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
5218             
5219             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
5220                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
5221             }else{
5222                 dir = f.sortDir;
5223             }
5224         }
5225         this.sortToggle[f.name] = dir;
5226         this.sortInfo = {field: f.name, direction: dir};
5227         if(!this.remoteSort){
5228             this.applySort();
5229             this.fireEvent("datachanged", this);
5230         }else{
5231             this.load(this.lastOptions);
5232         }
5233     },
5234
5235     /**
5236      * Calls the specified function for each of the Records in the cache.
5237      * @param {Function} fn The function to call. The Record is passed as the first parameter.
5238      * Returning <em>false</em> aborts and exits the iteration.
5239      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
5240      */
5241     each : function(fn, scope){
5242         this.data.each(fn, scope);
5243     },
5244
5245     /**
5246      * Gets all records modified since the last commit.  Modified records are persisted across load operations
5247      * (e.g., during paging).
5248      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
5249      */
5250     getModifiedRecords : function(){
5251         return this.modified;
5252     },
5253
5254     // private
5255     createFilterFn : function(property, value, anyMatch){
5256         if(!value.exec){ // not a regex
5257             value = String(value);
5258             if(value.length == 0){
5259                 return false;
5260             }
5261             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
5262         }
5263         return function(r){
5264             return value.test(r.data[property]);
5265         };
5266     },
5267
5268     /**
5269      * Sums the value of <i>property</i> for each record between start and end and returns the result.
5270      * @param {String} property A field on your records
5271      * @param {Number} start The record index to start at (defaults to 0)
5272      * @param {Number} end The last record index to include (defaults to length - 1)
5273      * @return {Number} The sum
5274      */
5275     sum : function(property, start, end){
5276         var rs = this.data.items, v = 0;
5277         start = start || 0;
5278         end = (end || end === 0) ? end : rs.length-1;
5279
5280         for(var i = start; i <= end; i++){
5281             v += (rs[i].data[property] || 0);
5282         }
5283         return v;
5284     },
5285
5286     /**
5287      * Filter the records by a specified property.
5288      * @param {String} field A field on your records
5289      * @param {String/RegExp} value Either a string that the field
5290      * should start with or a RegExp to test against the field
5291      * @param {Boolean} anyMatch True to match any part not just the beginning
5292      */
5293     filter : function(property, value, anyMatch){
5294         var fn = this.createFilterFn(property, value, anyMatch);
5295         return fn ? this.filterBy(fn) : this.clearFilter();
5296     },
5297
5298     /**
5299      * Filter by a function. The specified function will be called with each
5300      * record in this data source. If the function returns true the record is included,
5301      * otherwise it is filtered.
5302      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5303      * @param {Object} scope (optional) The scope of the function (defaults to this)
5304      */
5305     filterBy : function(fn, scope){
5306         this.snapshot = this.snapshot || this.data;
5307         this.data = this.queryBy(fn, scope||this);
5308         this.fireEvent("datachanged", this);
5309     },
5310
5311     /**
5312      * Query the records by a specified property.
5313      * @param {String} field A field on your records
5314      * @param {String/RegExp} value Either a string that the field
5315      * should start with or a RegExp to test against the field
5316      * @param {Boolean} anyMatch True to match any part not just the beginning
5317      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5318      */
5319     query : function(property, value, anyMatch){
5320         var fn = this.createFilterFn(property, value, anyMatch);
5321         return fn ? this.queryBy(fn) : this.data.clone();
5322     },
5323
5324     /**
5325      * Query by a function. The specified function will be called with each
5326      * record in this data source. If the function returns true the record is included
5327      * in the results.
5328      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5329      * @param {Object} scope (optional) The scope of the function (defaults to this)
5330       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5331      **/
5332     queryBy : function(fn, scope){
5333         var data = this.snapshot || this.data;
5334         return data.filterBy(fn, scope||this);
5335     },
5336
5337     /**
5338      * Collects unique values for a particular dataIndex from this store.
5339      * @param {String} dataIndex The property to collect
5340      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
5341      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
5342      * @return {Array} An array of the unique values
5343      **/
5344     collect : function(dataIndex, allowNull, bypassFilter){
5345         var d = (bypassFilter === true && this.snapshot) ?
5346                 this.snapshot.items : this.data.items;
5347         var v, sv, r = [], l = {};
5348         for(var i = 0, len = d.length; i < len; i++){
5349             v = d[i].data[dataIndex];
5350             sv = String(v);
5351             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
5352                 l[sv] = true;
5353                 r[r.length] = v;
5354             }
5355         }
5356         return r;
5357     },
5358
5359     /**
5360      * Revert to a view of the Record cache with no filtering applied.
5361      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
5362      */
5363     clearFilter : function(suppressEvent){
5364         if(this.snapshot && this.snapshot != this.data){
5365             this.data = this.snapshot;
5366             delete this.snapshot;
5367             if(suppressEvent !== true){
5368                 this.fireEvent("datachanged", this);
5369             }
5370         }
5371     },
5372
5373     // private
5374     afterEdit : function(record){
5375         if(this.modified.indexOf(record) == -1){
5376             this.modified.push(record);
5377         }
5378         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
5379     },
5380     
5381     // private
5382     afterReject : function(record){
5383         this.modified.remove(record);
5384         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
5385     },
5386
5387     // private
5388     afterCommit : function(record){
5389         this.modified.remove(record);
5390         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
5391     },
5392
5393     /**
5394      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
5395      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
5396      */
5397     commitChanges : function(){
5398         var m = this.modified.slice(0);
5399         this.modified = [];
5400         for(var i = 0, len = m.length; i < len; i++){
5401             m[i].commit();
5402         }
5403     },
5404
5405     /**
5406      * Cancel outstanding changes on all changed records.
5407      */
5408     rejectChanges : function(){
5409         var m = this.modified.slice(0);
5410         this.modified = [];
5411         for(var i = 0, len = m.length; i < len; i++){
5412             m[i].reject();
5413         }
5414     },
5415
5416     onMetaChange : function(meta, rtype, o){
5417         this.recordType = rtype;
5418         this.fields = rtype.prototype.fields;
5419         delete this.snapshot;
5420         this.sortInfo = meta.sortInfo || this.sortInfo;
5421         this.modified = [];
5422         this.fireEvent('metachange', this, this.reader.meta);
5423     }
5424 });/*
5425  * Based on:
5426  * Ext JS Library 1.1.1
5427  * Copyright(c) 2006-2007, Ext JS, LLC.
5428  *
5429  * Originally Released Under LGPL - original licence link has changed is not relivant.
5430  *
5431  * Fork - LGPL
5432  * <script type="text/javascript">
5433  */
5434
5435 /**
5436  * @class Roo.data.SimpleStore
5437  * @extends Roo.data.Store
5438  * Small helper class to make creating Stores from Array data easier.
5439  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
5440  * @cfg {Array} fields An array of field definition objects, or field name strings.
5441  * @cfg {Array} data The multi-dimensional array of data
5442  * @constructor
5443  * @param {Object} config
5444  */
5445 Roo.data.SimpleStore = function(config){
5446     Roo.data.SimpleStore.superclass.constructor.call(this, {
5447         isLocal : true,
5448         reader: new Roo.data.ArrayReader({
5449                 id: config.id
5450             },
5451             Roo.data.Record.create(config.fields)
5452         ),
5453         proxy : new Roo.data.MemoryProxy(config.data)
5454     });
5455     this.load();
5456 };
5457 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
5458  * Based on:
5459  * Ext JS Library 1.1.1
5460  * Copyright(c) 2006-2007, Ext JS, LLC.
5461  *
5462  * Originally Released Under LGPL - original licence link has changed is not relivant.
5463  *
5464  * Fork - LGPL
5465  * <script type="text/javascript">
5466  */
5467
5468 /**
5469 /**
5470  * @extends Roo.data.Store
5471  * @class Roo.data.JsonStore
5472  * Small helper class to make creating Stores for JSON data easier. <br/>
5473 <pre><code>
5474 var store = new Roo.data.JsonStore({
5475     url: 'get-images.php',
5476     root: 'images',
5477     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
5478 });
5479 </code></pre>
5480  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
5481  * JsonReader and HttpProxy (unless inline data is provided).</b>
5482  * @cfg {Array} fields An array of field definition objects, or field name strings.
5483  * @constructor
5484  * @param {Object} config
5485  */
5486 Roo.data.JsonStore = function(c){
5487     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
5488         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
5489         reader: new Roo.data.JsonReader(c, c.fields)
5490     }));
5491 };
5492 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
5493  * Based on:
5494  * Ext JS Library 1.1.1
5495  * Copyright(c) 2006-2007, Ext JS, LLC.
5496  *
5497  * Originally Released Under LGPL - original licence link has changed is not relivant.
5498  *
5499  * Fork - LGPL
5500  * <script type="text/javascript">
5501  */
5502
5503  
5504 Roo.data.Field = function(config){
5505     if(typeof config == "string"){
5506         config = {name: config};
5507     }
5508     Roo.apply(this, config);
5509     
5510     if(!this.type){
5511         this.type = "auto";
5512     }
5513     
5514     var st = Roo.data.SortTypes;
5515     // named sortTypes are supported, here we look them up
5516     if(typeof this.sortType == "string"){
5517         this.sortType = st[this.sortType];
5518     }
5519     
5520     // set default sortType for strings and dates
5521     if(!this.sortType){
5522         switch(this.type){
5523             case "string":
5524                 this.sortType = st.asUCString;
5525                 break;
5526             case "date":
5527                 this.sortType = st.asDate;
5528                 break;
5529             default:
5530                 this.sortType = st.none;
5531         }
5532     }
5533
5534     // define once
5535     var stripRe = /[\$,%]/g;
5536
5537     // prebuilt conversion function for this field, instead of
5538     // switching every time we're reading a value
5539     if(!this.convert){
5540         var cv, dateFormat = this.dateFormat;
5541         switch(this.type){
5542             case "":
5543             case "auto":
5544             case undefined:
5545                 cv = function(v){ return v; };
5546                 break;
5547             case "string":
5548                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
5549                 break;
5550             case "int":
5551                 cv = function(v){
5552                     return v !== undefined && v !== null && v !== '' ?
5553                            parseInt(String(v).replace(stripRe, ""), 10) : '';
5554                     };
5555                 break;
5556             case "float":
5557                 cv = function(v){
5558                     return v !== undefined && v !== null && v !== '' ?
5559                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
5560                     };
5561                 break;
5562             case "bool":
5563             case "boolean":
5564                 cv = function(v){ return v === true || v === "true" || v == 1; };
5565                 break;
5566             case "date":
5567                 cv = function(v){
5568                     if(!v){
5569                         return '';
5570                     }
5571                     if(v instanceof Date){
5572                         return v;
5573                     }
5574                     if(dateFormat){
5575                         if(dateFormat == "timestamp"){
5576                             return new Date(v*1000);
5577                         }
5578                         return Date.parseDate(v, dateFormat);
5579                     }
5580                     var parsed = Date.parse(v);
5581                     return parsed ? new Date(parsed) : null;
5582                 };
5583              break;
5584             
5585         }
5586         this.convert = cv;
5587     }
5588 };
5589
5590 Roo.data.Field.prototype = {
5591     dateFormat: null,
5592     defaultValue: "",
5593     mapping: null,
5594     sortType : null,
5595     sortDir : "ASC"
5596 };/*
5597  * Based on:
5598  * Ext JS Library 1.1.1
5599  * Copyright(c) 2006-2007, Ext JS, LLC.
5600  *
5601  * Originally Released Under LGPL - original licence link has changed is not relivant.
5602  *
5603  * Fork - LGPL
5604  * <script type="text/javascript">
5605  */
5606  
5607 // Base class for reading structured data from a data source.  This class is intended to be
5608 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
5609
5610 /**
5611  * @class Roo.data.DataReader
5612  * Base class for reading structured data from a data source.  This class is intended to be
5613  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
5614  */
5615
5616 Roo.data.DataReader = function(meta, recordType){
5617     
5618     this.meta = meta;
5619     
5620     this.recordType = recordType instanceof Array ? 
5621         Roo.data.Record.create(recordType) : recordType;
5622 };
5623
5624 Roo.data.DataReader.prototype = {
5625      /**
5626      * Create an empty record
5627      * @param {Object} data (optional) - overlay some values
5628      * @return {Roo.data.Record} record created.
5629      */
5630     newRow :  function(d) {
5631         var da =  {};
5632         this.recordType.prototype.fields.each(function(c) {
5633             switch( c.type) {
5634                 case 'int' : da[c.name] = 0; break;
5635                 case 'date' : da[c.name] = new Date(); break;
5636                 case 'float' : da[c.name] = 0.0; break;
5637                 case 'boolean' : da[c.name] = false; break;
5638                 default : da[c.name] = ""; break;
5639             }
5640             
5641         });
5642         return new this.recordType(Roo.apply(da, d));
5643     }
5644     
5645 };/*
5646  * Based on:
5647  * Ext JS Library 1.1.1
5648  * Copyright(c) 2006-2007, Ext JS, LLC.
5649  *
5650  * Originally Released Under LGPL - original licence link has changed is not relivant.
5651  *
5652  * Fork - LGPL
5653  * <script type="text/javascript">
5654  */
5655
5656 /**
5657  * @class Roo.data.DataProxy
5658  * @extends Roo.data.Observable
5659  * This class is an abstract base class for implementations which provide retrieval of
5660  * unformatted data objects.<br>
5661  * <p>
5662  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
5663  * (of the appropriate type which knows how to parse the data object) to provide a block of
5664  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
5665  * <p>
5666  * Custom implementations must implement the load method as described in
5667  * {@link Roo.data.HttpProxy#load}.
5668  */
5669 Roo.data.DataProxy = function(){
5670     this.addEvents({
5671         /**
5672          * @event beforeload
5673          * Fires before a network request is made to retrieve a data object.
5674          * @param {Object} This DataProxy object.
5675          * @param {Object} params The params parameter to the load function.
5676          */
5677         beforeload : true,
5678         /**
5679          * @event load
5680          * Fires before the load method's callback is called.
5681          * @param {Object} This DataProxy object.
5682          * @param {Object} o The data object.
5683          * @param {Object} arg The callback argument object passed to the load function.
5684          */
5685         load : true,
5686         /**
5687          * @event loadexception
5688          * Fires if an Exception occurs during data retrieval.
5689          * @param {Object} This DataProxy object.
5690          * @param {Object} o The data object.
5691          * @param {Object} arg The callback argument object passed to the load function.
5692          * @param {Object} e The Exception.
5693          */
5694         loadexception : true
5695     });
5696     Roo.data.DataProxy.superclass.constructor.call(this);
5697 };
5698
5699 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
5700
5701     /**
5702      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
5703      */
5704 /*
5705  * Based on:
5706  * Ext JS Library 1.1.1
5707  * Copyright(c) 2006-2007, Ext JS, LLC.
5708  *
5709  * Originally Released Under LGPL - original licence link has changed is not relivant.
5710  *
5711  * Fork - LGPL
5712  * <script type="text/javascript">
5713  */
5714 /**
5715  * @class Roo.data.MemoryProxy
5716  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
5717  * to the Reader when its load method is called.
5718  * @constructor
5719  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
5720  */
5721 Roo.data.MemoryProxy = function(data){
5722     if (data.data) {
5723         data = data.data;
5724     }
5725     Roo.data.MemoryProxy.superclass.constructor.call(this);
5726     this.data = data;
5727 };
5728
5729 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
5730     /**
5731      * Load data from the requested source (in this case an in-memory
5732      * data object passed to the constructor), read the data object into
5733      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5734      * process that block using the passed callback.
5735      * @param {Object} params This parameter is not used by the MemoryProxy class.
5736      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5737      * object into a block of Roo.data.Records.
5738      * @param {Function} callback The function into which to pass the block of Roo.data.records.
5739      * The function must be passed <ul>
5740      * <li>The Record block object</li>
5741      * <li>The "arg" argument from the load function</li>
5742      * <li>A boolean success indicator</li>
5743      * </ul>
5744      * @param {Object} scope The scope in which to call the callback
5745      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5746      */
5747     load : function(params, reader, callback, scope, arg){
5748         params = params || {};
5749         var result;
5750         try {
5751             result = reader.readRecords(this.data);
5752         }catch(e){
5753             this.fireEvent("loadexception", this, arg, null, e);
5754             callback.call(scope, null, arg, false);
5755             return;
5756         }
5757         callback.call(scope, result, arg, true);
5758     },
5759     
5760     // private
5761     update : function(params, records){
5762         
5763     }
5764 });/*
5765  * Based on:
5766  * Ext JS Library 1.1.1
5767  * Copyright(c) 2006-2007, Ext JS, LLC.
5768  *
5769  * Originally Released Under LGPL - original licence link has changed is not relivant.
5770  *
5771  * Fork - LGPL
5772  * <script type="text/javascript">
5773  */
5774 /**
5775  * @class Roo.data.HttpProxy
5776  * @extends Roo.data.DataProxy
5777  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
5778  * configured to reference a certain URL.<br><br>
5779  * <p>
5780  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
5781  * from which the running page was served.<br><br>
5782  * <p>
5783  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
5784  * <p>
5785  * Be aware that to enable the browser to parse an XML document, the server must set
5786  * the Content-Type header in the HTTP response to "text/xml".
5787  * @constructor
5788  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
5789  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
5790  * will be used to make the request.
5791  */
5792 Roo.data.HttpProxy = function(conn){
5793     Roo.data.HttpProxy.superclass.constructor.call(this);
5794     // is conn a conn config or a real conn?
5795     this.conn = conn;
5796     this.useAjax = !conn || !conn.events;
5797   
5798 };
5799
5800 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
5801     // thse are take from connection...
5802     
5803     /**
5804      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
5805      */
5806     /**
5807      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
5808      * extra parameters to each request made by this object. (defaults to undefined)
5809      */
5810     /**
5811      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
5812      *  to each request made by this object. (defaults to undefined)
5813      */
5814     /**
5815      * @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)
5816      */
5817     /**
5818      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
5819      */
5820      /**
5821      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
5822      * @type Boolean
5823      */
5824   
5825
5826     /**
5827      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
5828      * @type Boolean
5829      */
5830     /**
5831      * Return the {@link Roo.data.Connection} object being used by this Proxy.
5832      * @return {Connection} The Connection object. This object may be used to subscribe to events on
5833      * a finer-grained basis than the DataProxy events.
5834      */
5835     getConnection : function(){
5836         return this.useAjax ? Roo.Ajax : this.conn;
5837     },
5838
5839     /**
5840      * Load data from the configured {@link Roo.data.Connection}, read the data object into
5841      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
5842      * process that block using the passed callback.
5843      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5844      * for the request to the remote server.
5845      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5846      * object into a block of Roo.data.Records.
5847      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5848      * The function must be passed <ul>
5849      * <li>The Record block object</li>
5850      * <li>The "arg" argument from the load function</li>
5851      * <li>A boolean success indicator</li>
5852      * </ul>
5853      * @param {Object} scope The scope in which to call the callback
5854      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5855      */
5856     load : function(params, reader, callback, scope, arg){
5857         if(this.fireEvent("beforeload", this, params) !== false){
5858             var  o = {
5859                 params : params || {},
5860                 request: {
5861                     callback : callback,
5862                     scope : scope,
5863                     arg : arg
5864                 },
5865                 reader: reader,
5866                 callback : this.loadResponse,
5867                 scope: this
5868             };
5869             if(this.useAjax){
5870                 Roo.applyIf(o, this.conn);
5871                 if(this.activeRequest){
5872                     Roo.Ajax.abort(this.activeRequest);
5873                 }
5874                 this.activeRequest = Roo.Ajax.request(o);
5875             }else{
5876                 this.conn.request(o);
5877             }
5878         }else{
5879             callback.call(scope||this, null, arg, false);
5880         }
5881     },
5882
5883     // private
5884     loadResponse : function(o, success, response){
5885         delete this.activeRequest;
5886         if(!success){
5887             this.fireEvent("loadexception", this, o, response);
5888             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5889             return;
5890         }
5891         var result;
5892         try {
5893             result = o.reader.read(response);
5894         }catch(e){
5895             this.fireEvent("loadexception", this, o, response, e);
5896             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5897             return;
5898         }
5899         
5900         this.fireEvent("load", this, o, o.request.arg);
5901         o.request.callback.call(o.request.scope, result, o.request.arg, true);
5902     },
5903
5904     // private
5905     update : function(dataSet){
5906
5907     },
5908
5909     // private
5910     updateResponse : function(dataSet){
5911
5912     }
5913 });/*
5914  * Based on:
5915  * Ext JS Library 1.1.1
5916  * Copyright(c) 2006-2007, Ext JS, LLC.
5917  *
5918  * Originally Released Under LGPL - original licence link has changed is not relivant.
5919  *
5920  * Fork - LGPL
5921  * <script type="text/javascript">
5922  */
5923
5924 /**
5925  * @class Roo.data.ScriptTagProxy
5926  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
5927  * other than the originating domain of the running page.<br><br>
5928  * <p>
5929  * <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
5930  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
5931  * <p>
5932  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
5933  * source code that is used as the source inside a &lt;script> tag.<br><br>
5934  * <p>
5935  * In order for the browser to process the returned data, the server must wrap the data object
5936  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
5937  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
5938  * depending on whether the callback name was passed:
5939  * <p>
5940  * <pre><code>
5941 boolean scriptTag = false;
5942 String cb = request.getParameter("callback");
5943 if (cb != null) {
5944     scriptTag = true;
5945     response.setContentType("text/javascript");
5946 } else {
5947     response.setContentType("application/x-json");
5948 }
5949 Writer out = response.getWriter();
5950 if (scriptTag) {
5951     out.write(cb + "(");
5952 }
5953 out.print(dataBlock.toJsonString());
5954 if (scriptTag) {
5955     out.write(");");
5956 }
5957 </pre></code>
5958  *
5959  * @constructor
5960  * @param {Object} config A configuration object.
5961  */
5962 Roo.data.ScriptTagProxy = function(config){
5963     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
5964     Roo.apply(this, config);
5965     this.head = document.getElementsByTagName("head")[0];
5966 };
5967
5968 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
5969
5970 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
5971     /**
5972      * @cfg {String} url The URL from which to request the data object.
5973      */
5974     /**
5975      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
5976      */
5977     timeout : 30000,
5978     /**
5979      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
5980      * the server the name of the callback function set up by the load call to process the returned data object.
5981      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
5982      * javascript output which calls this named function passing the data object as its only parameter.
5983      */
5984     callbackParam : "callback",
5985     /**
5986      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
5987      * name to the request.
5988      */
5989     nocache : true,
5990
5991     /**
5992      * Load data from the configured URL, read the data object into
5993      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5994      * process that block using the passed callback.
5995      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5996      * for the request to the remote server.
5997      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5998      * object into a block of Roo.data.Records.
5999      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
6000      * The function must be passed <ul>
6001      * <li>The Record block object</li>
6002      * <li>The "arg" argument from the load function</li>
6003      * <li>A boolean success indicator</li>
6004      * </ul>
6005      * @param {Object} scope The scope in which to call the callback
6006      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
6007      */
6008     load : function(params, reader, callback, scope, arg){
6009         if(this.fireEvent("beforeload", this, params) !== false){
6010
6011             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
6012
6013             var url = this.url;
6014             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
6015             if(this.nocache){
6016                 url += "&_dc=" + (new Date().getTime());
6017             }
6018             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
6019             var trans = {
6020                 id : transId,
6021                 cb : "stcCallback"+transId,
6022                 scriptId : "stcScript"+transId,
6023                 params : params,
6024                 arg : arg,
6025                 url : url,
6026                 callback : callback,
6027                 scope : scope,
6028                 reader : reader
6029             };
6030             var conn = this;
6031
6032             window[trans.cb] = function(o){
6033                 conn.handleResponse(o, trans);
6034             };
6035
6036             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
6037
6038             if(this.autoAbort !== false){
6039                 this.abort();
6040             }
6041
6042             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
6043
6044             var script = document.createElement("script");
6045             script.setAttribute("src", url);
6046             script.setAttribute("type", "text/javascript");
6047             script.setAttribute("id", trans.scriptId);
6048             this.head.appendChild(script);
6049
6050             this.trans = trans;
6051         }else{
6052             callback.call(scope||this, null, arg, false);
6053         }
6054     },
6055
6056     // private
6057     isLoading : function(){
6058         return this.trans ? true : false;
6059     },
6060
6061     /**
6062      * Abort the current server request.
6063      */
6064     abort : function(){
6065         if(this.isLoading()){
6066             this.destroyTrans(this.trans);
6067         }
6068     },
6069
6070     // private
6071     destroyTrans : function(trans, isLoaded){
6072         this.head.removeChild(document.getElementById(trans.scriptId));
6073         clearTimeout(trans.timeoutId);
6074         if(isLoaded){
6075             window[trans.cb] = undefined;
6076             try{
6077                 delete window[trans.cb];
6078             }catch(e){}
6079         }else{
6080             // if hasn't been loaded, wait for load to remove it to prevent script error
6081             window[trans.cb] = function(){
6082                 window[trans.cb] = undefined;
6083                 try{
6084                     delete window[trans.cb];
6085                 }catch(e){}
6086             };
6087         }
6088     },
6089
6090     // private
6091     handleResponse : function(o, trans){
6092         this.trans = false;
6093         this.destroyTrans(trans, true);
6094         var result;
6095         try {
6096             result = trans.reader.readRecords(o);
6097         }catch(e){
6098             this.fireEvent("loadexception", this, o, trans.arg, e);
6099             trans.callback.call(trans.scope||window, null, trans.arg, false);
6100             return;
6101         }
6102         this.fireEvent("load", this, o, trans.arg);
6103         trans.callback.call(trans.scope||window, result, trans.arg, true);
6104     },
6105
6106     // private
6107     handleFailure : function(trans){
6108         this.trans = false;
6109         this.destroyTrans(trans, false);
6110         this.fireEvent("loadexception", this, null, trans.arg);
6111         trans.callback.call(trans.scope||window, null, trans.arg, false);
6112     }
6113 });/*
6114  * Based on:
6115  * Ext JS Library 1.1.1
6116  * Copyright(c) 2006-2007, Ext JS, LLC.
6117  *
6118  * Originally Released Under LGPL - original licence link has changed is not relivant.
6119  *
6120  * Fork - LGPL
6121  * <script type="text/javascript">
6122  */
6123
6124 /**
6125  * @class Roo.data.JsonReader
6126  * @extends Roo.data.DataReader
6127  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
6128  * based on mappings in a provided Roo.data.Record constructor.
6129  * 
6130  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
6131  * in the reply previously. 
6132  * 
6133  * <p>
6134  * Example code:
6135  * <pre><code>
6136 var RecordDef = Roo.data.Record.create([
6137     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6138     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6139 ]);
6140 var myReader = new Roo.data.JsonReader({
6141     totalProperty: "results",    // The property which contains the total dataset size (optional)
6142     root: "rows",                // The property which contains an Array of row objects
6143     id: "id"                     // The property within each row object that provides an ID for the record (optional)
6144 }, RecordDef);
6145 </code></pre>
6146  * <p>
6147  * This would consume a JSON file like this:
6148  * <pre><code>
6149 { 'results': 2, 'rows': [
6150     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
6151     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
6152 }
6153 </code></pre>
6154  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
6155  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6156  * paged from the remote server.
6157  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
6158  * @cfg {String} root name of the property which contains the Array of row objects.
6159  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
6160  * @constructor
6161  * Create a new JsonReader
6162  * @param {Object} meta Metadata configuration options
6163  * @param {Object} recordType Either an Array of field definition objects,
6164  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
6165  */
6166 Roo.data.JsonReader = function(meta, recordType){
6167     
6168     meta = meta || {};
6169     // set some defaults:
6170     Roo.applyIf(meta, {
6171         totalProperty: 'total',
6172         successProperty : 'success',
6173         root : 'data',
6174         id : 'id'
6175     });
6176     
6177     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6178 };
6179 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
6180     
6181     /**
6182      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
6183      * Used by Store query builder to append _requestMeta to params.
6184      * 
6185      */
6186     metaFromRemote : false,
6187     /**
6188      * This method is only used by a DataProxy which has retrieved data from a remote server.
6189      * @param {Object} response The XHR object which contains the JSON data in its responseText.
6190      * @return {Object} data A data block which is used by an Roo.data.Store object as
6191      * a cache of Roo.data.Records.
6192      */
6193     read : function(response){
6194         var json = response.responseText;
6195        
6196         var o = /* eval:var:o */ eval("("+json+")");
6197         if(!o) {
6198             throw {message: "JsonReader.read: Json object not found"};
6199         }
6200         
6201         if(o.metaData){
6202             
6203             delete this.ef;
6204             this.metaFromRemote = true;
6205             this.meta = o.metaData;
6206             this.recordType = Roo.data.Record.create(o.metaData.fields);
6207             this.onMetaChange(this.meta, this.recordType, o);
6208         }
6209         return this.readRecords(o);
6210     },
6211
6212     // private function a store will implement
6213     onMetaChange : function(meta, recordType, o){
6214
6215     },
6216
6217     /**
6218          * @ignore
6219          */
6220     simpleAccess: function(obj, subsc) {
6221         return obj[subsc];
6222     },
6223
6224         /**
6225          * @ignore
6226          */
6227     getJsonAccessor: function(){
6228         var re = /[\[\.]/;
6229         return function(expr) {
6230             try {
6231                 return(re.test(expr))
6232                     ? new Function("obj", "return obj." + expr)
6233                     : function(obj){
6234                         return obj[expr];
6235                     };
6236             } catch(e){}
6237             return Roo.emptyFn;
6238         };
6239     }(),
6240
6241     /**
6242      * Create a data block containing Roo.data.Records from an XML document.
6243      * @param {Object} o An object which contains an Array of row objects in the property specified
6244      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
6245      * which contains the total size of the dataset.
6246      * @return {Object} data A data block which is used by an Roo.data.Store object as
6247      * a cache of Roo.data.Records.
6248      */
6249     readRecords : function(o){
6250         /**
6251          * After any data loads, the raw JSON data is available for further custom processing.
6252          * @type Object
6253          */
6254         this.o = o;
6255         var s = this.meta, Record = this.recordType,
6256             f = Record.prototype.fields, fi = f.items, fl = f.length;
6257
6258 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
6259         if (!this.ef) {
6260             if(s.totalProperty) {
6261                     this.getTotal = this.getJsonAccessor(s.totalProperty);
6262                 }
6263                 if(s.successProperty) {
6264                     this.getSuccess = this.getJsonAccessor(s.successProperty);
6265                 }
6266                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
6267                 if (s.id) {
6268                         var g = this.getJsonAccessor(s.id);
6269                         this.getId = function(rec) {
6270                                 var r = g(rec);
6271                                 return (r === undefined || r === "") ? null : r;
6272                         };
6273                 } else {
6274                         this.getId = function(){return null;};
6275                 }
6276             this.ef = [];
6277             for(var jj = 0; jj < fl; jj++){
6278                 f = fi[jj];
6279                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
6280                 this.ef[jj] = this.getJsonAccessor(map);
6281             }
6282         }
6283
6284         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
6285         if(s.totalProperty){
6286             var vt = parseInt(this.getTotal(o), 10);
6287             if(!isNaN(vt)){
6288                 totalRecords = vt;
6289             }
6290         }
6291         if(s.successProperty){
6292             var vs = this.getSuccess(o);
6293             if(vs === false || vs === 'false'){
6294                 success = false;
6295             }
6296         }
6297         var records = [];
6298             for(var i = 0; i < c; i++){
6299                     var n = root[i];
6300                 var values = {};
6301                 var id = this.getId(n);
6302                 for(var j = 0; j < fl; j++){
6303                     f = fi[j];
6304                 var v = this.ef[j](n);
6305                 if (!f.convert) {
6306                     Roo.log('missing convert for ' + f.name);
6307                     Roo.log(f);
6308                     continue;
6309                 }
6310                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
6311                 }
6312                 var record = new Record(values, id);
6313                 record.json = n;
6314                 records[i] = record;
6315             }
6316             return {
6317             raw : o,
6318                 success : success,
6319                 records : records,
6320                 totalRecords : totalRecords
6321             };
6322     }
6323 });/*
6324  * Based on:
6325  * Ext JS Library 1.1.1
6326  * Copyright(c) 2006-2007, Ext JS, LLC.
6327  *
6328  * Originally Released Under LGPL - original licence link has changed is not relivant.
6329  *
6330  * Fork - LGPL
6331  * <script type="text/javascript">
6332  */
6333
6334 /**
6335  * @class Roo.data.XmlReader
6336  * @extends Roo.data.DataReader
6337  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
6338  * based on mappings in a provided Roo.data.Record constructor.<br><br>
6339  * <p>
6340  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
6341  * header in the HTTP response must be set to "text/xml".</em>
6342  * <p>
6343  * Example code:
6344  * <pre><code>
6345 var RecordDef = Roo.data.Record.create([
6346    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6347    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6348 ]);
6349 var myReader = new Roo.data.XmlReader({
6350    totalRecords: "results", // The element which contains the total dataset size (optional)
6351    record: "row",           // The repeated element which contains row information
6352    id: "id"                 // The element within the row that provides an ID for the record (optional)
6353 }, RecordDef);
6354 </code></pre>
6355  * <p>
6356  * This would consume an XML file like this:
6357  * <pre><code>
6358 &lt;?xml?>
6359 &lt;dataset>
6360  &lt;results>2&lt;/results>
6361  &lt;row>
6362    &lt;id>1&lt;/id>
6363    &lt;name>Bill&lt;/name>
6364    &lt;occupation>Gardener&lt;/occupation>
6365  &lt;/row>
6366  &lt;row>
6367    &lt;id>2&lt;/id>
6368    &lt;name>Ben&lt;/name>
6369    &lt;occupation>Horticulturalist&lt;/occupation>
6370  &lt;/row>
6371 &lt;/dataset>
6372 </code></pre>
6373  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
6374  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6375  * paged from the remote server.
6376  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
6377  * @cfg {String} success The DomQuery path to the success attribute used by forms.
6378  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
6379  * a record identifier value.
6380  * @constructor
6381  * Create a new XmlReader
6382  * @param {Object} meta Metadata configuration options
6383  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
6384  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
6385  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
6386  */
6387 Roo.data.XmlReader = function(meta, recordType){
6388     meta = meta || {};
6389     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6390 };
6391 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
6392     /**
6393      * This method is only used by a DataProxy which has retrieved data from a remote server.
6394          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
6395          * to contain a method called 'responseXML' that returns an XML document object.
6396      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6397      * a cache of Roo.data.Records.
6398      */
6399     read : function(response){
6400         var doc = response.responseXML;
6401         if(!doc) {
6402             throw {message: "XmlReader.read: XML Document not available"};
6403         }
6404         return this.readRecords(doc);
6405     },
6406
6407     /**
6408      * Create a data block containing Roo.data.Records from an XML document.
6409          * @param {Object} doc A parsed XML document.
6410      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6411      * a cache of Roo.data.Records.
6412      */
6413     readRecords : function(doc){
6414         /**
6415          * After any data loads/reads, the raw XML Document is available for further custom processing.
6416          * @type XMLDocument
6417          */
6418         this.xmlData = doc;
6419         var root = doc.documentElement || doc;
6420         var q = Roo.DomQuery;
6421         var recordType = this.recordType, fields = recordType.prototype.fields;
6422         var sid = this.meta.id;
6423         var totalRecords = 0, success = true;
6424         if(this.meta.totalRecords){
6425             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
6426         }
6427         
6428         if(this.meta.success){
6429             var sv = q.selectValue(this.meta.success, root, true);
6430             success = sv !== false && sv !== 'false';
6431         }
6432         var records = [];
6433         var ns = q.select(this.meta.record, root);
6434         for(var i = 0, len = ns.length; i < len; i++) {
6435                 var n = ns[i];
6436                 var values = {};
6437                 var id = sid ? q.selectValue(sid, n) : undefined;
6438                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6439                     var f = fields.items[j];
6440                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
6441                     v = f.convert(v);
6442                     values[f.name] = v;
6443                 }
6444                 var record = new recordType(values, id);
6445                 record.node = n;
6446                 records[records.length] = record;
6447             }
6448
6449             return {
6450                 success : success,
6451                 records : records,
6452                 totalRecords : totalRecords || records.length
6453             };
6454     }
6455 });/*
6456  * Based on:
6457  * Ext JS Library 1.1.1
6458  * Copyright(c) 2006-2007, Ext JS, LLC.
6459  *
6460  * Originally Released Under LGPL - original licence link has changed is not relivant.
6461  *
6462  * Fork - LGPL
6463  * <script type="text/javascript">
6464  */
6465
6466 /**
6467  * @class Roo.data.ArrayReader
6468  * @extends Roo.data.DataReader
6469  * Data reader class to create an Array of Roo.data.Record objects from an Array.
6470  * Each element of that Array represents a row of data fields. The
6471  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
6472  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
6473  * <p>
6474  * Example code:.
6475  * <pre><code>
6476 var RecordDef = Roo.data.Record.create([
6477     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
6478     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
6479 ]);
6480 var myReader = new Roo.data.ArrayReader({
6481     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
6482 }, RecordDef);
6483 </code></pre>
6484  * <p>
6485  * This would consume an Array like this:
6486  * <pre><code>
6487 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
6488   </code></pre>
6489  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
6490  * @constructor
6491  * Create a new JsonReader
6492  * @param {Object} meta Metadata configuration options.
6493  * @param {Object} recordType Either an Array of field definition objects
6494  * as specified to {@link Roo.data.Record#create},
6495  * or an {@link Roo.data.Record} object
6496  * created using {@link Roo.data.Record#create}.
6497  */
6498 Roo.data.ArrayReader = function(meta, recordType){
6499     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
6500 };
6501
6502 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
6503     /**
6504      * Create a data block containing Roo.data.Records from an XML document.
6505      * @param {Object} o An Array of row objects which represents the dataset.
6506      * @return {Object} data A data block which is used by an Roo.data.Store object as
6507      * a cache of Roo.data.Records.
6508      */
6509     readRecords : function(o){
6510         var sid = this.meta ? this.meta.id : null;
6511         var recordType = this.recordType, fields = recordType.prototype.fields;
6512         var records = [];
6513         var root = o;
6514             for(var i = 0; i < root.length; i++){
6515                     var n = root[i];
6516                 var values = {};
6517                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
6518                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6519                 var f = fields.items[j];
6520                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
6521                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
6522                 v = f.convert(v);
6523                 values[f.name] = v;
6524             }
6525                 var record = new recordType(values, id);
6526                 record.json = n;
6527                 records[records.length] = record;
6528             }
6529             return {
6530                 records : records,
6531                 totalRecords : records.length
6532             };
6533     }
6534 });/*
6535  * Based on:
6536  * Ext JS Library 1.1.1
6537  * Copyright(c) 2006-2007, Ext JS, LLC.
6538  *
6539  * Originally Released Under LGPL - original licence link has changed is not relivant.
6540  *
6541  * Fork - LGPL
6542  * <script type="text/javascript">
6543  */
6544
6545
6546 /**
6547  * @class Roo.data.Tree
6548  * @extends Roo.util.Observable
6549  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
6550  * in the tree have most standard DOM functionality.
6551  * @constructor
6552  * @param {Node} root (optional) The root node
6553  */
6554 Roo.data.Tree = function(root){
6555    this.nodeHash = {};
6556    /**
6557     * The root node for this tree
6558     * @type Node
6559     */
6560    this.root = null;
6561    if(root){
6562        this.setRootNode(root);
6563    }
6564    this.addEvents({
6565        /**
6566         * @event append
6567         * Fires when a new child node is appended to a node in this tree.
6568         * @param {Tree} tree The owner tree
6569         * @param {Node} parent The parent node
6570         * @param {Node} node The newly appended node
6571         * @param {Number} index The index of the newly appended node
6572         */
6573        "append" : true,
6574        /**
6575         * @event remove
6576         * Fires when a child node is removed from a node in this tree.
6577         * @param {Tree} tree The owner tree
6578         * @param {Node} parent The parent node
6579         * @param {Node} node The child node removed
6580         */
6581        "remove" : true,
6582        /**
6583         * @event move
6584         * Fires when a node is moved to a new location in the tree
6585         * @param {Tree} tree The owner tree
6586         * @param {Node} node The node moved
6587         * @param {Node} oldParent The old parent of this node
6588         * @param {Node} newParent The new parent of this node
6589         * @param {Number} index The index it was moved to
6590         */
6591        "move" : true,
6592        /**
6593         * @event insert
6594         * Fires when a new child node is inserted in a node in this tree.
6595         * @param {Tree} tree The owner tree
6596         * @param {Node} parent The parent node
6597         * @param {Node} node The child node inserted
6598         * @param {Node} refNode The child node the node was inserted before
6599         */
6600        "insert" : true,
6601        /**
6602         * @event beforeappend
6603         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
6604         * @param {Tree} tree The owner tree
6605         * @param {Node} parent The parent node
6606         * @param {Node} node The child node to be appended
6607         */
6608        "beforeappend" : true,
6609        /**
6610         * @event beforeremove
6611         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
6612         * @param {Tree} tree The owner tree
6613         * @param {Node} parent The parent node
6614         * @param {Node} node The child node to be removed
6615         */
6616        "beforeremove" : true,
6617        /**
6618         * @event beforemove
6619         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
6620         * @param {Tree} tree The owner tree
6621         * @param {Node} node The node being moved
6622         * @param {Node} oldParent The parent of the node
6623         * @param {Node} newParent The new parent the node is moving to
6624         * @param {Number} index The index it is being moved to
6625         */
6626        "beforemove" : true,
6627        /**
6628         * @event beforeinsert
6629         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
6630         * @param {Tree} tree The owner tree
6631         * @param {Node} parent The parent node
6632         * @param {Node} node The child node to be inserted
6633         * @param {Node} refNode The child node the node is being inserted before
6634         */
6635        "beforeinsert" : true
6636    });
6637
6638     Roo.data.Tree.superclass.constructor.call(this);
6639 };
6640
6641 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
6642     pathSeparator: "/",
6643
6644     proxyNodeEvent : function(){
6645         return this.fireEvent.apply(this, arguments);
6646     },
6647
6648     /**
6649      * Returns the root node for this tree.
6650      * @return {Node}
6651      */
6652     getRootNode : function(){
6653         return this.root;
6654     },
6655
6656     /**
6657      * Sets the root node for this tree.
6658      * @param {Node} node
6659      * @return {Node}
6660      */
6661     setRootNode : function(node){
6662         this.root = node;
6663         node.ownerTree = this;
6664         node.isRoot = true;
6665         this.registerNode(node);
6666         return node;
6667     },
6668
6669     /**
6670      * Gets a node in this tree by its id.
6671      * @param {String} id
6672      * @return {Node}
6673      */
6674     getNodeById : function(id){
6675         return this.nodeHash[id];
6676     },
6677
6678     registerNode : function(node){
6679         this.nodeHash[node.id] = node;
6680     },
6681
6682     unregisterNode : function(node){
6683         delete this.nodeHash[node.id];
6684     },
6685
6686     toString : function(){
6687         return "[Tree"+(this.id?" "+this.id:"")+"]";
6688     }
6689 });
6690
6691 /**
6692  * @class Roo.data.Node
6693  * @extends Roo.util.Observable
6694  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
6695  * @cfg {String} id The id for this node. If one is not specified, one is generated.
6696  * @constructor
6697  * @param {Object} attributes The attributes/config for the node
6698  */
6699 Roo.data.Node = function(attributes){
6700     /**
6701      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
6702      * @type {Object}
6703      */
6704     this.attributes = attributes || {};
6705     this.leaf = this.attributes.leaf;
6706     /**
6707      * The node id. @type String
6708      */
6709     this.id = this.attributes.id;
6710     if(!this.id){
6711         this.id = Roo.id(null, "ynode-");
6712         this.attributes.id = this.id;
6713     }
6714      
6715     
6716     /**
6717      * All child nodes of this node. @type Array
6718      */
6719     this.childNodes = [];
6720     if(!this.childNodes.indexOf){ // indexOf is a must
6721         this.childNodes.indexOf = function(o){
6722             for(var i = 0, len = this.length; i < len; i++){
6723                 if(this[i] == o) {
6724                     return i;
6725                 }
6726             }
6727             return -1;
6728         };
6729     }
6730     /**
6731      * The parent node for this node. @type Node
6732      */
6733     this.parentNode = null;
6734     /**
6735      * The first direct child node of this node, or null if this node has no child nodes. @type Node
6736      */
6737     this.firstChild = null;
6738     /**
6739      * The last direct child node of this node, or null if this node has no child nodes. @type Node
6740      */
6741     this.lastChild = null;
6742     /**
6743      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
6744      */
6745     this.previousSibling = null;
6746     /**
6747      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
6748      */
6749     this.nextSibling = null;
6750
6751     this.addEvents({
6752        /**
6753         * @event append
6754         * Fires when a new child node is appended
6755         * @param {Tree} tree The owner tree
6756         * @param {Node} this This node
6757         * @param {Node} node The newly appended node
6758         * @param {Number} index The index of the newly appended node
6759         */
6760        "append" : true,
6761        /**
6762         * @event remove
6763         * Fires when a child node is removed
6764         * @param {Tree} tree The owner tree
6765         * @param {Node} this This node
6766         * @param {Node} node The removed node
6767         */
6768        "remove" : true,
6769        /**
6770         * @event move
6771         * Fires when this node is moved to a new location in the tree
6772         * @param {Tree} tree The owner tree
6773         * @param {Node} this This node
6774         * @param {Node} oldParent The old parent of this node
6775         * @param {Node} newParent The new parent of this node
6776         * @param {Number} index The index it was moved to
6777         */
6778        "move" : true,
6779        /**
6780         * @event insert
6781         * Fires when a new child node is inserted.
6782         * @param {Tree} tree The owner tree
6783         * @param {Node} this This node
6784         * @param {Node} node The child node inserted
6785         * @param {Node} refNode The child node the node was inserted before
6786         */
6787        "insert" : true,
6788        /**
6789         * @event beforeappend
6790         * Fires before a new child is appended, return false to cancel the append.
6791         * @param {Tree} tree The owner tree
6792         * @param {Node} this This node
6793         * @param {Node} node The child node to be appended
6794         */
6795        "beforeappend" : true,
6796        /**
6797         * @event beforeremove
6798         * Fires before a child is removed, return false to cancel the remove.
6799         * @param {Tree} tree The owner tree
6800         * @param {Node} this This node
6801         * @param {Node} node The child node to be removed
6802         */
6803        "beforeremove" : true,
6804        /**
6805         * @event beforemove
6806         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
6807         * @param {Tree} tree The owner tree
6808         * @param {Node} this This node
6809         * @param {Node} oldParent The parent of this node
6810         * @param {Node} newParent The new parent this node is moving to
6811         * @param {Number} index The index it is being moved to
6812         */
6813        "beforemove" : true,
6814        /**
6815         * @event beforeinsert
6816         * Fires before a new child is inserted, return false to cancel the insert.
6817         * @param {Tree} tree The owner tree
6818         * @param {Node} this This node
6819         * @param {Node} node The child node to be inserted
6820         * @param {Node} refNode The child node the node is being inserted before
6821         */
6822        "beforeinsert" : true
6823    });
6824     this.listeners = this.attributes.listeners;
6825     Roo.data.Node.superclass.constructor.call(this);
6826 };
6827
6828 Roo.extend(Roo.data.Node, Roo.util.Observable, {
6829     fireEvent : function(evtName){
6830         // first do standard event for this node
6831         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
6832             return false;
6833         }
6834         // then bubble it up to the tree if the event wasn't cancelled
6835         var ot = this.getOwnerTree();
6836         if(ot){
6837             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
6838                 return false;
6839             }
6840         }
6841         return true;
6842     },
6843
6844     /**
6845      * Returns true if this node is a leaf
6846      * @return {Boolean}
6847      */
6848     isLeaf : function(){
6849         return this.leaf === true;
6850     },
6851
6852     // private
6853     setFirstChild : function(node){
6854         this.firstChild = node;
6855     },
6856
6857     //private
6858     setLastChild : function(node){
6859         this.lastChild = node;
6860     },
6861
6862
6863     /**
6864      * Returns true if this node is the last child of its parent
6865      * @return {Boolean}
6866      */
6867     isLast : function(){
6868        return (!this.parentNode ? true : this.parentNode.lastChild == this);
6869     },
6870
6871     /**
6872      * Returns true if this node is the first child of its parent
6873      * @return {Boolean}
6874      */
6875     isFirst : function(){
6876        return (!this.parentNode ? true : this.parentNode.firstChild == this);
6877     },
6878
6879     hasChildNodes : function(){
6880         return !this.isLeaf() && this.childNodes.length > 0;
6881     },
6882
6883     /**
6884      * Insert node(s) as the last child node of this node.
6885      * @param {Node/Array} node The node or Array of nodes to append
6886      * @return {Node} The appended node if single append, or null if an array was passed
6887      */
6888     appendChild : function(node){
6889         var multi = false;
6890         if(node instanceof Array){
6891             multi = node;
6892         }else if(arguments.length > 1){
6893             multi = arguments;
6894         }
6895         // if passed an array or multiple args do them one by one
6896         if(multi){
6897             for(var i = 0, len = multi.length; i < len; i++) {
6898                 this.appendChild(multi[i]);
6899             }
6900         }else{
6901             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
6902                 return false;
6903             }
6904             var index = this.childNodes.length;
6905             var oldParent = node.parentNode;
6906             // it's a move, make sure we move it cleanly
6907             if(oldParent){
6908                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
6909                     return false;
6910                 }
6911                 oldParent.removeChild(node);
6912             }
6913             index = this.childNodes.length;
6914             if(index == 0){
6915                 this.setFirstChild(node);
6916             }
6917             this.childNodes.push(node);
6918             node.parentNode = this;
6919             var ps = this.childNodes[index-1];
6920             if(ps){
6921                 node.previousSibling = ps;
6922                 ps.nextSibling = node;
6923             }else{
6924                 node.previousSibling = null;
6925             }
6926             node.nextSibling = null;
6927             this.setLastChild(node);
6928             node.setOwnerTree(this.getOwnerTree());
6929             this.fireEvent("append", this.ownerTree, this, node, index);
6930             if(oldParent){
6931                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
6932             }
6933             return node;
6934         }
6935     },
6936
6937     /**
6938      * Removes a child node from this node.
6939      * @param {Node} node The node to remove
6940      * @return {Node} The removed node
6941      */
6942     removeChild : function(node){
6943         var index = this.childNodes.indexOf(node);
6944         if(index == -1){
6945             return false;
6946         }
6947         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
6948             return false;
6949         }
6950
6951         // remove it from childNodes collection
6952         this.childNodes.splice(index, 1);
6953
6954         // update siblings
6955         if(node.previousSibling){
6956             node.previousSibling.nextSibling = node.nextSibling;
6957         }
6958         if(node.nextSibling){
6959             node.nextSibling.previousSibling = node.previousSibling;
6960         }
6961
6962         // update child refs
6963         if(this.firstChild == node){
6964             this.setFirstChild(node.nextSibling);
6965         }
6966         if(this.lastChild == node){
6967             this.setLastChild(node.previousSibling);
6968         }
6969
6970         node.setOwnerTree(null);
6971         // clear any references from the node
6972         node.parentNode = null;
6973         node.previousSibling = null;
6974         node.nextSibling = null;
6975         this.fireEvent("remove", this.ownerTree, this, node);
6976         return node;
6977     },
6978
6979     /**
6980      * Inserts the first node before the second node in this nodes childNodes collection.
6981      * @param {Node} node The node to insert
6982      * @param {Node} refNode The node to insert before (if null the node is appended)
6983      * @return {Node} The inserted node
6984      */
6985     insertBefore : function(node, refNode){
6986         if(!refNode){ // like standard Dom, refNode can be null for append
6987             return this.appendChild(node);
6988         }
6989         // nothing to do
6990         if(node == refNode){
6991             return false;
6992         }
6993
6994         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
6995             return false;
6996         }
6997         var index = this.childNodes.indexOf(refNode);
6998         var oldParent = node.parentNode;
6999         var refIndex = index;
7000
7001         // when moving internally, indexes will change after remove
7002         if(oldParent == this && this.childNodes.indexOf(node) < index){
7003             refIndex--;
7004         }
7005
7006         // it's a move, make sure we move it cleanly
7007         if(oldParent){
7008             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
7009                 return false;
7010             }
7011             oldParent.removeChild(node);
7012         }
7013         if(refIndex == 0){
7014             this.setFirstChild(node);
7015         }
7016         this.childNodes.splice(refIndex, 0, node);
7017         node.parentNode = this;
7018         var ps = this.childNodes[refIndex-1];
7019         if(ps){
7020             node.previousSibling = ps;
7021             ps.nextSibling = node;
7022         }else{
7023             node.previousSibling = null;
7024         }
7025         node.nextSibling = refNode;
7026         refNode.previousSibling = node;
7027         node.setOwnerTree(this.getOwnerTree());
7028         this.fireEvent("insert", this.ownerTree, this, node, refNode);
7029         if(oldParent){
7030             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
7031         }
7032         return node;
7033     },
7034
7035     /**
7036      * Returns the child node at the specified index.
7037      * @param {Number} index
7038      * @return {Node}
7039      */
7040     item : function(index){
7041         return this.childNodes[index];
7042     },
7043
7044     /**
7045      * Replaces one child node in this node with another.
7046      * @param {Node} newChild The replacement node
7047      * @param {Node} oldChild The node to replace
7048      * @return {Node} The replaced node
7049      */
7050     replaceChild : function(newChild, oldChild){
7051         this.insertBefore(newChild, oldChild);
7052         this.removeChild(oldChild);
7053         return oldChild;
7054     },
7055
7056     /**
7057      * Returns the index of a child node
7058      * @param {Node} node
7059      * @return {Number} The index of the node or -1 if it was not found
7060      */
7061     indexOf : function(child){
7062         return this.childNodes.indexOf(child);
7063     },
7064
7065     /**
7066      * Returns the tree this node is in.
7067      * @return {Tree}
7068      */
7069     getOwnerTree : function(){
7070         // if it doesn't have one, look for one
7071         if(!this.ownerTree){
7072             var p = this;
7073             while(p){
7074                 if(p.ownerTree){
7075                     this.ownerTree = p.ownerTree;
7076                     break;
7077                 }
7078                 p = p.parentNode;
7079             }
7080         }
7081         return this.ownerTree;
7082     },
7083
7084     /**
7085      * Returns depth of this node (the root node has a depth of 0)
7086      * @return {Number}
7087      */
7088     getDepth : function(){
7089         var depth = 0;
7090         var p = this;
7091         while(p.parentNode){
7092             ++depth;
7093             p = p.parentNode;
7094         }
7095         return depth;
7096     },
7097
7098     // private
7099     setOwnerTree : function(tree){
7100         // if it's move, we need to update everyone
7101         if(tree != this.ownerTree){
7102             if(this.ownerTree){
7103                 this.ownerTree.unregisterNode(this);
7104             }
7105             this.ownerTree = tree;
7106             var cs = this.childNodes;
7107             for(var i = 0, len = cs.length; i < len; i++) {
7108                 cs[i].setOwnerTree(tree);
7109             }
7110             if(tree){
7111                 tree.registerNode(this);
7112             }
7113         }
7114     },
7115
7116     /**
7117      * Returns the path for this node. The path can be used to expand or select this node programmatically.
7118      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
7119      * @return {String} The path
7120      */
7121     getPath : function(attr){
7122         attr = attr || "id";
7123         var p = this.parentNode;
7124         var b = [this.attributes[attr]];
7125         while(p){
7126             b.unshift(p.attributes[attr]);
7127             p = p.parentNode;
7128         }
7129         var sep = this.getOwnerTree().pathSeparator;
7130         return sep + b.join(sep);
7131     },
7132
7133     /**
7134      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7135      * function call will be the scope provided or the current node. The arguments to the function
7136      * will be the args provided or the current node. If the function returns false at any point,
7137      * the bubble is stopped.
7138      * @param {Function} fn The function to call
7139      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7140      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7141      */
7142     bubble : function(fn, scope, args){
7143         var p = this;
7144         while(p){
7145             if(fn.call(scope || p, args || p) === false){
7146                 break;
7147             }
7148             p = p.parentNode;
7149         }
7150     },
7151
7152     /**
7153      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7154      * function call will be the scope provided or the current node. The arguments to the function
7155      * will be the args provided or the current node. If the function returns false at any point,
7156      * the cascade is stopped on that branch.
7157      * @param {Function} fn The function to call
7158      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7159      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7160      */
7161     cascade : function(fn, scope, args){
7162         if(fn.call(scope || this, args || this) !== false){
7163             var cs = this.childNodes;
7164             for(var i = 0, len = cs.length; i < len; i++) {
7165                 cs[i].cascade(fn, scope, args);
7166             }
7167         }
7168     },
7169
7170     /**
7171      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
7172      * function call will be the scope provided or the current node. The arguments to the function
7173      * will be the args provided or the current node. If the function returns false at any point,
7174      * the iteration stops.
7175      * @param {Function} fn The function to call
7176      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7177      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7178      */
7179     eachChild : function(fn, scope, args){
7180         var cs = this.childNodes;
7181         for(var i = 0, len = cs.length; i < len; i++) {
7182                 if(fn.call(scope || this, args || cs[i]) === false){
7183                     break;
7184                 }
7185         }
7186     },
7187
7188     /**
7189      * Finds the first child that has the attribute with the specified value.
7190      * @param {String} attribute The attribute name
7191      * @param {Mixed} value The value to search for
7192      * @return {Node} The found child or null if none was found
7193      */
7194     findChild : function(attribute, value){
7195         var cs = this.childNodes;
7196         for(var i = 0, len = cs.length; i < len; i++) {
7197                 if(cs[i].attributes[attribute] == value){
7198                     return cs[i];
7199                 }
7200         }
7201         return null;
7202     },
7203
7204     /**
7205      * Finds the first child by a custom function. The child matches if the function passed
7206      * returns true.
7207      * @param {Function} fn
7208      * @param {Object} scope (optional)
7209      * @return {Node} The found child or null if none was found
7210      */
7211     findChildBy : function(fn, scope){
7212         var cs = this.childNodes;
7213         for(var i = 0, len = cs.length; i < len; i++) {
7214                 if(fn.call(scope||cs[i], cs[i]) === true){
7215                     return cs[i];
7216                 }
7217         }
7218         return null;
7219     },
7220
7221     /**
7222      * Sorts this nodes children using the supplied sort function
7223      * @param {Function} fn
7224      * @param {Object} scope (optional)
7225      */
7226     sort : function(fn, scope){
7227         var cs = this.childNodes;
7228         var len = cs.length;
7229         if(len > 0){
7230             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
7231             cs.sort(sortFn);
7232             for(var i = 0; i < len; i++){
7233                 var n = cs[i];
7234                 n.previousSibling = cs[i-1];
7235                 n.nextSibling = cs[i+1];
7236                 if(i == 0){
7237                     this.setFirstChild(n);
7238                 }
7239                 if(i == len-1){
7240                     this.setLastChild(n);
7241                 }
7242             }
7243         }
7244     },
7245
7246     /**
7247      * Returns true if this node is an ancestor (at any point) of the passed node.
7248      * @param {Node} node
7249      * @return {Boolean}
7250      */
7251     contains : function(node){
7252         return node.isAncestor(this);
7253     },
7254
7255     /**
7256      * Returns true if the passed node is an ancestor (at any point) of this node.
7257      * @param {Node} node
7258      * @return {Boolean}
7259      */
7260     isAncestor : function(node){
7261         var p = this.parentNode;
7262         while(p){
7263             if(p == node){
7264                 return true;
7265             }
7266             p = p.parentNode;
7267         }
7268         return false;
7269     },
7270
7271     toString : function(){
7272         return "[Node"+(this.id?" "+this.id:"")+"]";
7273     }
7274 });/*
7275  * Based on:
7276  * Ext JS Library 1.1.1
7277  * Copyright(c) 2006-2007, Ext JS, LLC.
7278  *
7279  * Originally Released Under LGPL - original licence link has changed is not relivant.
7280  *
7281  * Fork - LGPL
7282  * <script type="text/javascript">
7283  */
7284  
7285
7286 /**
7287  * @class Roo.ComponentMgr
7288  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
7289  * @singleton
7290  */
7291 Roo.ComponentMgr = function(){
7292     var all = new Roo.util.MixedCollection();
7293
7294     return {
7295         /**
7296          * Registers a component.
7297          * @param {Roo.Component} c The component
7298          */
7299         register : function(c){
7300             all.add(c);
7301         },
7302
7303         /**
7304          * Unregisters a component.
7305          * @param {Roo.Component} c The component
7306          */
7307         unregister : function(c){
7308             all.remove(c);
7309         },
7310
7311         /**
7312          * Returns a component by id
7313          * @param {String} id The component id
7314          */
7315         get : function(id){
7316             return all.get(id);
7317         },
7318
7319         /**
7320          * Registers a function that will be called when a specified component is added to ComponentMgr
7321          * @param {String} id The component id
7322          * @param {Funtction} fn The callback function
7323          * @param {Object} scope The scope of the callback
7324          */
7325         onAvailable : function(id, fn, scope){
7326             all.on("add", function(index, o){
7327                 if(o.id == id){
7328                     fn.call(scope || o, o);
7329                     all.un("add", fn, scope);
7330                 }
7331             });
7332         }
7333     };
7334 }();/*
7335  * Based on:
7336  * Ext JS Library 1.1.1
7337  * Copyright(c) 2006-2007, Ext JS, LLC.
7338  *
7339  * Originally Released Under LGPL - original licence link has changed is not relivant.
7340  *
7341  * Fork - LGPL
7342  * <script type="text/javascript">
7343  */
7344  
7345 /**
7346  * @class Roo.Component
7347  * @extends Roo.util.Observable
7348  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
7349  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
7350  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
7351  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
7352  * All visual components (widgets) that require rendering into a layout should subclass Component.
7353  * @constructor
7354  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
7355  * 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
7356  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
7357  */
7358 Roo.Component = function(config){
7359     config = config || {};
7360     if(config.tagName || config.dom || typeof config == "string"){ // element object
7361         config = {el: config, id: config.id || config};
7362     }
7363     this.initialConfig = config;
7364
7365     Roo.apply(this, config);
7366     this.addEvents({
7367         /**
7368          * @event disable
7369          * Fires after the component is disabled.
7370              * @param {Roo.Component} this
7371              */
7372         disable : true,
7373         /**
7374          * @event enable
7375          * Fires after the component is enabled.
7376              * @param {Roo.Component} this
7377              */
7378         enable : true,
7379         /**
7380          * @event beforeshow
7381          * Fires before the component is shown.  Return false to stop the show.
7382              * @param {Roo.Component} this
7383              */
7384         beforeshow : true,
7385         /**
7386          * @event show
7387          * Fires after the component is shown.
7388              * @param {Roo.Component} this
7389              */
7390         show : true,
7391         /**
7392          * @event beforehide
7393          * Fires before the component is hidden. Return false to stop the hide.
7394              * @param {Roo.Component} this
7395              */
7396         beforehide : true,
7397         /**
7398          * @event hide
7399          * Fires after the component is hidden.
7400              * @param {Roo.Component} this
7401              */
7402         hide : true,
7403         /**
7404          * @event beforerender
7405          * Fires before the component is rendered. Return false to stop the render.
7406              * @param {Roo.Component} this
7407              */
7408         beforerender : true,
7409         /**
7410          * @event render
7411          * Fires after the component is rendered.
7412              * @param {Roo.Component} this
7413              */
7414         render : true,
7415         /**
7416          * @event beforedestroy
7417          * Fires before the component is destroyed. Return false to stop the destroy.
7418              * @param {Roo.Component} this
7419              */
7420         beforedestroy : true,
7421         /**
7422          * @event destroy
7423          * Fires after the component is destroyed.
7424              * @param {Roo.Component} this
7425              */
7426         destroy : true
7427     });
7428     if(!this.id){
7429         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
7430     }
7431     Roo.ComponentMgr.register(this);
7432     Roo.Component.superclass.constructor.call(this);
7433     this.initComponent();
7434     if(this.renderTo){ // not supported by all components yet. use at your own risk!
7435         this.render(this.renderTo);
7436         delete this.renderTo;
7437     }
7438 };
7439
7440 /** @private */
7441 Roo.Component.AUTO_ID = 1000;
7442
7443 Roo.extend(Roo.Component, Roo.util.Observable, {
7444     /**
7445      * @scope Roo.Component.prototype
7446      * @type {Boolean}
7447      * true if this component is hidden. Read-only.
7448      */
7449     hidden : false,
7450     /**
7451      * @type {Boolean}
7452      * true if this component is disabled. Read-only.
7453      */
7454     disabled : false,
7455     /**
7456      * @type {Boolean}
7457      * true if this component has been rendered. Read-only.
7458      */
7459     rendered : false,
7460     
7461     /** @cfg {String} disableClass
7462      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
7463      */
7464     disabledClass : "x-item-disabled",
7465         /** @cfg {Boolean} allowDomMove
7466          * Whether the component can move the Dom node when rendering (defaults to true).
7467          */
7468     allowDomMove : true,
7469     /** @cfg {String} hideMode
7470      * How this component should hidden. Supported values are
7471      * "visibility" (css visibility), "offsets" (negative offset position) and
7472      * "display" (css display) - defaults to "display".
7473      */
7474     hideMode: 'display',
7475
7476     /** @private */
7477     ctype : "Roo.Component",
7478
7479     /**
7480      * @cfg {String} actionMode 
7481      * which property holds the element that used for  hide() / show() / disable() / enable()
7482      * default is 'el' 
7483      */
7484     actionMode : "el",
7485
7486     /** @private */
7487     getActionEl : function(){
7488         return this[this.actionMode];
7489     },
7490
7491     initComponent : Roo.emptyFn,
7492     /**
7493      * If this is a lazy rendering component, render it to its container element.
7494      * @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.
7495      */
7496     render : function(container, position){
7497         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
7498             if(!container && this.el){
7499                 this.el = Roo.get(this.el);
7500                 container = this.el.dom.parentNode;
7501                 this.allowDomMove = false;
7502             }
7503             this.container = Roo.get(container);
7504             this.rendered = true;
7505             if(position !== undefined){
7506                 if(typeof position == 'number'){
7507                     position = this.container.dom.childNodes[position];
7508                 }else{
7509                     position = Roo.getDom(position);
7510                 }
7511             }
7512             this.onRender(this.container, position || null);
7513             if(this.cls){
7514                 this.el.addClass(this.cls);
7515                 delete this.cls;
7516             }
7517             if(this.style){
7518                 this.el.applyStyles(this.style);
7519                 delete this.style;
7520             }
7521             this.fireEvent("render", this);
7522             this.afterRender(this.container);
7523             if(this.hidden){
7524                 this.hide();
7525             }
7526             if(this.disabled){
7527                 this.disable();
7528             }
7529         }
7530         return this;
7531     },
7532
7533     /** @private */
7534     // default function is not really useful
7535     onRender : function(ct, position){
7536         if(this.el){
7537             this.el = Roo.get(this.el);
7538             if(this.allowDomMove !== false){
7539                 ct.dom.insertBefore(this.el.dom, position);
7540             }
7541         }
7542     },
7543
7544     /** @private */
7545     getAutoCreate : function(){
7546         var cfg = typeof this.autoCreate == "object" ?
7547                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
7548         if(this.id && !cfg.id){
7549             cfg.id = this.id;
7550         }
7551         return cfg;
7552     },
7553
7554     /** @private */
7555     afterRender : Roo.emptyFn,
7556
7557     /**
7558      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
7559      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
7560      */
7561     destroy : function(){
7562         if(this.fireEvent("beforedestroy", this) !== false){
7563             this.purgeListeners();
7564             this.beforeDestroy();
7565             if(this.rendered){
7566                 this.el.removeAllListeners();
7567                 this.el.remove();
7568                 if(this.actionMode == "container"){
7569                     this.container.remove();
7570                 }
7571             }
7572             this.onDestroy();
7573             Roo.ComponentMgr.unregister(this);
7574             this.fireEvent("destroy", this);
7575         }
7576     },
7577
7578         /** @private */
7579     beforeDestroy : function(){
7580
7581     },
7582
7583         /** @private */
7584         onDestroy : function(){
7585
7586     },
7587
7588     /**
7589      * Returns the underlying {@link Roo.Element}.
7590      * @return {Roo.Element} The element
7591      */
7592     getEl : function(){
7593         return this.el;
7594     },
7595
7596     /**
7597      * Returns the id of this component.
7598      * @return {String}
7599      */
7600     getId : function(){
7601         return this.id;
7602     },
7603
7604     /**
7605      * Try to focus this component.
7606      * @param {Boolean} selectText True to also select the text in this component (if applicable)
7607      * @return {Roo.Component} this
7608      */
7609     focus : function(selectText){
7610         if(this.rendered){
7611             this.el.focus();
7612             if(selectText === true){
7613                 this.el.dom.select();
7614             }
7615         }
7616         return this;
7617     },
7618
7619     /** @private */
7620     blur : function(){
7621         if(this.rendered){
7622             this.el.blur();
7623         }
7624         return this;
7625     },
7626
7627     /**
7628      * Disable this component.
7629      * @return {Roo.Component} this
7630      */
7631     disable : function(){
7632         if(this.rendered){
7633             this.onDisable();
7634         }
7635         this.disabled = true;
7636         this.fireEvent("disable", this);
7637         return this;
7638     },
7639
7640         // private
7641     onDisable : function(){
7642         this.getActionEl().addClass(this.disabledClass);
7643         this.el.dom.disabled = true;
7644     },
7645
7646     /**
7647      * Enable this component.
7648      * @return {Roo.Component} this
7649      */
7650     enable : function(){
7651         if(this.rendered){
7652             this.onEnable();
7653         }
7654         this.disabled = false;
7655         this.fireEvent("enable", this);
7656         return this;
7657     },
7658
7659         // private
7660     onEnable : function(){
7661         this.getActionEl().removeClass(this.disabledClass);
7662         this.el.dom.disabled = false;
7663     },
7664
7665     /**
7666      * Convenience function for setting disabled/enabled by boolean.
7667      * @param {Boolean} disabled
7668      */
7669     setDisabled : function(disabled){
7670         this[disabled ? "disable" : "enable"]();
7671     },
7672
7673     /**
7674      * Show this component.
7675      * @return {Roo.Component} this
7676      */
7677     show: function(){
7678         if(this.fireEvent("beforeshow", this) !== false){
7679             this.hidden = false;
7680             if(this.rendered){
7681                 this.onShow();
7682             }
7683             this.fireEvent("show", this);
7684         }
7685         return this;
7686     },
7687
7688     // private
7689     onShow : function(){
7690         var ae = this.getActionEl();
7691         if(this.hideMode == 'visibility'){
7692             ae.dom.style.visibility = "visible";
7693         }else if(this.hideMode == 'offsets'){
7694             ae.removeClass('x-hidden');
7695         }else{
7696             ae.dom.style.display = "";
7697         }
7698     },
7699
7700     /**
7701      * Hide this component.
7702      * @return {Roo.Component} this
7703      */
7704     hide: function(){
7705         if(this.fireEvent("beforehide", this) !== false){
7706             this.hidden = true;
7707             if(this.rendered){
7708                 this.onHide();
7709             }
7710             this.fireEvent("hide", this);
7711         }
7712         return this;
7713     },
7714
7715     // private
7716     onHide : function(){
7717         var ae = this.getActionEl();
7718         if(this.hideMode == 'visibility'){
7719             ae.dom.style.visibility = "hidden";
7720         }else if(this.hideMode == 'offsets'){
7721             ae.addClass('x-hidden');
7722         }else{
7723             ae.dom.style.display = "none";
7724         }
7725     },
7726
7727     /**
7728      * Convenience function to hide or show this component by boolean.
7729      * @param {Boolean} visible True to show, false to hide
7730      * @return {Roo.Component} this
7731      */
7732     setVisible: function(visible){
7733         if(visible) {
7734             this.show();
7735         }else{
7736             this.hide();
7737         }
7738         return this;
7739     },
7740
7741     /**
7742      * Returns true if this component is visible.
7743      */
7744     isVisible : function(){
7745         return this.getActionEl().isVisible();
7746     },
7747
7748     cloneConfig : function(overrides){
7749         overrides = overrides || {};
7750         var id = overrides.id || Roo.id();
7751         var cfg = Roo.applyIf(overrides, this.initialConfig);
7752         cfg.id = id; // prevent dup id
7753         return new this.constructor(cfg);
7754     }
7755 });/*
7756  * Based on:
7757  * Ext JS Library 1.1.1
7758  * Copyright(c) 2006-2007, Ext JS, LLC.
7759  *
7760  * Originally Released Under LGPL - original licence link has changed is not relivant.
7761  *
7762  * Fork - LGPL
7763  * <script type="text/javascript">
7764  */
7765  (function(){ 
7766 /**
7767  * @class Roo.Layer
7768  * @extends Roo.Element
7769  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
7770  * automatic maintaining of shadow/shim positions.
7771  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
7772  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
7773  * you can pass a string with a CSS class name. False turns off the shadow.
7774  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
7775  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
7776  * @cfg {String} cls CSS class to add to the element
7777  * @cfg {Number} zindex Starting z-index (defaults to 11000)
7778  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
7779  * @constructor
7780  * @param {Object} config An object with config options.
7781  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
7782  */
7783
7784 Roo.Layer = function(config, existingEl){
7785     config = config || {};
7786     var dh = Roo.DomHelper;
7787     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
7788     if(existingEl){
7789         this.dom = Roo.getDom(existingEl);
7790     }
7791     if(!this.dom){
7792         var o = config.dh || {tag: "div", cls: "x-layer"};
7793         this.dom = dh.append(pel, o);
7794     }
7795     if(config.cls){
7796         this.addClass(config.cls);
7797     }
7798     this.constrain = config.constrain !== false;
7799     this.visibilityMode = Roo.Element.VISIBILITY;
7800     if(config.id){
7801         this.id = this.dom.id = config.id;
7802     }else{
7803         this.id = Roo.id(this.dom);
7804     }
7805     this.zindex = config.zindex || this.getZIndex();
7806     this.position("absolute", this.zindex);
7807     if(config.shadow){
7808         this.shadowOffset = config.shadowOffset || 4;
7809         this.shadow = new Roo.Shadow({
7810             offset : this.shadowOffset,
7811             mode : config.shadow
7812         });
7813     }else{
7814         this.shadowOffset = 0;
7815     }
7816     this.useShim = config.shim !== false && Roo.useShims;
7817     this.useDisplay = config.useDisplay;
7818     this.hide();
7819 };
7820
7821 var supr = Roo.Element.prototype;
7822
7823 // shims are shared among layer to keep from having 100 iframes
7824 var shims = [];
7825
7826 Roo.extend(Roo.Layer, Roo.Element, {
7827
7828     getZIndex : function(){
7829         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
7830     },
7831
7832     getShim : function(){
7833         if(!this.useShim){
7834             return null;
7835         }
7836         if(this.shim){
7837             return this.shim;
7838         }
7839         var shim = shims.shift();
7840         if(!shim){
7841             shim = this.createShim();
7842             shim.enableDisplayMode('block');
7843             shim.dom.style.display = 'none';
7844             shim.dom.style.visibility = 'visible';
7845         }
7846         var pn = this.dom.parentNode;
7847         if(shim.dom.parentNode != pn){
7848             pn.insertBefore(shim.dom, this.dom);
7849         }
7850         shim.setStyle('z-index', this.getZIndex()-2);
7851         this.shim = shim;
7852         return shim;
7853     },
7854
7855     hideShim : function(){
7856         if(this.shim){
7857             this.shim.setDisplayed(false);
7858             shims.push(this.shim);
7859             delete this.shim;
7860         }
7861     },
7862
7863     disableShadow : function(){
7864         if(this.shadow){
7865             this.shadowDisabled = true;
7866             this.shadow.hide();
7867             this.lastShadowOffset = this.shadowOffset;
7868             this.shadowOffset = 0;
7869         }
7870     },
7871
7872     enableShadow : function(show){
7873         if(this.shadow){
7874             this.shadowDisabled = false;
7875             this.shadowOffset = this.lastShadowOffset;
7876             delete this.lastShadowOffset;
7877             if(show){
7878                 this.sync(true);
7879             }
7880         }
7881     },
7882
7883     // private
7884     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
7885     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
7886     sync : function(doShow){
7887         var sw = this.shadow;
7888         if(!this.updating && this.isVisible() && (sw || this.useShim)){
7889             var sh = this.getShim();
7890
7891             var w = this.getWidth(),
7892                 h = this.getHeight();
7893
7894             var l = this.getLeft(true),
7895                 t = this.getTop(true);
7896
7897             if(sw && !this.shadowDisabled){
7898                 if(doShow && !sw.isVisible()){
7899                     sw.show(this);
7900                 }else{
7901                     sw.realign(l, t, w, h);
7902                 }
7903                 if(sh){
7904                     if(doShow){
7905                        sh.show();
7906                     }
7907                     // fit the shim behind the shadow, so it is shimmed too
7908                     var a = sw.adjusts, s = sh.dom.style;
7909                     s.left = (Math.min(l, l+a.l))+"px";
7910                     s.top = (Math.min(t, t+a.t))+"px";
7911                     s.width = (w+a.w)+"px";
7912                     s.height = (h+a.h)+"px";
7913                 }
7914             }else if(sh){
7915                 if(doShow){
7916                    sh.show();
7917                 }
7918                 sh.setSize(w, h);
7919                 sh.setLeftTop(l, t);
7920             }
7921             
7922         }
7923     },
7924
7925     // private
7926     destroy : function(){
7927         this.hideShim();
7928         if(this.shadow){
7929             this.shadow.hide();
7930         }
7931         this.removeAllListeners();
7932         var pn = this.dom.parentNode;
7933         if(pn){
7934             pn.removeChild(this.dom);
7935         }
7936         Roo.Element.uncache(this.id);
7937     },
7938
7939     remove : function(){
7940         this.destroy();
7941     },
7942
7943     // private
7944     beginUpdate : function(){
7945         this.updating = true;
7946     },
7947
7948     // private
7949     endUpdate : function(){
7950         this.updating = false;
7951         this.sync(true);
7952     },
7953
7954     // private
7955     hideUnders : function(negOffset){
7956         if(this.shadow){
7957             this.shadow.hide();
7958         }
7959         this.hideShim();
7960     },
7961
7962     // private
7963     constrainXY : function(){
7964         if(this.constrain){
7965             var vw = Roo.lib.Dom.getViewWidth(),
7966                 vh = Roo.lib.Dom.getViewHeight();
7967             var s = Roo.get(document).getScroll();
7968
7969             var xy = this.getXY();
7970             var x = xy[0], y = xy[1];   
7971             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
7972             // only move it if it needs it
7973             var moved = false;
7974             // first validate right/bottom
7975             if((x + w) > vw+s.left){
7976                 x = vw - w - this.shadowOffset;
7977                 moved = true;
7978             }
7979             if((y + h) > vh+s.top){
7980                 y = vh - h - this.shadowOffset;
7981                 moved = true;
7982             }
7983             // then make sure top/left isn't negative
7984             if(x < s.left){
7985                 x = s.left;
7986                 moved = true;
7987             }
7988             if(y < s.top){
7989                 y = s.top;
7990                 moved = true;
7991             }
7992             if(moved){
7993                 if(this.avoidY){
7994                     var ay = this.avoidY;
7995                     if(y <= ay && (y+h) >= ay){
7996                         y = ay-h-5;   
7997                     }
7998                 }
7999                 xy = [x, y];
8000                 this.storeXY(xy);
8001                 supr.setXY.call(this, xy);
8002                 this.sync();
8003             }
8004         }
8005     },
8006
8007     isVisible : function(){
8008         return this.visible;    
8009     },
8010
8011     // private
8012     showAction : function(){
8013         this.visible = true; // track visibility to prevent getStyle calls
8014         if(this.useDisplay === true){
8015             this.setDisplayed("");
8016         }else if(this.lastXY){
8017             supr.setXY.call(this, this.lastXY);
8018         }else if(this.lastLT){
8019             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
8020         }
8021     },
8022
8023     // private
8024     hideAction : function(){
8025         this.visible = false;
8026         if(this.useDisplay === true){
8027             this.setDisplayed(false);
8028         }else{
8029             this.setLeftTop(-10000,-10000);
8030         }
8031     },
8032
8033     // overridden Element method
8034     setVisible : function(v, a, d, c, e){
8035         if(v){
8036             this.showAction();
8037         }
8038         if(a && v){
8039             var cb = function(){
8040                 this.sync(true);
8041                 if(c){
8042                     c();
8043                 }
8044             }.createDelegate(this);
8045             supr.setVisible.call(this, true, true, d, cb, e);
8046         }else{
8047             if(!v){
8048                 this.hideUnders(true);
8049             }
8050             var cb = c;
8051             if(a){
8052                 cb = function(){
8053                     this.hideAction();
8054                     if(c){
8055                         c();
8056                     }
8057                 }.createDelegate(this);
8058             }
8059             supr.setVisible.call(this, v, a, d, cb, e);
8060             if(v){
8061                 this.sync(true);
8062             }else if(!a){
8063                 this.hideAction();
8064             }
8065         }
8066     },
8067
8068     storeXY : function(xy){
8069         delete this.lastLT;
8070         this.lastXY = xy;
8071     },
8072
8073     storeLeftTop : function(left, top){
8074         delete this.lastXY;
8075         this.lastLT = [left, top];
8076     },
8077
8078     // private
8079     beforeFx : function(){
8080         this.beforeAction();
8081         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
8082     },
8083
8084     // private
8085     afterFx : function(){
8086         Roo.Layer.superclass.afterFx.apply(this, arguments);
8087         this.sync(this.isVisible());
8088     },
8089
8090     // private
8091     beforeAction : function(){
8092         if(!this.updating && this.shadow){
8093             this.shadow.hide();
8094         }
8095     },
8096
8097     // overridden Element method
8098     setLeft : function(left){
8099         this.storeLeftTop(left, this.getTop(true));
8100         supr.setLeft.apply(this, arguments);
8101         this.sync();
8102     },
8103
8104     setTop : function(top){
8105         this.storeLeftTop(this.getLeft(true), top);
8106         supr.setTop.apply(this, arguments);
8107         this.sync();
8108     },
8109
8110     setLeftTop : function(left, top){
8111         this.storeLeftTop(left, top);
8112         supr.setLeftTop.apply(this, arguments);
8113         this.sync();
8114     },
8115
8116     setXY : function(xy, a, d, c, e){
8117         this.fixDisplay();
8118         this.beforeAction();
8119         this.storeXY(xy);
8120         var cb = this.createCB(c);
8121         supr.setXY.call(this, xy, a, d, cb, e);
8122         if(!a){
8123             cb();
8124         }
8125     },
8126
8127     // private
8128     createCB : function(c){
8129         var el = this;
8130         return function(){
8131             el.constrainXY();
8132             el.sync(true);
8133             if(c){
8134                 c();
8135             }
8136         };
8137     },
8138
8139     // overridden Element method
8140     setX : function(x, a, d, c, e){
8141         this.setXY([x, this.getY()], a, d, c, e);
8142     },
8143
8144     // overridden Element method
8145     setY : function(y, a, d, c, e){
8146         this.setXY([this.getX(), y], a, d, c, e);
8147     },
8148
8149     // overridden Element method
8150     setSize : function(w, h, a, d, c, e){
8151         this.beforeAction();
8152         var cb = this.createCB(c);
8153         supr.setSize.call(this, w, h, a, d, cb, e);
8154         if(!a){
8155             cb();
8156         }
8157     },
8158
8159     // overridden Element method
8160     setWidth : function(w, a, d, c, e){
8161         this.beforeAction();
8162         var cb = this.createCB(c);
8163         supr.setWidth.call(this, w, a, d, cb, e);
8164         if(!a){
8165             cb();
8166         }
8167     },
8168
8169     // overridden Element method
8170     setHeight : function(h, a, d, c, e){
8171         this.beforeAction();
8172         var cb = this.createCB(c);
8173         supr.setHeight.call(this, h, a, d, cb, e);
8174         if(!a){
8175             cb();
8176         }
8177     },
8178
8179     // overridden Element method
8180     setBounds : function(x, y, w, h, a, d, c, e){
8181         this.beforeAction();
8182         var cb = this.createCB(c);
8183         if(!a){
8184             this.storeXY([x, y]);
8185             supr.setXY.call(this, [x, y]);
8186             supr.setSize.call(this, w, h, a, d, cb, e);
8187             cb();
8188         }else{
8189             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
8190         }
8191         return this;
8192     },
8193     
8194     /**
8195      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
8196      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
8197      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
8198      * @param {Number} zindex The new z-index to set
8199      * @return {this} The Layer
8200      */
8201     setZIndex : function(zindex){
8202         this.zindex = zindex;
8203         this.setStyle("z-index", zindex + 2);
8204         if(this.shadow){
8205             this.shadow.setZIndex(zindex + 1);
8206         }
8207         if(this.shim){
8208             this.shim.setStyle("z-index", zindex);
8209         }
8210     }
8211 });
8212 })();/*
8213  * Based on:
8214  * Ext JS Library 1.1.1
8215  * Copyright(c) 2006-2007, Ext JS, LLC.
8216  *
8217  * Originally Released Under LGPL - original licence link has changed is not relivant.
8218  *
8219  * Fork - LGPL
8220  * <script type="text/javascript">
8221  */
8222
8223
8224 /**
8225  * @class Roo.Shadow
8226  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
8227  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
8228  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
8229  * @constructor
8230  * Create a new Shadow
8231  * @param {Object} config The config object
8232  */
8233 Roo.Shadow = function(config){
8234     Roo.apply(this, config);
8235     if(typeof this.mode != "string"){
8236         this.mode = this.defaultMode;
8237     }
8238     var o = this.offset, a = {h: 0};
8239     var rad = Math.floor(this.offset/2);
8240     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
8241         case "drop":
8242             a.w = 0;
8243             a.l = a.t = o;
8244             a.t -= 1;
8245             if(Roo.isIE){
8246                 a.l -= this.offset + rad;
8247                 a.t -= this.offset + rad;
8248                 a.w -= rad;
8249                 a.h -= rad;
8250                 a.t += 1;
8251             }
8252         break;
8253         case "sides":
8254             a.w = (o*2);
8255             a.l = -o;
8256             a.t = o-1;
8257             if(Roo.isIE){
8258                 a.l -= (this.offset - rad);
8259                 a.t -= this.offset + rad;
8260                 a.l += 1;
8261                 a.w -= (this.offset - rad)*2;
8262                 a.w -= rad + 1;
8263                 a.h -= 1;
8264             }
8265         break;
8266         case "frame":
8267             a.w = a.h = (o*2);
8268             a.l = a.t = -o;
8269             a.t += 1;
8270             a.h -= 2;
8271             if(Roo.isIE){
8272                 a.l -= (this.offset - rad);
8273                 a.t -= (this.offset - rad);
8274                 a.l += 1;
8275                 a.w -= (this.offset + rad + 1);
8276                 a.h -= (this.offset + rad);
8277                 a.h += 1;
8278             }
8279         break;
8280     };
8281
8282     this.adjusts = a;
8283 };
8284
8285 Roo.Shadow.prototype = {
8286     /**
8287      * @cfg {String} mode
8288      * The shadow display mode.  Supports the following options:<br />
8289      * sides: Shadow displays on both sides and bottom only<br />
8290      * frame: Shadow displays equally on all four sides<br />
8291      * drop: Traditional bottom-right drop shadow (default)
8292      */
8293     /**
8294      * @cfg {String} offset
8295      * The number of pixels to offset the shadow from the element (defaults to 4)
8296      */
8297     offset: 4,
8298
8299     // private
8300     defaultMode: "drop",
8301
8302     /**
8303      * Displays the shadow under the target element
8304      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
8305      */
8306     show : function(target){
8307         target = Roo.get(target);
8308         if(!this.el){
8309             this.el = Roo.Shadow.Pool.pull();
8310             if(this.el.dom.nextSibling != target.dom){
8311                 this.el.insertBefore(target);
8312             }
8313         }
8314         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
8315         if(Roo.isIE){
8316             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
8317         }
8318         this.realign(
8319             target.getLeft(true),
8320             target.getTop(true),
8321             target.getWidth(),
8322             target.getHeight()
8323         );
8324         this.el.dom.style.display = "block";
8325     },
8326
8327     /**
8328      * Returns true if the shadow is visible, else false
8329      */
8330     isVisible : function(){
8331         return this.el ? true : false;  
8332     },
8333
8334     /**
8335      * Direct alignment when values are already available. Show must be called at least once before
8336      * calling this method to ensure it is initialized.
8337      * @param {Number} left The target element left position
8338      * @param {Number} top The target element top position
8339      * @param {Number} width The target element width
8340      * @param {Number} height The target element height
8341      */
8342     realign : function(l, t, w, h){
8343         if(!this.el){
8344             return;
8345         }
8346         var a = this.adjusts, d = this.el.dom, s = d.style;
8347         var iea = 0;
8348         s.left = (l+a.l)+"px";
8349         s.top = (t+a.t)+"px";
8350         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
8351  
8352         if(s.width != sws || s.height != shs){
8353             s.width = sws;
8354             s.height = shs;
8355             if(!Roo.isIE){
8356                 var cn = d.childNodes;
8357                 var sww = Math.max(0, (sw-12))+"px";
8358                 cn[0].childNodes[1].style.width = sww;
8359                 cn[1].childNodes[1].style.width = sww;
8360                 cn[2].childNodes[1].style.width = sww;
8361                 cn[1].style.height = Math.max(0, (sh-12))+"px";
8362             }
8363         }
8364     },
8365
8366     /**
8367      * Hides this shadow
8368      */
8369     hide : function(){
8370         if(this.el){
8371             this.el.dom.style.display = "none";
8372             Roo.Shadow.Pool.push(this.el);
8373             delete this.el;
8374         }
8375     },
8376
8377     /**
8378      * Adjust the z-index of this shadow
8379      * @param {Number} zindex The new z-index
8380      */
8381     setZIndex : function(z){
8382         this.zIndex = z;
8383         if(this.el){
8384             this.el.setStyle("z-index", z);
8385         }
8386     }
8387 };
8388
8389 // Private utility class that manages the internal Shadow cache
8390 Roo.Shadow.Pool = function(){
8391     var p = [];
8392     var markup = Roo.isIE ?
8393                  '<div class="x-ie-shadow"></div>' :
8394                  '<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>';
8395     return {
8396         pull : function(){
8397             var sh = p.shift();
8398             if(!sh){
8399                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
8400                 sh.autoBoxAdjust = false;
8401             }
8402             return sh;
8403         },
8404
8405         push : function(sh){
8406             p.push(sh);
8407         }
8408     };
8409 }();/*
8410  * Based on:
8411  * Ext JS Library 1.1.1
8412  * Copyright(c) 2006-2007, Ext JS, LLC.
8413  *
8414  * Originally Released Under LGPL - original licence link has changed is not relivant.
8415  *
8416  * Fork - LGPL
8417  * <script type="text/javascript">
8418  */
8419
8420 /**
8421  * @class Roo.BoxComponent
8422  * @extends Roo.Component
8423  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
8424  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
8425  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
8426  * layout containers.
8427  * @constructor
8428  * @param {Roo.Element/String/Object} config The configuration options.
8429  */
8430 Roo.BoxComponent = function(config){
8431     Roo.Component.call(this, config);
8432     this.addEvents({
8433         /**
8434          * @event resize
8435          * Fires after the component is resized.
8436              * @param {Roo.Component} this
8437              * @param {Number} adjWidth The box-adjusted width that was set
8438              * @param {Number} adjHeight The box-adjusted height that was set
8439              * @param {Number} rawWidth The width that was originally specified
8440              * @param {Number} rawHeight The height that was originally specified
8441              */
8442         resize : true,
8443         /**
8444          * @event move
8445          * Fires after the component is moved.
8446              * @param {Roo.Component} this
8447              * @param {Number} x The new x position
8448              * @param {Number} y The new y position
8449              */
8450         move : true
8451     });
8452 };
8453
8454 Roo.extend(Roo.BoxComponent, Roo.Component, {
8455     // private, set in afterRender to signify that the component has been rendered
8456     boxReady : false,
8457     // private, used to defer height settings to subclasses
8458     deferHeight: false,
8459     /** @cfg {Number} width
8460      * width (optional) size of component
8461      */
8462      /** @cfg {Number} height
8463      * height (optional) size of component
8464      */
8465      
8466     /**
8467      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
8468      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
8469      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
8470      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
8471      * @return {Roo.BoxComponent} this
8472      */
8473     setSize : function(w, h){
8474         // support for standard size objects
8475         if(typeof w == 'object'){
8476             h = w.height;
8477             w = w.width;
8478         }
8479         // not rendered
8480         if(!this.boxReady){
8481             this.width = w;
8482             this.height = h;
8483             return this;
8484         }
8485
8486         // prevent recalcs when not needed
8487         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
8488             return this;
8489         }
8490         this.lastSize = {width: w, height: h};
8491
8492         var adj = this.adjustSize(w, h);
8493         var aw = adj.width, ah = adj.height;
8494         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
8495             var rz = this.getResizeEl();
8496             if(!this.deferHeight && aw !== undefined && ah !== undefined){
8497                 rz.setSize(aw, ah);
8498             }else if(!this.deferHeight && ah !== undefined){
8499                 rz.setHeight(ah);
8500             }else if(aw !== undefined){
8501                 rz.setWidth(aw);
8502             }
8503             this.onResize(aw, ah, w, h);
8504             this.fireEvent('resize', this, aw, ah, w, h);
8505         }
8506         return this;
8507     },
8508
8509     /**
8510      * Gets the current size of the component's underlying element.
8511      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8512      */
8513     getSize : function(){
8514         return this.el.getSize();
8515     },
8516
8517     /**
8518      * Gets the current XY position of the component's underlying element.
8519      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8520      * @return {Array} The XY position of the element (e.g., [100, 200])
8521      */
8522     getPosition : function(local){
8523         if(local === true){
8524             return [this.el.getLeft(true), this.el.getTop(true)];
8525         }
8526         return this.xy || this.el.getXY();
8527     },
8528
8529     /**
8530      * Gets the current box measurements of the component's underlying element.
8531      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8532      * @returns {Object} box An object in the format {x, y, width, height}
8533      */
8534     getBox : function(local){
8535         var s = this.el.getSize();
8536         if(local){
8537             s.x = this.el.getLeft(true);
8538             s.y = this.el.getTop(true);
8539         }else{
8540             var xy = this.xy || this.el.getXY();
8541             s.x = xy[0];
8542             s.y = xy[1];
8543         }
8544         return s;
8545     },
8546
8547     /**
8548      * Sets the current box measurements of the component's underlying element.
8549      * @param {Object} box An object in the format {x, y, width, height}
8550      * @returns {Roo.BoxComponent} this
8551      */
8552     updateBox : function(box){
8553         this.setSize(box.width, box.height);
8554         this.setPagePosition(box.x, box.y);
8555         return this;
8556     },
8557
8558     // protected
8559     getResizeEl : function(){
8560         return this.resizeEl || this.el;
8561     },
8562
8563     // protected
8564     getPositionEl : function(){
8565         return this.positionEl || this.el;
8566     },
8567
8568     /**
8569      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
8570      * This method fires the move event.
8571      * @param {Number} left The new left
8572      * @param {Number} top The new top
8573      * @returns {Roo.BoxComponent} this
8574      */
8575     setPosition : function(x, y){
8576         this.x = x;
8577         this.y = y;
8578         if(!this.boxReady){
8579             return this;
8580         }
8581         var adj = this.adjustPosition(x, y);
8582         var ax = adj.x, ay = adj.y;
8583
8584         var el = this.getPositionEl();
8585         if(ax !== undefined || ay !== undefined){
8586             if(ax !== undefined && ay !== undefined){
8587                 el.setLeftTop(ax, ay);
8588             }else if(ax !== undefined){
8589                 el.setLeft(ax);
8590             }else if(ay !== undefined){
8591                 el.setTop(ay);
8592             }
8593             this.onPosition(ax, ay);
8594             this.fireEvent('move', this, ax, ay);
8595         }
8596         return this;
8597     },
8598
8599     /**
8600      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
8601      * This method fires the move event.
8602      * @param {Number} x The new x position
8603      * @param {Number} y The new y position
8604      * @returns {Roo.BoxComponent} this
8605      */
8606     setPagePosition : function(x, y){
8607         this.pageX = x;
8608         this.pageY = y;
8609         if(!this.boxReady){
8610             return;
8611         }
8612         if(x === undefined || y === undefined){ // cannot translate undefined points
8613             return;
8614         }
8615         var p = this.el.translatePoints(x, y);
8616         this.setPosition(p.left, p.top);
8617         return this;
8618     },
8619
8620     // private
8621     onRender : function(ct, position){
8622         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
8623         if(this.resizeEl){
8624             this.resizeEl = Roo.get(this.resizeEl);
8625         }
8626         if(this.positionEl){
8627             this.positionEl = Roo.get(this.positionEl);
8628         }
8629     },
8630
8631     // private
8632     afterRender : function(){
8633         Roo.BoxComponent.superclass.afterRender.call(this);
8634         this.boxReady = true;
8635         this.setSize(this.width, this.height);
8636         if(this.x || this.y){
8637             this.setPosition(this.x, this.y);
8638         }
8639         if(this.pageX || this.pageY){
8640             this.setPagePosition(this.pageX, this.pageY);
8641         }
8642     },
8643
8644     /**
8645      * Force the component's size to recalculate based on the underlying element's current height and width.
8646      * @returns {Roo.BoxComponent} this
8647      */
8648     syncSize : function(){
8649         delete this.lastSize;
8650         this.setSize(this.el.getWidth(), this.el.getHeight());
8651         return this;
8652     },
8653
8654     /**
8655      * Called after the component is resized, this method is empty by default but can be implemented by any
8656      * subclass that needs to perform custom logic after a resize occurs.
8657      * @param {Number} adjWidth The box-adjusted width that was set
8658      * @param {Number} adjHeight The box-adjusted height that was set
8659      * @param {Number} rawWidth The width that was originally specified
8660      * @param {Number} rawHeight The height that was originally specified
8661      */
8662     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
8663
8664     },
8665
8666     /**
8667      * Called after the component is moved, this method is empty by default but can be implemented by any
8668      * subclass that needs to perform custom logic after a move occurs.
8669      * @param {Number} x The new x position
8670      * @param {Number} y The new y position
8671      */
8672     onPosition : function(x, y){
8673
8674     },
8675
8676     // private
8677     adjustSize : function(w, h){
8678         if(this.autoWidth){
8679             w = 'auto';
8680         }
8681         if(this.autoHeight){
8682             h = 'auto';
8683         }
8684         return {width : w, height: h};
8685     },
8686
8687     // private
8688     adjustPosition : function(x, y){
8689         return {x : x, y: y};
8690     }
8691 });/*
8692  * Based on:
8693  * Ext JS Library 1.1.1
8694  * Copyright(c) 2006-2007, Ext JS, LLC.
8695  *
8696  * Originally Released Under LGPL - original licence link has changed is not relivant.
8697  *
8698  * Fork - LGPL
8699  * <script type="text/javascript">
8700  */
8701
8702
8703 /**
8704  * @class Roo.SplitBar
8705  * @extends Roo.util.Observable
8706  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
8707  * <br><br>
8708  * Usage:
8709  * <pre><code>
8710 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
8711                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
8712 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
8713 split.minSize = 100;
8714 split.maxSize = 600;
8715 split.animate = true;
8716 split.on('moved', splitterMoved);
8717 </code></pre>
8718  * @constructor
8719  * Create a new SplitBar
8720  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
8721  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
8722  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8723  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
8724                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
8725                         position of the SplitBar).
8726  */
8727 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
8728     
8729     /** @private */
8730     this.el = Roo.get(dragElement, true);
8731     this.el.dom.unselectable = "on";
8732     /** @private */
8733     this.resizingEl = Roo.get(resizingElement, true);
8734
8735     /**
8736      * @private
8737      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8738      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
8739      * @type Number
8740      */
8741     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
8742     
8743     /**
8744      * The minimum size of the resizing element. (Defaults to 0)
8745      * @type Number
8746      */
8747     this.minSize = 0;
8748     
8749     /**
8750      * The maximum size of the resizing element. (Defaults to 2000)
8751      * @type Number
8752      */
8753     this.maxSize = 2000;
8754     
8755     /**
8756      * Whether to animate the transition to the new size
8757      * @type Boolean
8758      */
8759     this.animate = false;
8760     
8761     /**
8762      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
8763      * @type Boolean
8764      */
8765     this.useShim = false;
8766     
8767     /** @private */
8768     this.shim = null;
8769     
8770     if(!existingProxy){
8771         /** @private */
8772         this.proxy = Roo.SplitBar.createProxy(this.orientation);
8773     }else{
8774         this.proxy = Roo.get(existingProxy).dom;
8775     }
8776     /** @private */
8777     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
8778     
8779     /** @private */
8780     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
8781     
8782     /** @private */
8783     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
8784     
8785     /** @private */
8786     this.dragSpecs = {};
8787     
8788     /**
8789      * @private The adapter to use to positon and resize elements
8790      */
8791     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
8792     this.adapter.init(this);
8793     
8794     if(this.orientation == Roo.SplitBar.HORIZONTAL){
8795         /** @private */
8796         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
8797         this.el.addClass("x-splitbar-h");
8798     }else{
8799         /** @private */
8800         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
8801         this.el.addClass("x-splitbar-v");
8802     }
8803     
8804     this.addEvents({
8805         /**
8806          * @event resize
8807          * Fires when the splitter is moved (alias for {@link #event-moved})
8808          * @param {Roo.SplitBar} this
8809          * @param {Number} newSize the new width or height
8810          */
8811         "resize" : true,
8812         /**
8813          * @event moved
8814          * Fires when the splitter is moved
8815          * @param {Roo.SplitBar} this
8816          * @param {Number} newSize the new width or height
8817          */
8818         "moved" : true,
8819         /**
8820          * @event beforeresize
8821          * Fires before the splitter is dragged
8822          * @param {Roo.SplitBar} this
8823          */
8824         "beforeresize" : true,
8825
8826         "beforeapply" : true
8827     });
8828
8829     Roo.util.Observable.call(this);
8830 };
8831
8832 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
8833     onStartProxyDrag : function(x, y){
8834         this.fireEvent("beforeresize", this);
8835         if(!this.overlay){
8836             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
8837             o.unselectable();
8838             o.enableDisplayMode("block");
8839             // all splitbars share the same overlay
8840             Roo.SplitBar.prototype.overlay = o;
8841         }
8842         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8843         this.overlay.show();
8844         Roo.get(this.proxy).setDisplayed("block");
8845         var size = this.adapter.getElementSize(this);
8846         this.activeMinSize = this.getMinimumSize();;
8847         this.activeMaxSize = this.getMaximumSize();;
8848         var c1 = size - this.activeMinSize;
8849         var c2 = Math.max(this.activeMaxSize - size, 0);
8850         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8851             this.dd.resetConstraints();
8852             this.dd.setXConstraint(
8853                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
8854                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
8855             );
8856             this.dd.setYConstraint(0, 0);
8857         }else{
8858             this.dd.resetConstraints();
8859             this.dd.setXConstraint(0, 0);
8860             this.dd.setYConstraint(
8861                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
8862                 this.placement == Roo.SplitBar.TOP ? c2 : c1
8863             );
8864          }
8865         this.dragSpecs.startSize = size;
8866         this.dragSpecs.startPoint = [x, y];
8867         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
8868     },
8869     
8870     /** 
8871      * @private Called after the drag operation by the DDProxy
8872      */
8873     onEndProxyDrag : function(e){
8874         Roo.get(this.proxy).setDisplayed(false);
8875         var endPoint = Roo.lib.Event.getXY(e);
8876         if(this.overlay){
8877             this.overlay.hide();
8878         }
8879         var newSize;
8880         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8881             newSize = this.dragSpecs.startSize + 
8882                 (this.placement == Roo.SplitBar.LEFT ?
8883                     endPoint[0] - this.dragSpecs.startPoint[0] :
8884                     this.dragSpecs.startPoint[0] - endPoint[0]
8885                 );
8886         }else{
8887             newSize = this.dragSpecs.startSize + 
8888                 (this.placement == Roo.SplitBar.TOP ?
8889                     endPoint[1] - this.dragSpecs.startPoint[1] :
8890                     this.dragSpecs.startPoint[1] - endPoint[1]
8891                 );
8892         }
8893         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
8894         if(newSize != this.dragSpecs.startSize){
8895             if(this.fireEvent('beforeapply', this, newSize) !== false){
8896                 this.adapter.setElementSize(this, newSize);
8897                 this.fireEvent("moved", this, newSize);
8898                 this.fireEvent("resize", this, newSize);
8899             }
8900         }
8901     },
8902     
8903     /**
8904      * Get the adapter this SplitBar uses
8905      * @return The adapter object
8906      */
8907     getAdapter : function(){
8908         return this.adapter;
8909     },
8910     
8911     /**
8912      * Set the adapter this SplitBar uses
8913      * @param {Object} adapter A SplitBar adapter object
8914      */
8915     setAdapter : function(adapter){
8916         this.adapter = adapter;
8917         this.adapter.init(this);
8918     },
8919     
8920     /**
8921      * Gets the minimum size for the resizing element
8922      * @return {Number} The minimum size
8923      */
8924     getMinimumSize : function(){
8925         return this.minSize;
8926     },
8927     
8928     /**
8929      * Sets the minimum size for the resizing element
8930      * @param {Number} minSize The minimum size
8931      */
8932     setMinimumSize : function(minSize){
8933         this.minSize = minSize;
8934     },
8935     
8936     /**
8937      * Gets the maximum size for the resizing element
8938      * @return {Number} The maximum size
8939      */
8940     getMaximumSize : function(){
8941         return this.maxSize;
8942     },
8943     
8944     /**
8945      * Sets the maximum size for the resizing element
8946      * @param {Number} maxSize The maximum size
8947      */
8948     setMaximumSize : function(maxSize){
8949         this.maxSize = maxSize;
8950     },
8951     
8952     /**
8953      * Sets the initialize size for the resizing element
8954      * @param {Number} size The initial size
8955      */
8956     setCurrentSize : function(size){
8957         var oldAnimate = this.animate;
8958         this.animate = false;
8959         this.adapter.setElementSize(this, size);
8960         this.animate = oldAnimate;
8961     },
8962     
8963     /**
8964      * Destroy this splitbar. 
8965      * @param {Boolean} removeEl True to remove the element
8966      */
8967     destroy : function(removeEl){
8968         if(this.shim){
8969             this.shim.remove();
8970         }
8971         this.dd.unreg();
8972         this.proxy.parentNode.removeChild(this.proxy);
8973         if(removeEl){
8974             this.el.remove();
8975         }
8976     }
8977 });
8978
8979 /**
8980  * @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.
8981  */
8982 Roo.SplitBar.createProxy = function(dir){
8983     var proxy = new Roo.Element(document.createElement("div"));
8984     proxy.unselectable();
8985     var cls = 'x-splitbar-proxy';
8986     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
8987     document.body.appendChild(proxy.dom);
8988     return proxy.dom;
8989 };
8990
8991 /** 
8992  * @class Roo.SplitBar.BasicLayoutAdapter
8993  * Default Adapter. It assumes the splitter and resizing element are not positioned
8994  * elements and only gets/sets the width of the element. Generally used for table based layouts.
8995  */
8996 Roo.SplitBar.BasicLayoutAdapter = function(){
8997 };
8998
8999 Roo.SplitBar.BasicLayoutAdapter.prototype = {
9000     // do nothing for now
9001     init : function(s){
9002     
9003     },
9004     /**
9005      * Called before drag operations to get the current size of the resizing element. 
9006      * @param {Roo.SplitBar} s The SplitBar using this adapter
9007      */
9008      getElementSize : function(s){
9009         if(s.orientation == Roo.SplitBar.HORIZONTAL){
9010             return s.resizingEl.getWidth();
9011         }else{
9012             return s.resizingEl.getHeight();
9013         }
9014     },
9015     
9016     /**
9017      * Called after drag operations to set the size of the resizing element.
9018      * @param {Roo.SplitBar} s The SplitBar using this adapter
9019      * @param {Number} newSize The new size to set
9020      * @param {Function} onComplete A function to be invoked when resizing is complete
9021      */
9022     setElementSize : function(s, newSize, onComplete){
9023         if(s.orientation == Roo.SplitBar.HORIZONTAL){
9024             if(!s.animate){
9025                 s.resizingEl.setWidth(newSize);
9026                 if(onComplete){
9027                     onComplete(s, newSize);
9028                 }
9029             }else{
9030                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
9031             }
9032         }else{
9033             
9034             if(!s.animate){
9035                 s.resizingEl.setHeight(newSize);
9036                 if(onComplete){
9037                     onComplete(s, newSize);
9038                 }
9039             }else{
9040                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
9041             }
9042         }
9043     }
9044 };
9045
9046 /** 
9047  *@class Roo.SplitBar.AbsoluteLayoutAdapter
9048  * @extends Roo.SplitBar.BasicLayoutAdapter
9049  * Adapter that  moves the splitter element to align with the resized sizing element. 
9050  * Used with an absolute positioned SplitBar.
9051  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
9052  * document.body, make sure you assign an id to the body element.
9053  */
9054 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
9055     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
9056     this.container = Roo.get(container);
9057 };
9058
9059 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
9060     init : function(s){
9061         this.basic.init(s);
9062     },
9063     
9064     getElementSize : function(s){
9065         return this.basic.getElementSize(s);
9066     },
9067     
9068     setElementSize : function(s, newSize, onComplete){
9069         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
9070     },
9071     
9072     moveSplitter : function(s){
9073         var yes = Roo.SplitBar;
9074         switch(s.placement){
9075             case yes.LEFT:
9076                 s.el.setX(s.resizingEl.getRight());
9077                 break;
9078             case yes.RIGHT:
9079                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
9080                 break;
9081             case yes.TOP:
9082                 s.el.setY(s.resizingEl.getBottom());
9083                 break;
9084             case yes.BOTTOM:
9085                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
9086                 break;
9087         }
9088     }
9089 };
9090
9091 /**
9092  * Orientation constant - Create a vertical SplitBar
9093  * @static
9094  * @type Number
9095  */
9096 Roo.SplitBar.VERTICAL = 1;
9097
9098 /**
9099  * Orientation constant - Create a horizontal SplitBar
9100  * @static
9101  * @type Number
9102  */
9103 Roo.SplitBar.HORIZONTAL = 2;
9104
9105 /**
9106  * Placement constant - The resizing element is to the left of the splitter element
9107  * @static
9108  * @type Number
9109  */
9110 Roo.SplitBar.LEFT = 1;
9111
9112 /**
9113  * Placement constant - The resizing element is to the right of the splitter element
9114  * @static
9115  * @type Number
9116  */
9117 Roo.SplitBar.RIGHT = 2;
9118
9119 /**
9120  * Placement constant - The resizing element is positioned above the splitter element
9121  * @static
9122  * @type Number
9123  */
9124 Roo.SplitBar.TOP = 3;
9125
9126 /**
9127  * Placement constant - The resizing element is positioned under splitter element
9128  * @static
9129  * @type Number
9130  */
9131 Roo.SplitBar.BOTTOM = 4;
9132 /*
9133  * Based on:
9134  * Ext JS Library 1.1.1
9135  * Copyright(c) 2006-2007, Ext JS, LLC.
9136  *
9137  * Originally Released Under LGPL - original licence link has changed is not relivant.
9138  *
9139  * Fork - LGPL
9140  * <script type="text/javascript">
9141  */
9142
9143 /**
9144  * @class Roo.View
9145  * @extends Roo.util.Observable
9146  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
9147  * This class also supports single and multi selection modes. <br>
9148  * Create a data model bound view:
9149  <pre><code>
9150  var store = new Roo.data.Store(...);
9151
9152  var view = new Roo.View({
9153     el : "my-element",
9154     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
9155  
9156     singleSelect: true,
9157     selectedClass: "ydataview-selected",
9158     store: store
9159  });
9160
9161  // listen for node click?
9162  view.on("click", function(vw, index, node, e){
9163  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9164  });
9165
9166  // load XML data
9167  dataModel.load("foobar.xml");
9168  </code></pre>
9169  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9170  * <br><br>
9171  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9172  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9173  * 
9174  * Note: old style constructor is still suported (container, template, config)
9175  * 
9176  * @constructor
9177  * Create a new View
9178  * @param {Object} config The config object
9179  * 
9180  */
9181 Roo.View = function(config, depreciated_tpl, depreciated_config){
9182     
9183     if (typeof(depreciated_tpl) == 'undefined') {
9184         // new way.. - universal constructor.
9185         Roo.apply(this, config);
9186         this.el  = Roo.get(this.el);
9187     } else {
9188         // old format..
9189         this.el  = Roo.get(config);
9190         this.tpl = depreciated_tpl;
9191         Roo.apply(this, depreciated_config);
9192     }
9193     this.wrapEl  = this.el.wrap().wrap();
9194     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
9195     
9196     
9197     if(typeof(this.tpl) == "string"){
9198         this.tpl = new Roo.Template(this.tpl);
9199     } else {
9200         // support xtype ctors..
9201         this.tpl = new Roo.factory(this.tpl, Roo);
9202     }
9203     
9204     
9205     this.tpl.compile();
9206    
9207   
9208     
9209      
9210     /** @private */
9211     this.addEvents({
9212         /**
9213          * @event beforeclick
9214          * Fires before a click is processed. Returns false to cancel the default action.
9215          * @param {Roo.View} this
9216          * @param {Number} index The index of the target node
9217          * @param {HTMLElement} node The target node
9218          * @param {Roo.EventObject} e The raw event object
9219          */
9220             "beforeclick" : true,
9221         /**
9222          * @event click
9223          * Fires when a template node is clicked.
9224          * @param {Roo.View} this
9225          * @param {Number} index The index of the target node
9226          * @param {HTMLElement} node The target node
9227          * @param {Roo.EventObject} e The raw event object
9228          */
9229             "click" : true,
9230         /**
9231          * @event dblclick
9232          * Fires when a template node is double clicked.
9233          * @param {Roo.View} this
9234          * @param {Number} index The index of the target node
9235          * @param {HTMLElement} node The target node
9236          * @param {Roo.EventObject} e The raw event object
9237          */
9238             "dblclick" : true,
9239         /**
9240          * @event contextmenu
9241          * Fires when a template node is right clicked.
9242          * @param {Roo.View} this
9243          * @param {Number} index The index of the target node
9244          * @param {HTMLElement} node The target node
9245          * @param {Roo.EventObject} e The raw event object
9246          */
9247             "contextmenu" : true,
9248         /**
9249          * @event selectionchange
9250          * Fires when the selected nodes change.
9251          * @param {Roo.View} this
9252          * @param {Array} selections Array of the selected nodes
9253          */
9254             "selectionchange" : true,
9255     
9256         /**
9257          * @event beforeselect
9258          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9259          * @param {Roo.View} this
9260          * @param {HTMLElement} node The node to be selected
9261          * @param {Array} selections Array of currently selected nodes
9262          */
9263             "beforeselect" : true,
9264         /**
9265          * @event preparedata
9266          * Fires on every row to render, to allow you to change the data.
9267          * @param {Roo.View} this
9268          * @param {Object} data to be rendered (change this)
9269          */
9270           "preparedata" : true
9271           
9272           
9273         });
9274
9275
9276
9277     this.el.on({
9278         "click": this.onClick,
9279         "dblclick": this.onDblClick,
9280         "contextmenu": this.onContextMenu,
9281         scope:this
9282     });
9283
9284     this.selections = [];
9285     this.nodes = [];
9286     this.cmp = new Roo.CompositeElementLite([]);
9287     if(this.store){
9288         this.store = Roo.factory(this.store, Roo.data);
9289         this.setStore(this.store, true);
9290     }
9291     
9292     if ( this.footer && this.footer.xtype) {
9293            
9294          var fctr = this.wrapEl.appendChild(document.createElement("div"));
9295         
9296         this.footer.dataSource = this.store
9297         this.footer.container = fctr;
9298         this.footer = Roo.factory(this.footer, Roo);
9299         fctr.insertFirst(this.el);
9300         
9301         // this is a bit insane - as the paging toolbar seems to detach the el..
9302 //        dom.parentNode.parentNode.parentNode
9303          // they get detached?
9304     }
9305     
9306     
9307     Roo.View.superclass.constructor.call(this);
9308     
9309     
9310 };
9311
9312 Roo.extend(Roo.View, Roo.util.Observable, {
9313     
9314      /**
9315      * @cfg {Roo.data.Store} store Data store to load data from.
9316      */
9317     store : false,
9318     
9319     /**
9320      * @cfg {String|Roo.Element} el The container element.
9321      */
9322     el : '',
9323     
9324     /**
9325      * @cfg {String|Roo.Template} tpl The template used by this View 
9326      */
9327     tpl : false,
9328     /**
9329      * @cfg {String} dataName the named area of the template to use as the data area
9330      *                          Works with domtemplates roo-name="name"
9331      */
9332     dataName: false,
9333     /**
9334      * @cfg {String} selectedClass The css class to add to selected nodes
9335      */
9336     selectedClass : "x-view-selected",
9337      /**
9338      * @cfg {String} emptyText The empty text to show when nothing is loaded.
9339      */
9340     emptyText : "",
9341     
9342     /**
9343      * @cfg {String} text to display on mask (default Loading)
9344      */
9345     mask : false,
9346     /**
9347      * @cfg {Boolean} multiSelect Allow multiple selection
9348      */
9349     multiSelect : false,
9350     /**
9351      * @cfg {Boolean} singleSelect Allow single selection
9352      */
9353     singleSelect:  false,
9354     
9355     /**
9356      * @cfg {Boolean} toggleSelect - selecting 
9357      */
9358     toggleSelect : false,
9359     
9360     /**
9361      * Returns the element this view is bound to.
9362      * @return {Roo.Element}
9363      */
9364     getEl : function(){
9365         return this.wrapEl;
9366     },
9367     
9368     
9369
9370     /**
9371      * Refreshes the view. - called by datachanged on the store. - do not call directly.
9372      */
9373     refresh : function(){
9374         var t = this.tpl;
9375         
9376         // if we are using something like 'domtemplate', then
9377         // the what gets used is:
9378         // t.applySubtemplate(NAME, data, wrapping data..)
9379         // the outer template then get' applied with
9380         //     the store 'extra data'
9381         // and the body get's added to the
9382         //      roo-name="data" node?
9383         //      <span class='roo-tpl-{name}'></span> ?????
9384         
9385         
9386         
9387         this.clearSelections();
9388         this.el.update("");
9389         var html = [];
9390         var records = this.store.getRange();
9391         if(records.length < 1) {
9392             
9393             // is this valid??  = should it render a template??
9394             
9395             this.el.update(this.emptyText);
9396             return;
9397         }
9398         var el = this.el;
9399         if (this.dataName) {
9400             this.el.update(t.apply(this.store.meta)); //????
9401             el = this.el.child('.roo-tpl-' + this.dataName);
9402         }
9403         
9404         for(var i = 0, len = records.length; i < len; i++){
9405             var data = this.prepareData(records[i].data, i, records[i]);
9406             this.fireEvent("preparedata", this, data, i, records[i]);
9407             html[html.length] = Roo.util.Format.trim(
9408                 this.dataName ?
9409                     t.applySubtemplate(this.dataName, data, this.store.meta) :
9410                     t.apply(data)
9411             );
9412         }
9413         
9414         
9415         
9416         el.update(html.join(""));
9417         this.nodes = el.dom.childNodes;
9418         this.updateIndexes(0);
9419     },
9420
9421     /**
9422      * Function to override to reformat the data that is sent to
9423      * the template for each node.
9424      * DEPRICATED - use the preparedata event handler.
9425      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9426      * a JSON object for an UpdateManager bound view).
9427      */
9428     prepareData : function(data, index, record)
9429     {
9430         this.fireEvent("preparedata", this, data, index, record);
9431         return data;
9432     },
9433
9434     onUpdate : function(ds, record){
9435         this.clearSelections();
9436         var index = this.store.indexOf(record);
9437         var n = this.nodes[index];
9438         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
9439         n.parentNode.removeChild(n);
9440         this.updateIndexes(index, index);
9441     },
9442
9443     
9444     
9445 // --------- FIXME     
9446     onAdd : function(ds, records, index)
9447     {
9448         this.clearSelections();
9449         if(this.nodes.length == 0){
9450             this.refresh();
9451             return;
9452         }
9453         var n = this.nodes[index];
9454         for(var i = 0, len = records.length; i < len; i++){
9455             var d = this.prepareData(records[i].data, i, records[i]);
9456             if(n){
9457                 this.tpl.insertBefore(n, d);
9458             }else{
9459                 
9460                 this.tpl.append(this.el, d);
9461             }
9462         }
9463         this.updateIndexes(index);
9464     },
9465
9466     onRemove : function(ds, record, index){
9467         this.clearSelections();
9468         var el = this.dataName  ?
9469             this.el.child('.roo-tpl-' + this.dataName) :
9470             this.el; 
9471         el.dom.removeChild(this.nodes[index]);
9472         this.updateIndexes(index);
9473     },
9474
9475     /**
9476      * Refresh an individual node.
9477      * @param {Number} index
9478      */
9479     refreshNode : function(index){
9480         this.onUpdate(this.store, this.store.getAt(index));
9481     },
9482
9483     updateIndexes : function(startIndex, endIndex){
9484         var ns = this.nodes;
9485         startIndex = startIndex || 0;
9486         endIndex = endIndex || ns.length - 1;
9487         for(var i = startIndex; i <= endIndex; i++){
9488             ns[i].nodeIndex = i;
9489         }
9490     },
9491
9492     /**
9493      * Changes the data store this view uses and refresh the view.
9494      * @param {Store} store
9495      */
9496     setStore : function(store, initial){
9497         if(!initial && this.store){
9498             this.store.un("datachanged", this.refresh);
9499             this.store.un("add", this.onAdd);
9500             this.store.un("remove", this.onRemove);
9501             this.store.un("update", this.onUpdate);
9502             this.store.un("clear", this.refresh);
9503             this.store.un("beforeload", this.onBeforeLoad);
9504             this.store.un("load", this.onLoad);
9505             this.store.un("loadexception", this.onLoad);
9506         }
9507         if(store){
9508           
9509             store.on("datachanged", this.refresh, this);
9510             store.on("add", this.onAdd, this);
9511             store.on("remove", this.onRemove, this);
9512             store.on("update", this.onUpdate, this);
9513             store.on("clear", this.refresh, this);
9514             store.on("beforeload", this.onBeforeLoad, this);
9515             store.on("load", this.onLoad, this);
9516             store.on("loadexception", this.onLoad, this);
9517         }
9518         
9519         if(store){
9520             this.refresh();
9521         }
9522     },
9523     /**
9524      * onbeforeLoad - masks the loading area.
9525      *
9526      */
9527     onBeforeLoad : function()
9528     {
9529         this.el.update("");
9530         this.el.mask(this.mask ? this.mask : "Loading" ); 
9531     },
9532     onLoad : function ()
9533     {
9534         this.el.unmask();
9535     },
9536     
9537
9538     /**
9539      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9540      * @param {HTMLElement} node
9541      * @return {HTMLElement} The template node
9542      */
9543     findItemFromChild : function(node){
9544         var el = this.dataName  ?
9545             this.el.child('.roo-tpl-' + this.dataName,true) :
9546             this.el.dom; 
9547         
9548         if(!node || node.parentNode == el){
9549                     return node;
9550             }
9551             var p = node.parentNode;
9552             while(p && p != el){
9553             if(p.parentNode == el){
9554                 return p;
9555             }
9556             p = p.parentNode;
9557         }
9558             return null;
9559     },
9560
9561     /** @ignore */
9562     onClick : function(e){
9563         var item = this.findItemFromChild(e.getTarget());
9564         if(item){
9565             var index = this.indexOf(item);
9566             if(this.onItemClick(item, index, e) !== false){
9567                 this.fireEvent("click", this, index, item, e);
9568             }
9569         }else{
9570             this.clearSelections();
9571         }
9572     },
9573
9574     /** @ignore */
9575     onContextMenu : function(e){
9576         var item = this.findItemFromChild(e.getTarget());
9577         if(item){
9578             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9579         }
9580     },
9581
9582     /** @ignore */
9583     onDblClick : function(e){
9584         var item = this.findItemFromChild(e.getTarget());
9585         if(item){
9586             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9587         }
9588     },
9589
9590     onItemClick : function(item, index, e)
9591     {
9592         if(this.fireEvent("beforeclick", this, index, item, e) === false){
9593             return false;
9594         }
9595         if (this.toggleSelect) {
9596             var m = this.isSelected(item) ? 'unselect' : 'select';
9597             Roo.log(m);
9598             var _t = this;
9599             _t[m](item, true, false);
9600             return true;
9601         }
9602         if(this.multiSelect || this.singleSelect){
9603             if(this.multiSelect && e.shiftKey && this.lastSelection){
9604                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9605             }else{
9606                 this.select(item, this.multiSelect && e.ctrlKey);
9607                 this.lastSelection = item;
9608             }
9609             e.preventDefault();
9610         }
9611         return true;
9612     },
9613
9614     /**
9615      * Get the number of selected nodes.
9616      * @return {Number}
9617      */
9618     getSelectionCount : function(){
9619         return this.selections.length;
9620     },
9621
9622     /**
9623      * Get the currently selected nodes.
9624      * @return {Array} An array of HTMLElements
9625      */
9626     getSelectedNodes : function(){
9627         return this.selections;
9628     },
9629
9630     /**
9631      * Get the indexes of the selected nodes.
9632      * @return {Array}
9633      */
9634     getSelectedIndexes : function(){
9635         var indexes = [], s = this.selections;
9636         for(var i = 0, len = s.length; i < len; i++){
9637             indexes.push(s[i].nodeIndex);
9638         }
9639         return indexes;
9640     },
9641
9642     /**
9643      * Clear all selections
9644      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9645      */
9646     clearSelections : function(suppressEvent){
9647         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9648             this.cmp.elements = this.selections;
9649             this.cmp.removeClass(this.selectedClass);
9650             this.selections = [];
9651             if(!suppressEvent){
9652                 this.fireEvent("selectionchange", this, this.selections);
9653             }
9654         }
9655     },
9656
9657     /**
9658      * Returns true if the passed node is selected
9659      * @param {HTMLElement/Number} node The node or node index
9660      * @return {Boolean}
9661      */
9662     isSelected : function(node){
9663         var s = this.selections;
9664         if(s.length < 1){
9665             return false;
9666         }
9667         node = this.getNode(node);
9668         return s.indexOf(node) !== -1;
9669     },
9670
9671     /**
9672      * Selects nodes.
9673      * @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
9674      * @param {Boolean} keepExisting (optional) true to keep existing selections
9675      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9676      */
9677     select : function(nodeInfo, keepExisting, suppressEvent){
9678         if(nodeInfo instanceof Array){
9679             if(!keepExisting){
9680                 this.clearSelections(true);
9681             }
9682             for(var i = 0, len = nodeInfo.length; i < len; i++){
9683                 this.select(nodeInfo[i], true, true);
9684             }
9685             return;
9686         } 
9687         var node = this.getNode(nodeInfo);
9688         if(!node || this.isSelected(node)){
9689             return; // already selected.
9690         }
9691         if(!keepExisting){
9692             this.clearSelections(true);
9693         }
9694         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9695             Roo.fly(node).addClass(this.selectedClass);
9696             this.selections.push(node);
9697             if(!suppressEvent){
9698                 this.fireEvent("selectionchange", this, this.selections);
9699             }
9700         }
9701         
9702         
9703     },
9704       /**
9705      * Unselects nodes.
9706      * @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
9707      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
9708      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9709      */
9710     unselect : function(nodeInfo, keepExisting, suppressEvent)
9711     {
9712         if(nodeInfo instanceof Array){
9713             Roo.each(this.selections, function(s) {
9714                 this.unselect(s, nodeInfo);
9715             }, this);
9716             return;
9717         }
9718         var node = this.getNode(nodeInfo);
9719         if(!node || !this.isSelected(node)){
9720             Roo.log("not selected");
9721             return; // not selected.
9722         }
9723         // fireevent???
9724         var ns = [];
9725         Roo.each(this.selections, function(s) {
9726             if (s == node ) {
9727                 Roo.fly(node).removeClass(this.selectedClass);
9728
9729                 return;
9730             }
9731             ns.push(s);
9732         },this);
9733         
9734         this.selections= ns;
9735         this.fireEvent("selectionchange", this, this.selections);
9736     },
9737
9738     /**
9739      * Gets a template node.
9740      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9741      * @return {HTMLElement} The node or null if it wasn't found
9742      */
9743     getNode : function(nodeInfo){
9744         if(typeof nodeInfo == "string"){
9745             return document.getElementById(nodeInfo);
9746         }else if(typeof nodeInfo == "number"){
9747             return this.nodes[nodeInfo];
9748         }
9749         return nodeInfo;
9750     },
9751
9752     /**
9753      * Gets a range template nodes.
9754      * @param {Number} startIndex
9755      * @param {Number} endIndex
9756      * @return {Array} An array of nodes
9757      */
9758     getNodes : function(start, end){
9759         var ns = this.nodes;
9760         start = start || 0;
9761         end = typeof end == "undefined" ? ns.length - 1 : end;
9762         var nodes = [];
9763         if(start <= end){
9764             for(var i = start; i <= end; i++){
9765                 nodes.push(ns[i]);
9766             }
9767         } else{
9768             for(var i = start; i >= end; i--){
9769                 nodes.push(ns[i]);
9770             }
9771         }
9772         return nodes;
9773     },
9774
9775     /**
9776      * Finds the index of the passed node
9777      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9778      * @return {Number} The index of the node or -1
9779      */
9780     indexOf : function(node){
9781         node = this.getNode(node);
9782         if(typeof node.nodeIndex == "number"){
9783             return node.nodeIndex;
9784         }
9785         var ns = this.nodes;
9786         for(var i = 0, len = ns.length; i < len; i++){
9787             if(ns[i] == node){
9788                 return i;
9789             }
9790         }
9791         return -1;
9792     }
9793 });
9794 /*
9795  * Based on:
9796  * Ext JS Library 1.1.1
9797  * Copyright(c) 2006-2007, Ext JS, LLC.
9798  *
9799  * Originally Released Under LGPL - original licence link has changed is not relivant.
9800  *
9801  * Fork - LGPL
9802  * <script type="text/javascript">
9803  */
9804
9805 /**
9806  * @class Roo.JsonView
9807  * @extends Roo.View
9808  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
9809 <pre><code>
9810 var view = new Roo.JsonView({
9811     container: "my-element",
9812     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
9813     multiSelect: true, 
9814     jsonRoot: "data" 
9815 });
9816
9817 // listen for node click?
9818 view.on("click", function(vw, index, node, e){
9819     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9820 });
9821
9822 // direct load of JSON data
9823 view.load("foobar.php");
9824
9825 // Example from my blog list
9826 var tpl = new Roo.Template(
9827     '&lt;div class="entry"&gt;' +
9828     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
9829     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
9830     "&lt;/div&gt;&lt;hr /&gt;"
9831 );
9832
9833 var moreView = new Roo.JsonView({
9834     container :  "entry-list", 
9835     template : tpl,
9836     jsonRoot: "posts"
9837 });
9838 moreView.on("beforerender", this.sortEntries, this);
9839 moreView.load({
9840     url: "/blog/get-posts.php",
9841     params: "allposts=true",
9842     text: "Loading Blog Entries..."
9843 });
9844 </code></pre>
9845
9846 * Note: old code is supported with arguments : (container, template, config)
9847
9848
9849  * @constructor
9850  * Create a new JsonView
9851  * 
9852  * @param {Object} config The config object
9853  * 
9854  */
9855 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
9856     
9857     
9858     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
9859
9860     var um = this.el.getUpdateManager();
9861     um.setRenderer(this);
9862     um.on("update", this.onLoad, this);
9863     um.on("failure", this.onLoadException, this);
9864
9865     /**
9866      * @event beforerender
9867      * Fires before rendering of the downloaded JSON data.
9868      * @param {Roo.JsonView} this
9869      * @param {Object} data The JSON data loaded
9870      */
9871     /**
9872      * @event load
9873      * Fires when data is loaded.
9874      * @param {Roo.JsonView} this
9875      * @param {Object} data The JSON data loaded
9876      * @param {Object} response The raw Connect response object
9877      */
9878     /**
9879      * @event loadexception
9880      * Fires when loading fails.
9881      * @param {Roo.JsonView} this
9882      * @param {Object} response The raw Connect response object
9883      */
9884     this.addEvents({
9885         'beforerender' : true,
9886         'load' : true,
9887         'loadexception' : true
9888     });
9889 };
9890 Roo.extend(Roo.JsonView, Roo.View, {
9891     /**
9892      * @type {String} The root property in the loaded JSON object that contains the data
9893      */
9894     jsonRoot : "",
9895
9896     /**
9897      * Refreshes the view.
9898      */
9899     refresh : function(){
9900         this.clearSelections();
9901         this.el.update("");
9902         var html = [];
9903         var o = this.jsonData;
9904         if(o && o.length > 0){
9905             for(var i = 0, len = o.length; i < len; i++){
9906                 var data = this.prepareData(o[i], i, o);
9907                 html[html.length] = this.tpl.apply(data);
9908             }
9909         }else{
9910             html.push(this.emptyText);
9911         }
9912         this.el.update(html.join(""));
9913         this.nodes = this.el.dom.childNodes;
9914         this.updateIndexes(0);
9915     },
9916
9917     /**
9918      * 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.
9919      * @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:
9920      <pre><code>
9921      view.load({
9922          url: "your-url.php",
9923          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
9924          callback: yourFunction,
9925          scope: yourObject, //(optional scope)
9926          discardUrl: false,
9927          nocache: false,
9928          text: "Loading...",
9929          timeout: 30,
9930          scripts: false
9931      });
9932      </code></pre>
9933      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
9934      * 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.
9935      * @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}
9936      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9937      * @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.
9938      */
9939     load : function(){
9940         var um = this.el.getUpdateManager();
9941         um.update.apply(um, arguments);
9942     },
9943
9944     render : function(el, response){
9945         this.clearSelections();
9946         this.el.update("");
9947         var o;
9948         try{
9949             o = Roo.util.JSON.decode(response.responseText);
9950             if(this.jsonRoot){
9951                 
9952                 o = o[this.jsonRoot];
9953             }
9954         } catch(e){
9955         }
9956         /**
9957          * The current JSON data or null
9958          */
9959         this.jsonData = o;
9960         this.beforeRender();
9961         this.refresh();
9962     },
9963
9964 /**
9965  * Get the number of records in the current JSON dataset
9966  * @return {Number}
9967  */
9968     getCount : function(){
9969         return this.jsonData ? this.jsonData.length : 0;
9970     },
9971
9972 /**
9973  * Returns the JSON object for the specified node(s)
9974  * @param {HTMLElement/Array} node The node or an array of nodes
9975  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
9976  * you get the JSON object for the node
9977  */
9978     getNodeData : function(node){
9979         if(node instanceof Array){
9980             var data = [];
9981             for(var i = 0, len = node.length; i < len; i++){
9982                 data.push(this.getNodeData(node[i]));
9983             }
9984             return data;
9985         }
9986         return this.jsonData[this.indexOf(node)] || null;
9987     },
9988
9989     beforeRender : function(){
9990         this.snapshot = this.jsonData;
9991         if(this.sortInfo){
9992             this.sort.apply(this, this.sortInfo);
9993         }
9994         this.fireEvent("beforerender", this, this.jsonData);
9995     },
9996
9997     onLoad : function(el, o){
9998         this.fireEvent("load", this, this.jsonData, o);
9999     },
10000
10001     onLoadException : function(el, o){
10002         this.fireEvent("loadexception", this, o);
10003     },
10004
10005 /**
10006  * Filter the data by a specific property.
10007  * @param {String} property A property on your JSON objects
10008  * @param {String/RegExp} value Either string that the property values
10009  * should start with, or a RegExp to test against the property
10010  */
10011     filter : function(property, value){
10012         if(this.jsonData){
10013             var data = [];
10014             var ss = this.snapshot;
10015             if(typeof value == "string"){
10016                 var vlen = value.length;
10017                 if(vlen == 0){
10018                     this.clearFilter();
10019                     return;
10020                 }
10021                 value = value.toLowerCase();
10022                 for(var i = 0, len = ss.length; i < len; i++){
10023                     var o = ss[i];
10024                     if(o[property].substr(0, vlen).toLowerCase() == value){
10025                         data.push(o);
10026                     }
10027                 }
10028             } else if(value.exec){ // regex?
10029                 for(var i = 0, len = ss.length; i < len; i++){
10030                     var o = ss[i];
10031                     if(value.test(o[property])){
10032                         data.push(o);
10033                     }
10034                 }
10035             } else{
10036                 return;
10037             }
10038             this.jsonData = data;
10039             this.refresh();
10040         }
10041     },
10042
10043 /**
10044  * Filter by a function. The passed function will be called with each
10045  * object in the current dataset. If the function returns true the value is kept,
10046  * otherwise it is filtered.
10047  * @param {Function} fn
10048  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
10049  */
10050     filterBy : function(fn, scope){
10051         if(this.jsonData){
10052             var data = [];
10053             var ss = this.snapshot;
10054             for(var i = 0, len = ss.length; i < len; i++){
10055                 var o = ss[i];
10056                 if(fn.call(scope || this, o)){
10057                     data.push(o);
10058                 }
10059             }
10060             this.jsonData = data;
10061             this.refresh();
10062         }
10063     },
10064
10065 /**
10066  * Clears the current filter.
10067  */
10068     clearFilter : function(){
10069         if(this.snapshot && this.jsonData != this.snapshot){
10070             this.jsonData = this.snapshot;
10071             this.refresh();
10072         }
10073     },
10074
10075
10076 /**
10077  * Sorts the data for this view and refreshes it.
10078  * @param {String} property A property on your JSON objects to sort on
10079  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
10080  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
10081  */
10082     sort : function(property, dir, sortType){
10083         this.sortInfo = Array.prototype.slice.call(arguments, 0);
10084         if(this.jsonData){
10085             var p = property;
10086             var dsc = dir && dir.toLowerCase() == "desc";
10087             var f = function(o1, o2){
10088                 var v1 = sortType ? sortType(o1[p]) : o1[p];
10089                 var v2 = sortType ? sortType(o2[p]) : o2[p];
10090                 ;
10091                 if(v1 < v2){
10092                     return dsc ? +1 : -1;
10093                 } else if(v1 > v2){
10094                     return dsc ? -1 : +1;
10095                 } else{
10096                     return 0;
10097                 }
10098             };
10099             this.jsonData.sort(f);
10100             this.refresh();
10101             if(this.jsonData != this.snapshot){
10102                 this.snapshot.sort(f);
10103             }
10104         }
10105     }
10106 });/*
10107  * Based on:
10108  * Ext JS Library 1.1.1
10109  * Copyright(c) 2006-2007, Ext JS, LLC.
10110  *
10111  * Originally Released Under LGPL - original licence link has changed is not relivant.
10112  *
10113  * Fork - LGPL
10114  * <script type="text/javascript">
10115  */
10116  
10117
10118 /**
10119  * @class Roo.ColorPalette
10120  * @extends Roo.Component
10121  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
10122  * Here's an example of typical usage:
10123  * <pre><code>
10124 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
10125 cp.render('my-div');
10126
10127 cp.on('select', function(palette, selColor){
10128     // do something with selColor
10129 });
10130 </code></pre>
10131  * @constructor
10132  * Create a new ColorPalette
10133  * @param {Object} config The config object
10134  */
10135 Roo.ColorPalette = function(config){
10136     Roo.ColorPalette.superclass.constructor.call(this, config);
10137     this.addEvents({
10138         /**
10139              * @event select
10140              * Fires when a color is selected
10141              * @param {ColorPalette} this
10142              * @param {String} color The 6-digit color hex code (without the # symbol)
10143              */
10144         select: true
10145     });
10146
10147     if(this.handler){
10148         this.on("select", this.handler, this.scope, true);
10149     }
10150 };
10151 Roo.extend(Roo.ColorPalette, Roo.Component, {
10152     /**
10153      * @cfg {String} itemCls
10154      * The CSS class to apply to the containing element (defaults to "x-color-palette")
10155      */
10156     itemCls : "x-color-palette",
10157     /**
10158      * @cfg {String} value
10159      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
10160      * the hex codes are case-sensitive.
10161      */
10162     value : null,
10163     clickEvent:'click',
10164     // private
10165     ctype: "Roo.ColorPalette",
10166
10167     /**
10168      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
10169      */
10170     allowReselect : false,
10171
10172     /**
10173      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
10174      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
10175      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
10176      * of colors with the width setting until the box is symmetrical.</p>
10177      * <p>You can override individual colors if needed:</p>
10178      * <pre><code>
10179 var cp = new Roo.ColorPalette();
10180 cp.colors[0] = "FF0000";  // change the first box to red
10181 </code></pre>
10182
10183 Or you can provide a custom array of your own for complete control:
10184 <pre><code>
10185 var cp = new Roo.ColorPalette();
10186 cp.colors = ["000000", "993300", "333300"];
10187 </code></pre>
10188      * @type Array
10189      */
10190     colors : [
10191         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
10192         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
10193         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
10194         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
10195         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
10196     ],
10197
10198     // private
10199     onRender : function(container, position){
10200         var t = new Roo.MasterTemplate(
10201             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
10202         );
10203         var c = this.colors;
10204         for(var i = 0, len = c.length; i < len; i++){
10205             t.add([c[i]]);
10206         }
10207         var el = document.createElement("div");
10208         el.className = this.itemCls;
10209         t.overwrite(el);
10210         container.dom.insertBefore(el, position);
10211         this.el = Roo.get(el);
10212         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
10213         if(this.clickEvent != 'click'){
10214             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
10215         }
10216     },
10217
10218     // private
10219     afterRender : function(){
10220         Roo.ColorPalette.superclass.afterRender.call(this);
10221         if(this.value){
10222             var s = this.value;
10223             this.value = null;
10224             this.select(s);
10225         }
10226     },
10227
10228     // private
10229     handleClick : function(e, t){
10230         e.preventDefault();
10231         if(!this.disabled){
10232             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
10233             this.select(c.toUpperCase());
10234         }
10235     },
10236
10237     /**
10238      * Selects the specified color in the palette (fires the select event)
10239      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
10240      */
10241     select : function(color){
10242         color = color.replace("#", "");
10243         if(color != this.value || this.allowReselect){
10244             var el = this.el;
10245             if(this.value){
10246                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
10247             }
10248             el.child("a.color-"+color).addClass("x-color-palette-sel");
10249             this.value = color;
10250             this.fireEvent("select", this, color);
10251         }
10252     }
10253 });/*
10254  * Based on:
10255  * Ext JS Library 1.1.1
10256  * Copyright(c) 2006-2007, Ext JS, LLC.
10257  *
10258  * Originally Released Under LGPL - original licence link has changed is not relivant.
10259  *
10260  * Fork - LGPL
10261  * <script type="text/javascript">
10262  */
10263  
10264 /**
10265  * @class Roo.DatePicker
10266  * @extends Roo.Component
10267  * Simple date picker class.
10268  * @constructor
10269  * Create a new DatePicker
10270  * @param {Object} config The config object
10271  */
10272 Roo.DatePicker = function(config){
10273     Roo.DatePicker.superclass.constructor.call(this, config);
10274
10275     this.value = config && config.value ?
10276                  config.value.clearTime() : new Date().clearTime();
10277
10278     this.addEvents({
10279         /**
10280              * @event select
10281              * Fires when a date is selected
10282              * @param {DatePicker} this
10283              * @param {Date} date The selected date
10284              */
10285         'select': true,
10286         /**
10287              * @event monthchange
10288              * Fires when the displayed month changes 
10289              * @param {DatePicker} this
10290              * @param {Date} date The selected month
10291              */
10292         'monthchange': true
10293     });
10294
10295     if(this.handler){
10296         this.on("select", this.handler,  this.scope || this);
10297     }
10298     // build the disabledDatesRE
10299     if(!this.disabledDatesRE && this.disabledDates){
10300         var dd = this.disabledDates;
10301         var re = "(?:";
10302         for(var i = 0; i < dd.length; i++){
10303             re += dd[i];
10304             if(i != dd.length-1) re += "|";
10305         }
10306         this.disabledDatesRE = new RegExp(re + ")");
10307     }
10308 };
10309
10310 Roo.extend(Roo.DatePicker, Roo.Component, {
10311     /**
10312      * @cfg {String} todayText
10313      * The text to display on the button that selects the current date (defaults to "Today")
10314      */
10315     todayText : "Today",
10316     /**
10317      * @cfg {String} okText
10318      * The text to display on the ok button
10319      */
10320     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
10321     /**
10322      * @cfg {String} cancelText
10323      * The text to display on the cancel button
10324      */
10325     cancelText : "Cancel",
10326     /**
10327      * @cfg {String} todayTip
10328      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
10329      */
10330     todayTip : "{0} (Spacebar)",
10331     /**
10332      * @cfg {Date} minDate
10333      * Minimum allowable date (JavaScript date object, defaults to null)
10334      */
10335     minDate : null,
10336     /**
10337      * @cfg {Date} maxDate
10338      * Maximum allowable date (JavaScript date object, defaults to null)
10339      */
10340     maxDate : null,
10341     /**
10342      * @cfg {String} minText
10343      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
10344      */
10345     minText : "This date is before the minimum date",
10346     /**
10347      * @cfg {String} maxText
10348      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
10349      */
10350     maxText : "This date is after the maximum date",
10351     /**
10352      * @cfg {String} format
10353      * The default date format string which can be overriden for localization support.  The format must be
10354      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
10355      */
10356     format : "m/d/y",
10357     /**
10358      * @cfg {Array} disabledDays
10359      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
10360      */
10361     disabledDays : null,
10362     /**
10363      * @cfg {String} disabledDaysText
10364      * The tooltip to display when the date falls on a disabled day (defaults to "")
10365      */
10366     disabledDaysText : "",
10367     /**
10368      * @cfg {RegExp} disabledDatesRE
10369      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
10370      */
10371     disabledDatesRE : null,
10372     /**
10373      * @cfg {String} disabledDatesText
10374      * The tooltip text to display when the date falls on a disabled date (defaults to "")
10375      */
10376     disabledDatesText : "",
10377     /**
10378      * @cfg {Boolean} constrainToViewport
10379      * True to constrain the date picker to the viewport (defaults to true)
10380      */
10381     constrainToViewport : true,
10382     /**
10383      * @cfg {Array} monthNames
10384      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
10385      */
10386     monthNames : Date.monthNames,
10387     /**
10388      * @cfg {Array} dayNames
10389      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
10390      */
10391     dayNames : Date.dayNames,
10392     /**
10393      * @cfg {String} nextText
10394      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
10395      */
10396     nextText: 'Next Month (Control+Right)',
10397     /**
10398      * @cfg {String} prevText
10399      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
10400      */
10401     prevText: 'Previous Month (Control+Left)',
10402     /**
10403      * @cfg {String} monthYearText
10404      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
10405      */
10406     monthYearText: 'Choose a month (Control+Up/Down to move years)',
10407     /**
10408      * @cfg {Number} startDay
10409      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
10410      */
10411     startDay : 0,
10412     /**
10413      * @cfg {Bool} showClear
10414      * Show a clear button (usefull for date form elements that can be blank.)
10415      */
10416     
10417     showClear: false,
10418     
10419     /**
10420      * Sets the value of the date field
10421      * @param {Date} value The date to set
10422      */
10423     setValue : function(value){
10424         var old = this.value;
10425         
10426         if (typeof(value) == 'string') {
10427          
10428             value = Date.parseDate(value, this.format);
10429         }
10430         if (!value) {
10431             value = new Date();
10432         }
10433         
10434         this.value = value.clearTime(true);
10435         if(this.el){
10436             this.update(this.value);
10437         }
10438     },
10439
10440     /**
10441      * Gets the current selected value of the date field
10442      * @return {Date} The selected date
10443      */
10444     getValue : function(){
10445         return this.value;
10446     },
10447
10448     // private
10449     focus : function(){
10450         if(this.el){
10451             this.update(this.activeDate);
10452         }
10453     },
10454
10455     // privateval
10456     onRender : function(container, position){
10457         
10458         var m = [
10459              '<table cellspacing="0">',
10460                 '<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>',
10461                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
10462         var dn = this.dayNames;
10463         for(var i = 0; i < 7; i++){
10464             var d = this.startDay+i;
10465             if(d > 6){
10466                 d = d-7;
10467             }
10468             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
10469         }
10470         m[m.length] = "</tr></thead><tbody><tr>";
10471         for(var i = 0; i < 42; i++) {
10472             if(i % 7 == 0 && i != 0){
10473                 m[m.length] = "</tr><tr>";
10474             }
10475             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
10476         }
10477         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
10478             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
10479
10480         var el = document.createElement("div");
10481         el.className = "x-date-picker";
10482         el.innerHTML = m.join("");
10483
10484         container.dom.insertBefore(el, position);
10485
10486         this.el = Roo.get(el);
10487         this.eventEl = Roo.get(el.firstChild);
10488
10489         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
10490             handler: this.showPrevMonth,
10491             scope: this,
10492             preventDefault:true,
10493             stopDefault:true
10494         });
10495
10496         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
10497             handler: this.showNextMonth,
10498             scope: this,
10499             preventDefault:true,
10500             stopDefault:true
10501         });
10502
10503         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
10504
10505         this.monthPicker = this.el.down('div.x-date-mp');
10506         this.monthPicker.enableDisplayMode('block');
10507         
10508         var kn = new Roo.KeyNav(this.eventEl, {
10509             "left" : function(e){
10510                 e.ctrlKey ?
10511                     this.showPrevMonth() :
10512                     this.update(this.activeDate.add("d", -1));
10513             },
10514
10515             "right" : function(e){
10516                 e.ctrlKey ?
10517                     this.showNextMonth() :
10518                     this.update(this.activeDate.add("d", 1));
10519             },
10520
10521             "up" : function(e){
10522                 e.ctrlKey ?
10523                     this.showNextYear() :
10524                     this.update(this.activeDate.add("d", -7));
10525             },
10526
10527             "down" : function(e){
10528                 e.ctrlKey ?
10529                     this.showPrevYear() :
10530                     this.update(this.activeDate.add("d", 7));
10531             },
10532
10533             "pageUp" : function(e){
10534                 this.showNextMonth();
10535             },
10536
10537             "pageDown" : function(e){
10538                 this.showPrevMonth();
10539             },
10540
10541             "enter" : function(e){
10542                 e.stopPropagation();
10543                 return true;
10544             },
10545
10546             scope : this
10547         });
10548
10549         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
10550
10551         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
10552
10553         this.el.unselectable();
10554         
10555         this.cells = this.el.select("table.x-date-inner tbody td");
10556         this.textNodes = this.el.query("table.x-date-inner tbody span");
10557
10558         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
10559             text: "&#160;",
10560             tooltip: this.monthYearText
10561         });
10562
10563         this.mbtn.on('click', this.showMonthPicker, this);
10564         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
10565
10566
10567         var today = (new Date()).dateFormat(this.format);
10568         
10569         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
10570         if (this.showClear) {
10571             baseTb.add( new Roo.Toolbar.Fill());
10572         }
10573         baseTb.add({
10574             text: String.format(this.todayText, today),
10575             tooltip: String.format(this.todayTip, today),
10576             handler: this.selectToday,
10577             scope: this
10578         });
10579         
10580         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
10581             
10582         //});
10583         if (this.showClear) {
10584             
10585             baseTb.add( new Roo.Toolbar.Fill());
10586             baseTb.add({
10587                 text: '&#160;',
10588                 cls: 'x-btn-icon x-btn-clear',
10589                 handler: function() {
10590                     //this.value = '';
10591                     this.fireEvent("select", this, '');
10592                 },
10593                 scope: this
10594             });
10595         }
10596         
10597         
10598         if(Roo.isIE){
10599             this.el.repaint();
10600         }
10601         this.update(this.value);
10602     },
10603
10604     createMonthPicker : function(){
10605         if(!this.monthPicker.dom.firstChild){
10606             var buf = ['<table border="0" cellspacing="0">'];
10607             for(var i = 0; i < 6; i++){
10608                 buf.push(
10609                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
10610                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
10611                     i == 0 ?
10612                     '<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>' :
10613                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
10614                 );
10615             }
10616             buf.push(
10617                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
10618                     this.okText,
10619                     '</button><button type="button" class="x-date-mp-cancel">',
10620                     this.cancelText,
10621                     '</button></td></tr>',
10622                 '</table>'
10623             );
10624             this.monthPicker.update(buf.join(''));
10625             this.monthPicker.on('click', this.onMonthClick, this);
10626             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
10627
10628             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
10629             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
10630
10631             this.mpMonths.each(function(m, a, i){
10632                 i += 1;
10633                 if((i%2) == 0){
10634                     m.dom.xmonth = 5 + Math.round(i * .5);
10635                 }else{
10636                     m.dom.xmonth = Math.round((i-1) * .5);
10637                 }
10638             });
10639         }
10640     },
10641
10642     showMonthPicker : function(){
10643         this.createMonthPicker();
10644         var size = this.el.getSize();
10645         this.monthPicker.setSize(size);
10646         this.monthPicker.child('table').setSize(size);
10647
10648         this.mpSelMonth = (this.activeDate || this.value).getMonth();
10649         this.updateMPMonth(this.mpSelMonth);
10650         this.mpSelYear = (this.activeDate || this.value).getFullYear();
10651         this.updateMPYear(this.mpSelYear);
10652
10653         this.monthPicker.slideIn('t', {duration:.2});
10654     },
10655
10656     updateMPYear : function(y){
10657         this.mpyear = y;
10658         var ys = this.mpYears.elements;
10659         for(var i = 1; i <= 10; i++){
10660             var td = ys[i-1], y2;
10661             if((i%2) == 0){
10662                 y2 = y + Math.round(i * .5);
10663                 td.firstChild.innerHTML = y2;
10664                 td.xyear = y2;
10665             }else{
10666                 y2 = y - (5-Math.round(i * .5));
10667                 td.firstChild.innerHTML = y2;
10668                 td.xyear = y2;
10669             }
10670             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
10671         }
10672     },
10673
10674     updateMPMonth : function(sm){
10675         this.mpMonths.each(function(m, a, i){
10676             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
10677         });
10678     },
10679
10680     selectMPMonth: function(m){
10681         
10682     },
10683
10684     onMonthClick : function(e, t){
10685         e.stopEvent();
10686         var el = new Roo.Element(t), pn;
10687         if(el.is('button.x-date-mp-cancel')){
10688             this.hideMonthPicker();
10689         }
10690         else if(el.is('button.x-date-mp-ok')){
10691             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10692             this.hideMonthPicker();
10693         }
10694         else if(pn = el.up('td.x-date-mp-month', 2)){
10695             this.mpMonths.removeClass('x-date-mp-sel');
10696             pn.addClass('x-date-mp-sel');
10697             this.mpSelMonth = pn.dom.xmonth;
10698         }
10699         else if(pn = el.up('td.x-date-mp-year', 2)){
10700             this.mpYears.removeClass('x-date-mp-sel');
10701             pn.addClass('x-date-mp-sel');
10702             this.mpSelYear = pn.dom.xyear;
10703         }
10704         else if(el.is('a.x-date-mp-prev')){
10705             this.updateMPYear(this.mpyear-10);
10706         }
10707         else if(el.is('a.x-date-mp-next')){
10708             this.updateMPYear(this.mpyear+10);
10709         }
10710     },
10711
10712     onMonthDblClick : function(e, t){
10713         e.stopEvent();
10714         var el = new Roo.Element(t), pn;
10715         if(pn = el.up('td.x-date-mp-month', 2)){
10716             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
10717             this.hideMonthPicker();
10718         }
10719         else if(pn = el.up('td.x-date-mp-year', 2)){
10720             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10721             this.hideMonthPicker();
10722         }
10723     },
10724
10725     hideMonthPicker : function(disableAnim){
10726         if(this.monthPicker){
10727             if(disableAnim === true){
10728                 this.monthPicker.hide();
10729             }else{
10730                 this.monthPicker.slideOut('t', {duration:.2});
10731             }
10732         }
10733     },
10734
10735     // private
10736     showPrevMonth : function(e){
10737         this.update(this.activeDate.add("mo", -1));
10738     },
10739
10740     // private
10741     showNextMonth : function(e){
10742         this.update(this.activeDate.add("mo", 1));
10743     },
10744
10745     // private
10746     showPrevYear : function(){
10747         this.update(this.activeDate.add("y", -1));
10748     },
10749
10750     // private
10751     showNextYear : function(){
10752         this.update(this.activeDate.add("y", 1));
10753     },
10754
10755     // private
10756     handleMouseWheel : function(e){
10757         var delta = e.getWheelDelta();
10758         if(delta > 0){
10759             this.showPrevMonth();
10760             e.stopEvent();
10761         } else if(delta < 0){
10762             this.showNextMonth();
10763             e.stopEvent();
10764         }
10765     },
10766
10767     // private
10768     handleDateClick : function(e, t){
10769         e.stopEvent();
10770         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
10771             this.setValue(new Date(t.dateValue));
10772             this.fireEvent("select", this, this.value);
10773         }
10774     },
10775
10776     // private
10777     selectToday : function(){
10778         this.setValue(new Date().clearTime());
10779         this.fireEvent("select", this, this.value);
10780     },
10781
10782     // private
10783     update : function(date)
10784     {
10785         var vd = this.activeDate;
10786         this.activeDate = date;
10787         if(vd && this.el){
10788             var t = date.getTime();
10789             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10790                 this.cells.removeClass("x-date-selected");
10791                 this.cells.each(function(c){
10792                    if(c.dom.firstChild.dateValue == t){
10793                        c.addClass("x-date-selected");
10794                        setTimeout(function(){
10795                             try{c.dom.firstChild.focus();}catch(e){}
10796                        }, 50);
10797                        return false;
10798                    }
10799                 });
10800                 return;
10801             }
10802         }
10803         
10804         var days = date.getDaysInMonth();
10805         var firstOfMonth = date.getFirstDateOfMonth();
10806         var startingPos = firstOfMonth.getDay()-this.startDay;
10807
10808         if(startingPos <= this.startDay){
10809             startingPos += 7;
10810         }
10811
10812         var pm = date.add("mo", -1);
10813         var prevStart = pm.getDaysInMonth()-startingPos;
10814
10815         var cells = this.cells.elements;
10816         var textEls = this.textNodes;
10817         days += startingPos;
10818
10819         // convert everything to numbers so it's fast
10820         var day = 86400000;
10821         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10822         var today = new Date().clearTime().getTime();
10823         var sel = date.clearTime().getTime();
10824         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10825         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10826         var ddMatch = this.disabledDatesRE;
10827         var ddText = this.disabledDatesText;
10828         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10829         var ddaysText = this.disabledDaysText;
10830         var format = this.format;
10831
10832         var setCellClass = function(cal, cell){
10833             cell.title = "";
10834             var t = d.getTime();
10835             cell.firstChild.dateValue = t;
10836             if(t == today){
10837                 cell.className += " x-date-today";
10838                 cell.title = cal.todayText;
10839             }
10840             if(t == sel){
10841                 cell.className += " x-date-selected";
10842                 setTimeout(function(){
10843                     try{cell.firstChild.focus();}catch(e){}
10844                 }, 50);
10845             }
10846             // disabling
10847             if(t < min) {
10848                 cell.className = " x-date-disabled";
10849                 cell.title = cal.minText;
10850                 return;
10851             }
10852             if(t > max) {
10853                 cell.className = " x-date-disabled";
10854                 cell.title = cal.maxText;
10855                 return;
10856             }
10857             if(ddays){
10858                 if(ddays.indexOf(d.getDay()) != -1){
10859                     cell.title = ddaysText;
10860                     cell.className = " x-date-disabled";
10861                 }
10862             }
10863             if(ddMatch && format){
10864                 var fvalue = d.dateFormat(format);
10865                 if(ddMatch.test(fvalue)){
10866                     cell.title = ddText.replace("%0", fvalue);
10867                     cell.className = " x-date-disabled";
10868                 }
10869             }
10870         };
10871
10872         var i = 0;
10873         for(; i < startingPos; i++) {
10874             textEls[i].innerHTML = (++prevStart);
10875             d.setDate(d.getDate()+1);
10876             cells[i].className = "x-date-prevday";
10877             setCellClass(this, cells[i]);
10878         }
10879         for(; i < days; i++){
10880             intDay = i - startingPos + 1;
10881             textEls[i].innerHTML = (intDay);
10882             d.setDate(d.getDate()+1);
10883             cells[i].className = "x-date-active";
10884             setCellClass(this, cells[i]);
10885         }
10886         var extraDays = 0;
10887         for(; i < 42; i++) {
10888              textEls[i].innerHTML = (++extraDays);
10889              d.setDate(d.getDate()+1);
10890              cells[i].className = "x-date-nextday";
10891              setCellClass(this, cells[i]);
10892         }
10893
10894         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
10895         this.fireEvent('monthchange', this, date);
10896         
10897         if(!this.internalRender){
10898             var main = this.el.dom.firstChild;
10899             var w = main.offsetWidth;
10900             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10901             Roo.fly(main).setWidth(w);
10902             this.internalRender = true;
10903             // opera does not respect the auto grow header center column
10904             // then, after it gets a width opera refuses to recalculate
10905             // without a second pass
10906             if(Roo.isOpera && !this.secondPass){
10907                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10908                 this.secondPass = true;
10909                 this.update.defer(10, this, [date]);
10910             }
10911         }
10912         
10913         
10914     }
10915 });        /*
10916  * Based on:
10917  * Ext JS Library 1.1.1
10918  * Copyright(c) 2006-2007, Ext JS, LLC.
10919  *
10920  * Originally Released Under LGPL - original licence link has changed is not relivant.
10921  *
10922  * Fork - LGPL
10923  * <script type="text/javascript">
10924  */
10925 /**
10926  * @class Roo.TabPanel
10927  * @extends Roo.util.Observable
10928  * A lightweight tab container.
10929  * <br><br>
10930  * Usage:
10931  * <pre><code>
10932 // basic tabs 1, built from existing content
10933 var tabs = new Roo.TabPanel("tabs1");
10934 tabs.addTab("script", "View Script");
10935 tabs.addTab("markup", "View Markup");
10936 tabs.activate("script");
10937
10938 // more advanced tabs, built from javascript
10939 var jtabs = new Roo.TabPanel("jtabs");
10940 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
10941
10942 // set up the UpdateManager
10943 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
10944 var updater = tab2.getUpdateManager();
10945 updater.setDefaultUrl("ajax1.htm");
10946 tab2.on('activate', updater.refresh, updater, true);
10947
10948 // Use setUrl for Ajax loading
10949 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
10950 tab3.setUrl("ajax2.htm", null, true);
10951
10952 // Disabled tab
10953 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
10954 tab4.disable();
10955
10956 jtabs.activate("jtabs-1");
10957  * </code></pre>
10958  * @constructor
10959  * Create a new TabPanel.
10960  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
10961  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
10962  */
10963 Roo.TabPanel = function(container, config){
10964     /**
10965     * The container element for this TabPanel.
10966     * @type Roo.Element
10967     */
10968     this.el = Roo.get(container, true);
10969     if(config){
10970         if(typeof config == "boolean"){
10971             this.tabPosition = config ? "bottom" : "top";
10972         }else{
10973             Roo.apply(this, config);
10974         }
10975     }
10976     if(this.tabPosition == "bottom"){
10977         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10978         this.el.addClass("x-tabs-bottom");
10979     }
10980     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
10981     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
10982     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
10983     if(Roo.isIE){
10984         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
10985     }
10986     if(this.tabPosition != "bottom"){
10987         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
10988          * @type Roo.Element
10989          */
10990         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10991         this.el.addClass("x-tabs-top");
10992     }
10993     this.items = [];
10994
10995     this.bodyEl.setStyle("position", "relative");
10996
10997     this.active = null;
10998     this.activateDelegate = this.activate.createDelegate(this);
10999
11000     this.addEvents({
11001         /**
11002          * @event tabchange
11003          * Fires when the active tab changes
11004          * @param {Roo.TabPanel} this
11005          * @param {Roo.TabPanelItem} activePanel The new active tab
11006          */
11007         "tabchange": true,
11008         /**
11009          * @event beforetabchange
11010          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
11011          * @param {Roo.TabPanel} this
11012          * @param {Object} e Set cancel to true on this object to cancel the tab change
11013          * @param {Roo.TabPanelItem} tab The tab being changed to
11014          */
11015         "beforetabchange" : true
11016     });
11017
11018     Roo.EventManager.onWindowResize(this.onResize, this);
11019     this.cpad = this.el.getPadding("lr");
11020     this.hiddenCount = 0;
11021
11022
11023     // toolbar on the tabbar support...
11024     if (this.toolbar) {
11025         var tcfg = this.toolbar;
11026         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
11027         this.toolbar = new Roo.Toolbar(tcfg);
11028         if (Roo.isSafari) {
11029             var tbl = tcfg.container.child('table', true);
11030             tbl.setAttribute('width', '100%');
11031         }
11032         
11033     }
11034    
11035
11036
11037     Roo.TabPanel.superclass.constructor.call(this);
11038 };
11039
11040 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
11041     /*
11042      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
11043      */
11044     tabPosition : "top",
11045     /*
11046      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
11047      */
11048     currentTabWidth : 0,
11049     /*
11050      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
11051      */
11052     minTabWidth : 40,
11053     /*
11054      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
11055      */
11056     maxTabWidth : 250,
11057     /*
11058      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
11059      */
11060     preferredTabWidth : 175,
11061     /*
11062      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
11063      */
11064     resizeTabs : false,
11065     /*
11066      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
11067      */
11068     monitorResize : true,
11069     /*
11070      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
11071      */
11072     toolbar : false,
11073
11074     /**
11075      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
11076      * @param {String} id The id of the div to use <b>or create</b>
11077      * @param {String} text The text for the tab
11078      * @param {String} content (optional) Content to put in the TabPanelItem body
11079      * @param {Boolean} closable (optional) True to create a close icon on the tab
11080      * @return {Roo.TabPanelItem} The created TabPanelItem
11081      */
11082     addTab : function(id, text, content, closable){
11083         var item = new Roo.TabPanelItem(this, id, text, closable);
11084         this.addTabItem(item);
11085         if(content){
11086             item.setContent(content);
11087         }
11088         return item;
11089     },
11090
11091     /**
11092      * Returns the {@link Roo.TabPanelItem} with the specified id/index
11093      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
11094      * @return {Roo.TabPanelItem}
11095      */
11096     getTab : function(id){
11097         return this.items[id];
11098     },
11099
11100     /**
11101      * Hides the {@link Roo.TabPanelItem} with the specified id/index
11102      * @param {String/Number} id The id or index of the TabPanelItem to hide.
11103      */
11104     hideTab : function(id){
11105         var t = this.items[id];
11106         if(!t.isHidden()){
11107            t.setHidden(true);
11108            this.hiddenCount++;
11109            this.autoSizeTabs();
11110         }
11111     },
11112
11113     /**
11114      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
11115      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
11116      */
11117     unhideTab : function(id){
11118         var t = this.items[id];
11119         if(t.isHidden()){
11120            t.setHidden(false);
11121            this.hiddenCount--;
11122            this.autoSizeTabs();
11123         }
11124     },
11125
11126     /**
11127      * Adds an existing {@link Roo.TabPanelItem}.
11128      * @param {Roo.TabPanelItem} item The TabPanelItem to add
11129      */
11130     addTabItem : function(item){
11131         this.items[item.id] = item;
11132         this.items.push(item);
11133         if(this.resizeTabs){
11134            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
11135            this.autoSizeTabs();
11136         }else{
11137             item.autoSize();
11138         }
11139     },
11140
11141     /**
11142      * Removes a {@link Roo.TabPanelItem}.
11143      * @param {String/Number} id The id or index of the TabPanelItem to remove.
11144      */
11145     removeTab : function(id){
11146         var items = this.items;
11147         var tab = items[id];
11148         if(!tab) { return; }
11149         var index = items.indexOf(tab);
11150         if(this.active == tab && items.length > 1){
11151             var newTab = this.getNextAvailable(index);
11152             if(newTab) {
11153                 newTab.activate();
11154             }
11155         }
11156         this.stripEl.dom.removeChild(tab.pnode.dom);
11157         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
11158             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
11159         }
11160         items.splice(index, 1);
11161         delete this.items[tab.id];
11162         tab.fireEvent("close", tab);
11163         tab.purgeListeners();
11164         this.autoSizeTabs();
11165     },
11166
11167     getNextAvailable : function(start){
11168         var items = this.items;
11169         var index = start;
11170         // look for a next tab that will slide over to
11171         // replace the one being removed
11172         while(index < items.length){
11173             var item = items[++index];
11174             if(item && !item.isHidden()){
11175                 return item;
11176             }
11177         }
11178         // if one isn't found select the previous tab (on the left)
11179         index = start;
11180         while(index >= 0){
11181             var item = items[--index];
11182             if(item && !item.isHidden()){
11183                 return item;
11184             }
11185         }
11186         return null;
11187     },
11188
11189     /**
11190      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
11191      * @param {String/Number} id The id or index of the TabPanelItem to disable.
11192      */
11193     disableTab : function(id){
11194         var tab = this.items[id];
11195         if(tab && this.active != tab){
11196             tab.disable();
11197         }
11198     },
11199
11200     /**
11201      * Enables a {@link Roo.TabPanelItem} that is disabled.
11202      * @param {String/Number} id The id or index of the TabPanelItem to enable.
11203      */
11204     enableTab : function(id){
11205         var tab = this.items[id];
11206         tab.enable();
11207     },
11208
11209     /**
11210      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
11211      * @param {String/Number} id The id or index of the TabPanelItem to activate.
11212      * @return {Roo.TabPanelItem} The TabPanelItem.
11213      */
11214     activate : function(id){
11215         var tab = this.items[id];
11216         if(!tab){
11217             return null;
11218         }
11219         if(tab == this.active || tab.disabled){
11220             return tab;
11221         }
11222         var e = {};
11223         this.fireEvent("beforetabchange", this, e, tab);
11224         if(e.cancel !== true && !tab.disabled){
11225             if(this.active){
11226                 this.active.hide();
11227             }
11228             this.active = this.items[id];
11229             this.active.show();
11230             this.fireEvent("tabchange", this, this.active);
11231         }
11232         return tab;
11233     },
11234
11235     /**
11236      * Gets the active {@link Roo.TabPanelItem}.
11237      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
11238      */
11239     getActiveTab : function(){
11240         return this.active;
11241     },
11242
11243     /**
11244      * Updates the tab body element to fit the height of the container element
11245      * for overflow scrolling
11246      * @param {Number} targetHeight (optional) Override the starting height from the elements height
11247      */
11248     syncHeight : function(targetHeight){
11249         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
11250         var bm = this.bodyEl.getMargins();
11251         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
11252         this.bodyEl.setHeight(newHeight);
11253         return newHeight;
11254     },
11255
11256     onResize : function(){
11257         if(this.monitorResize){
11258             this.autoSizeTabs();
11259         }
11260     },
11261
11262     /**
11263      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
11264      */
11265     beginUpdate : function(){
11266         this.updating = true;
11267     },
11268
11269     /**
11270      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
11271      */
11272     endUpdate : function(){
11273         this.updating = false;
11274         this.autoSizeTabs();
11275     },
11276
11277     /**
11278      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
11279      */
11280     autoSizeTabs : function(){
11281         var count = this.items.length;
11282         var vcount = count - this.hiddenCount;
11283         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
11284         var w = Math.max(this.el.getWidth() - this.cpad, 10);
11285         var availWidth = Math.floor(w / vcount);
11286         var b = this.stripBody;
11287         if(b.getWidth() > w){
11288             var tabs = this.items;
11289             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
11290             if(availWidth < this.minTabWidth){
11291                 /*if(!this.sleft){    // incomplete scrolling code
11292                     this.createScrollButtons();
11293                 }
11294                 this.showScroll();
11295                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
11296             }
11297         }else{
11298             if(this.currentTabWidth < this.preferredTabWidth){
11299                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
11300             }
11301         }
11302     },
11303
11304     /**
11305      * Returns the number of tabs in this TabPanel.
11306      * @return {Number}
11307      */
11308      getCount : function(){
11309          return this.items.length;
11310      },
11311
11312     /**
11313      * Resizes all the tabs to the passed width
11314      * @param {Number} The new width
11315      */
11316     setTabWidth : function(width){
11317         this.currentTabWidth = width;
11318         for(var i = 0, len = this.items.length; i < len; i++) {
11319                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
11320         }
11321     },
11322
11323     /**
11324      * Destroys this TabPanel
11325      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
11326      */
11327     destroy : function(removeEl){
11328         Roo.EventManager.removeResizeListener(this.onResize, this);
11329         for(var i = 0, len = this.items.length; i < len; i++){
11330             this.items[i].purgeListeners();
11331         }
11332         if(removeEl === true){
11333             this.el.update("");
11334             this.el.remove();
11335         }
11336     }
11337 });
11338
11339 /**
11340  * @class Roo.TabPanelItem
11341  * @extends Roo.util.Observable
11342  * Represents an individual item (tab plus body) in a TabPanel.
11343  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
11344  * @param {String} id The id of this TabPanelItem
11345  * @param {String} text The text for the tab of this TabPanelItem
11346  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
11347  */
11348 Roo.TabPanelItem = function(tabPanel, id, text, closable){
11349     /**
11350      * The {@link Roo.TabPanel} this TabPanelItem belongs to
11351      * @type Roo.TabPanel
11352      */
11353     this.tabPanel = tabPanel;
11354     /**
11355      * The id for this TabPanelItem
11356      * @type String
11357      */
11358     this.id = id;
11359     /** @private */
11360     this.disabled = false;
11361     /** @private */
11362     this.text = text;
11363     /** @private */
11364     this.loaded = false;
11365     this.closable = closable;
11366
11367     /**
11368      * The body element for this TabPanelItem.
11369      * @type Roo.Element
11370      */
11371     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
11372     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
11373     this.bodyEl.setStyle("display", "block");
11374     this.bodyEl.setStyle("zoom", "1");
11375     this.hideAction();
11376
11377     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
11378     /** @private */
11379     this.el = Roo.get(els.el, true);
11380     this.inner = Roo.get(els.inner, true);
11381     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
11382     this.pnode = Roo.get(els.el.parentNode, true);
11383     this.el.on("mousedown", this.onTabMouseDown, this);
11384     this.el.on("click", this.onTabClick, this);
11385     /** @private */
11386     if(closable){
11387         var c = Roo.get(els.close, true);
11388         c.dom.title = this.closeText;
11389         c.addClassOnOver("close-over");
11390         c.on("click", this.closeClick, this);
11391      }
11392
11393     this.addEvents({
11394          /**
11395          * @event activate
11396          * Fires when this tab becomes the active tab.
11397          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11398          * @param {Roo.TabPanelItem} this
11399          */
11400         "activate": true,
11401         /**
11402          * @event beforeclose
11403          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
11404          * @param {Roo.TabPanelItem} this
11405          * @param {Object} e Set cancel to true on this object to cancel the close.
11406          */
11407         "beforeclose": true,
11408         /**
11409          * @event close
11410          * Fires when this tab is closed.
11411          * @param {Roo.TabPanelItem} this
11412          */
11413          "close": true,
11414         /**
11415          * @event deactivate
11416          * Fires when this tab is no longer the active tab.
11417          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11418          * @param {Roo.TabPanelItem} this
11419          */
11420          "deactivate" : true
11421     });
11422     this.hidden = false;
11423
11424     Roo.TabPanelItem.superclass.constructor.call(this);
11425 };
11426
11427 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
11428     purgeListeners : function(){
11429        Roo.util.Observable.prototype.purgeListeners.call(this);
11430        this.el.removeAllListeners();
11431     },
11432     /**
11433      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
11434      */
11435     show : function(){
11436         this.pnode.addClass("on");
11437         this.showAction();
11438         if(Roo.isOpera){
11439             this.tabPanel.stripWrap.repaint();
11440         }
11441         this.fireEvent("activate", this.tabPanel, this);
11442     },
11443
11444     /**
11445      * Returns true if this tab is the active tab.
11446      * @return {Boolean}
11447      */
11448     isActive : function(){
11449         return this.tabPanel.getActiveTab() == this;
11450     },
11451
11452     /**
11453      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
11454      */
11455     hide : function(){
11456         this.pnode.removeClass("on");
11457         this.hideAction();
11458         this.fireEvent("deactivate", this.tabPanel, this);
11459     },
11460
11461     hideAction : function(){
11462         this.bodyEl.hide();
11463         this.bodyEl.setStyle("position", "absolute");
11464         this.bodyEl.setLeft("-20000px");
11465         this.bodyEl.setTop("-20000px");
11466     },
11467
11468     showAction : function(){
11469         this.bodyEl.setStyle("position", "relative");
11470         this.bodyEl.setTop("");
11471         this.bodyEl.setLeft("");
11472         this.bodyEl.show();
11473     },
11474
11475     /**
11476      * Set the tooltip for the tab.
11477      * @param {String} tooltip The tab's tooltip
11478      */
11479     setTooltip : function(text){
11480         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
11481             this.textEl.dom.qtip = text;
11482             this.textEl.dom.removeAttribute('title');
11483         }else{
11484             this.textEl.dom.title = text;
11485         }
11486     },
11487
11488     onTabClick : function(e){
11489         e.preventDefault();
11490         this.tabPanel.activate(this.id);
11491     },
11492
11493     onTabMouseDown : function(e){
11494         e.preventDefault();
11495         this.tabPanel.activate(this.id);
11496     },
11497
11498     getWidth : function(){
11499         return this.inner.getWidth();
11500     },
11501
11502     setWidth : function(width){
11503         var iwidth = width - this.pnode.getPadding("lr");
11504         this.inner.setWidth(iwidth);
11505         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
11506         this.pnode.setWidth(width);
11507     },
11508
11509     /**
11510      * Show or hide the tab
11511      * @param {Boolean} hidden True to hide or false to show.
11512      */
11513     setHidden : function(hidden){
11514         this.hidden = hidden;
11515         this.pnode.setStyle("display", hidden ? "none" : "");
11516     },
11517
11518     /**
11519      * Returns true if this tab is "hidden"
11520      * @return {Boolean}
11521      */
11522     isHidden : function(){
11523         return this.hidden;
11524     },
11525
11526     /**
11527      * Returns the text for this tab
11528      * @return {String}
11529      */
11530     getText : function(){
11531         return this.text;
11532     },
11533
11534     autoSize : function(){
11535         //this.el.beginMeasure();
11536         this.textEl.setWidth(1);
11537         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
11538         //this.el.endMeasure();
11539     },
11540
11541     /**
11542      * Sets the text for the tab (Note: this also sets the tooltip text)
11543      * @param {String} text The tab's text and tooltip
11544      */
11545     setText : function(text){
11546         this.text = text;
11547         this.textEl.update(text);
11548         this.setTooltip(text);
11549         if(!this.tabPanel.resizeTabs){
11550             this.autoSize();
11551         }
11552     },
11553     /**
11554      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
11555      */
11556     activate : function(){
11557         this.tabPanel.activate(this.id);
11558     },
11559
11560     /**
11561      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
11562      */
11563     disable : function(){
11564         if(this.tabPanel.active != this){
11565             this.disabled = true;
11566             this.pnode.addClass("disabled");
11567         }
11568     },
11569
11570     /**
11571      * Enables this TabPanelItem if it was previously disabled.
11572      */
11573     enable : function(){
11574         this.disabled = false;
11575         this.pnode.removeClass("disabled");
11576     },
11577
11578     /**
11579      * Sets the content for this TabPanelItem.
11580      * @param {String} content The content
11581      * @param {Boolean} loadScripts true to look for and load scripts
11582      */
11583     setContent : function(content, loadScripts){
11584         this.bodyEl.update(content, loadScripts);
11585     },
11586
11587     /**
11588      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
11589      * @return {Roo.UpdateManager} The UpdateManager
11590      */
11591     getUpdateManager : function(){
11592         return this.bodyEl.getUpdateManager();
11593     },
11594
11595     /**
11596      * Set a URL to be used to load the content for this TabPanelItem.
11597      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
11598      * @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)
11599      * @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)
11600      * @return {Roo.UpdateManager} The UpdateManager
11601      */
11602     setUrl : function(url, params, loadOnce){
11603         if(this.refreshDelegate){
11604             this.un('activate', this.refreshDelegate);
11605         }
11606         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
11607         this.on("activate", this.refreshDelegate);
11608         return this.bodyEl.getUpdateManager();
11609     },
11610
11611     /** @private */
11612     _handleRefresh : function(url, params, loadOnce){
11613         if(!loadOnce || !this.loaded){
11614             var updater = this.bodyEl.getUpdateManager();
11615             updater.update(url, params, this._setLoaded.createDelegate(this));
11616         }
11617     },
11618
11619     /**
11620      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
11621      *   Will fail silently if the setUrl method has not been called.
11622      *   This does not activate the panel, just updates its content.
11623      */
11624     refresh : function(){
11625         if(this.refreshDelegate){
11626            this.loaded = false;
11627            this.refreshDelegate();
11628         }
11629     },
11630
11631     /** @private */
11632     _setLoaded : function(){
11633         this.loaded = true;
11634     },
11635
11636     /** @private */
11637     closeClick : function(e){
11638         var o = {};
11639         e.stopEvent();
11640         this.fireEvent("beforeclose", this, o);
11641         if(o.cancel !== true){
11642             this.tabPanel.removeTab(this.id);
11643         }
11644     },
11645     /**
11646      * The text displayed in the tooltip for the close icon.
11647      * @type String
11648      */
11649     closeText : "Close this tab"
11650 });
11651
11652 /** @private */
11653 Roo.TabPanel.prototype.createStrip = function(container){
11654     var strip = document.createElement("div");
11655     strip.className = "x-tabs-wrap";
11656     container.appendChild(strip);
11657     return strip;
11658 };
11659 /** @private */
11660 Roo.TabPanel.prototype.createStripList = function(strip){
11661     // div wrapper for retard IE
11662     // returns the "tr" element.
11663     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
11664         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
11665         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
11666     return strip.firstChild.firstChild.firstChild.firstChild;
11667 };
11668 /** @private */
11669 Roo.TabPanel.prototype.createBody = function(container){
11670     var body = document.createElement("div");
11671     Roo.id(body, "tab-body");
11672     Roo.fly(body).addClass("x-tabs-body");
11673     container.appendChild(body);
11674     return body;
11675 };
11676 /** @private */
11677 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
11678     var body = Roo.getDom(id);
11679     if(!body){
11680         body = document.createElement("div");
11681         body.id = id;
11682     }
11683     Roo.fly(body).addClass("x-tabs-item-body");
11684     bodyEl.insertBefore(body, bodyEl.firstChild);
11685     return body;
11686 };
11687 /** @private */
11688 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
11689     var td = document.createElement("td");
11690     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
11691     //stripEl.appendChild(td);
11692     if(closable){
11693         td.className = "x-tabs-closable";
11694         if(!this.closeTpl){
11695             this.closeTpl = new Roo.Template(
11696                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11697                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
11698                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
11699             );
11700         }
11701         var el = this.closeTpl.overwrite(td, {"text": text});
11702         var close = el.getElementsByTagName("div")[0];
11703         var inner = el.getElementsByTagName("em")[0];
11704         return {"el": el, "close": close, "inner": inner};
11705     } else {
11706         if(!this.tabTpl){
11707             this.tabTpl = new Roo.Template(
11708                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11709                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
11710             );
11711         }
11712         var el = this.tabTpl.overwrite(td, {"text": text});
11713         var inner = el.getElementsByTagName("em")[0];
11714         return {"el": el, "inner": inner};
11715     }
11716 };/*
11717  * Based on:
11718  * Ext JS Library 1.1.1
11719  * Copyright(c) 2006-2007, Ext JS, LLC.
11720  *
11721  * Originally Released Under LGPL - original licence link has changed is not relivant.
11722  *
11723  * Fork - LGPL
11724  * <script type="text/javascript">
11725  */
11726
11727 /**
11728  * @class Roo.Button
11729  * @extends Roo.util.Observable
11730  * Simple Button class
11731  * @cfg {String} text The button text
11732  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
11733  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
11734  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
11735  * @cfg {Object} scope The scope of the handler
11736  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
11737  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
11738  * @cfg {Boolean} hidden True to start hidden (defaults to false)
11739  * @cfg {Boolean} disabled True to start disabled (defaults to false)
11740  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
11741  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
11742    applies if enableToggle = true)
11743  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
11744  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
11745   an {@link Roo.util.ClickRepeater} config object (defaults to false).
11746  * @constructor
11747  * Create a new button
11748  * @param {Object} config The config object
11749  */
11750 Roo.Button = function(renderTo, config)
11751 {
11752     if (!config) {
11753         config = renderTo;
11754         renderTo = config.renderTo || false;
11755     }
11756     
11757     Roo.apply(this, config);
11758     this.addEvents({
11759         /**
11760              * @event click
11761              * Fires when this button is clicked
11762              * @param {Button} this
11763              * @param {EventObject} e The click event
11764              */
11765             "click" : true,
11766         /**
11767              * @event toggle
11768              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
11769              * @param {Button} this
11770              * @param {Boolean} pressed
11771              */
11772             "toggle" : true,
11773         /**
11774              * @event mouseover
11775              * Fires when the mouse hovers over the button
11776              * @param {Button} this
11777              * @param {Event} e The event object
11778              */
11779         'mouseover' : true,
11780         /**
11781              * @event mouseout
11782              * Fires when the mouse exits the button
11783              * @param {Button} this
11784              * @param {Event} e The event object
11785              */
11786         'mouseout': true,
11787          /**
11788              * @event render
11789              * Fires when the button is rendered
11790              * @param {Button} this
11791              */
11792         'render': true
11793     });
11794     if(this.menu){
11795         this.menu = Roo.menu.MenuMgr.get(this.menu);
11796     }
11797     // register listeners first!!  - so render can be captured..
11798     Roo.util.Observable.call(this);
11799     if(renderTo){
11800         this.render(renderTo);
11801     }
11802     
11803   
11804 };
11805
11806 Roo.extend(Roo.Button, Roo.util.Observable, {
11807     /**
11808      * 
11809      */
11810     
11811     /**
11812      * Read-only. True if this button is hidden
11813      * @type Boolean
11814      */
11815     hidden : false,
11816     /**
11817      * Read-only. True if this button is disabled
11818      * @type Boolean
11819      */
11820     disabled : false,
11821     /**
11822      * Read-only. True if this button is pressed (only if enableToggle = true)
11823      * @type Boolean
11824      */
11825     pressed : false,
11826
11827     /**
11828      * @cfg {Number} tabIndex 
11829      * The DOM tabIndex for this button (defaults to undefined)
11830      */
11831     tabIndex : undefined,
11832
11833     /**
11834      * @cfg {Boolean} enableToggle
11835      * True to enable pressed/not pressed toggling (defaults to false)
11836      */
11837     enableToggle: false,
11838     /**
11839      * @cfg {Mixed} menu
11840      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
11841      */
11842     menu : undefined,
11843     /**
11844      * @cfg {String} menuAlign
11845      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
11846      */
11847     menuAlign : "tl-bl?",
11848
11849     /**
11850      * @cfg {String} iconCls
11851      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
11852      */
11853     iconCls : undefined,
11854     /**
11855      * @cfg {String} type
11856      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
11857      */
11858     type : 'button',
11859
11860     // private
11861     menuClassTarget: 'tr',
11862
11863     /**
11864      * @cfg {String} clickEvent
11865      * The type of event to map to the button's event handler (defaults to 'click')
11866      */
11867     clickEvent : 'click',
11868
11869     /**
11870      * @cfg {Boolean} handleMouseEvents
11871      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
11872      */
11873     handleMouseEvents : true,
11874
11875     /**
11876      * @cfg {String} tooltipType
11877      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
11878      */
11879     tooltipType : 'qtip',
11880
11881     /**
11882      * @cfg {String} cls
11883      * A CSS class to apply to the button's main element.
11884      */
11885     
11886     /**
11887      * @cfg {Roo.Template} template (Optional)
11888      * An {@link Roo.Template} with which to create the Button's main element. This Template must
11889      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
11890      * require code modifications if required elements (e.g. a button) aren't present.
11891      */
11892
11893     // private
11894     render : function(renderTo){
11895         var btn;
11896         if(this.hideParent){
11897             this.parentEl = Roo.get(renderTo);
11898         }
11899         if(!this.dhconfig){
11900             if(!this.template){
11901                 if(!Roo.Button.buttonTemplate){
11902                     // hideous table template
11903                     Roo.Button.buttonTemplate = new Roo.Template(
11904                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
11905                         '<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>',
11906                         "</tr></tbody></table>");
11907                 }
11908                 this.template = Roo.Button.buttonTemplate;
11909             }
11910             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
11911             var btnEl = btn.child("button:first");
11912             btnEl.on('focus', this.onFocus, this);
11913             btnEl.on('blur', this.onBlur, this);
11914             if(this.cls){
11915                 btn.addClass(this.cls);
11916             }
11917             if(this.icon){
11918                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
11919             }
11920             if(this.iconCls){
11921                 btnEl.addClass(this.iconCls);
11922                 if(!this.cls){
11923                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11924                 }
11925             }
11926             if(this.tabIndex !== undefined){
11927                 btnEl.dom.tabIndex = this.tabIndex;
11928             }
11929             if(this.tooltip){
11930                 if(typeof this.tooltip == 'object'){
11931                     Roo.QuickTips.tips(Roo.apply({
11932                           target: btnEl.id
11933                     }, this.tooltip));
11934                 } else {
11935                     btnEl.dom[this.tooltipType] = this.tooltip;
11936                 }
11937             }
11938         }else{
11939             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
11940         }
11941         this.el = btn;
11942         if(this.id){
11943             this.el.dom.id = this.el.id = this.id;
11944         }
11945         if(this.menu){
11946             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
11947             this.menu.on("show", this.onMenuShow, this);
11948             this.menu.on("hide", this.onMenuHide, this);
11949         }
11950         btn.addClass("x-btn");
11951         if(Roo.isIE && !Roo.isIE7){
11952             this.autoWidth.defer(1, this);
11953         }else{
11954             this.autoWidth();
11955         }
11956         if(this.handleMouseEvents){
11957             btn.on("mouseover", this.onMouseOver, this);
11958             btn.on("mouseout", this.onMouseOut, this);
11959             btn.on("mousedown", this.onMouseDown, this);
11960         }
11961         btn.on(this.clickEvent, this.onClick, this);
11962         //btn.on("mouseup", this.onMouseUp, this);
11963         if(this.hidden){
11964             this.hide();
11965         }
11966         if(this.disabled){
11967             this.disable();
11968         }
11969         Roo.ButtonToggleMgr.register(this);
11970         if(this.pressed){
11971             this.el.addClass("x-btn-pressed");
11972         }
11973         if(this.repeat){
11974             var repeater = new Roo.util.ClickRepeater(btn,
11975                 typeof this.repeat == "object" ? this.repeat : {}
11976             );
11977             repeater.on("click", this.onClick,  this);
11978         }
11979         
11980         this.fireEvent('render', this);
11981         
11982     },
11983     /**
11984      * Returns the button's underlying element
11985      * @return {Roo.Element} The element
11986      */
11987     getEl : function(){
11988         return this.el;  
11989     },
11990     
11991     /**
11992      * Destroys this Button and removes any listeners.
11993      */
11994     destroy : function(){
11995         Roo.ButtonToggleMgr.unregister(this);
11996         this.el.removeAllListeners();
11997         this.purgeListeners();
11998         this.el.remove();
11999     },
12000
12001     // private
12002     autoWidth : function(){
12003         if(this.el){
12004             this.el.setWidth("auto");
12005             if(Roo.isIE7 && Roo.isStrict){
12006                 var ib = this.el.child('button');
12007                 if(ib && ib.getWidth() > 20){
12008                     ib.clip();
12009                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
12010                 }
12011             }
12012             if(this.minWidth){
12013                 if(this.hidden){
12014                     this.el.beginMeasure();
12015                 }
12016                 if(this.el.getWidth() < this.minWidth){
12017                     this.el.setWidth(this.minWidth);
12018                 }
12019                 if(this.hidden){
12020                     this.el.endMeasure();
12021                 }
12022             }
12023         }
12024     },
12025
12026     /**
12027      * Assigns this button's click handler
12028      * @param {Function} handler The function to call when the button is clicked
12029      * @param {Object} scope (optional) Scope for the function passed in
12030      */
12031     setHandler : function(handler, scope){
12032         this.handler = handler;
12033         this.scope = scope;  
12034     },
12035     
12036     /**
12037      * Sets this button's text
12038      * @param {String} text The button text
12039      */
12040     setText : function(text){
12041         this.text = text;
12042         if(this.el){
12043             this.el.child("td.x-btn-center button.x-btn-text").update(text);
12044         }
12045         this.autoWidth();
12046     },
12047     
12048     /**
12049      * Gets the text for this button
12050      * @return {String} The button text
12051      */
12052     getText : function(){
12053         return this.text;  
12054     },
12055     
12056     /**
12057      * Show this button
12058      */
12059     show: function(){
12060         this.hidden = false;
12061         if(this.el){
12062             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
12063         }
12064     },
12065     
12066     /**
12067      * Hide this button
12068      */
12069     hide: function(){
12070         this.hidden = true;
12071         if(this.el){
12072             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
12073         }
12074     },
12075     
12076     /**
12077      * Convenience function for boolean show/hide
12078      * @param {Boolean} visible True to show, false to hide
12079      */
12080     setVisible: function(visible){
12081         if(visible) {
12082             this.show();
12083         }else{
12084             this.hide();
12085         }
12086     },
12087     
12088     /**
12089      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
12090      * @param {Boolean} state (optional) Force a particular state
12091      */
12092     toggle : function(state){
12093         state = state === undefined ? !this.pressed : state;
12094         if(state != this.pressed){
12095             if(state){
12096                 this.el.addClass("x-btn-pressed");
12097                 this.pressed = true;
12098                 this.fireEvent("toggle", this, true);
12099             }else{
12100                 this.el.removeClass("x-btn-pressed");
12101                 this.pressed = false;
12102                 this.fireEvent("toggle", this, false);
12103             }
12104             if(this.toggleHandler){
12105                 this.toggleHandler.call(this.scope || this, this, state);
12106             }
12107         }
12108     },
12109     
12110     /**
12111      * Focus the button
12112      */
12113     focus : function(){
12114         this.el.child('button:first').focus();
12115     },
12116     
12117     /**
12118      * Disable this button
12119      */
12120     disable : function(){
12121         if(this.el){
12122             this.el.addClass("x-btn-disabled");
12123         }
12124         this.disabled = true;
12125     },
12126     
12127     /**
12128      * Enable this button
12129      */
12130     enable : function(){
12131         if(this.el){
12132             this.el.removeClass("x-btn-disabled");
12133         }
12134         this.disabled = false;
12135     },
12136
12137     /**
12138      * Convenience function for boolean enable/disable
12139      * @param {Boolean} enabled True to enable, false to disable
12140      */
12141     setDisabled : function(v){
12142         this[v !== true ? "enable" : "disable"]();
12143     },
12144
12145     // private
12146     onClick : function(e){
12147         if(e){
12148             e.preventDefault();
12149         }
12150         if(e.button != 0){
12151             return;
12152         }
12153         if(!this.disabled){
12154             if(this.enableToggle){
12155                 this.toggle();
12156             }
12157             if(this.menu && !this.menu.isVisible()){
12158                 this.menu.show(this.el, this.menuAlign);
12159             }
12160             this.fireEvent("click", this, e);
12161             if(this.handler){
12162                 this.el.removeClass("x-btn-over");
12163                 this.handler.call(this.scope || this, this, e);
12164             }
12165         }
12166     },
12167     // private
12168     onMouseOver : function(e){
12169         if(!this.disabled){
12170             this.el.addClass("x-btn-over");
12171             this.fireEvent('mouseover', this, e);
12172         }
12173     },
12174     // private
12175     onMouseOut : function(e){
12176         if(!e.within(this.el,  true)){
12177             this.el.removeClass("x-btn-over");
12178             this.fireEvent('mouseout', this, e);
12179         }
12180     },
12181     // private
12182     onFocus : function(e){
12183         if(!this.disabled){
12184             this.el.addClass("x-btn-focus");
12185         }
12186     },
12187     // private
12188     onBlur : function(e){
12189         this.el.removeClass("x-btn-focus");
12190     },
12191     // private
12192     onMouseDown : function(e){
12193         if(!this.disabled && e.button == 0){
12194             this.el.addClass("x-btn-click");
12195             Roo.get(document).on('mouseup', this.onMouseUp, this);
12196         }
12197     },
12198     // private
12199     onMouseUp : function(e){
12200         if(e.button == 0){
12201             this.el.removeClass("x-btn-click");
12202             Roo.get(document).un('mouseup', this.onMouseUp, this);
12203         }
12204     },
12205     // private
12206     onMenuShow : function(e){
12207         this.el.addClass("x-btn-menu-active");
12208     },
12209     // private
12210     onMenuHide : function(e){
12211         this.el.removeClass("x-btn-menu-active");
12212     }   
12213 });
12214
12215 // Private utility class used by Button
12216 Roo.ButtonToggleMgr = function(){
12217    var groups = {};
12218    
12219    function toggleGroup(btn, state){
12220        if(state){
12221            var g = groups[btn.toggleGroup];
12222            for(var i = 0, l = g.length; i < l; i++){
12223                if(g[i] != btn){
12224                    g[i].toggle(false);
12225                }
12226            }
12227        }
12228    }
12229    
12230    return {
12231        register : function(btn){
12232            if(!btn.toggleGroup){
12233                return;
12234            }
12235            var g = groups[btn.toggleGroup];
12236            if(!g){
12237                g = groups[btn.toggleGroup] = [];
12238            }
12239            g.push(btn);
12240            btn.on("toggle", toggleGroup);
12241        },
12242        
12243        unregister : function(btn){
12244            if(!btn.toggleGroup){
12245                return;
12246            }
12247            var g = groups[btn.toggleGroup];
12248            if(g){
12249                g.remove(btn);
12250                btn.un("toggle", toggleGroup);
12251            }
12252        }
12253    };
12254 }();/*
12255  * Based on:
12256  * Ext JS Library 1.1.1
12257  * Copyright(c) 2006-2007, Ext JS, LLC.
12258  *
12259  * Originally Released Under LGPL - original licence link has changed is not relivant.
12260  *
12261  * Fork - LGPL
12262  * <script type="text/javascript">
12263  */
12264  
12265 /**
12266  * @class Roo.SplitButton
12267  * @extends Roo.Button
12268  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
12269  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
12270  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
12271  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
12272  * @cfg {String} arrowTooltip The title attribute of the arrow
12273  * @constructor
12274  * Create a new menu button
12275  * @param {String/HTMLElement/Element} renderTo The element to append the button to
12276  * @param {Object} config The config object
12277  */
12278 Roo.SplitButton = function(renderTo, config){
12279     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
12280     /**
12281      * @event arrowclick
12282      * Fires when this button's arrow is clicked
12283      * @param {SplitButton} this
12284      * @param {EventObject} e The click event
12285      */
12286     this.addEvents({"arrowclick":true});
12287 };
12288
12289 Roo.extend(Roo.SplitButton, Roo.Button, {
12290     render : function(renderTo){
12291         // this is one sweet looking template!
12292         var tpl = new Roo.Template(
12293             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
12294             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
12295             '<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>',
12296             "</tbody></table></td><td>",
12297             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
12298             '<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>',
12299             "</tbody></table></td></tr></table>"
12300         );
12301         var btn = tpl.append(renderTo, [this.text, this.type], true);
12302         var btnEl = btn.child("button");
12303         if(this.cls){
12304             btn.addClass(this.cls);
12305         }
12306         if(this.icon){
12307             btnEl.setStyle('background-image', 'url(' +this.icon +')');
12308         }
12309         if(this.iconCls){
12310             btnEl.addClass(this.iconCls);
12311             if(!this.cls){
12312                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
12313             }
12314         }
12315         this.el = btn;
12316         if(this.handleMouseEvents){
12317             btn.on("mouseover", this.onMouseOver, this);
12318             btn.on("mouseout", this.onMouseOut, this);
12319             btn.on("mousedown", this.onMouseDown, this);
12320             btn.on("mouseup", this.onMouseUp, this);
12321         }
12322         btn.on(this.clickEvent, this.onClick, this);
12323         if(this.tooltip){
12324             if(typeof this.tooltip == 'object'){
12325                 Roo.QuickTips.tips(Roo.apply({
12326                       target: btnEl.id
12327                 }, this.tooltip));
12328             } else {
12329                 btnEl.dom[this.tooltipType] = this.tooltip;
12330             }
12331         }
12332         if(this.arrowTooltip){
12333             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
12334         }
12335         if(this.hidden){
12336             this.hide();
12337         }
12338         if(this.disabled){
12339             this.disable();
12340         }
12341         if(this.pressed){
12342             this.el.addClass("x-btn-pressed");
12343         }
12344         if(Roo.isIE && !Roo.isIE7){
12345             this.autoWidth.defer(1, this);
12346         }else{
12347             this.autoWidth();
12348         }
12349         if(this.menu){
12350             this.menu.on("show", this.onMenuShow, this);
12351             this.menu.on("hide", this.onMenuHide, this);
12352         }
12353         this.fireEvent('render', this);
12354     },
12355
12356     // private
12357     autoWidth : function(){
12358         if(this.el){
12359             var tbl = this.el.child("table:first");
12360             var tbl2 = this.el.child("table:last");
12361             this.el.setWidth("auto");
12362             tbl.setWidth("auto");
12363             if(Roo.isIE7 && Roo.isStrict){
12364                 var ib = this.el.child('button:first');
12365                 if(ib && ib.getWidth() > 20){
12366                     ib.clip();
12367                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
12368                 }
12369             }
12370             if(this.minWidth){
12371                 if(this.hidden){
12372                     this.el.beginMeasure();
12373                 }
12374                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
12375                     tbl.setWidth(this.minWidth-tbl2.getWidth());
12376                 }
12377                 if(this.hidden){
12378                     this.el.endMeasure();
12379                 }
12380             }
12381             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
12382         } 
12383     },
12384     /**
12385      * Sets this button's click handler
12386      * @param {Function} handler The function to call when the button is clicked
12387      * @param {Object} scope (optional) Scope for the function passed above
12388      */
12389     setHandler : function(handler, scope){
12390         this.handler = handler;
12391         this.scope = scope;  
12392     },
12393     
12394     /**
12395      * Sets this button's arrow click handler
12396      * @param {Function} handler The function to call when the arrow is clicked
12397      * @param {Object} scope (optional) Scope for the function passed above
12398      */
12399     setArrowHandler : function(handler, scope){
12400         this.arrowHandler = handler;
12401         this.scope = scope;  
12402     },
12403     
12404     /**
12405      * Focus the button
12406      */
12407     focus : function(){
12408         if(this.el){
12409             this.el.child("button:first").focus();
12410         }
12411     },
12412
12413     // private
12414     onClick : function(e){
12415         e.preventDefault();
12416         if(!this.disabled){
12417             if(e.getTarget(".x-btn-menu-arrow-wrap")){
12418                 if(this.menu && !this.menu.isVisible()){
12419                     this.menu.show(this.el, this.menuAlign);
12420                 }
12421                 this.fireEvent("arrowclick", this, e);
12422                 if(this.arrowHandler){
12423                     this.arrowHandler.call(this.scope || this, this, e);
12424                 }
12425             }else{
12426                 this.fireEvent("click", this, e);
12427                 if(this.handler){
12428                     this.handler.call(this.scope || this, this, e);
12429                 }
12430             }
12431         }
12432     },
12433     // private
12434     onMouseDown : function(e){
12435         if(!this.disabled){
12436             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
12437         }
12438     },
12439     // private
12440     onMouseUp : function(e){
12441         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
12442     }   
12443 });
12444
12445
12446 // backwards compat
12447 Roo.MenuButton = Roo.SplitButton;/*
12448  * Based on:
12449  * Ext JS Library 1.1.1
12450  * Copyright(c) 2006-2007, Ext JS, LLC.
12451  *
12452  * Originally Released Under LGPL - original licence link has changed is not relivant.
12453  *
12454  * Fork - LGPL
12455  * <script type="text/javascript">
12456  */
12457
12458 /**
12459  * @class Roo.Toolbar
12460  * Basic Toolbar class.
12461  * @constructor
12462  * Creates a new Toolbar
12463  * @param {Object} container The config object
12464  */ 
12465 Roo.Toolbar = function(container, buttons, config)
12466 {
12467     /// old consturctor format still supported..
12468     if(container instanceof Array){ // omit the container for later rendering
12469         buttons = container;
12470         config = buttons;
12471         container = null;
12472     }
12473     if (typeof(container) == 'object' && container.xtype) {
12474         config = container;
12475         container = config.container;
12476         buttons = config.buttons || []; // not really - use items!!
12477     }
12478     var xitems = [];
12479     if (config && config.items) {
12480         xitems = config.items;
12481         delete config.items;
12482     }
12483     Roo.apply(this, config);
12484     this.buttons = buttons;
12485     
12486     if(container){
12487         this.render(container);
12488     }
12489     this.xitems = xitems;
12490     Roo.each(xitems, function(b) {
12491         this.add(b);
12492     }, this);
12493     
12494 };
12495
12496 Roo.Toolbar.prototype = {
12497     /**
12498      * @cfg {Array} items
12499      * array of button configs or elements to add (will be converted to a MixedCollection)
12500      */
12501     
12502     /**
12503      * @cfg {String/HTMLElement/Element} container
12504      * The id or element that will contain the toolbar
12505      */
12506     // private
12507     render : function(ct){
12508         this.el = Roo.get(ct);
12509         if(this.cls){
12510             this.el.addClass(this.cls);
12511         }
12512         // using a table allows for vertical alignment
12513         // 100% width is needed by Safari...
12514         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
12515         this.tr = this.el.child("tr", true);
12516         var autoId = 0;
12517         this.items = new Roo.util.MixedCollection(false, function(o){
12518             return o.id || ("item" + (++autoId));
12519         });
12520         if(this.buttons){
12521             this.add.apply(this, this.buttons);
12522             delete this.buttons;
12523         }
12524     },
12525
12526     /**
12527      * Adds element(s) to the toolbar -- this function takes a variable number of 
12528      * arguments of mixed type and adds them to the toolbar.
12529      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
12530      * <ul>
12531      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
12532      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
12533      * <li>Field: Any form field (equivalent to {@link #addField})</li>
12534      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
12535      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
12536      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
12537      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
12538      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
12539      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
12540      * </ul>
12541      * @param {Mixed} arg2
12542      * @param {Mixed} etc.
12543      */
12544     add : function(){
12545         var a = arguments, l = a.length;
12546         for(var i = 0; i < l; i++){
12547             this._add(a[i]);
12548         }
12549     },
12550     // private..
12551     _add : function(el) {
12552         
12553         if (el.xtype) {
12554             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
12555         }
12556         
12557         if (el.applyTo){ // some kind of form field
12558             return this.addField(el);
12559         } 
12560         if (el.render){ // some kind of Toolbar.Item
12561             return this.addItem(el);
12562         }
12563         if (typeof el == "string"){ // string
12564             if(el == "separator" || el == "-"){
12565                 return this.addSeparator();
12566             }
12567             if (el == " "){
12568                 return this.addSpacer();
12569             }
12570             if(el == "->"){
12571                 return this.addFill();
12572             }
12573             return this.addText(el);
12574             
12575         }
12576         if(el.tagName){ // element
12577             return this.addElement(el);
12578         }
12579         if(typeof el == "object"){ // must be button config?
12580             return this.addButton(el);
12581         }
12582         // and now what?!?!
12583         return false;
12584         
12585     },
12586     
12587     /**
12588      * Add an Xtype element
12589      * @param {Object} xtype Xtype Object
12590      * @return {Object} created Object
12591      */
12592     addxtype : function(e){
12593         return this.add(e);  
12594     },
12595     
12596     /**
12597      * Returns the Element for this toolbar.
12598      * @return {Roo.Element}
12599      */
12600     getEl : function(){
12601         return this.el;  
12602     },
12603     
12604     /**
12605      * Adds a separator
12606      * @return {Roo.Toolbar.Item} The separator item
12607      */
12608     addSeparator : function(){
12609         return this.addItem(new Roo.Toolbar.Separator());
12610     },
12611
12612     /**
12613      * Adds a spacer element
12614      * @return {Roo.Toolbar.Spacer} The spacer item
12615      */
12616     addSpacer : function(){
12617         return this.addItem(new Roo.Toolbar.Spacer());
12618     },
12619
12620     /**
12621      * Adds a fill element that forces subsequent additions to the right side of the toolbar
12622      * @return {Roo.Toolbar.Fill} The fill item
12623      */
12624     addFill : function(){
12625         return this.addItem(new Roo.Toolbar.Fill());
12626     },
12627
12628     /**
12629      * Adds any standard HTML element to the toolbar
12630      * @param {String/HTMLElement/Element} el The element or id of the element to add
12631      * @return {Roo.Toolbar.Item} The element's item
12632      */
12633     addElement : function(el){
12634         return this.addItem(new Roo.Toolbar.Item(el));
12635     },
12636     /**
12637      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
12638      * @type Roo.util.MixedCollection  
12639      */
12640     items : false,
12641      
12642     /**
12643      * Adds any Toolbar.Item or subclass
12644      * @param {Roo.Toolbar.Item} item
12645      * @return {Roo.Toolbar.Item} The item
12646      */
12647     addItem : function(item){
12648         var td = this.nextBlock();
12649         item.render(td);
12650         this.items.add(item);
12651         return item;
12652     },
12653     
12654     /**
12655      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
12656      * @param {Object/Array} config A button config or array of configs
12657      * @return {Roo.Toolbar.Button/Array}
12658      */
12659     addButton : function(config){
12660         if(config instanceof Array){
12661             var buttons = [];
12662             for(var i = 0, len = config.length; i < len; i++) {
12663                 buttons.push(this.addButton(config[i]));
12664             }
12665             return buttons;
12666         }
12667         var b = config;
12668         if(!(config instanceof Roo.Toolbar.Button)){
12669             b = config.split ?
12670                 new Roo.Toolbar.SplitButton(config) :
12671                 new Roo.Toolbar.Button(config);
12672         }
12673         var td = this.nextBlock();
12674         b.render(td);
12675         this.items.add(b);
12676         return b;
12677     },
12678     
12679     /**
12680      * Adds text to the toolbar
12681      * @param {String} text The text to add
12682      * @return {Roo.Toolbar.Item} The element's item
12683      */
12684     addText : function(text){
12685         return this.addItem(new Roo.Toolbar.TextItem(text));
12686     },
12687     
12688     /**
12689      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
12690      * @param {Number} index The index where the item is to be inserted
12691      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
12692      * @return {Roo.Toolbar.Button/Item}
12693      */
12694     insertButton : function(index, item){
12695         if(item instanceof Array){
12696             var buttons = [];
12697             for(var i = 0, len = item.length; i < len; i++) {
12698                buttons.push(this.insertButton(index + i, item[i]));
12699             }
12700             return buttons;
12701         }
12702         if (!(item instanceof Roo.Toolbar.Button)){
12703            item = new Roo.Toolbar.Button(item);
12704         }
12705         var td = document.createElement("td");
12706         this.tr.insertBefore(td, this.tr.childNodes[index]);
12707         item.render(td);
12708         this.items.insert(index, item);
12709         return item;
12710     },
12711     
12712     /**
12713      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
12714      * @param {Object} config
12715      * @return {Roo.Toolbar.Item} The element's item
12716      */
12717     addDom : function(config, returnEl){
12718         var td = this.nextBlock();
12719         Roo.DomHelper.overwrite(td, config);
12720         var ti = new Roo.Toolbar.Item(td.firstChild);
12721         ti.render(td);
12722         this.items.add(ti);
12723         return ti;
12724     },
12725
12726     /**
12727      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
12728      * @type Roo.util.MixedCollection  
12729      */
12730     fields : false,
12731     
12732     /**
12733      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
12734      * Note: the field should not have been rendered yet. For a field that has already been
12735      * rendered, use {@link #addElement}.
12736      * @param {Roo.form.Field} field
12737      * @return {Roo.ToolbarItem}
12738      */
12739      
12740       
12741     addField : function(field) {
12742         if (!this.fields) {
12743             var autoId = 0;
12744             this.fields = new Roo.util.MixedCollection(false, function(o){
12745                 return o.id || ("item" + (++autoId));
12746             });
12747
12748         }
12749         
12750         var td = this.nextBlock();
12751         field.render(td);
12752         var ti = new Roo.Toolbar.Item(td.firstChild);
12753         ti.render(td);
12754         this.items.add(ti);
12755         this.fields.add(field);
12756         return ti;
12757     },
12758     /**
12759      * Hide the toolbar
12760      * @method hide
12761      */
12762      
12763       
12764     hide : function()
12765     {
12766         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
12767         this.el.child('div').hide();
12768     },
12769     /**
12770      * Show the toolbar
12771      * @method show
12772      */
12773     show : function()
12774     {
12775         this.el.child('div').show();
12776     },
12777       
12778     // private
12779     nextBlock : function(){
12780         var td = document.createElement("td");
12781         this.tr.appendChild(td);
12782         return td;
12783     },
12784
12785     // private
12786     destroy : function(){
12787         if(this.items){ // rendered?
12788             Roo.destroy.apply(Roo, this.items.items);
12789         }
12790         if(this.fields){ // rendered?
12791             Roo.destroy.apply(Roo, this.fields.items);
12792         }
12793         Roo.Element.uncache(this.el, this.tr);
12794     }
12795 };
12796
12797 /**
12798  * @class Roo.Toolbar.Item
12799  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
12800  * @constructor
12801  * Creates a new Item
12802  * @param {HTMLElement} el 
12803  */
12804 Roo.Toolbar.Item = function(el){
12805     this.el = Roo.getDom(el);
12806     this.id = Roo.id(this.el);
12807     this.hidden = false;
12808 };
12809
12810 Roo.Toolbar.Item.prototype = {
12811     
12812     /**
12813      * Get this item's HTML Element
12814      * @return {HTMLElement}
12815      */
12816     getEl : function(){
12817        return this.el;  
12818     },
12819
12820     // private
12821     render : function(td){
12822         this.td = td;
12823         td.appendChild(this.el);
12824     },
12825     
12826     /**
12827      * Removes and destroys this item.
12828      */
12829     destroy : function(){
12830         this.td.parentNode.removeChild(this.td);
12831     },
12832     
12833     /**
12834      * Shows this item.
12835      */
12836     show: function(){
12837         this.hidden = false;
12838         this.td.style.display = "";
12839     },
12840     
12841     /**
12842      * Hides this item.
12843      */
12844     hide: function(){
12845         this.hidden = true;
12846         this.td.style.display = "none";
12847     },
12848     
12849     /**
12850      * Convenience function for boolean show/hide.
12851      * @param {Boolean} visible true to show/false to hide
12852      */
12853     setVisible: function(visible){
12854         if(visible) {
12855             this.show();
12856         }else{
12857             this.hide();
12858         }
12859     },
12860     
12861     /**
12862      * Try to focus this item.
12863      */
12864     focus : function(){
12865         Roo.fly(this.el).focus();
12866     },
12867     
12868     /**
12869      * Disables this item.
12870      */
12871     disable : function(){
12872         Roo.fly(this.td).addClass("x-item-disabled");
12873         this.disabled = true;
12874         this.el.disabled = true;
12875     },
12876     
12877     /**
12878      * Enables this item.
12879      */
12880     enable : function(){
12881         Roo.fly(this.td).removeClass("x-item-disabled");
12882         this.disabled = false;
12883         this.el.disabled = false;
12884     }
12885 };
12886
12887
12888 /**
12889  * @class Roo.Toolbar.Separator
12890  * @extends Roo.Toolbar.Item
12891  * A simple toolbar separator class
12892  * @constructor
12893  * Creates a new Separator
12894  */
12895 Roo.Toolbar.Separator = function(){
12896     var s = document.createElement("span");
12897     s.className = "ytb-sep";
12898     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
12899 };
12900 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
12901     enable:Roo.emptyFn,
12902     disable:Roo.emptyFn,
12903     focus:Roo.emptyFn
12904 });
12905
12906 /**
12907  * @class Roo.Toolbar.Spacer
12908  * @extends Roo.Toolbar.Item
12909  * A simple element that adds extra horizontal space to a toolbar.
12910  * @constructor
12911  * Creates a new Spacer
12912  */
12913 Roo.Toolbar.Spacer = function(){
12914     var s = document.createElement("div");
12915     s.className = "ytb-spacer";
12916     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
12917 };
12918 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
12919     enable:Roo.emptyFn,
12920     disable:Roo.emptyFn,
12921     focus:Roo.emptyFn
12922 });
12923
12924 /**
12925  * @class Roo.Toolbar.Fill
12926  * @extends Roo.Toolbar.Spacer
12927  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
12928  * @constructor
12929  * Creates a new Spacer
12930  */
12931 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
12932     // private
12933     render : function(td){
12934         td.style.width = '100%';
12935         Roo.Toolbar.Fill.superclass.render.call(this, td);
12936     }
12937 });
12938
12939 /**
12940  * @class Roo.Toolbar.TextItem
12941  * @extends Roo.Toolbar.Item
12942  * A simple class that renders text directly into a toolbar.
12943  * @constructor
12944  * Creates a new TextItem
12945  * @param {String} text
12946  */
12947 Roo.Toolbar.TextItem = function(text){
12948     if (typeof(text) == 'object') {
12949         text = text.text;
12950     }
12951     var s = document.createElement("span");
12952     s.className = "ytb-text";
12953     s.innerHTML = text;
12954     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
12955 };
12956 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
12957     enable:Roo.emptyFn,
12958     disable:Roo.emptyFn,
12959     focus:Roo.emptyFn
12960 });
12961
12962 /**
12963  * @class Roo.Toolbar.Button
12964  * @extends Roo.Button
12965  * A button that renders into a toolbar.
12966  * @constructor
12967  * Creates a new Button
12968  * @param {Object} config A standard {@link Roo.Button} config object
12969  */
12970 Roo.Toolbar.Button = function(config){
12971     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
12972 };
12973 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
12974     render : function(td){
12975         this.td = td;
12976         Roo.Toolbar.Button.superclass.render.call(this, td);
12977     },
12978     
12979     /**
12980      * Removes and destroys this button
12981      */
12982     destroy : function(){
12983         Roo.Toolbar.Button.superclass.destroy.call(this);
12984         this.td.parentNode.removeChild(this.td);
12985     },
12986     
12987     /**
12988      * Shows this button
12989      */
12990     show: function(){
12991         this.hidden = false;
12992         this.td.style.display = "";
12993     },
12994     
12995     /**
12996      * Hides this button
12997      */
12998     hide: function(){
12999         this.hidden = true;
13000         this.td.style.display = "none";
13001     },
13002
13003     /**
13004      * Disables this item
13005      */
13006     disable : function(){
13007         Roo.fly(this.td).addClass("x-item-disabled");
13008         this.disabled = true;
13009     },
13010
13011     /**
13012      * Enables this item
13013      */
13014     enable : function(){
13015         Roo.fly(this.td).removeClass("x-item-disabled");
13016         this.disabled = false;
13017     }
13018 });
13019 // backwards compat
13020 Roo.ToolbarButton = Roo.Toolbar.Button;
13021
13022 /**
13023  * @class Roo.Toolbar.SplitButton
13024  * @extends Roo.SplitButton
13025  * A menu button that renders into a toolbar.
13026  * @constructor
13027  * Creates a new SplitButton
13028  * @param {Object} config A standard {@link Roo.SplitButton} config object
13029  */
13030 Roo.Toolbar.SplitButton = function(config){
13031     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
13032 };
13033 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
13034     render : function(td){
13035         this.td = td;
13036         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
13037     },
13038     
13039     /**
13040      * Removes and destroys this button
13041      */
13042     destroy : function(){
13043         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
13044         this.td.parentNode.removeChild(this.td);
13045     },
13046     
13047     /**
13048      * Shows this button
13049      */
13050     show: function(){
13051         this.hidden = false;
13052         this.td.style.display = "";
13053     },
13054     
13055     /**
13056      * Hides this button
13057      */
13058     hide: function(){
13059         this.hidden = true;
13060         this.td.style.display = "none";
13061     }
13062 });
13063
13064 // backwards compat
13065 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
13066  * Based on:
13067  * Ext JS Library 1.1.1
13068  * Copyright(c) 2006-2007, Ext JS, LLC.
13069  *
13070  * Originally Released Under LGPL - original licence link has changed is not relivant.
13071  *
13072  * Fork - LGPL
13073  * <script type="text/javascript">
13074  */
13075  
13076 /**
13077  * @class Roo.PagingToolbar
13078  * @extends Roo.Toolbar
13079  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
13080  * @constructor
13081  * Create a new PagingToolbar
13082  * @param {Object} config The config object
13083  */
13084 Roo.PagingToolbar = function(el, ds, config)
13085 {
13086     // old args format still supported... - xtype is prefered..
13087     if (typeof(el) == 'object' && el.xtype) {
13088         // created from xtype...
13089         config = el;
13090         ds = el.dataSource;
13091         el = config.container;
13092     }
13093     var items = [];
13094     if (config.items) {
13095         items = config.items;
13096         config.items = [];
13097     }
13098     
13099     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
13100     this.ds = ds;
13101     this.cursor = 0;
13102     this.renderButtons(this.el);
13103     this.bind(ds);
13104     
13105     // supprot items array.
13106    
13107     Roo.each(items, function(e) {
13108         this.add(Roo.factory(e));
13109     },this);
13110     
13111 };
13112
13113 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
13114     /**
13115      * @cfg {Roo.data.Store} dataSource
13116      * The underlying data store providing the paged data
13117      */
13118     /**
13119      * @cfg {String/HTMLElement/Element} container
13120      * container The id or element that will contain the toolbar
13121      */
13122     /**
13123      * @cfg {Boolean} displayInfo
13124      * True to display the displayMsg (defaults to false)
13125      */
13126     /**
13127      * @cfg {Number} pageSize
13128      * The number of records to display per page (defaults to 20)
13129      */
13130     pageSize: 20,
13131     /**
13132      * @cfg {String} displayMsg
13133      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
13134      */
13135     displayMsg : 'Displaying {0} - {1} of {2}',
13136     /**
13137      * @cfg {String} emptyMsg
13138      * The message to display when no records are found (defaults to "No data to display")
13139      */
13140     emptyMsg : 'No data to display',
13141     /**
13142      * Customizable piece of the default paging text (defaults to "Page")
13143      * @type String
13144      */
13145     beforePageText : "Page",
13146     /**
13147      * Customizable piece of the default paging text (defaults to "of %0")
13148      * @type String
13149      */
13150     afterPageText : "of {0}",
13151     /**
13152      * Customizable piece of the default paging text (defaults to "First Page")
13153      * @type String
13154      */
13155     firstText : "First Page",
13156     /**
13157      * Customizable piece of the default paging text (defaults to "Previous Page")
13158      * @type String
13159      */
13160     prevText : "Previous Page",
13161     /**
13162      * Customizable piece of the default paging text (defaults to "Next Page")
13163      * @type String
13164      */
13165     nextText : "Next Page",
13166     /**
13167      * Customizable piece of the default paging text (defaults to "Last Page")
13168      * @type String
13169      */
13170     lastText : "Last Page",
13171     /**
13172      * Customizable piece of the default paging text (defaults to "Refresh")
13173      * @type String
13174      */
13175     refreshText : "Refresh",
13176
13177     // private
13178     renderButtons : function(el){
13179         Roo.PagingToolbar.superclass.render.call(this, el);
13180         this.first = this.addButton({
13181             tooltip: this.firstText,
13182             cls: "x-btn-icon x-grid-page-first",
13183             disabled: true,
13184             handler: this.onClick.createDelegate(this, ["first"])
13185         });
13186         this.prev = this.addButton({
13187             tooltip: this.prevText,
13188             cls: "x-btn-icon x-grid-page-prev",
13189             disabled: true,
13190             handler: this.onClick.createDelegate(this, ["prev"])
13191         });
13192         //this.addSeparator();
13193         this.add(this.beforePageText);
13194         this.field = Roo.get(this.addDom({
13195            tag: "input",
13196            type: "text",
13197            size: "3",
13198            value: "1",
13199            cls: "x-grid-page-number"
13200         }).el);
13201         this.field.on("keydown", this.onPagingKeydown, this);
13202         this.field.on("focus", function(){this.dom.select();});
13203         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
13204         this.field.setHeight(18);
13205         //this.addSeparator();
13206         this.next = this.addButton({
13207             tooltip: this.nextText,
13208             cls: "x-btn-icon x-grid-page-next",
13209             disabled: true,
13210             handler: this.onClick.createDelegate(this, ["next"])
13211         });
13212         this.last = this.addButton({
13213             tooltip: this.lastText,
13214             cls: "x-btn-icon x-grid-page-last",
13215             disabled: true,
13216             handler: this.onClick.createDelegate(this, ["last"])
13217         });
13218         //this.addSeparator();
13219         this.loading = this.addButton({
13220             tooltip: this.refreshText,
13221             cls: "x-btn-icon x-grid-loading",
13222             handler: this.onClick.createDelegate(this, ["refresh"])
13223         });
13224
13225         if(this.displayInfo){
13226             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
13227         }
13228     },
13229
13230     // private
13231     updateInfo : function(){
13232         if(this.displayEl){
13233             var count = this.ds.getCount();
13234             var msg = count == 0 ?
13235                 this.emptyMsg :
13236                 String.format(
13237                     this.displayMsg,
13238                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
13239                 );
13240             this.displayEl.update(msg);
13241         }
13242     },
13243
13244     // private
13245     onLoad : function(ds, r, o){
13246        this.cursor = o.params ? o.params.start : 0;
13247        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
13248
13249        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
13250        this.field.dom.value = ap;
13251        this.first.setDisabled(ap == 1);
13252        this.prev.setDisabled(ap == 1);
13253        this.next.setDisabled(ap == ps);
13254        this.last.setDisabled(ap == ps);
13255        this.loading.enable();
13256        this.updateInfo();
13257     },
13258
13259     // private
13260     getPageData : function(){
13261         var total = this.ds.getTotalCount();
13262         return {
13263             total : total,
13264             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
13265             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
13266         };
13267     },
13268
13269     // private
13270     onLoadError : function(){
13271         this.loading.enable();
13272     },
13273
13274     // private
13275     onPagingKeydown : function(e){
13276         var k = e.getKey();
13277         var d = this.getPageData();
13278         if(k == e.RETURN){
13279             var v = this.field.dom.value, pageNum;
13280             if(!v || isNaN(pageNum = parseInt(v, 10))){
13281                 this.field.dom.value = d.activePage;
13282                 return;
13283             }
13284             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
13285             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13286             e.stopEvent();
13287         }
13288         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))
13289         {
13290           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
13291           this.field.dom.value = pageNum;
13292           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
13293           e.stopEvent();
13294         }
13295         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13296         {
13297           var v = this.field.dom.value, pageNum; 
13298           var increment = (e.shiftKey) ? 10 : 1;
13299           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13300             increment *= -1;
13301           if(!v || isNaN(pageNum = parseInt(v, 10))) {
13302             this.field.dom.value = d.activePage;
13303             return;
13304           }
13305           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
13306           {
13307             this.field.dom.value = parseInt(v, 10) + increment;
13308             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
13309             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13310           }
13311           e.stopEvent();
13312         }
13313     },
13314
13315     // private
13316     beforeLoad : function(){
13317         if(this.loading){
13318             this.loading.disable();
13319         }
13320     },
13321
13322     // private
13323     onClick : function(which){
13324         var ds = this.ds;
13325         switch(which){
13326             case "first":
13327                 ds.load({params:{start: 0, limit: this.pageSize}});
13328             break;
13329             case "prev":
13330                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
13331             break;
13332             case "next":
13333                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
13334             break;
13335             case "last":
13336                 var total = ds.getTotalCount();
13337                 var extra = total % this.pageSize;
13338                 var lastStart = extra ? (total - extra) : total-this.pageSize;
13339                 ds.load({params:{start: lastStart, limit: this.pageSize}});
13340             break;
13341             case "refresh":
13342                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
13343             break;
13344         }
13345     },
13346
13347     /**
13348      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
13349      * @param {Roo.data.Store} store The data store to unbind
13350      */
13351     unbind : function(ds){
13352         ds.un("beforeload", this.beforeLoad, this);
13353         ds.un("load", this.onLoad, this);
13354         ds.un("loadexception", this.onLoadError, this);
13355         ds.un("remove", this.updateInfo, this);
13356         ds.un("add", this.updateInfo, this);
13357         this.ds = undefined;
13358     },
13359
13360     /**
13361      * Binds the paging toolbar to the specified {@link Roo.data.Store}
13362      * @param {Roo.data.Store} store The data store to bind
13363      */
13364     bind : function(ds){
13365         ds.on("beforeload", this.beforeLoad, this);
13366         ds.on("load", this.onLoad, this);
13367         ds.on("loadexception", this.onLoadError, this);
13368         ds.on("remove", this.updateInfo, this);
13369         ds.on("add", this.updateInfo, this);
13370         this.ds = ds;
13371     }
13372 });/*
13373  * Based on:
13374  * Ext JS Library 1.1.1
13375  * Copyright(c) 2006-2007, Ext JS, LLC.
13376  *
13377  * Originally Released Under LGPL - original licence link has changed is not relivant.
13378  *
13379  * Fork - LGPL
13380  * <script type="text/javascript">
13381  */
13382
13383 /**
13384  * @class Roo.Resizable
13385  * @extends Roo.util.Observable
13386  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
13387  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
13388  * 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
13389  * the element will be wrapped for you automatically.</p>
13390  * <p>Here is the list of valid resize handles:</p>
13391  * <pre>
13392 Value   Description
13393 ------  -------------------
13394  'n'     north
13395  's'     south
13396  'e'     east
13397  'w'     west
13398  'nw'    northwest
13399  'sw'    southwest
13400  'se'    southeast
13401  'ne'    northeast
13402  'hd'    horizontal drag
13403  'all'   all
13404 </pre>
13405  * <p>Here's an example showing the creation of a typical Resizable:</p>
13406  * <pre><code>
13407 var resizer = new Roo.Resizable("element-id", {
13408     handles: 'all',
13409     minWidth: 200,
13410     minHeight: 100,
13411     maxWidth: 500,
13412     maxHeight: 400,
13413     pinned: true
13414 });
13415 resizer.on("resize", myHandler);
13416 </code></pre>
13417  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
13418  * resizer.east.setDisplayed(false);</p>
13419  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
13420  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
13421  * resize operation's new size (defaults to [0, 0])
13422  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
13423  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
13424  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
13425  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
13426  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
13427  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
13428  * @cfg {Number} width The width of the element in pixels (defaults to null)
13429  * @cfg {Number} height The height of the element in pixels (defaults to null)
13430  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
13431  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
13432  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
13433  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
13434  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
13435  * in favor of the handles config option (defaults to false)
13436  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
13437  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
13438  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
13439  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
13440  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
13441  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
13442  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
13443  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
13444  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
13445  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
13446  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
13447  * @constructor
13448  * Create a new resizable component
13449  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
13450  * @param {Object} config configuration options
13451   */
13452 Roo.Resizable = function(el, config)
13453 {
13454     this.el = Roo.get(el);
13455
13456     if(config && config.wrap){
13457         config.resizeChild = this.el;
13458         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
13459         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
13460         this.el.setStyle("overflow", "hidden");
13461         this.el.setPositioning(config.resizeChild.getPositioning());
13462         config.resizeChild.clearPositioning();
13463         if(!config.width || !config.height){
13464             var csize = config.resizeChild.getSize();
13465             this.el.setSize(csize.width, csize.height);
13466         }
13467         if(config.pinned && !config.adjustments){
13468             config.adjustments = "auto";
13469         }
13470     }
13471
13472     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
13473     this.proxy.unselectable();
13474     this.proxy.enableDisplayMode('block');
13475
13476     Roo.apply(this, config);
13477
13478     if(this.pinned){
13479         this.disableTrackOver = true;
13480         this.el.addClass("x-resizable-pinned");
13481     }
13482     // if the element isn't positioned, make it relative
13483     var position = this.el.getStyle("position");
13484     if(position != "absolute" && position != "fixed"){
13485         this.el.setStyle("position", "relative");
13486     }
13487     if(!this.handles){ // no handles passed, must be legacy style
13488         this.handles = 's,e,se';
13489         if(this.multiDirectional){
13490             this.handles += ',n,w';
13491         }
13492     }
13493     if(this.handles == "all"){
13494         this.handles = "n s e w ne nw se sw";
13495     }
13496     var hs = this.handles.split(/\s*?[,;]\s*?| /);
13497     var ps = Roo.Resizable.positions;
13498     for(var i = 0, len = hs.length; i < len; i++){
13499         if(hs[i] && ps[hs[i]]){
13500             var pos = ps[hs[i]];
13501             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
13502         }
13503     }
13504     // legacy
13505     this.corner = this.southeast;
13506     
13507     // updateBox = the box can move..
13508     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
13509         this.updateBox = true;
13510     }
13511
13512     this.activeHandle = null;
13513
13514     if(this.resizeChild){
13515         if(typeof this.resizeChild == "boolean"){
13516             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
13517         }else{
13518             this.resizeChild = Roo.get(this.resizeChild, true);
13519         }
13520     }
13521     
13522     if(this.adjustments == "auto"){
13523         var rc = this.resizeChild;
13524         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
13525         if(rc && (hw || hn)){
13526             rc.position("relative");
13527             rc.setLeft(hw ? hw.el.getWidth() : 0);
13528             rc.setTop(hn ? hn.el.getHeight() : 0);
13529         }
13530         this.adjustments = [
13531             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
13532             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
13533         ];
13534     }
13535
13536     if(this.draggable){
13537         this.dd = this.dynamic ?
13538             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
13539         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
13540     }
13541
13542     // public events
13543     this.addEvents({
13544         /**
13545          * @event beforeresize
13546          * Fired before resize is allowed. Set enabled to false to cancel resize.
13547          * @param {Roo.Resizable} this
13548          * @param {Roo.EventObject} e The mousedown event
13549          */
13550         "beforeresize" : true,
13551         /**
13552          * @event resizing
13553          * Fired a resizing.
13554          * @param {Roo.Resizable} this
13555          * @param {Number} x The new x position
13556          * @param {Number} y The new y position
13557          * @param {Number} w The new w width
13558          * @param {Number} h The new h hight
13559          * @param {Roo.EventObject} e The mouseup event
13560          */
13561         "resizing" : true,
13562         /**
13563          * @event resize
13564          * Fired after a resize.
13565          * @param {Roo.Resizable} this
13566          * @param {Number} width The new width
13567          * @param {Number} height The new height
13568          * @param {Roo.EventObject} e The mouseup event
13569          */
13570         "resize" : true
13571     });
13572
13573     if(this.width !== null && this.height !== null){
13574         this.resizeTo(this.width, this.height);
13575     }else{
13576         this.updateChildSize();
13577     }
13578     if(Roo.isIE){
13579         this.el.dom.style.zoom = 1;
13580     }
13581     Roo.Resizable.superclass.constructor.call(this);
13582 };
13583
13584 Roo.extend(Roo.Resizable, Roo.util.Observable, {
13585         resizeChild : false,
13586         adjustments : [0, 0],
13587         minWidth : 5,
13588         minHeight : 5,
13589         maxWidth : 10000,
13590         maxHeight : 10000,
13591         enabled : true,
13592         animate : false,
13593         duration : .35,
13594         dynamic : false,
13595         handles : false,
13596         multiDirectional : false,
13597         disableTrackOver : false,
13598         easing : 'easeOutStrong',
13599         widthIncrement : 0,
13600         heightIncrement : 0,
13601         pinned : false,
13602         width : null,
13603         height : null,
13604         preserveRatio : false,
13605         transparent: false,
13606         minX: 0,
13607         minY: 0,
13608         draggable: false,
13609
13610         /**
13611          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
13612          */
13613         constrainTo: undefined,
13614         /**
13615          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
13616          */
13617         resizeRegion: undefined,
13618
13619
13620     /**
13621      * Perform a manual resize
13622      * @param {Number} width
13623      * @param {Number} height
13624      */
13625     resizeTo : function(width, height){
13626         this.el.setSize(width, height);
13627         this.updateChildSize();
13628         this.fireEvent("resize", this, width, height, null);
13629     },
13630
13631     // private
13632     startSizing : function(e, handle){
13633         this.fireEvent("beforeresize", this, e);
13634         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
13635
13636             if(!this.overlay){
13637                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
13638                 this.overlay.unselectable();
13639                 this.overlay.enableDisplayMode("block");
13640                 this.overlay.on("mousemove", this.onMouseMove, this);
13641                 this.overlay.on("mouseup", this.onMouseUp, this);
13642             }
13643             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
13644
13645             this.resizing = true;
13646             this.startBox = this.el.getBox();
13647             this.startPoint = e.getXY();
13648             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
13649                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
13650
13651             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13652             this.overlay.show();
13653
13654             if(this.constrainTo) {
13655                 var ct = Roo.get(this.constrainTo);
13656                 this.resizeRegion = ct.getRegion().adjust(
13657                     ct.getFrameWidth('t'),
13658                     ct.getFrameWidth('l'),
13659                     -ct.getFrameWidth('b'),
13660                     -ct.getFrameWidth('r')
13661                 );
13662             }
13663
13664             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
13665             this.proxy.show();
13666             this.proxy.setBox(this.startBox);
13667             if(!this.dynamic){
13668                 this.proxy.setStyle('visibility', 'visible');
13669             }
13670         }
13671     },
13672
13673     // private
13674     onMouseDown : function(handle, e){
13675         if(this.enabled){
13676             e.stopEvent();
13677             this.activeHandle = handle;
13678             this.startSizing(e, handle);
13679         }
13680     },
13681
13682     // private
13683     onMouseUp : function(e){
13684         var size = this.resizeElement();
13685         this.resizing = false;
13686         this.handleOut();
13687         this.overlay.hide();
13688         this.proxy.hide();
13689         this.fireEvent("resize", this, size.width, size.height, e);
13690     },
13691
13692     // private
13693     updateChildSize : function(){
13694         
13695         if(this.resizeChild){
13696             var el = this.el;
13697             var child = this.resizeChild;
13698             var adj = this.adjustments;
13699             if(el.dom.offsetWidth){
13700                 var b = el.getSize(true);
13701                 child.setSize(b.width+adj[0], b.height+adj[1]);
13702             }
13703             // Second call here for IE
13704             // The first call enables instant resizing and
13705             // the second call corrects scroll bars if they
13706             // exist
13707             if(Roo.isIE){
13708                 setTimeout(function(){
13709                     if(el.dom.offsetWidth){
13710                         var b = el.getSize(true);
13711                         child.setSize(b.width+adj[0], b.height+adj[1]);
13712                     }
13713                 }, 10);
13714             }
13715         }
13716     },
13717
13718     // private
13719     snap : function(value, inc, min){
13720         if(!inc || !value) return value;
13721         var newValue = value;
13722         var m = value % inc;
13723         if(m > 0){
13724             if(m > (inc/2)){
13725                 newValue = value + (inc-m);
13726             }else{
13727                 newValue = value - m;
13728             }
13729         }
13730         return Math.max(min, newValue);
13731     },
13732
13733     // private
13734     resizeElement : function(){
13735         var box = this.proxy.getBox();
13736         if(this.updateBox){
13737             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
13738         }else{
13739             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
13740         }
13741         this.updateChildSize();
13742         if(!this.dynamic){
13743             this.proxy.hide();
13744         }
13745         return box;
13746     },
13747
13748     // private
13749     constrain : function(v, diff, m, mx){
13750         if(v - diff < m){
13751             diff = v - m;
13752         }else if(v - diff > mx){
13753             diff = mx - v;
13754         }
13755         return diff;
13756     },
13757
13758     // private
13759     onMouseMove : function(e){
13760         
13761         if(this.enabled){
13762             try{// try catch so if something goes wrong the user doesn't get hung
13763
13764             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
13765                 return;
13766             }
13767
13768             //var curXY = this.startPoint;
13769             var curSize = this.curSize || this.startBox;
13770             var x = this.startBox.x, y = this.startBox.y;
13771             var ox = x, oy = y;
13772             var w = curSize.width, h = curSize.height;
13773             var ow = w, oh = h;
13774             var mw = this.minWidth, mh = this.minHeight;
13775             var mxw = this.maxWidth, mxh = this.maxHeight;
13776             var wi = this.widthIncrement;
13777             var hi = this.heightIncrement;
13778
13779             var eventXY = e.getXY();
13780             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
13781             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
13782
13783             var pos = this.activeHandle.position;
13784
13785             switch(pos){
13786                 case "east":
13787                     w += diffX;
13788                     w = Math.min(Math.max(mw, w), mxw);
13789                     break;
13790              
13791                 case "south":
13792                     h += diffY;
13793                     h = Math.min(Math.max(mh, h), mxh);
13794                     break;
13795                 case "southeast":
13796                     w += diffX;
13797                     h += diffY;
13798                     w = Math.min(Math.max(mw, w), mxw);
13799                     h = Math.min(Math.max(mh, h), mxh);
13800                     break;
13801                 case "north":
13802                     diffY = this.constrain(h, diffY, mh, mxh);
13803                     y += diffY;
13804                     h -= diffY;
13805                     break;
13806                 case "hdrag":
13807                     
13808                     if (wi) {
13809                         var adiffX = Math.abs(diffX);
13810                         var sub = (adiffX % wi); // how much 
13811                         if (sub > (wi/2)) { // far enough to snap
13812                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
13813                         } else {
13814                             // remove difference.. 
13815                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
13816                         }
13817                     }
13818                     x += diffX;
13819                     x = Math.max(this.minX, x);
13820                     break;
13821                 case "west":
13822                     diffX = this.constrain(w, diffX, mw, mxw);
13823                     x += diffX;
13824                     w -= diffX;
13825                     break;
13826                 case "northeast":
13827                     w += diffX;
13828                     w = Math.min(Math.max(mw, w), mxw);
13829                     diffY = this.constrain(h, diffY, mh, mxh);
13830                     y += diffY;
13831                     h -= diffY;
13832                     break;
13833                 case "northwest":
13834                     diffX = this.constrain(w, diffX, mw, mxw);
13835                     diffY = this.constrain(h, diffY, mh, mxh);
13836                     y += diffY;
13837                     h -= diffY;
13838                     x += diffX;
13839                     w -= diffX;
13840                     break;
13841                case "southwest":
13842                     diffX = this.constrain(w, diffX, mw, mxw);
13843                     h += diffY;
13844                     h = Math.min(Math.max(mh, h), mxh);
13845                     x += diffX;
13846                     w -= diffX;
13847                     break;
13848             }
13849
13850             var sw = this.snap(w, wi, mw);
13851             var sh = this.snap(h, hi, mh);
13852             if(sw != w || sh != h){
13853                 switch(pos){
13854                     case "northeast":
13855                         y -= sh - h;
13856                     break;
13857                     case "north":
13858                         y -= sh - h;
13859                         break;
13860                     case "southwest":
13861                         x -= sw - w;
13862                     break;
13863                     case "west":
13864                         x -= sw - w;
13865                         break;
13866                     case "northwest":
13867                         x -= sw - w;
13868                         y -= sh - h;
13869                     break;
13870                 }
13871                 w = sw;
13872                 h = sh;
13873             }
13874
13875             if(this.preserveRatio){
13876                 switch(pos){
13877                     case "southeast":
13878                     case "east":
13879                         h = oh * (w/ow);
13880                         h = Math.min(Math.max(mh, h), mxh);
13881                         w = ow * (h/oh);
13882                        break;
13883                     case "south":
13884                         w = ow * (h/oh);
13885                         w = Math.min(Math.max(mw, w), mxw);
13886                         h = oh * (w/ow);
13887                         break;
13888                     case "northeast":
13889                         w = ow * (h/oh);
13890                         w = Math.min(Math.max(mw, w), mxw);
13891                         h = oh * (w/ow);
13892                     break;
13893                     case "north":
13894                         var tw = w;
13895                         w = ow * (h/oh);
13896                         w = Math.min(Math.max(mw, w), mxw);
13897                         h = oh * (w/ow);
13898                         x += (tw - w) / 2;
13899                         break;
13900                     case "southwest":
13901                         h = oh * (w/ow);
13902                         h = Math.min(Math.max(mh, h), mxh);
13903                         var tw = w;
13904                         w = ow * (h/oh);
13905                         x += tw - w;
13906                         break;
13907                     case "west":
13908                         var th = h;
13909                         h = oh * (w/ow);
13910                         h = Math.min(Math.max(mh, h), mxh);
13911                         y += (th - h) / 2;
13912                         var tw = w;
13913                         w = ow * (h/oh);
13914                         x += tw - w;
13915                        break;
13916                     case "northwest":
13917                         var tw = w;
13918                         var th = h;
13919                         h = oh * (w/ow);
13920                         h = Math.min(Math.max(mh, h), mxh);
13921                         w = ow * (h/oh);
13922                         y += th - h;
13923                         x += tw - w;
13924                        break;
13925
13926                 }
13927             }
13928             if (pos == 'hdrag') {
13929                 w = ow;
13930             }
13931             this.proxy.setBounds(x, y, w, h);
13932             if(this.dynamic){
13933                 this.resizeElement();
13934             }
13935             }catch(e){}
13936         }
13937         this.fireEvent("resizing", this, x, y, w, h, e);
13938     },
13939
13940     // private
13941     handleOver : function(){
13942         if(this.enabled){
13943             this.el.addClass("x-resizable-over");
13944         }
13945     },
13946
13947     // private
13948     handleOut : function(){
13949         if(!this.resizing){
13950             this.el.removeClass("x-resizable-over");
13951         }
13952     },
13953
13954     /**
13955      * Returns the element this component is bound to.
13956      * @return {Roo.Element}
13957      */
13958     getEl : function(){
13959         return this.el;
13960     },
13961
13962     /**
13963      * Returns the resizeChild element (or null).
13964      * @return {Roo.Element}
13965      */
13966     getResizeChild : function(){
13967         return this.resizeChild;
13968     },
13969     groupHandler : function()
13970     {
13971         
13972     },
13973     /**
13974      * Destroys this resizable. If the element was wrapped and
13975      * removeEl is not true then the element remains.
13976      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
13977      */
13978     destroy : function(removeEl){
13979         this.proxy.remove();
13980         if(this.overlay){
13981             this.overlay.removeAllListeners();
13982             this.overlay.remove();
13983         }
13984         var ps = Roo.Resizable.positions;
13985         for(var k in ps){
13986             if(typeof ps[k] != "function" && this[ps[k]]){
13987                 var h = this[ps[k]];
13988                 h.el.removeAllListeners();
13989                 h.el.remove();
13990             }
13991         }
13992         if(removeEl){
13993             this.el.update("");
13994             this.el.remove();
13995         }
13996     }
13997 });
13998
13999 // private
14000 // hash to map config positions to true positions
14001 Roo.Resizable.positions = {
14002     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
14003     hd: "hdrag"
14004 };
14005
14006 // private
14007 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
14008     if(!this.tpl){
14009         // only initialize the template if resizable is used
14010         var tpl = Roo.DomHelper.createTemplate(
14011             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
14012         );
14013         tpl.compile();
14014         Roo.Resizable.Handle.prototype.tpl = tpl;
14015     }
14016     this.position = pos;
14017     this.rz = rz;
14018     // show north drag fro topdra
14019     var handlepos = pos == 'hdrag' ? 'north' : pos;
14020     
14021     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
14022     if (pos == 'hdrag') {
14023         this.el.setStyle('cursor', 'pointer');
14024     }
14025     this.el.unselectable();
14026     if(transparent){
14027         this.el.setOpacity(0);
14028     }
14029     this.el.on("mousedown", this.onMouseDown, this);
14030     if(!disableTrackOver){
14031         this.el.on("mouseover", this.onMouseOver, this);
14032         this.el.on("mouseout", this.onMouseOut, this);
14033     }
14034 };
14035
14036 // private
14037 Roo.Resizable.Handle.prototype = {
14038     afterResize : function(rz){
14039         // do nothing
14040     },
14041     // private
14042     onMouseDown : function(e){
14043         this.rz.onMouseDown(this, e);
14044     },
14045     // private
14046     onMouseOver : function(e){
14047         this.rz.handleOver(this, e);
14048     },
14049     // private
14050     onMouseOut : function(e){
14051         this.rz.handleOut(this, e);
14052     }
14053 };/*
14054  * Based on:
14055  * Ext JS Library 1.1.1
14056  * Copyright(c) 2006-2007, Ext JS, LLC.
14057  *
14058  * Originally Released Under LGPL - original licence link has changed is not relivant.
14059  *
14060  * Fork - LGPL
14061  * <script type="text/javascript">
14062  */
14063
14064 /**
14065  * @class Roo.Editor
14066  * @extends Roo.Component
14067  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
14068  * @constructor
14069  * Create a new Editor
14070  * @param {Roo.form.Field} field The Field object (or descendant)
14071  * @param {Object} config The config object
14072  */
14073 Roo.Editor = function(field, config){
14074     Roo.Editor.superclass.constructor.call(this, config);
14075     this.field = field;
14076     this.addEvents({
14077         /**
14078              * @event beforestartedit
14079              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
14080              * false from the handler of this event.
14081              * @param {Editor} this
14082              * @param {Roo.Element} boundEl The underlying element bound to this editor
14083              * @param {Mixed} value The field value being set
14084              */
14085         "beforestartedit" : true,
14086         /**
14087              * @event startedit
14088              * Fires when this editor is displayed
14089              * @param {Roo.Element} boundEl The underlying element bound to this editor
14090              * @param {Mixed} value The starting field value
14091              */
14092         "startedit" : true,
14093         /**
14094              * @event beforecomplete
14095              * Fires after a change has been made to the field, but before the change is reflected in the underlying
14096              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
14097              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
14098              * event will not fire since no edit actually occurred.
14099              * @param {Editor} this
14100              * @param {Mixed} value The current field value
14101              * @param {Mixed} startValue The original field value
14102              */
14103         "beforecomplete" : true,
14104         /**
14105              * @event complete
14106              * Fires after editing is complete and any changed value has been written to the underlying field.
14107              * @param {Editor} this
14108              * @param {Mixed} value The current field value
14109              * @param {Mixed} startValue The original field value
14110              */
14111         "complete" : true,
14112         /**
14113          * @event specialkey
14114          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
14115          * {@link Roo.EventObject#getKey} to determine which key was pressed.
14116          * @param {Roo.form.Field} this
14117          * @param {Roo.EventObject} e The event object
14118          */
14119         "specialkey" : true
14120     });
14121 };
14122
14123 Roo.extend(Roo.Editor, Roo.Component, {
14124     /**
14125      * @cfg {Boolean/String} autosize
14126      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
14127      * or "height" to adopt the height only (defaults to false)
14128      */
14129     /**
14130      * @cfg {Boolean} revertInvalid
14131      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
14132      * validation fails (defaults to true)
14133      */
14134     /**
14135      * @cfg {Boolean} ignoreNoChange
14136      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
14137      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
14138      * will never be ignored.
14139      */
14140     /**
14141      * @cfg {Boolean} hideEl
14142      * False to keep the bound element visible while the editor is displayed (defaults to true)
14143      */
14144     /**
14145      * @cfg {Mixed} value
14146      * The data value of the underlying field (defaults to "")
14147      */
14148     value : "",
14149     /**
14150      * @cfg {String} alignment
14151      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
14152      */
14153     alignment: "c-c?",
14154     /**
14155      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
14156      * for bottom-right shadow (defaults to "frame")
14157      */
14158     shadow : "frame",
14159     /**
14160      * @cfg {Boolean} constrain True to constrain the editor to the viewport
14161      */
14162     constrain : false,
14163     /**
14164      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
14165      */
14166     completeOnEnter : false,
14167     /**
14168      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
14169      */
14170     cancelOnEsc : false,
14171     /**
14172      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
14173      */
14174     updateEl : false,
14175
14176     // private
14177     onRender : function(ct, position){
14178         this.el = new Roo.Layer({
14179             shadow: this.shadow,
14180             cls: "x-editor",
14181             parentEl : ct,
14182             shim : this.shim,
14183             shadowOffset:4,
14184             id: this.id,
14185             constrain: this.constrain
14186         });
14187         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
14188         if(this.field.msgTarget != 'title'){
14189             this.field.msgTarget = 'qtip';
14190         }
14191         this.field.render(this.el);
14192         if(Roo.isGecko){
14193             this.field.el.dom.setAttribute('autocomplete', 'off');
14194         }
14195         this.field.on("specialkey", this.onSpecialKey, this);
14196         if(this.swallowKeys){
14197             this.field.el.swallowEvent(['keydown','keypress']);
14198         }
14199         this.field.show();
14200         this.field.on("blur", this.onBlur, this);
14201         if(this.field.grow){
14202             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
14203         }
14204     },
14205
14206     onSpecialKey : function(field, e)
14207     {
14208         //Roo.log('editor onSpecialKey');
14209         if(this.completeOnEnter && e.getKey() == e.ENTER){
14210             e.stopEvent();
14211             this.completeEdit();
14212             return;
14213         }
14214         // do not fire special key otherwise it might hide close the editor...
14215         if(e.getKey() == e.ENTER){    
14216             return;
14217         }
14218         if(this.cancelOnEsc && e.getKey() == e.ESC){
14219             this.cancelEdit();
14220             return;
14221         } 
14222         this.fireEvent('specialkey', field, e);
14223     
14224     },
14225
14226     /**
14227      * Starts the editing process and shows the editor.
14228      * @param {String/HTMLElement/Element} el The element to edit
14229      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
14230       * to the innerHTML of el.
14231      */
14232     startEdit : function(el, value){
14233         if(this.editing){
14234             this.completeEdit();
14235         }
14236         this.boundEl = Roo.get(el);
14237         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
14238         if(!this.rendered){
14239             this.render(this.parentEl || document.body);
14240         }
14241         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
14242             return;
14243         }
14244         this.startValue = v;
14245         this.field.setValue(v);
14246         if(this.autoSize){
14247             var sz = this.boundEl.getSize();
14248             switch(this.autoSize){
14249                 case "width":
14250                 this.setSize(sz.width,  "");
14251                 break;
14252                 case "height":
14253                 this.setSize("",  sz.height);
14254                 break;
14255                 default:
14256                 this.setSize(sz.width,  sz.height);
14257             }
14258         }
14259         this.el.alignTo(this.boundEl, this.alignment);
14260         this.editing = true;
14261         if(Roo.QuickTips){
14262             Roo.QuickTips.disable();
14263         }
14264         this.show();
14265     },
14266
14267     /**
14268      * Sets the height and width of this editor.
14269      * @param {Number} width The new width
14270      * @param {Number} height The new height
14271      */
14272     setSize : function(w, h){
14273         this.field.setSize(w, h);
14274         if(this.el){
14275             this.el.sync();
14276         }
14277     },
14278
14279     /**
14280      * Realigns the editor to the bound field based on the current alignment config value.
14281      */
14282     realign : function(){
14283         this.el.alignTo(this.boundEl, this.alignment);
14284     },
14285
14286     /**
14287      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
14288      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
14289      */
14290     completeEdit : function(remainVisible){
14291         if(!this.editing){
14292             return;
14293         }
14294         var v = this.getValue();
14295         if(this.revertInvalid !== false && !this.field.isValid()){
14296             v = this.startValue;
14297             this.cancelEdit(true);
14298         }
14299         if(String(v) === String(this.startValue) && this.ignoreNoChange){
14300             this.editing = false;
14301             this.hide();
14302             return;
14303         }
14304         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
14305             this.editing = false;
14306             if(this.updateEl && this.boundEl){
14307                 this.boundEl.update(v);
14308             }
14309             if(remainVisible !== true){
14310                 this.hide();
14311             }
14312             this.fireEvent("complete", this, v, this.startValue);
14313         }
14314     },
14315
14316     // private
14317     onShow : function(){
14318         this.el.show();
14319         if(this.hideEl !== false){
14320             this.boundEl.hide();
14321         }
14322         this.field.show();
14323         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
14324             this.fixIEFocus = true;
14325             this.deferredFocus.defer(50, this);
14326         }else{
14327             this.field.focus();
14328         }
14329         this.fireEvent("startedit", this.boundEl, this.startValue);
14330     },
14331
14332     deferredFocus : function(){
14333         if(this.editing){
14334             this.field.focus();
14335         }
14336     },
14337
14338     /**
14339      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
14340      * reverted to the original starting value.
14341      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
14342      * cancel (defaults to false)
14343      */
14344     cancelEdit : function(remainVisible){
14345         if(this.editing){
14346             this.setValue(this.startValue);
14347             if(remainVisible !== true){
14348                 this.hide();
14349             }
14350         }
14351     },
14352
14353     // private
14354     onBlur : function(){
14355         if(this.allowBlur !== true && this.editing){
14356             this.completeEdit();
14357         }
14358     },
14359
14360     // private
14361     onHide : function(){
14362         if(this.editing){
14363             this.completeEdit();
14364             return;
14365         }
14366         this.field.blur();
14367         if(this.field.collapse){
14368             this.field.collapse();
14369         }
14370         this.el.hide();
14371         if(this.hideEl !== false){
14372             this.boundEl.show();
14373         }
14374         if(Roo.QuickTips){
14375             Roo.QuickTips.enable();
14376         }
14377     },
14378
14379     /**
14380      * Sets the data value of the editor
14381      * @param {Mixed} value Any valid value supported by the underlying field
14382      */
14383     setValue : function(v){
14384         this.field.setValue(v);
14385     },
14386
14387     /**
14388      * Gets the data value of the editor
14389      * @return {Mixed} The data value
14390      */
14391     getValue : function(){
14392         return this.field.getValue();
14393     }
14394 });/*
14395  * Based on:
14396  * Ext JS Library 1.1.1
14397  * Copyright(c) 2006-2007, Ext JS, LLC.
14398  *
14399  * Originally Released Under LGPL - original licence link has changed is not relivant.
14400  *
14401  * Fork - LGPL
14402  * <script type="text/javascript">
14403  */
14404  
14405 /**
14406  * @class Roo.BasicDialog
14407  * @extends Roo.util.Observable
14408  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
14409  * <pre><code>
14410 var dlg = new Roo.BasicDialog("my-dlg", {
14411     height: 200,
14412     width: 300,
14413     minHeight: 100,
14414     minWidth: 150,
14415     modal: true,
14416     proxyDrag: true,
14417     shadow: true
14418 });
14419 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
14420 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
14421 dlg.addButton('Cancel', dlg.hide, dlg);
14422 dlg.show();
14423 </code></pre>
14424   <b>A Dialog should always be a direct child of the body element.</b>
14425  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
14426  * @cfg {String} title Default text to display in the title bar (defaults to null)
14427  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14428  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14429  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
14430  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
14431  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
14432  * (defaults to null with no animation)
14433  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
14434  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
14435  * property for valid values (defaults to 'all')
14436  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
14437  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
14438  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
14439  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
14440  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
14441  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
14442  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
14443  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
14444  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
14445  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
14446  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
14447  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
14448  * draggable = true (defaults to false)
14449  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
14450  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14451  * shadow (defaults to false)
14452  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
14453  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
14454  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
14455  * @cfg {Array} buttons Array of buttons
14456  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
14457  * @constructor
14458  * Create a new BasicDialog.
14459  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
14460  * @param {Object} config Configuration options
14461  */
14462 Roo.BasicDialog = function(el, config){
14463     this.el = Roo.get(el);
14464     var dh = Roo.DomHelper;
14465     if(!this.el && config && config.autoCreate){
14466         if(typeof config.autoCreate == "object"){
14467             if(!config.autoCreate.id){
14468                 config.autoCreate.id = el;
14469             }
14470             this.el = dh.append(document.body,
14471                         config.autoCreate, true);
14472         }else{
14473             this.el = dh.append(document.body,
14474                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
14475         }
14476     }
14477     el = this.el;
14478     el.setDisplayed(true);
14479     el.hide = this.hideAction;
14480     this.id = el.id;
14481     el.addClass("x-dlg");
14482
14483     Roo.apply(this, config);
14484
14485     this.proxy = el.createProxy("x-dlg-proxy");
14486     this.proxy.hide = this.hideAction;
14487     this.proxy.setOpacity(.5);
14488     this.proxy.hide();
14489
14490     if(config.width){
14491         el.setWidth(config.width);
14492     }
14493     if(config.height){
14494         el.setHeight(config.height);
14495     }
14496     this.size = el.getSize();
14497     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
14498         this.xy = [config.x,config.y];
14499     }else{
14500         this.xy = el.getCenterXY(true);
14501     }
14502     /** The header element @type Roo.Element */
14503     this.header = el.child("> .x-dlg-hd");
14504     /** The body element @type Roo.Element */
14505     this.body = el.child("> .x-dlg-bd");
14506     /** The footer element @type Roo.Element */
14507     this.footer = el.child("> .x-dlg-ft");
14508
14509     if(!this.header){
14510         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
14511     }
14512     if(!this.body){
14513         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
14514     }
14515
14516     this.header.unselectable();
14517     if(this.title){
14518         this.header.update(this.title);
14519     }
14520     // this element allows the dialog to be focused for keyboard event
14521     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
14522     this.focusEl.swallowEvent("click", true);
14523
14524     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
14525
14526     // wrap the body and footer for special rendering
14527     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
14528     if(this.footer){
14529         this.bwrap.dom.appendChild(this.footer.dom);
14530     }
14531
14532     this.bg = this.el.createChild({
14533         tag: "div", cls:"x-dlg-bg",
14534         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
14535     });
14536     this.centerBg = this.bg.child("div.x-dlg-bg-center");
14537
14538
14539     if(this.autoScroll !== false && !this.autoTabs){
14540         this.body.setStyle("overflow", "auto");
14541     }
14542
14543     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
14544
14545     if(this.closable !== false){
14546         this.el.addClass("x-dlg-closable");
14547         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
14548         this.close.on("click", this.closeClick, this);
14549         this.close.addClassOnOver("x-dlg-close-over");
14550     }
14551     if(this.collapsible !== false){
14552         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
14553         this.collapseBtn.on("click", this.collapseClick, this);
14554         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
14555         this.header.on("dblclick", this.collapseClick, this);
14556     }
14557     if(this.resizable !== false){
14558         this.el.addClass("x-dlg-resizable");
14559         this.resizer = new Roo.Resizable(el, {
14560             minWidth: this.minWidth || 80,
14561             minHeight:this.minHeight || 80,
14562             handles: this.resizeHandles || "all",
14563             pinned: true
14564         });
14565         this.resizer.on("beforeresize", this.beforeResize, this);
14566         this.resizer.on("resize", this.onResize, this);
14567     }
14568     if(this.draggable !== false){
14569         el.addClass("x-dlg-draggable");
14570         if (!this.proxyDrag) {
14571             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
14572         }
14573         else {
14574             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
14575         }
14576         dd.setHandleElId(this.header.id);
14577         dd.endDrag = this.endMove.createDelegate(this);
14578         dd.startDrag = this.startMove.createDelegate(this);
14579         dd.onDrag = this.onDrag.createDelegate(this);
14580         dd.scroll = false;
14581         this.dd = dd;
14582     }
14583     if(this.modal){
14584         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
14585         this.mask.enableDisplayMode("block");
14586         this.mask.hide();
14587         this.el.addClass("x-dlg-modal");
14588     }
14589     if(this.shadow){
14590         this.shadow = new Roo.Shadow({
14591             mode : typeof this.shadow == "string" ? this.shadow : "sides",
14592             offset : this.shadowOffset
14593         });
14594     }else{
14595         this.shadowOffset = 0;
14596     }
14597     if(Roo.useShims && this.shim !== false){
14598         this.shim = this.el.createShim();
14599         this.shim.hide = this.hideAction;
14600         this.shim.hide();
14601     }else{
14602         this.shim = false;
14603     }
14604     if(this.autoTabs){
14605         this.initTabs();
14606     }
14607     if (this.buttons) { 
14608         var bts= this.buttons;
14609         this.buttons = [];
14610         Roo.each(bts, function(b) {
14611             this.addButton(b);
14612         }, this);
14613     }
14614     
14615     
14616     this.addEvents({
14617         /**
14618          * @event keydown
14619          * Fires when a key is pressed
14620          * @param {Roo.BasicDialog} this
14621          * @param {Roo.EventObject} e
14622          */
14623         "keydown" : true,
14624         /**
14625          * @event move
14626          * Fires when this dialog is moved by the user.
14627          * @param {Roo.BasicDialog} this
14628          * @param {Number} x The new page X
14629          * @param {Number} y The new page Y
14630          */
14631         "move" : true,
14632         /**
14633          * @event resize
14634          * Fires when this dialog is resized by the user.
14635          * @param {Roo.BasicDialog} this
14636          * @param {Number} width The new width
14637          * @param {Number} height The new height
14638          */
14639         "resize" : true,
14640         /**
14641          * @event beforehide
14642          * Fires before this dialog is hidden.
14643          * @param {Roo.BasicDialog} this
14644          */
14645         "beforehide" : true,
14646         /**
14647          * @event hide
14648          * Fires when this dialog is hidden.
14649          * @param {Roo.BasicDialog} this
14650          */
14651         "hide" : true,
14652         /**
14653          * @event beforeshow
14654          * Fires before this dialog is shown.
14655          * @param {Roo.BasicDialog} this
14656          */
14657         "beforeshow" : true,
14658         /**
14659          * @event show
14660          * Fires when this dialog is shown.
14661          * @param {Roo.BasicDialog} this
14662          */
14663         "show" : true
14664     });
14665     el.on("keydown", this.onKeyDown, this);
14666     el.on("mousedown", this.toFront, this);
14667     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
14668     this.el.hide();
14669     Roo.DialogManager.register(this);
14670     Roo.BasicDialog.superclass.constructor.call(this);
14671 };
14672
14673 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
14674     shadowOffset: Roo.isIE ? 6 : 5,
14675     minHeight: 80,
14676     minWidth: 200,
14677     minButtonWidth: 75,
14678     defaultButton: null,
14679     buttonAlign: "right",
14680     tabTag: 'div',
14681     firstShow: true,
14682
14683     /**
14684      * Sets the dialog title text
14685      * @param {String} text The title text to display
14686      * @return {Roo.BasicDialog} this
14687      */
14688     setTitle : function(text){
14689         this.header.update(text);
14690         return this;
14691     },
14692
14693     // private
14694     closeClick : function(){
14695         this.hide();
14696     },
14697
14698     // private
14699     collapseClick : function(){
14700         this[this.collapsed ? "expand" : "collapse"]();
14701     },
14702
14703     /**
14704      * Collapses the dialog to its minimized state (only the title bar is visible).
14705      * Equivalent to the user clicking the collapse dialog button.
14706      */
14707     collapse : function(){
14708         if(!this.collapsed){
14709             this.collapsed = true;
14710             this.el.addClass("x-dlg-collapsed");
14711             this.restoreHeight = this.el.getHeight();
14712             this.resizeTo(this.el.getWidth(), this.header.getHeight());
14713         }
14714     },
14715
14716     /**
14717      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
14718      * clicking the expand dialog button.
14719      */
14720     expand : function(){
14721         if(this.collapsed){
14722             this.collapsed = false;
14723             this.el.removeClass("x-dlg-collapsed");
14724             this.resizeTo(this.el.getWidth(), this.restoreHeight);
14725         }
14726     },
14727
14728     /**
14729      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
14730      * @return {Roo.TabPanel} The tabs component
14731      */
14732     initTabs : function(){
14733         var tabs = this.getTabs();
14734         while(tabs.getTab(0)){
14735             tabs.removeTab(0);
14736         }
14737         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
14738             var dom = el.dom;
14739             tabs.addTab(Roo.id(dom), dom.title);
14740             dom.title = "";
14741         });
14742         tabs.activate(0);
14743         return tabs;
14744     },
14745
14746     // private
14747     beforeResize : function(){
14748         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
14749     },
14750
14751     // private
14752     onResize : function(){
14753         this.refreshSize();
14754         this.syncBodyHeight();
14755         this.adjustAssets();
14756         this.focus();
14757         this.fireEvent("resize", this, this.size.width, this.size.height);
14758     },
14759
14760     // private
14761     onKeyDown : function(e){
14762         if(this.isVisible()){
14763             this.fireEvent("keydown", this, e);
14764         }
14765     },
14766
14767     /**
14768      * Resizes the dialog.
14769      * @param {Number} width
14770      * @param {Number} height
14771      * @return {Roo.BasicDialog} this
14772      */
14773     resizeTo : function(width, height){
14774         this.el.setSize(width, height);
14775         this.size = {width: width, height: height};
14776         this.syncBodyHeight();
14777         if(this.fixedcenter){
14778             this.center();
14779         }
14780         if(this.isVisible()){
14781             this.constrainXY();
14782             this.adjustAssets();
14783         }
14784         this.fireEvent("resize", this, width, height);
14785         return this;
14786     },
14787
14788
14789     /**
14790      * Resizes the dialog to fit the specified content size.
14791      * @param {Number} width
14792      * @param {Number} height
14793      * @return {Roo.BasicDialog} this
14794      */
14795     setContentSize : function(w, h){
14796         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
14797         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
14798         //if(!this.el.isBorderBox()){
14799             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
14800             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
14801         //}
14802         if(this.tabs){
14803             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
14804             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
14805         }
14806         this.resizeTo(w, h);
14807         return this;
14808     },
14809
14810     /**
14811      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
14812      * executed in response to a particular key being pressed while the dialog is active.
14813      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
14814      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14815      * @param {Function} fn The function to call
14816      * @param {Object} scope (optional) The scope of the function
14817      * @return {Roo.BasicDialog} this
14818      */
14819     addKeyListener : function(key, fn, scope){
14820         var keyCode, shift, ctrl, alt;
14821         if(typeof key == "object" && !(key instanceof Array)){
14822             keyCode = key["key"];
14823             shift = key["shift"];
14824             ctrl = key["ctrl"];
14825             alt = key["alt"];
14826         }else{
14827             keyCode = key;
14828         }
14829         var handler = function(dlg, e){
14830             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14831                 var k = e.getKey();
14832                 if(keyCode instanceof Array){
14833                     for(var i = 0, len = keyCode.length; i < len; i++){
14834                         if(keyCode[i] == k){
14835                           fn.call(scope || window, dlg, k, e);
14836                           return;
14837                         }
14838                     }
14839                 }else{
14840                     if(k == keyCode){
14841                         fn.call(scope || window, dlg, k, e);
14842                     }
14843                 }
14844             }
14845         };
14846         this.on("keydown", handler);
14847         return this;
14848     },
14849
14850     /**
14851      * Returns the TabPanel component (creates it if it doesn't exist).
14852      * Note: If you wish to simply check for the existence of tabs without creating them,
14853      * check for a null 'tabs' property.
14854      * @return {Roo.TabPanel} The tabs component
14855      */
14856     getTabs : function(){
14857         if(!this.tabs){
14858             this.el.addClass("x-dlg-auto-tabs");
14859             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
14860             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
14861         }
14862         return this.tabs;
14863     },
14864
14865     /**
14866      * Adds a button to the footer section of the dialog.
14867      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
14868      * object or a valid Roo.DomHelper element config
14869      * @param {Function} handler The function called when the button is clicked
14870      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
14871      * @return {Roo.Button} The new button
14872      */
14873     addButton : function(config, handler, scope){
14874         var dh = Roo.DomHelper;
14875         if(!this.footer){
14876             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
14877         }
14878         if(!this.btnContainer){
14879             var tb = this.footer.createChild({
14880
14881                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
14882                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
14883             }, null, true);
14884             this.btnContainer = tb.firstChild.firstChild.firstChild;
14885         }
14886         var bconfig = {
14887             handler: handler,
14888             scope: scope,
14889             minWidth: this.minButtonWidth,
14890             hideParent:true
14891         };
14892         if(typeof config == "string"){
14893             bconfig.text = config;
14894         }else{
14895             if(config.tag){
14896                 bconfig.dhconfig = config;
14897             }else{
14898                 Roo.apply(bconfig, config);
14899             }
14900         }
14901         var fc = false;
14902         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
14903             bconfig.position = Math.max(0, bconfig.position);
14904             fc = this.btnContainer.childNodes[bconfig.position];
14905         }
14906          
14907         var btn = new Roo.Button(
14908             fc ? 
14909                 this.btnContainer.insertBefore(document.createElement("td"),fc)
14910                 : this.btnContainer.appendChild(document.createElement("td")),
14911             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
14912             bconfig
14913         );
14914         this.syncBodyHeight();
14915         if(!this.buttons){
14916             /**
14917              * Array of all the buttons that have been added to this dialog via addButton
14918              * @type Array
14919              */
14920             this.buttons = [];
14921         }
14922         this.buttons.push(btn);
14923         return btn;
14924     },
14925
14926     /**
14927      * Sets the default button to be focused when the dialog is displayed.
14928      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
14929      * @return {Roo.BasicDialog} this
14930      */
14931     setDefaultButton : function(btn){
14932         this.defaultButton = btn;
14933         return this;
14934     },
14935
14936     // private
14937     getHeaderFooterHeight : function(safe){
14938         var height = 0;
14939         if(this.header){
14940            height += this.header.getHeight();
14941         }
14942         if(this.footer){
14943            var fm = this.footer.getMargins();
14944             height += (this.footer.getHeight()+fm.top+fm.bottom);
14945         }
14946         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
14947         height += this.centerBg.getPadding("tb");
14948         return height;
14949     },
14950
14951     // private
14952     syncBodyHeight : function()
14953     {
14954         var bd = this.body, // the text
14955             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
14956             bw = this.bwrap;
14957         var height = this.size.height - this.getHeaderFooterHeight(false);
14958         bd.setHeight(height-bd.getMargins("tb"));
14959         var hh = this.header.getHeight();
14960         var h = this.size.height-hh;
14961         cb.setHeight(h);
14962         
14963         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
14964         bw.setHeight(h-cb.getPadding("tb"));
14965         
14966         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
14967         bd.setWidth(bw.getWidth(true));
14968         if(this.tabs){
14969             this.tabs.syncHeight();
14970             if(Roo.isIE){
14971                 this.tabs.el.repaint();
14972             }
14973         }
14974     },
14975
14976     /**
14977      * Restores the previous state of the dialog if Roo.state is configured.
14978      * @return {Roo.BasicDialog} this
14979      */
14980     restoreState : function(){
14981         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
14982         if(box && box.width){
14983             this.xy = [box.x, box.y];
14984             this.resizeTo(box.width, box.height);
14985         }
14986         return this;
14987     },
14988
14989     // private
14990     beforeShow : function(){
14991         this.expand();
14992         if(this.fixedcenter){
14993             this.xy = this.el.getCenterXY(true);
14994         }
14995         if(this.modal){
14996             Roo.get(document.body).addClass("x-body-masked");
14997             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14998             this.mask.show();
14999         }
15000         this.constrainXY();
15001     },
15002
15003     // private
15004     animShow : function(){
15005         var b = Roo.get(this.animateTarget).getBox();
15006         this.proxy.setSize(b.width, b.height);
15007         this.proxy.setLocation(b.x, b.y);
15008         this.proxy.show();
15009         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
15010                     true, .35, this.showEl.createDelegate(this));
15011     },
15012
15013     /**
15014      * Shows the dialog.
15015      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
15016      * @return {Roo.BasicDialog} this
15017      */
15018     show : function(animateTarget){
15019         if (this.fireEvent("beforeshow", this) === false){
15020             return;
15021         }
15022         if(this.syncHeightBeforeShow){
15023             this.syncBodyHeight();
15024         }else if(this.firstShow){
15025             this.firstShow = false;
15026             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
15027         }
15028         this.animateTarget = animateTarget || this.animateTarget;
15029         if(!this.el.isVisible()){
15030             this.beforeShow();
15031             if(this.animateTarget && Roo.get(this.animateTarget)){
15032                 this.animShow();
15033             }else{
15034                 this.showEl();
15035             }
15036         }
15037         return this;
15038     },
15039
15040     // private
15041     showEl : function(){
15042         this.proxy.hide();
15043         this.el.setXY(this.xy);
15044         this.el.show();
15045         this.adjustAssets(true);
15046         this.toFront();
15047         this.focus();
15048         // IE peekaboo bug - fix found by Dave Fenwick
15049         if(Roo.isIE){
15050             this.el.repaint();
15051         }
15052         this.fireEvent("show", this);
15053     },
15054
15055     /**
15056      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
15057      * dialog itself will receive focus.
15058      */
15059     focus : function(){
15060         if(this.defaultButton){
15061             this.defaultButton.focus();
15062         }else{
15063             this.focusEl.focus();
15064         }
15065     },
15066
15067     // private
15068     constrainXY : function(){
15069         if(this.constraintoviewport !== false){
15070             if(!this.viewSize){
15071                 if(this.container){
15072                     var s = this.container.getSize();
15073                     this.viewSize = [s.width, s.height];
15074                 }else{
15075                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
15076                 }
15077             }
15078             var s = Roo.get(this.container||document).getScroll();
15079
15080             var x = this.xy[0], y = this.xy[1];
15081             var w = this.size.width, h = this.size.height;
15082             var vw = this.viewSize[0], vh = this.viewSize[1];
15083             // only move it if it needs it
15084             var moved = false;
15085             // first validate right/bottom
15086             if(x + w > vw+s.left){
15087                 x = vw - w;
15088                 moved = true;
15089             }
15090             if(y + h > vh+s.top){
15091                 y = vh - h;
15092                 moved = true;
15093             }
15094             // then make sure top/left isn't negative
15095             if(x < s.left){
15096                 x = s.left;
15097                 moved = true;
15098             }
15099             if(y < s.top){
15100                 y = s.top;
15101                 moved = true;
15102             }
15103             if(moved){
15104                 // cache xy
15105                 this.xy = [x, y];
15106                 if(this.isVisible()){
15107                     this.el.setLocation(x, y);
15108                     this.adjustAssets();
15109                 }
15110             }
15111         }
15112     },
15113
15114     // private
15115     onDrag : function(){
15116         if(!this.proxyDrag){
15117             this.xy = this.el.getXY();
15118             this.adjustAssets();
15119         }
15120     },
15121
15122     // private
15123     adjustAssets : function(doShow){
15124         var x = this.xy[0], y = this.xy[1];
15125         var w = this.size.width, h = this.size.height;
15126         if(doShow === true){
15127             if(this.shadow){
15128                 this.shadow.show(this.el);
15129             }
15130             if(this.shim){
15131                 this.shim.show();
15132             }
15133         }
15134         if(this.shadow && this.shadow.isVisible()){
15135             this.shadow.show(this.el);
15136         }
15137         if(this.shim && this.shim.isVisible()){
15138             this.shim.setBounds(x, y, w, h);
15139         }
15140     },
15141
15142     // private
15143     adjustViewport : function(w, h){
15144         if(!w || !h){
15145             w = Roo.lib.Dom.getViewWidth();
15146             h = Roo.lib.Dom.getViewHeight();
15147         }
15148         // cache the size
15149         this.viewSize = [w, h];
15150         if(this.modal && this.mask.isVisible()){
15151             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
15152             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15153         }
15154         if(this.isVisible()){
15155             this.constrainXY();
15156         }
15157     },
15158
15159     /**
15160      * Destroys this dialog and all its supporting elements (including any tabs, shim,
15161      * shadow, proxy, mask, etc.)  Also removes all event listeners.
15162      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
15163      */
15164     destroy : function(removeEl){
15165         if(this.isVisible()){
15166             this.animateTarget = null;
15167             this.hide();
15168         }
15169         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
15170         if(this.tabs){
15171             this.tabs.destroy(removeEl);
15172         }
15173         Roo.destroy(
15174              this.shim,
15175              this.proxy,
15176              this.resizer,
15177              this.close,
15178              this.mask
15179         );
15180         if(this.dd){
15181             this.dd.unreg();
15182         }
15183         if(this.buttons){
15184            for(var i = 0, len = this.buttons.length; i < len; i++){
15185                this.buttons[i].destroy();
15186            }
15187         }
15188         this.el.removeAllListeners();
15189         if(removeEl === true){
15190             this.el.update("");
15191             this.el.remove();
15192         }
15193         Roo.DialogManager.unregister(this);
15194     },
15195
15196     // private
15197     startMove : function(){
15198         if(this.proxyDrag){
15199             this.proxy.show();
15200         }
15201         if(this.constraintoviewport !== false){
15202             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
15203         }
15204     },
15205
15206     // private
15207     endMove : function(){
15208         if(!this.proxyDrag){
15209             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
15210         }else{
15211             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
15212             this.proxy.hide();
15213         }
15214         this.refreshSize();
15215         this.adjustAssets();
15216         this.focus();
15217         this.fireEvent("move", this, this.xy[0], this.xy[1]);
15218     },
15219
15220     /**
15221      * Brings this dialog to the front of any other visible dialogs
15222      * @return {Roo.BasicDialog} this
15223      */
15224     toFront : function(){
15225         Roo.DialogManager.bringToFront(this);
15226         return this;
15227     },
15228
15229     /**
15230      * Sends this dialog to the back (under) of any other visible dialogs
15231      * @return {Roo.BasicDialog} this
15232      */
15233     toBack : function(){
15234         Roo.DialogManager.sendToBack(this);
15235         return this;
15236     },
15237
15238     /**
15239      * Centers this dialog in the viewport
15240      * @return {Roo.BasicDialog} this
15241      */
15242     center : function(){
15243         var xy = this.el.getCenterXY(true);
15244         this.moveTo(xy[0], xy[1]);
15245         return this;
15246     },
15247
15248     /**
15249      * Moves the dialog's top-left corner to the specified point
15250      * @param {Number} x
15251      * @param {Number} y
15252      * @return {Roo.BasicDialog} this
15253      */
15254     moveTo : function(x, y){
15255         this.xy = [x,y];
15256         if(this.isVisible()){
15257             this.el.setXY(this.xy);
15258             this.adjustAssets();
15259         }
15260         return this;
15261     },
15262
15263     /**
15264      * Aligns the dialog to the specified element
15265      * @param {String/HTMLElement/Roo.Element} element The element to align to.
15266      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
15267      * @param {Array} offsets (optional) Offset the positioning by [x, y]
15268      * @return {Roo.BasicDialog} this
15269      */
15270     alignTo : function(element, position, offsets){
15271         this.xy = this.el.getAlignToXY(element, position, offsets);
15272         if(this.isVisible()){
15273             this.el.setXY(this.xy);
15274             this.adjustAssets();
15275         }
15276         return this;
15277     },
15278
15279     /**
15280      * Anchors an element to another element and realigns it when the window is resized.
15281      * @param {String/HTMLElement/Roo.Element} element The element to align to.
15282      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
15283      * @param {Array} offsets (optional) Offset the positioning by [x, y]
15284      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
15285      * is a number, it is used as the buffer delay (defaults to 50ms).
15286      * @return {Roo.BasicDialog} this
15287      */
15288     anchorTo : function(el, alignment, offsets, monitorScroll){
15289         var action = function(){
15290             this.alignTo(el, alignment, offsets);
15291         };
15292         Roo.EventManager.onWindowResize(action, this);
15293         var tm = typeof monitorScroll;
15294         if(tm != 'undefined'){
15295             Roo.EventManager.on(window, 'scroll', action, this,
15296                 {buffer: tm == 'number' ? monitorScroll : 50});
15297         }
15298         action.call(this);
15299         return this;
15300     },
15301
15302     /**
15303      * Returns true if the dialog is visible
15304      * @return {Boolean}
15305      */
15306     isVisible : function(){
15307         return this.el.isVisible();
15308     },
15309
15310     // private
15311     animHide : function(callback){
15312         var b = Roo.get(this.animateTarget).getBox();
15313         this.proxy.show();
15314         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
15315         this.el.hide();
15316         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
15317                     this.hideEl.createDelegate(this, [callback]));
15318     },
15319
15320     /**
15321      * Hides the dialog.
15322      * @param {Function} callback (optional) Function to call when the dialog is hidden
15323      * @return {Roo.BasicDialog} this
15324      */
15325     hide : function(callback){
15326         if (this.fireEvent("beforehide", this) === false){
15327             return;
15328         }
15329         if(this.shadow){
15330             this.shadow.hide();
15331         }
15332         if(this.shim) {
15333           this.shim.hide();
15334         }
15335         // sometimes animateTarget seems to get set.. causing problems...
15336         // this just double checks..
15337         if(this.animateTarget && Roo.get(this.animateTarget)) {
15338            this.animHide(callback);
15339         }else{
15340             this.el.hide();
15341             this.hideEl(callback);
15342         }
15343         return this;
15344     },
15345
15346     // private
15347     hideEl : function(callback){
15348         this.proxy.hide();
15349         if(this.modal){
15350             this.mask.hide();
15351             Roo.get(document.body).removeClass("x-body-masked");
15352         }
15353         this.fireEvent("hide", this);
15354         if(typeof callback == "function"){
15355             callback();
15356         }
15357     },
15358
15359     // private
15360     hideAction : function(){
15361         this.setLeft("-10000px");
15362         this.setTop("-10000px");
15363         this.setStyle("visibility", "hidden");
15364     },
15365
15366     // private
15367     refreshSize : function(){
15368         this.size = this.el.getSize();
15369         this.xy = this.el.getXY();
15370         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
15371     },
15372
15373     // private
15374     // z-index is managed by the DialogManager and may be overwritten at any time
15375     setZIndex : function(index){
15376         if(this.modal){
15377             this.mask.setStyle("z-index", index);
15378         }
15379         if(this.shim){
15380             this.shim.setStyle("z-index", ++index);
15381         }
15382         if(this.shadow){
15383             this.shadow.setZIndex(++index);
15384         }
15385         this.el.setStyle("z-index", ++index);
15386         if(this.proxy){
15387             this.proxy.setStyle("z-index", ++index);
15388         }
15389         if(this.resizer){
15390             this.resizer.proxy.setStyle("z-index", ++index);
15391         }
15392
15393         this.lastZIndex = index;
15394     },
15395
15396     /**
15397      * Returns the element for this dialog
15398      * @return {Roo.Element} The underlying dialog Element
15399      */
15400     getEl : function(){
15401         return this.el;
15402     }
15403 });
15404
15405 /**
15406  * @class Roo.DialogManager
15407  * Provides global access to BasicDialogs that have been created and
15408  * support for z-indexing (layering) multiple open dialogs.
15409  */
15410 Roo.DialogManager = function(){
15411     var list = {};
15412     var accessList = [];
15413     var front = null;
15414
15415     // private
15416     var sortDialogs = function(d1, d2){
15417         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
15418     };
15419
15420     // private
15421     var orderDialogs = function(){
15422         accessList.sort(sortDialogs);
15423         var seed = Roo.DialogManager.zseed;
15424         for(var i = 0, len = accessList.length; i < len; i++){
15425             var dlg = accessList[i];
15426             if(dlg){
15427                 dlg.setZIndex(seed + (i*10));
15428             }
15429         }
15430     };
15431
15432     return {
15433         /**
15434          * The starting z-index for BasicDialogs (defaults to 9000)
15435          * @type Number The z-index value
15436          */
15437         zseed : 9000,
15438
15439         // private
15440         register : function(dlg){
15441             list[dlg.id] = dlg;
15442             accessList.push(dlg);
15443         },
15444
15445         // private
15446         unregister : function(dlg){
15447             delete list[dlg.id];
15448             var i=0;
15449             var len=0;
15450             if(!accessList.indexOf){
15451                 for(  i = 0, len = accessList.length; i < len; i++){
15452                     if(accessList[i] == dlg){
15453                         accessList.splice(i, 1);
15454                         return;
15455                     }
15456                 }
15457             }else{
15458                  i = accessList.indexOf(dlg);
15459                 if(i != -1){
15460                     accessList.splice(i, 1);
15461                 }
15462             }
15463         },
15464
15465         /**
15466          * Gets a registered dialog by id
15467          * @param {String/Object} id The id of the dialog or a dialog
15468          * @return {Roo.BasicDialog} this
15469          */
15470         get : function(id){
15471             return typeof id == "object" ? id : list[id];
15472         },
15473
15474         /**
15475          * Brings the specified dialog to the front
15476          * @param {String/Object} dlg The id of the dialog or a dialog
15477          * @return {Roo.BasicDialog} this
15478          */
15479         bringToFront : function(dlg){
15480             dlg = this.get(dlg);
15481             if(dlg != front){
15482                 front = dlg;
15483                 dlg._lastAccess = new Date().getTime();
15484                 orderDialogs();
15485             }
15486             return dlg;
15487         },
15488
15489         /**
15490          * Sends the specified dialog to the back
15491          * @param {String/Object} dlg The id of the dialog or a dialog
15492          * @return {Roo.BasicDialog} this
15493          */
15494         sendToBack : function(dlg){
15495             dlg = this.get(dlg);
15496             dlg._lastAccess = -(new Date().getTime());
15497             orderDialogs();
15498             return dlg;
15499         },
15500
15501         /**
15502          * Hides all dialogs
15503          */
15504         hideAll : function(){
15505             for(var id in list){
15506                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
15507                     list[id].hide();
15508                 }
15509             }
15510         }
15511     };
15512 }();
15513
15514 /**
15515  * @class Roo.LayoutDialog
15516  * @extends Roo.BasicDialog
15517  * Dialog which provides adjustments for working with a layout in a Dialog.
15518  * Add your necessary layout config options to the dialog's config.<br>
15519  * Example usage (including a nested layout):
15520  * <pre><code>
15521 if(!dialog){
15522     dialog = new Roo.LayoutDialog("download-dlg", {
15523         modal: true,
15524         width:600,
15525         height:450,
15526         shadow:true,
15527         minWidth:500,
15528         minHeight:350,
15529         autoTabs:true,
15530         proxyDrag:true,
15531         // layout config merges with the dialog config
15532         center:{
15533             tabPosition: "top",
15534             alwaysShowTabs: true
15535         }
15536     });
15537     dialog.addKeyListener(27, dialog.hide, dialog);
15538     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
15539     dialog.addButton("Build It!", this.getDownload, this);
15540
15541     // we can even add nested layouts
15542     var innerLayout = new Roo.BorderLayout("dl-inner", {
15543         east: {
15544             initialSize: 200,
15545             autoScroll:true,
15546             split:true
15547         },
15548         center: {
15549             autoScroll:true
15550         }
15551     });
15552     innerLayout.beginUpdate();
15553     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
15554     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
15555     innerLayout.endUpdate(true);
15556
15557     var layout = dialog.getLayout();
15558     layout.beginUpdate();
15559     layout.add("center", new Roo.ContentPanel("standard-panel",
15560                         {title: "Download the Source", fitToFrame:true}));
15561     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
15562                {title: "Build your own roo.js"}));
15563     layout.getRegion("center").showPanel(sp);
15564     layout.endUpdate();
15565 }
15566 </code></pre>
15567     * @constructor
15568     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
15569     * @param {Object} config configuration options
15570   */
15571 Roo.LayoutDialog = function(el, cfg){
15572     
15573     var config=  cfg;
15574     if (typeof(cfg) == 'undefined') {
15575         config = Roo.apply({}, el);
15576         // not sure why we use documentElement here.. - it should always be body.
15577         // IE7 borks horribly if we use documentElement.
15578         // webkit also does not like documentElement - it creates a body element...
15579         el = Roo.get( document.body || document.documentElement ).createChild();
15580         //config.autoCreate = true;
15581     }
15582     
15583     
15584     config.autoTabs = false;
15585     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
15586     this.body.setStyle({overflow:"hidden", position:"relative"});
15587     this.layout = new Roo.BorderLayout(this.body.dom, config);
15588     this.layout.monitorWindowResize = false;
15589     this.el.addClass("x-dlg-auto-layout");
15590     // fix case when center region overwrites center function
15591     this.center = Roo.BasicDialog.prototype.center;
15592     this.on("show", this.layout.layout, this.layout, true);
15593     if (config.items) {
15594         var xitems = config.items;
15595         delete config.items;
15596         Roo.each(xitems, this.addxtype, this);
15597     }
15598     
15599     
15600 };
15601 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
15602     /**
15603      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
15604      * @deprecated
15605      */
15606     endUpdate : function(){
15607         this.layout.endUpdate();
15608     },
15609
15610     /**
15611      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
15612      *  @deprecated
15613      */
15614     beginUpdate : function(){
15615         this.layout.beginUpdate();
15616     },
15617
15618     /**
15619      * Get the BorderLayout for this dialog
15620      * @return {Roo.BorderLayout}
15621      */
15622     getLayout : function(){
15623         return this.layout;
15624     },
15625
15626     showEl : function(){
15627         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
15628         if(Roo.isIE7){
15629             this.layout.layout();
15630         }
15631     },
15632
15633     // private
15634     // Use the syncHeightBeforeShow config option to control this automatically
15635     syncBodyHeight : function(){
15636         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
15637         if(this.layout){this.layout.layout();}
15638     },
15639     
15640       /**
15641      * Add an xtype element (actually adds to the layout.)
15642      * @return {Object} xdata xtype object data.
15643      */
15644     
15645     addxtype : function(c) {
15646         return this.layout.addxtype(c);
15647     }
15648 });/*
15649  * Based on:
15650  * Ext JS Library 1.1.1
15651  * Copyright(c) 2006-2007, Ext JS, LLC.
15652  *
15653  * Originally Released Under LGPL - original licence link has changed is not relivant.
15654  *
15655  * Fork - LGPL
15656  * <script type="text/javascript">
15657  */
15658  
15659 /**
15660  * @class Roo.MessageBox
15661  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
15662  * Example usage:
15663  *<pre><code>
15664 // Basic alert:
15665 Roo.Msg.alert('Status', 'Changes saved successfully.');
15666
15667 // Prompt for user data:
15668 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
15669     if (btn == 'ok'){
15670         // process text value...
15671     }
15672 });
15673
15674 // Show a dialog using config options:
15675 Roo.Msg.show({
15676    title:'Save Changes?',
15677    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
15678    buttons: Roo.Msg.YESNOCANCEL,
15679    fn: processResult,
15680    animEl: 'elId'
15681 });
15682 </code></pre>
15683  * @singleton
15684  */
15685 Roo.MessageBox = function(){
15686     var dlg, opt, mask, waitTimer;
15687     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
15688     var buttons, activeTextEl, bwidth;
15689
15690     // private
15691     var handleButton = function(button){
15692         dlg.hide();
15693         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
15694     };
15695
15696     // private
15697     var handleHide = function(){
15698         if(opt && opt.cls){
15699             dlg.el.removeClass(opt.cls);
15700         }
15701         if(waitTimer){
15702             Roo.TaskMgr.stop(waitTimer);
15703             waitTimer = null;
15704         }
15705     };
15706
15707     // private
15708     var updateButtons = function(b){
15709         var width = 0;
15710         if(!b){
15711             buttons["ok"].hide();
15712             buttons["cancel"].hide();
15713             buttons["yes"].hide();
15714             buttons["no"].hide();
15715             dlg.footer.dom.style.display = 'none';
15716             return width;
15717         }
15718         dlg.footer.dom.style.display = '';
15719         for(var k in buttons){
15720             if(typeof buttons[k] != "function"){
15721                 if(b[k]){
15722                     buttons[k].show();
15723                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
15724                     width += buttons[k].el.getWidth()+15;
15725                 }else{
15726                     buttons[k].hide();
15727                 }
15728             }
15729         }
15730         return width;
15731     };
15732
15733     // private
15734     var handleEsc = function(d, k, e){
15735         if(opt && opt.closable !== false){
15736             dlg.hide();
15737         }
15738         if(e){
15739             e.stopEvent();
15740         }
15741     };
15742
15743     return {
15744         /**
15745          * Returns a reference to the underlying {@link Roo.BasicDialog} element
15746          * @return {Roo.BasicDialog} The BasicDialog element
15747          */
15748         getDialog : function(){
15749            if(!dlg){
15750                 dlg = new Roo.BasicDialog("x-msg-box", {
15751                     autoCreate : true,
15752                     shadow: true,
15753                     draggable: true,
15754                     resizable:false,
15755                     constraintoviewport:false,
15756                     fixedcenter:true,
15757                     collapsible : false,
15758                     shim:true,
15759                     modal: true,
15760                     width:400, height:100,
15761                     buttonAlign:"center",
15762                     closeClick : function(){
15763                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
15764                             handleButton("no");
15765                         }else{
15766                             handleButton("cancel");
15767                         }
15768                     }
15769                 });
15770                 dlg.on("hide", handleHide);
15771                 mask = dlg.mask;
15772                 dlg.addKeyListener(27, handleEsc);
15773                 buttons = {};
15774                 var bt = this.buttonText;
15775                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
15776                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
15777                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
15778                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
15779                 bodyEl = dlg.body.createChild({
15780
15781                     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>'
15782                 });
15783                 msgEl = bodyEl.dom.firstChild;
15784                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
15785                 textboxEl.enableDisplayMode();
15786                 textboxEl.addKeyListener([10,13], function(){
15787                     if(dlg.isVisible() && opt && opt.buttons){
15788                         if(opt.buttons.ok){
15789                             handleButton("ok");
15790                         }else if(opt.buttons.yes){
15791                             handleButton("yes");
15792                         }
15793                     }
15794                 });
15795                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
15796                 textareaEl.enableDisplayMode();
15797                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
15798                 progressEl.enableDisplayMode();
15799                 var pf = progressEl.dom.firstChild;
15800                 if (pf) {
15801                     pp = Roo.get(pf.firstChild);
15802                     pp.setHeight(pf.offsetHeight);
15803                 }
15804                 
15805             }
15806             return dlg;
15807         },
15808
15809         /**
15810          * Updates the message box body text
15811          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
15812          * the XHTML-compliant non-breaking space character '&amp;#160;')
15813          * @return {Roo.MessageBox} This message box
15814          */
15815         updateText : function(text){
15816             if(!dlg.isVisible() && !opt.width){
15817                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
15818             }
15819             msgEl.innerHTML = text || '&#160;';
15820       
15821             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
15822             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
15823             var w = Math.max(
15824                     Math.min(opt.width || cw , this.maxWidth), 
15825                     Math.max(opt.minWidth || this.minWidth, bwidth)
15826             );
15827             if(opt.prompt){
15828                 activeTextEl.setWidth(w);
15829             }
15830             if(dlg.isVisible()){
15831                 dlg.fixedcenter = false;
15832             }
15833             // to big, make it scroll. = But as usual stupid IE does not support
15834             // !important..
15835             
15836             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
15837                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
15838                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
15839             } else {
15840                 bodyEl.dom.style.height = '';
15841                 bodyEl.dom.style.overflowY = '';
15842             }
15843             if (cw > w) {
15844                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
15845             } else {
15846                 bodyEl.dom.style.overflowX = '';
15847             }
15848             
15849             dlg.setContentSize(w, bodyEl.getHeight());
15850             if(dlg.isVisible()){
15851                 dlg.fixedcenter = true;
15852             }
15853             return this;
15854         },
15855
15856         /**
15857          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
15858          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
15859          * @param {Number} value Any number between 0 and 1 (e.g., .5)
15860          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
15861          * @return {Roo.MessageBox} This message box
15862          */
15863         updateProgress : function(value, text){
15864             if(text){
15865                 this.updateText(text);
15866             }
15867             if (pp) { // weird bug on my firefox - for some reason this is not defined
15868                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
15869             }
15870             return this;
15871         },        
15872
15873         /**
15874          * Returns true if the message box is currently displayed
15875          * @return {Boolean} True if the message box is visible, else false
15876          */
15877         isVisible : function(){
15878             return dlg && dlg.isVisible();  
15879         },
15880
15881         /**
15882          * Hides the message box if it is displayed
15883          */
15884         hide : function(){
15885             if(this.isVisible()){
15886                 dlg.hide();
15887             }  
15888         },
15889
15890         /**
15891          * Displays a new message box, or reinitializes an existing message box, based on the config options
15892          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
15893          * The following config object properties are supported:
15894          * <pre>
15895 Property    Type             Description
15896 ----------  ---------------  ------------------------------------------------------------------------------------
15897 animEl            String/Element   An id or Element from which the message box should animate as it opens and
15898                                    closes (defaults to undefined)
15899 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
15900                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
15901 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
15902                                    progress and wait dialogs will ignore this property and always hide the
15903                                    close button as they can only be closed programmatically.
15904 cls               String           A custom CSS class to apply to the message box element
15905 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
15906                                    displayed (defaults to 75)
15907 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
15908                                    function will be btn (the name of the button that was clicked, if applicable,
15909                                    e.g. "ok"), and text (the value of the active text field, if applicable).
15910                                    Progress and wait dialogs will ignore this option since they do not respond to
15911                                    user actions and can only be closed programmatically, so any required function
15912                                    should be called by the same code after it closes the dialog.
15913 icon              String           A CSS class that provides a background image to be used as an icon for
15914                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
15915 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
15916 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
15917 modal             Boolean          False to allow user interaction with the page while the message box is
15918                                    displayed (defaults to true)
15919 msg               String           A string that will replace the existing message box body text (defaults
15920                                    to the XHTML-compliant non-breaking space character '&#160;')
15921 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
15922 progress          Boolean          True to display a progress bar (defaults to false)
15923 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
15924 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
15925 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
15926 title             String           The title text
15927 value             String           The string value to set into the active textbox element if displayed
15928 wait              Boolean          True to display a progress bar (defaults to false)
15929 width             Number           The width of the dialog in pixels
15930 </pre>
15931          *
15932          * Example usage:
15933          * <pre><code>
15934 Roo.Msg.show({
15935    title: 'Address',
15936    msg: 'Please enter your address:',
15937    width: 300,
15938    buttons: Roo.MessageBox.OKCANCEL,
15939    multiline: true,
15940    fn: saveAddress,
15941    animEl: 'addAddressBtn'
15942 });
15943 </code></pre>
15944          * @param {Object} config Configuration options
15945          * @return {Roo.MessageBox} This message box
15946          */
15947         show : function(options)
15948         {
15949             
15950             // this causes nightmares if you show one dialog after another
15951             // especially on callbacks..
15952              
15953             if(this.isVisible()){
15954                 
15955                 this.hide();
15956                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
15957                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
15958                 Roo.log("New Dialog Message:" +  options.msg )
15959                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
15960                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
15961                 
15962             }
15963             var d = this.getDialog();
15964             opt = options;
15965             d.setTitle(opt.title || "&#160;");
15966             d.close.setDisplayed(opt.closable !== false);
15967             activeTextEl = textboxEl;
15968             opt.prompt = opt.prompt || (opt.multiline ? true : false);
15969             if(opt.prompt){
15970                 if(opt.multiline){
15971                     textboxEl.hide();
15972                     textareaEl.show();
15973                     textareaEl.setHeight(typeof opt.multiline == "number" ?
15974                         opt.multiline : this.defaultTextHeight);
15975                     activeTextEl = textareaEl;
15976                 }else{
15977                     textboxEl.show();
15978                     textareaEl.hide();
15979                 }
15980             }else{
15981                 textboxEl.hide();
15982                 textareaEl.hide();
15983             }
15984             progressEl.setDisplayed(opt.progress === true);
15985             this.updateProgress(0);
15986             activeTextEl.dom.value = opt.value || "";
15987             if(opt.prompt){
15988                 dlg.setDefaultButton(activeTextEl);
15989             }else{
15990                 var bs = opt.buttons;
15991                 var db = null;
15992                 if(bs && bs.ok){
15993                     db = buttons["ok"];
15994                 }else if(bs && bs.yes){
15995                     db = buttons["yes"];
15996                 }
15997                 dlg.setDefaultButton(db);
15998             }
15999             bwidth = updateButtons(opt.buttons);
16000             this.updateText(opt.msg);
16001             if(opt.cls){
16002                 d.el.addClass(opt.cls);
16003             }
16004             d.proxyDrag = opt.proxyDrag === true;
16005             d.modal = opt.modal !== false;
16006             d.mask = opt.modal !== false ? mask : false;
16007             if(!d.isVisible()){
16008                 // force it to the end of the z-index stack so it gets a cursor in FF
16009                 document.body.appendChild(dlg.el.dom);
16010                 d.animateTarget = null;
16011                 d.show(options.animEl);
16012             }
16013             return this;
16014         },
16015
16016         /**
16017          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
16018          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
16019          * and closing the message box when the process is complete.
16020          * @param {String} title The title bar text
16021          * @param {String} msg The message box body text
16022          * @return {Roo.MessageBox} This message box
16023          */
16024         progress : function(title, msg){
16025             this.show({
16026                 title : title,
16027                 msg : msg,
16028                 buttons: false,
16029                 progress:true,
16030                 closable:false,
16031                 minWidth: this.minProgressWidth,
16032                 modal : true
16033             });
16034             return this;
16035         },
16036
16037         /**
16038          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
16039          * If a callback function is passed it will be called after the user clicks the button, and the
16040          * id of the button that was clicked will be passed as the only parameter to the callback
16041          * (could also be the top-right close button).
16042          * @param {String} title The title bar text
16043          * @param {String} msg The message box body text
16044          * @param {Function} fn (optional) The callback function invoked after the message box is closed
16045          * @param {Object} scope (optional) The scope of the callback function
16046          * @return {Roo.MessageBox} This message box
16047          */
16048         alert : function(title, msg, fn, scope){
16049             this.show({
16050                 title : title,
16051                 msg : msg,
16052                 buttons: this.OK,
16053                 fn: fn,
16054                 scope : scope,
16055                 modal : true
16056             });
16057             return this;
16058         },
16059
16060         /**
16061          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
16062          * interaction while waiting for a long-running process to complete that does not have defined intervals.
16063          * You are responsible for closing the message box when the process is complete.
16064          * @param {String} msg The message box body text
16065          * @param {String} title (optional) The title bar text
16066          * @return {Roo.MessageBox} This message box
16067          */
16068         wait : function(msg, title){
16069             this.show({
16070                 title : title,
16071                 msg : msg,
16072                 buttons: false,
16073                 closable:false,
16074                 progress:true,
16075                 modal:true,
16076                 width:300,
16077                 wait:true
16078             });
16079             waitTimer = Roo.TaskMgr.start({
16080                 run: function(i){
16081                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
16082                 },
16083                 interval: 1000
16084             });
16085             return this;
16086         },
16087
16088         /**
16089          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
16090          * If a callback function is passed it will be called after the user clicks either button, and the id of the
16091          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
16092          * @param {String} title The title bar text
16093          * @param {String} msg The message box body text
16094          * @param {Function} fn (optional) The callback function invoked after the message box is closed
16095          * @param {Object} scope (optional) The scope of the callback function
16096          * @return {Roo.MessageBox} This message box
16097          */
16098         confirm : function(title, msg, fn, scope){
16099             this.show({
16100                 title : title,
16101                 msg : msg,
16102                 buttons: this.YESNO,
16103                 fn: fn,
16104                 scope : scope,
16105                 modal : true
16106             });
16107             return this;
16108         },
16109
16110         /**
16111          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
16112          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
16113          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
16114          * (could also be the top-right close button) and the text that was entered will be passed as the two
16115          * parameters to the callback.
16116          * @param {String} title The title bar text
16117          * @param {String} msg The message box body text
16118          * @param {Function} fn (optional) The callback function invoked after the message box is closed
16119          * @param {Object} scope (optional) The scope of the callback function
16120          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
16121          * property, or the height in pixels to create the textbox (defaults to false / single-line)
16122          * @return {Roo.MessageBox} This message box
16123          */
16124         prompt : function(title, msg, fn, scope, multiline){
16125             this.show({
16126                 title : title,
16127                 msg : msg,
16128                 buttons: this.OKCANCEL,
16129                 fn: fn,
16130                 minWidth:250,
16131                 scope : scope,
16132                 prompt:true,
16133                 multiline: multiline,
16134                 modal : true
16135             });
16136             return this;
16137         },
16138
16139         /**
16140          * Button config that displays a single OK button
16141          * @type Object
16142          */
16143         OK : {ok:true},
16144         /**
16145          * Button config that displays Yes and No buttons
16146          * @type Object
16147          */
16148         YESNO : {yes:true, no:true},
16149         /**
16150          * Button config that displays OK and Cancel buttons
16151          * @type Object
16152          */
16153         OKCANCEL : {ok:true, cancel:true},
16154         /**
16155          * Button config that displays Yes, No and Cancel buttons
16156          * @type Object
16157          */
16158         YESNOCANCEL : {yes:true, no:true, cancel:true},
16159
16160         /**
16161          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
16162          * @type Number
16163          */
16164         defaultTextHeight : 75,
16165         /**
16166          * The maximum width in pixels of the message box (defaults to 600)
16167          * @type Number
16168          */
16169         maxWidth : 600,
16170         /**
16171          * The minimum width in pixels of the message box (defaults to 100)
16172          * @type Number
16173          */
16174         minWidth : 100,
16175         /**
16176          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
16177          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
16178          * @type Number
16179          */
16180         minProgressWidth : 250,
16181         /**
16182          * An object containing the default button text strings that can be overriden for localized language support.
16183          * Supported properties are: ok, cancel, yes and no.
16184          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
16185          * @type Object
16186          */
16187         buttonText : {
16188             ok : "OK",
16189             cancel : "Cancel",
16190             yes : "Yes",
16191             no : "No"
16192         }
16193     };
16194 }();
16195
16196 /**
16197  * Shorthand for {@link Roo.MessageBox}
16198  */
16199 Roo.Msg = Roo.MessageBox;/*
16200  * Based on:
16201  * Ext JS Library 1.1.1
16202  * Copyright(c) 2006-2007, Ext JS, LLC.
16203  *
16204  * Originally Released Under LGPL - original licence link has changed is not relivant.
16205  *
16206  * Fork - LGPL
16207  * <script type="text/javascript">
16208  */
16209 /**
16210  * @class Roo.QuickTips
16211  * Provides attractive and customizable tooltips for any element.
16212  * @singleton
16213  */
16214 Roo.QuickTips = function(){
16215     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
16216     var ce, bd, xy, dd;
16217     var visible = false, disabled = true, inited = false;
16218     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
16219     
16220     var onOver = function(e){
16221         if(disabled){
16222             return;
16223         }
16224         var t = e.getTarget();
16225         if(!t || t.nodeType !== 1 || t == document || t == document.body){
16226             return;
16227         }
16228         if(ce && t == ce.el){
16229             clearTimeout(hideProc);
16230             return;
16231         }
16232         if(t && tagEls[t.id]){
16233             tagEls[t.id].el = t;
16234             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
16235             return;
16236         }
16237         var ttp, et = Roo.fly(t);
16238         var ns = cfg.namespace;
16239         if(tm.interceptTitles && t.title){
16240             ttp = t.title;
16241             t.qtip = ttp;
16242             t.removeAttribute("title");
16243             e.preventDefault();
16244         }else{
16245             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
16246         }
16247         if(ttp){
16248             showProc = show.defer(tm.showDelay, tm, [{
16249                 el: t, 
16250                 text: ttp, 
16251                 width: et.getAttributeNS(ns, cfg.width),
16252                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
16253                 title: et.getAttributeNS(ns, cfg.title),
16254                     cls: et.getAttributeNS(ns, cfg.cls)
16255             }]);
16256         }
16257     };
16258     
16259     var onOut = function(e){
16260         clearTimeout(showProc);
16261         var t = e.getTarget();
16262         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
16263             hideProc = setTimeout(hide, tm.hideDelay);
16264         }
16265     };
16266     
16267     var onMove = function(e){
16268         if(disabled){
16269             return;
16270         }
16271         xy = e.getXY();
16272         xy[1] += 18;
16273         if(tm.trackMouse && ce){
16274             el.setXY(xy);
16275         }
16276     };
16277     
16278     var onDown = function(e){
16279         clearTimeout(showProc);
16280         clearTimeout(hideProc);
16281         if(!e.within(el)){
16282             if(tm.hideOnClick){
16283                 hide();
16284                 tm.disable();
16285                 tm.enable.defer(100, tm);
16286             }
16287         }
16288     };
16289     
16290     var getPad = function(){
16291         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
16292     };
16293
16294     var show = function(o){
16295         if(disabled){
16296             return;
16297         }
16298         clearTimeout(dismissProc);
16299         ce = o;
16300         if(removeCls){ // in case manually hidden
16301             el.removeClass(removeCls);
16302             removeCls = null;
16303         }
16304         if(ce.cls){
16305             el.addClass(ce.cls);
16306             removeCls = ce.cls;
16307         }
16308         if(ce.title){
16309             tipTitle.update(ce.title);
16310             tipTitle.show();
16311         }else{
16312             tipTitle.update('');
16313             tipTitle.hide();
16314         }
16315         el.dom.style.width  = tm.maxWidth+'px';
16316         //tipBody.dom.style.width = '';
16317         tipBodyText.update(o.text);
16318         var p = getPad(), w = ce.width;
16319         if(!w){
16320             var td = tipBodyText.dom;
16321             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
16322             if(aw > tm.maxWidth){
16323                 w = tm.maxWidth;
16324             }else if(aw < tm.minWidth){
16325                 w = tm.minWidth;
16326             }else{
16327                 w = aw;
16328             }
16329         }
16330         //tipBody.setWidth(w);
16331         el.setWidth(parseInt(w, 10) + p);
16332         if(ce.autoHide === false){
16333             close.setDisplayed(true);
16334             if(dd){
16335                 dd.unlock();
16336             }
16337         }else{
16338             close.setDisplayed(false);
16339             if(dd){
16340                 dd.lock();
16341             }
16342         }
16343         if(xy){
16344             el.avoidY = xy[1]-18;
16345             el.setXY(xy);
16346         }
16347         if(tm.animate){
16348             el.setOpacity(.1);
16349             el.setStyle("visibility", "visible");
16350             el.fadeIn({callback: afterShow});
16351         }else{
16352             afterShow();
16353         }
16354     };
16355     
16356     var afterShow = function(){
16357         if(ce){
16358             el.show();
16359             esc.enable();
16360             if(tm.autoDismiss && ce.autoHide !== false){
16361                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
16362             }
16363         }
16364     };
16365     
16366     var hide = function(noanim){
16367         clearTimeout(dismissProc);
16368         clearTimeout(hideProc);
16369         ce = null;
16370         if(el.isVisible()){
16371             esc.disable();
16372             if(noanim !== true && tm.animate){
16373                 el.fadeOut({callback: afterHide});
16374             }else{
16375                 afterHide();
16376             } 
16377         }
16378     };
16379     
16380     var afterHide = function(){
16381         el.hide();
16382         if(removeCls){
16383             el.removeClass(removeCls);
16384             removeCls = null;
16385         }
16386     };
16387     
16388     return {
16389         /**
16390         * @cfg {Number} minWidth
16391         * The minimum width of the quick tip (defaults to 40)
16392         */
16393        minWidth : 40,
16394         /**
16395         * @cfg {Number} maxWidth
16396         * The maximum width of the quick tip (defaults to 300)
16397         */
16398        maxWidth : 300,
16399         /**
16400         * @cfg {Boolean} interceptTitles
16401         * True to automatically use the element's DOM title value if available (defaults to false)
16402         */
16403        interceptTitles : false,
16404         /**
16405         * @cfg {Boolean} trackMouse
16406         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
16407         */
16408        trackMouse : false,
16409         /**
16410         * @cfg {Boolean} hideOnClick
16411         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
16412         */
16413        hideOnClick : true,
16414         /**
16415         * @cfg {Number} showDelay
16416         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
16417         */
16418        showDelay : 500,
16419         /**
16420         * @cfg {Number} hideDelay
16421         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
16422         */
16423        hideDelay : 200,
16424         /**
16425         * @cfg {Boolean} autoHide
16426         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
16427         * Used in conjunction with hideDelay.
16428         */
16429        autoHide : true,
16430         /**
16431         * @cfg {Boolean}
16432         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
16433         * (defaults to true).  Used in conjunction with autoDismissDelay.
16434         */
16435        autoDismiss : true,
16436         /**
16437         * @cfg {Number}
16438         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
16439         */
16440        autoDismissDelay : 5000,
16441        /**
16442         * @cfg {Boolean} animate
16443         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
16444         */
16445        animate : false,
16446
16447        /**
16448         * @cfg {String} title
16449         * Title text to display (defaults to '').  This can be any valid HTML markup.
16450         */
16451         title: '',
16452        /**
16453         * @cfg {String} text
16454         * Body text to display (defaults to '').  This can be any valid HTML markup.
16455         */
16456         text : '',
16457        /**
16458         * @cfg {String} cls
16459         * A CSS class to apply to the base quick tip element (defaults to '').
16460         */
16461         cls : '',
16462        /**
16463         * @cfg {Number} width
16464         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
16465         * minWidth or maxWidth.
16466         */
16467         width : null,
16468
16469     /**
16470      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
16471      * or display QuickTips in a page.
16472      */
16473        init : function(){
16474           tm = Roo.QuickTips;
16475           cfg = tm.tagConfig;
16476           if(!inited){
16477               if(!Roo.isReady){ // allow calling of init() before onReady
16478                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
16479                   return;
16480               }
16481               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
16482               el.fxDefaults = {stopFx: true};
16483               // maximum custom styling
16484               //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>');
16485               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>');              
16486               tipTitle = el.child('h3');
16487               tipTitle.enableDisplayMode("block");
16488               tipBody = el.child('div.x-tip-bd');
16489               tipBodyText = el.child('div.x-tip-bd-inner');
16490               //bdLeft = el.child('div.x-tip-bd-left');
16491               //bdRight = el.child('div.x-tip-bd-right');
16492               close = el.child('div.x-tip-close');
16493               close.enableDisplayMode("block");
16494               close.on("click", hide);
16495               var d = Roo.get(document);
16496               d.on("mousedown", onDown);
16497               d.on("mouseover", onOver);
16498               d.on("mouseout", onOut);
16499               d.on("mousemove", onMove);
16500               esc = d.addKeyListener(27, hide);
16501               esc.disable();
16502               if(Roo.dd.DD){
16503                   dd = el.initDD("default", null, {
16504                       onDrag : function(){
16505                           el.sync();  
16506                       }
16507                   });
16508                   dd.setHandleElId(tipTitle.id);
16509                   dd.lock();
16510               }
16511               inited = true;
16512           }
16513           this.enable(); 
16514        },
16515
16516     /**
16517      * Configures a new quick tip instance and assigns it to a target element.  The following config options
16518      * are supported:
16519      * <pre>
16520 Property    Type                   Description
16521 ----------  ---------------------  ------------------------------------------------------------------------
16522 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
16523      * </ul>
16524      * @param {Object} config The config object
16525      */
16526        register : function(config){
16527            var cs = config instanceof Array ? config : arguments;
16528            for(var i = 0, len = cs.length; i < len; i++) {
16529                var c = cs[i];
16530                var target = c.target;
16531                if(target){
16532                    if(target instanceof Array){
16533                        for(var j = 0, jlen = target.length; j < jlen; j++){
16534                            tagEls[target[j]] = c;
16535                        }
16536                    }else{
16537                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
16538                    }
16539                }
16540            }
16541        },
16542
16543     /**
16544      * Removes this quick tip from its element and destroys it.
16545      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
16546      */
16547        unregister : function(el){
16548            delete tagEls[Roo.id(el)];
16549        },
16550
16551     /**
16552      * Enable this quick tip.
16553      */
16554        enable : function(){
16555            if(inited && disabled){
16556                locks.pop();
16557                if(locks.length < 1){
16558                    disabled = false;
16559                }
16560            }
16561        },
16562
16563     /**
16564      * Disable this quick tip.
16565      */
16566        disable : function(){
16567           disabled = true;
16568           clearTimeout(showProc);
16569           clearTimeout(hideProc);
16570           clearTimeout(dismissProc);
16571           if(ce){
16572               hide(true);
16573           }
16574           locks.push(1);
16575        },
16576
16577     /**
16578      * Returns true if the quick tip is enabled, else false.
16579      */
16580        isEnabled : function(){
16581             return !disabled;
16582        },
16583
16584         // private
16585        tagConfig : {
16586            namespace : "ext",
16587            attribute : "qtip",
16588            width : "width",
16589            target : "target",
16590            title : "qtitle",
16591            hide : "hide",
16592            cls : "qclass"
16593        }
16594    };
16595 }();
16596
16597 // backwards compat
16598 Roo.QuickTips.tips = Roo.QuickTips.register;/*
16599  * Based on:
16600  * Ext JS Library 1.1.1
16601  * Copyright(c) 2006-2007, Ext JS, LLC.
16602  *
16603  * Originally Released Under LGPL - original licence link has changed is not relivant.
16604  *
16605  * Fork - LGPL
16606  * <script type="text/javascript">
16607  */
16608  
16609
16610 /**
16611  * @class Roo.tree.TreePanel
16612  * @extends Roo.data.Tree
16613
16614  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
16615  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
16616  * @cfg {Boolean} enableDD true to enable drag and drop
16617  * @cfg {Boolean} enableDrag true to enable just drag
16618  * @cfg {Boolean} enableDrop true to enable just drop
16619  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
16620  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
16621  * @cfg {String} ddGroup The DD group this TreePanel belongs to
16622  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
16623  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
16624  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
16625  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
16626  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
16627  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
16628  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
16629  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
16630  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
16631  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
16632  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
16633  * @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>
16634  * @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>
16635  * 
16636  * @constructor
16637  * @param {String/HTMLElement/Element} el The container element
16638  * @param {Object} config
16639  */
16640 Roo.tree.TreePanel = function(el, config){
16641     var root = false;
16642     var loader = false;
16643     if (config.root) {
16644         root = config.root;
16645         delete config.root;
16646     }
16647     if (config.loader) {
16648         loader = config.loader;
16649         delete config.loader;
16650     }
16651     
16652     Roo.apply(this, config);
16653     Roo.tree.TreePanel.superclass.constructor.call(this);
16654     this.el = Roo.get(el);
16655     this.el.addClass('x-tree');
16656     //console.log(root);
16657     if (root) {
16658         this.setRootNode( Roo.factory(root, Roo.tree));
16659     }
16660     if (loader) {
16661         this.loader = Roo.factory(loader, Roo.tree);
16662     }
16663    /**
16664     * Read-only. The id of the container element becomes this TreePanel's id.
16665     */
16666     this.id = this.el.id;
16667     this.addEvents({
16668         /**
16669         * @event beforeload
16670         * Fires before a node is loaded, return false to cancel
16671         * @param {Node} node The node being loaded
16672         */
16673         "beforeload" : true,
16674         /**
16675         * @event load
16676         * Fires when a node is loaded
16677         * @param {Node} node The node that was loaded
16678         */
16679         "load" : true,
16680         /**
16681         * @event textchange
16682         * Fires when the text for a node is changed
16683         * @param {Node} node The node
16684         * @param {String} text The new text
16685         * @param {String} oldText The old text
16686         */
16687         "textchange" : true,
16688         /**
16689         * @event beforeexpand
16690         * Fires before a node is expanded, return false to cancel.
16691         * @param {Node} node The node
16692         * @param {Boolean} deep
16693         * @param {Boolean} anim
16694         */
16695         "beforeexpand" : true,
16696         /**
16697         * @event beforecollapse
16698         * Fires before a node is collapsed, return false to cancel.
16699         * @param {Node} node The node
16700         * @param {Boolean} deep
16701         * @param {Boolean} anim
16702         */
16703         "beforecollapse" : true,
16704         /**
16705         * @event expand
16706         * Fires when a node is expanded
16707         * @param {Node} node The node
16708         */
16709         "expand" : true,
16710         /**
16711         * @event disabledchange
16712         * Fires when the disabled status of a node changes
16713         * @param {Node} node The node
16714         * @param {Boolean} disabled
16715         */
16716         "disabledchange" : true,
16717         /**
16718         * @event collapse
16719         * Fires when a node is collapsed
16720         * @param {Node} node The node
16721         */
16722         "collapse" : true,
16723         /**
16724         * @event beforeclick
16725         * Fires before click processing on a node. Return false to cancel the default action.
16726         * @param {Node} node The node
16727         * @param {Roo.EventObject} e The event object
16728         */
16729         "beforeclick":true,
16730         /**
16731         * @event checkchange
16732         * Fires when a node with a checkbox's checked property changes
16733         * @param {Node} this This node
16734         * @param {Boolean} checked
16735         */
16736         "checkchange":true,
16737         /**
16738         * @event click
16739         * Fires when a node is clicked
16740         * @param {Node} node The node
16741         * @param {Roo.EventObject} e The event object
16742         */
16743         "click":true,
16744         /**
16745         * @event dblclick
16746         * Fires when a node is double clicked
16747         * @param {Node} node The node
16748         * @param {Roo.EventObject} e The event object
16749         */
16750         "dblclick":true,
16751         /**
16752         * @event contextmenu
16753         * Fires when a node is right clicked
16754         * @param {Node} node The node
16755         * @param {Roo.EventObject} e The event object
16756         */
16757         "contextmenu":true,
16758         /**
16759         * @event beforechildrenrendered
16760         * Fires right before the child nodes for a node are rendered
16761         * @param {Node} node The node
16762         */
16763         "beforechildrenrendered":true,
16764         /**
16765         * @event startdrag
16766         * Fires when a node starts being dragged
16767         * @param {Roo.tree.TreePanel} this
16768         * @param {Roo.tree.TreeNode} node
16769         * @param {event} e The raw browser event
16770         */ 
16771        "startdrag" : true,
16772        /**
16773         * @event enddrag
16774         * Fires when a drag operation is complete
16775         * @param {Roo.tree.TreePanel} this
16776         * @param {Roo.tree.TreeNode} node
16777         * @param {event} e The raw browser event
16778         */
16779        "enddrag" : true,
16780        /**
16781         * @event dragdrop
16782         * Fires when a dragged node is dropped on a valid DD target
16783         * @param {Roo.tree.TreePanel} this
16784         * @param {Roo.tree.TreeNode} node
16785         * @param {DD} dd The dd it was dropped on
16786         * @param {event} e The raw browser event
16787         */
16788        "dragdrop" : true,
16789        /**
16790         * @event beforenodedrop
16791         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
16792         * passed to handlers has the following properties:<br />
16793         * <ul style="padding:5px;padding-left:16px;">
16794         * <li>tree - The TreePanel</li>
16795         * <li>target - The node being targeted for the drop</li>
16796         * <li>data - The drag data from the drag source</li>
16797         * <li>point - The point of the drop - append, above or below</li>
16798         * <li>source - The drag source</li>
16799         * <li>rawEvent - Raw mouse event</li>
16800         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
16801         * to be inserted by setting them on this object.</li>
16802         * <li>cancel - Set this to true to cancel the drop.</li>
16803         * </ul>
16804         * @param {Object} dropEvent
16805         */
16806        "beforenodedrop" : true,
16807        /**
16808         * @event nodedrop
16809         * Fires after a DD object is dropped on a node in this tree. The dropEvent
16810         * passed to handlers has the following properties:<br />
16811         * <ul style="padding:5px;padding-left:16px;">
16812         * <li>tree - The TreePanel</li>
16813         * <li>target - The node being targeted for the drop</li>
16814         * <li>data - The drag data from the drag source</li>
16815         * <li>point - The point of the drop - append, above or below</li>
16816         * <li>source - The drag source</li>
16817         * <li>rawEvent - Raw mouse event</li>
16818         * <li>dropNode - Dropped node(s).</li>
16819         * </ul>
16820         * @param {Object} dropEvent
16821         */
16822        "nodedrop" : true,
16823         /**
16824         * @event nodedragover
16825         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
16826         * passed to handlers has the following properties:<br />
16827         * <ul style="padding:5px;padding-left:16px;">
16828         * <li>tree - The TreePanel</li>
16829         * <li>target - The node being targeted for the drop</li>
16830         * <li>data - The drag data from the drag source</li>
16831         * <li>point - The point of the drop - append, above or below</li>
16832         * <li>source - The drag source</li>
16833         * <li>rawEvent - Raw mouse event</li>
16834         * <li>dropNode - Drop node(s) provided by the source.</li>
16835         * <li>cancel - Set this to true to signal drop not allowed.</li>
16836         * </ul>
16837         * @param {Object} dragOverEvent
16838         */
16839        "nodedragover" : true
16840         
16841     });
16842     if(this.singleExpand){
16843        this.on("beforeexpand", this.restrictExpand, this);
16844     }
16845     if (this.editor) {
16846         this.editor.tree = this;
16847         this.editor = Roo.factory(this.editor, Roo.tree);
16848     }
16849     
16850     if (this.selModel) {
16851         this.selModel = Roo.factory(this.selModel, Roo.tree);
16852     }
16853    
16854 };
16855 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
16856     rootVisible : true,
16857     animate: Roo.enableFx,
16858     lines : true,
16859     enableDD : false,
16860     hlDrop : Roo.enableFx,
16861   
16862     renderer: false,
16863     
16864     rendererTip: false,
16865     // private
16866     restrictExpand : function(node){
16867         var p = node.parentNode;
16868         if(p){
16869             if(p.expandedChild && p.expandedChild.parentNode == p){
16870                 p.expandedChild.collapse();
16871             }
16872             p.expandedChild = node;
16873         }
16874     },
16875
16876     // private override
16877     setRootNode : function(node){
16878         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
16879         if(!this.rootVisible){
16880             node.ui = new Roo.tree.RootTreeNodeUI(node);
16881         }
16882         return node;
16883     },
16884
16885     /**
16886      * Returns the container element for this TreePanel
16887      */
16888     getEl : function(){
16889         return this.el;
16890     },
16891
16892     /**
16893      * Returns the default TreeLoader for this TreePanel
16894      */
16895     getLoader : function(){
16896         return this.loader;
16897     },
16898
16899     /**
16900      * Expand all nodes
16901      */
16902     expandAll : function(){
16903         this.root.expand(true);
16904     },
16905
16906     /**
16907      * Collapse all nodes
16908      */
16909     collapseAll : function(){
16910         this.root.collapse(true);
16911     },
16912
16913     /**
16914      * Returns the selection model used by this TreePanel
16915      */
16916     getSelectionModel : function(){
16917         if(!this.selModel){
16918             this.selModel = new Roo.tree.DefaultSelectionModel();
16919         }
16920         return this.selModel;
16921     },
16922
16923     /**
16924      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
16925      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
16926      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
16927      * @return {Array}
16928      */
16929     getChecked : function(a, startNode){
16930         startNode = startNode || this.root;
16931         var r = [];
16932         var f = function(){
16933             if(this.attributes.checked){
16934                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
16935             }
16936         }
16937         startNode.cascade(f);
16938         return r;
16939     },
16940
16941     /**
16942      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16943      * @param {String} path
16944      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16945      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
16946      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
16947      */
16948     expandPath : function(path, attr, callback){
16949         attr = attr || "id";
16950         var keys = path.split(this.pathSeparator);
16951         var curNode = this.root;
16952         if(curNode.attributes[attr] != keys[1]){ // invalid root
16953             if(callback){
16954                 callback(false, null);
16955             }
16956             return;
16957         }
16958         var index = 1;
16959         var f = function(){
16960             if(++index == keys.length){
16961                 if(callback){
16962                     callback(true, curNode);
16963                 }
16964                 return;
16965             }
16966             var c = curNode.findChild(attr, keys[index]);
16967             if(!c){
16968                 if(callback){
16969                     callback(false, curNode);
16970                 }
16971                 return;
16972             }
16973             curNode = c;
16974             c.expand(false, false, f);
16975         };
16976         curNode.expand(false, false, f);
16977     },
16978
16979     /**
16980      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16981      * @param {String} path
16982      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16983      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
16984      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
16985      */
16986     selectPath : function(path, attr, callback){
16987         attr = attr || "id";
16988         var keys = path.split(this.pathSeparator);
16989         var v = keys.pop();
16990         if(keys.length > 0){
16991             var f = function(success, node){
16992                 if(success && node){
16993                     var n = node.findChild(attr, v);
16994                     if(n){
16995                         n.select();
16996                         if(callback){
16997                             callback(true, n);
16998                         }
16999                     }else if(callback){
17000                         callback(false, n);
17001                     }
17002                 }else{
17003                     if(callback){
17004                         callback(false, n);
17005                     }
17006                 }
17007             };
17008             this.expandPath(keys.join(this.pathSeparator), attr, f);
17009         }else{
17010             this.root.select();
17011             if(callback){
17012                 callback(true, this.root);
17013             }
17014         }
17015     },
17016
17017     getTreeEl : function(){
17018         return this.el;
17019     },
17020
17021     /**
17022      * Trigger rendering of this TreePanel
17023      */
17024     render : function(){
17025         if (this.innerCt) {
17026             return this; // stop it rendering more than once!!
17027         }
17028         
17029         this.innerCt = this.el.createChild({tag:"ul",
17030                cls:"x-tree-root-ct " +
17031                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
17032
17033         if(this.containerScroll){
17034             Roo.dd.ScrollManager.register(this.el);
17035         }
17036         if((this.enableDD || this.enableDrop) && !this.dropZone){
17037            /**
17038             * The dropZone used by this tree if drop is enabled
17039             * @type Roo.tree.TreeDropZone
17040             */
17041              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
17042                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
17043            });
17044         }
17045         if((this.enableDD || this.enableDrag) && !this.dragZone){
17046            /**
17047             * The dragZone used by this tree if drag is enabled
17048             * @type Roo.tree.TreeDragZone
17049             */
17050             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
17051                ddGroup: this.ddGroup || "TreeDD",
17052                scroll: this.ddScroll
17053            });
17054         }
17055         this.getSelectionModel().init(this);
17056         if (!this.root) {
17057             Roo.log("ROOT not set in tree");
17058             return this;
17059         }
17060         this.root.render();
17061         if(!this.rootVisible){
17062             this.root.renderChildren();
17063         }
17064         return this;
17065     }
17066 });/*
17067  * Based on:
17068  * Ext JS Library 1.1.1
17069  * Copyright(c) 2006-2007, Ext JS, LLC.
17070  *
17071  * Originally Released Under LGPL - original licence link has changed is not relivant.
17072  *
17073  * Fork - LGPL
17074  * <script type="text/javascript">
17075  */
17076  
17077
17078 /**
17079  * @class Roo.tree.DefaultSelectionModel
17080  * @extends Roo.util.Observable
17081  * The default single selection for a TreePanel.
17082  * @param {Object} cfg Configuration
17083  */
17084 Roo.tree.DefaultSelectionModel = function(cfg){
17085    this.selNode = null;
17086    
17087    
17088    
17089    this.addEvents({
17090        /**
17091         * @event selectionchange
17092         * Fires when the selected node changes
17093         * @param {DefaultSelectionModel} this
17094         * @param {TreeNode} node the new selection
17095         */
17096        "selectionchange" : true,
17097
17098        /**
17099         * @event beforeselect
17100         * Fires before the selected node changes, return false to cancel the change
17101         * @param {DefaultSelectionModel} this
17102         * @param {TreeNode} node the new selection
17103         * @param {TreeNode} node the old selection
17104         */
17105        "beforeselect" : true
17106    });
17107    
17108     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
17109 };
17110
17111 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
17112     init : function(tree){
17113         this.tree = tree;
17114         tree.getTreeEl().on("keydown", this.onKeyDown, this);
17115         tree.on("click", this.onNodeClick, this);
17116     },
17117     
17118     onNodeClick : function(node, e){
17119         if (e.ctrlKey && this.selNode == node)  {
17120             this.unselect(node);
17121             return;
17122         }
17123         this.select(node);
17124     },
17125     
17126     /**
17127      * Select a node.
17128      * @param {TreeNode} node The node to select
17129      * @return {TreeNode} The selected node
17130      */
17131     select : function(node){
17132         var last = this.selNode;
17133         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
17134             if(last){
17135                 last.ui.onSelectedChange(false);
17136             }
17137             this.selNode = node;
17138             node.ui.onSelectedChange(true);
17139             this.fireEvent("selectionchange", this, node, last);
17140         }
17141         return node;
17142     },
17143     
17144     /**
17145      * Deselect a node.
17146      * @param {TreeNode} node The node to unselect
17147      */
17148     unselect : function(node){
17149         if(this.selNode == node){
17150             this.clearSelections();
17151         }    
17152     },
17153     
17154     /**
17155      * Clear all selections
17156      */
17157     clearSelections : function(){
17158         var n = this.selNode;
17159         if(n){
17160             n.ui.onSelectedChange(false);
17161             this.selNode = null;
17162             this.fireEvent("selectionchange", this, null);
17163         }
17164         return n;
17165     },
17166     
17167     /**
17168      * Get the selected node
17169      * @return {TreeNode} The selected node
17170      */
17171     getSelectedNode : function(){
17172         return this.selNode;    
17173     },
17174     
17175     /**
17176      * Returns true if the node is selected
17177      * @param {TreeNode} node The node to check
17178      * @return {Boolean}
17179      */
17180     isSelected : function(node){
17181         return this.selNode == node;  
17182     },
17183
17184     /**
17185      * Selects the node above the selected node in the tree, intelligently walking the nodes
17186      * @return TreeNode The new selection
17187      */
17188     selectPrevious : function(){
17189         var s = this.selNode || this.lastSelNode;
17190         if(!s){
17191             return null;
17192         }
17193         var ps = s.previousSibling;
17194         if(ps){
17195             if(!ps.isExpanded() || ps.childNodes.length < 1){
17196                 return this.select(ps);
17197             } else{
17198                 var lc = ps.lastChild;
17199                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
17200                     lc = lc.lastChild;
17201                 }
17202                 return this.select(lc);
17203             }
17204         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
17205             return this.select(s.parentNode);
17206         }
17207         return null;
17208     },
17209
17210     /**
17211      * Selects the node above the selected node in the tree, intelligently walking the nodes
17212      * @return TreeNode The new selection
17213      */
17214     selectNext : function(){
17215         var s = this.selNode || this.lastSelNode;
17216         if(!s){
17217             return null;
17218         }
17219         if(s.firstChild && s.isExpanded()){
17220              return this.select(s.firstChild);
17221          }else if(s.nextSibling){
17222              return this.select(s.nextSibling);
17223          }else if(s.parentNode){
17224             var newS = null;
17225             s.parentNode.bubble(function(){
17226                 if(this.nextSibling){
17227                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
17228                     return false;
17229                 }
17230             });
17231             return newS;
17232          }
17233         return null;
17234     },
17235
17236     onKeyDown : function(e){
17237         var s = this.selNode || this.lastSelNode;
17238         // undesirable, but required
17239         var sm = this;
17240         if(!s){
17241             return;
17242         }
17243         var k = e.getKey();
17244         switch(k){
17245              case e.DOWN:
17246                  e.stopEvent();
17247                  this.selectNext();
17248              break;
17249              case e.UP:
17250                  e.stopEvent();
17251                  this.selectPrevious();
17252              break;
17253              case e.RIGHT:
17254                  e.preventDefault();
17255                  if(s.hasChildNodes()){
17256                      if(!s.isExpanded()){
17257                          s.expand();
17258                      }else if(s.firstChild){
17259                          this.select(s.firstChild, e);
17260                      }
17261                  }
17262              break;
17263              case e.LEFT:
17264                  e.preventDefault();
17265                  if(s.hasChildNodes() && s.isExpanded()){
17266                      s.collapse();
17267                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
17268                      this.select(s.parentNode, e);
17269                  }
17270              break;
17271         };
17272     }
17273 });
17274
17275 /**
17276  * @class Roo.tree.MultiSelectionModel
17277  * @extends Roo.util.Observable
17278  * Multi selection for a TreePanel.
17279  * @param {Object} cfg Configuration
17280  */
17281 Roo.tree.MultiSelectionModel = function(){
17282    this.selNodes = [];
17283    this.selMap = {};
17284    this.addEvents({
17285        /**
17286         * @event selectionchange
17287         * Fires when the selected nodes change
17288         * @param {MultiSelectionModel} this
17289         * @param {Array} nodes Array of the selected nodes
17290         */
17291        "selectionchange" : true
17292    });
17293    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
17294    
17295 };
17296
17297 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
17298     init : function(tree){
17299         this.tree = tree;
17300         tree.getTreeEl().on("keydown", this.onKeyDown, this);
17301         tree.on("click", this.onNodeClick, this);
17302     },
17303     
17304     onNodeClick : function(node, e){
17305         this.select(node, e, e.ctrlKey);
17306     },
17307     
17308     /**
17309      * Select a node.
17310      * @param {TreeNode} node The node to select
17311      * @param {EventObject} e (optional) An event associated with the selection
17312      * @param {Boolean} keepExisting True to retain existing selections
17313      * @return {TreeNode} The selected node
17314      */
17315     select : function(node, e, keepExisting){
17316         if(keepExisting !== true){
17317             this.clearSelections(true);
17318         }
17319         if(this.isSelected(node)){
17320             this.lastSelNode = node;
17321             return node;
17322         }
17323         this.selNodes.push(node);
17324         this.selMap[node.id] = node;
17325         this.lastSelNode = node;
17326         node.ui.onSelectedChange(true);
17327         this.fireEvent("selectionchange", this, this.selNodes);
17328         return node;
17329     },
17330     
17331     /**
17332      * Deselect a node.
17333      * @param {TreeNode} node The node to unselect
17334      */
17335     unselect : function(node){
17336         if(this.selMap[node.id]){
17337             node.ui.onSelectedChange(false);
17338             var sn = this.selNodes;
17339             var index = -1;
17340             if(sn.indexOf){
17341                 index = sn.indexOf(node);
17342             }else{
17343                 for(var i = 0, len = sn.length; i < len; i++){
17344                     if(sn[i] == node){
17345                         index = i;
17346                         break;
17347                     }
17348                 }
17349             }
17350             if(index != -1){
17351                 this.selNodes.splice(index, 1);
17352             }
17353             delete this.selMap[node.id];
17354             this.fireEvent("selectionchange", this, this.selNodes);
17355         }
17356     },
17357     
17358     /**
17359      * Clear all selections
17360      */
17361     clearSelections : function(suppressEvent){
17362         var sn = this.selNodes;
17363         if(sn.length > 0){
17364             for(var i = 0, len = sn.length; i < len; i++){
17365                 sn[i].ui.onSelectedChange(false);
17366             }
17367             this.selNodes = [];
17368             this.selMap = {};
17369             if(suppressEvent !== true){
17370                 this.fireEvent("selectionchange", this, this.selNodes);
17371             }
17372         }
17373     },
17374     
17375     /**
17376      * Returns true if the node is selected
17377      * @param {TreeNode} node The node to check
17378      * @return {Boolean}
17379      */
17380     isSelected : function(node){
17381         return this.selMap[node.id] ? true : false;  
17382     },
17383     
17384     /**
17385      * Returns an array of the selected nodes
17386      * @return {Array}
17387      */
17388     getSelectedNodes : function(){
17389         return this.selNodes;    
17390     },
17391
17392     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
17393
17394     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
17395
17396     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
17397 });/*
17398  * Based on:
17399  * Ext JS Library 1.1.1
17400  * Copyright(c) 2006-2007, Ext JS, LLC.
17401  *
17402  * Originally Released Under LGPL - original licence link has changed is not relivant.
17403  *
17404  * Fork - LGPL
17405  * <script type="text/javascript">
17406  */
17407  
17408 /**
17409  * @class Roo.tree.TreeNode
17410  * @extends Roo.data.Node
17411  * @cfg {String} text The text for this node
17412  * @cfg {Boolean} expanded true to start the node expanded
17413  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
17414  * @cfg {Boolean} allowDrop false if this node cannot be drop on
17415  * @cfg {Boolean} disabled true to start the node disabled
17416  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
17417  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
17418  * @cfg {String} cls A css class to be added to the node
17419  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
17420  * @cfg {String} href URL of the link used for the node (defaults to #)
17421  * @cfg {String} hrefTarget target frame for the link
17422  * @cfg {String} qtip An Ext QuickTip for the node
17423  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
17424  * @cfg {Boolean} singleClickExpand True for single click expand on this node
17425  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
17426  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
17427  * (defaults to undefined with no checkbox rendered)
17428  * @constructor
17429  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
17430  */
17431 Roo.tree.TreeNode = function(attributes){
17432     attributes = attributes || {};
17433     if(typeof attributes == "string"){
17434         attributes = {text: attributes};
17435     }
17436     this.childrenRendered = false;
17437     this.rendered = false;
17438     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
17439     this.expanded = attributes.expanded === true;
17440     this.isTarget = attributes.isTarget !== false;
17441     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
17442     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
17443
17444     /**
17445      * Read-only. The text for this node. To change it use setText().
17446      * @type String
17447      */
17448     this.text = attributes.text;
17449     /**
17450      * True if this node is disabled.
17451      * @type Boolean
17452      */
17453     this.disabled = attributes.disabled === true;
17454
17455     this.addEvents({
17456         /**
17457         * @event textchange
17458         * Fires when the text for this node is changed
17459         * @param {Node} this This node
17460         * @param {String} text The new text
17461         * @param {String} oldText The old text
17462         */
17463         "textchange" : true,
17464         /**
17465         * @event beforeexpand
17466         * Fires before this node is expanded, return false to cancel.
17467         * @param {Node} this This node
17468         * @param {Boolean} deep
17469         * @param {Boolean} anim
17470         */
17471         "beforeexpand" : true,
17472         /**
17473         * @event beforecollapse
17474         * Fires before this node is collapsed, return false to cancel.
17475         * @param {Node} this This node
17476         * @param {Boolean} deep
17477         * @param {Boolean} anim
17478         */
17479         "beforecollapse" : true,
17480         /**
17481         * @event expand
17482         * Fires when this node is expanded
17483         * @param {Node} this This node
17484         */
17485         "expand" : true,
17486         /**
17487         * @event disabledchange
17488         * Fires when the disabled status of this node changes
17489         * @param {Node} this This node
17490         * @param {Boolean} disabled
17491         */
17492         "disabledchange" : true,
17493         /**
17494         * @event collapse
17495         * Fires when this node is collapsed
17496         * @param {Node} this This node
17497         */
17498         "collapse" : true,
17499         /**
17500         * @event beforeclick
17501         * Fires before click processing. Return false to cancel the default action.
17502         * @param {Node} this This node
17503         * @param {Roo.EventObject} e The event object
17504         */
17505         "beforeclick":true,
17506         /**
17507         * @event checkchange
17508         * Fires when a node with a checkbox's checked property changes
17509         * @param {Node} this This node
17510         * @param {Boolean} checked
17511         */
17512         "checkchange":true,
17513         /**
17514         * @event click
17515         * Fires when this node is clicked
17516         * @param {Node} this This node
17517         * @param {Roo.EventObject} e The event object
17518         */
17519         "click":true,
17520         /**
17521         * @event dblclick
17522         * Fires when this node is double clicked
17523         * @param {Node} this This node
17524         * @param {Roo.EventObject} e The event object
17525         */
17526         "dblclick":true,
17527         /**
17528         * @event contextmenu
17529         * Fires when this node is right clicked
17530         * @param {Node} this This node
17531         * @param {Roo.EventObject} e The event object
17532         */
17533         "contextmenu":true,
17534         /**
17535         * @event beforechildrenrendered
17536         * Fires right before the child nodes for this node are rendered
17537         * @param {Node} this This node
17538         */
17539         "beforechildrenrendered":true
17540     });
17541
17542     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
17543
17544     /**
17545      * Read-only. The UI for this node
17546      * @type TreeNodeUI
17547      */
17548     this.ui = new uiClass(this);
17549     
17550     // finally support items[]
17551     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
17552         return;
17553     }
17554     
17555     
17556     Roo.each(this.attributes.items, function(c) {
17557         this.appendChild(Roo.factory(c,Roo.Tree));
17558     }, this);
17559     delete this.attributes.items;
17560     
17561     
17562     
17563 };
17564 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
17565     preventHScroll: true,
17566     /**
17567      * Returns true if this node is expanded
17568      * @return {Boolean}
17569      */
17570     isExpanded : function(){
17571         return this.expanded;
17572     },
17573
17574     /**
17575      * Returns the UI object for this node
17576      * @return {TreeNodeUI}
17577      */
17578     getUI : function(){
17579         return this.ui;
17580     },
17581
17582     // private override
17583     setFirstChild : function(node){
17584         var of = this.firstChild;
17585         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
17586         if(this.childrenRendered && of && node != of){
17587             of.renderIndent(true, true);
17588         }
17589         if(this.rendered){
17590             this.renderIndent(true, true);
17591         }
17592     },
17593
17594     // private override
17595     setLastChild : function(node){
17596         var ol = this.lastChild;
17597         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
17598         if(this.childrenRendered && ol && node != ol){
17599             ol.renderIndent(true, true);
17600         }
17601         if(this.rendered){
17602             this.renderIndent(true, true);
17603         }
17604     },
17605
17606     // these methods are overridden to provide lazy rendering support
17607     // private override
17608     appendChild : function()
17609     {
17610         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
17611         if(node && this.childrenRendered){
17612             node.render();
17613         }
17614         this.ui.updateExpandIcon();
17615         return node;
17616     },
17617
17618     // private override
17619     removeChild : function(node){
17620         this.ownerTree.getSelectionModel().unselect(node);
17621         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
17622         // if it's been rendered remove dom node
17623         if(this.childrenRendered){
17624             node.ui.remove();
17625         }
17626         if(this.childNodes.length < 1){
17627             this.collapse(false, false);
17628         }else{
17629             this.ui.updateExpandIcon();
17630         }
17631         if(!this.firstChild) {
17632             this.childrenRendered = false;
17633         }
17634         return node;
17635     },
17636
17637     // private override
17638     insertBefore : function(node, refNode){
17639         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
17640         if(newNode && refNode && this.childrenRendered){
17641             node.render();
17642         }
17643         this.ui.updateExpandIcon();
17644         return newNode;
17645     },
17646
17647     /**
17648      * Sets the text for this node
17649      * @param {String} text
17650      */
17651     setText : function(text){
17652         var oldText = this.text;
17653         this.text = text;
17654         this.attributes.text = text;
17655         if(this.rendered){ // event without subscribing
17656             this.ui.onTextChange(this, text, oldText);
17657         }
17658         this.fireEvent("textchange", this, text, oldText);
17659     },
17660
17661     /**
17662      * Triggers selection of this node
17663      */
17664     select : function(){
17665         this.getOwnerTree().getSelectionModel().select(this);
17666     },
17667
17668     /**
17669      * Triggers deselection of this node
17670      */
17671     unselect : function(){
17672         this.getOwnerTree().getSelectionModel().unselect(this);
17673     },
17674
17675     /**
17676      * Returns true if this node is selected
17677      * @return {Boolean}
17678      */
17679     isSelected : function(){
17680         return this.getOwnerTree().getSelectionModel().isSelected(this);
17681     },
17682
17683     /**
17684      * Expand this node.
17685      * @param {Boolean} deep (optional) True to expand all children as well
17686      * @param {Boolean} anim (optional) false to cancel the default animation
17687      * @param {Function} callback (optional) A callback to be called when
17688      * expanding this node completes (does not wait for deep expand to complete).
17689      * Called with 1 parameter, this node.
17690      */
17691     expand : function(deep, anim, callback){
17692         if(!this.expanded){
17693             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
17694                 return;
17695             }
17696             if(!this.childrenRendered){
17697                 this.renderChildren();
17698             }
17699             this.expanded = true;
17700             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
17701                 this.ui.animExpand(function(){
17702                     this.fireEvent("expand", this);
17703                     if(typeof callback == "function"){
17704                         callback(this);
17705                     }
17706                     if(deep === true){
17707                         this.expandChildNodes(true);
17708                     }
17709                 }.createDelegate(this));
17710                 return;
17711             }else{
17712                 this.ui.expand();
17713                 this.fireEvent("expand", this);
17714                 if(typeof callback == "function"){
17715                     callback(this);
17716                 }
17717             }
17718         }else{
17719            if(typeof callback == "function"){
17720                callback(this);
17721            }
17722         }
17723         if(deep === true){
17724             this.expandChildNodes(true);
17725         }
17726     },
17727
17728     isHiddenRoot : function(){
17729         return this.isRoot && !this.getOwnerTree().rootVisible;
17730     },
17731
17732     /**
17733      * Collapse this node.
17734      * @param {Boolean} deep (optional) True to collapse all children as well
17735      * @param {Boolean} anim (optional) false to cancel the default animation
17736      */
17737     collapse : function(deep, anim){
17738         if(this.expanded && !this.isHiddenRoot()){
17739             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
17740                 return;
17741             }
17742             this.expanded = false;
17743             if((this.getOwnerTree().animate && anim !== false) || anim){
17744                 this.ui.animCollapse(function(){
17745                     this.fireEvent("collapse", this);
17746                     if(deep === true){
17747                         this.collapseChildNodes(true);
17748                     }
17749                 }.createDelegate(this));
17750                 return;
17751             }else{
17752                 this.ui.collapse();
17753                 this.fireEvent("collapse", this);
17754             }
17755         }
17756         if(deep === true){
17757             var cs = this.childNodes;
17758             for(var i = 0, len = cs.length; i < len; i++) {
17759                 cs[i].collapse(true, false);
17760             }
17761         }
17762     },
17763
17764     // private
17765     delayedExpand : function(delay){
17766         if(!this.expandProcId){
17767             this.expandProcId = this.expand.defer(delay, this);
17768         }
17769     },
17770
17771     // private
17772     cancelExpand : function(){
17773         if(this.expandProcId){
17774             clearTimeout(this.expandProcId);
17775         }
17776         this.expandProcId = false;
17777     },
17778
17779     /**
17780      * Toggles expanded/collapsed state of the node
17781      */
17782     toggle : function(){
17783         if(this.expanded){
17784             this.collapse();
17785         }else{
17786             this.expand();
17787         }
17788     },
17789
17790     /**
17791      * Ensures all parent nodes are expanded
17792      */
17793     ensureVisible : function(callback){
17794         var tree = this.getOwnerTree();
17795         tree.expandPath(this.parentNode.getPath(), false, function(){
17796             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
17797             Roo.callback(callback);
17798         }.createDelegate(this));
17799     },
17800
17801     /**
17802      * Expand all child nodes
17803      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
17804      */
17805     expandChildNodes : function(deep){
17806         var cs = this.childNodes;
17807         for(var i = 0, len = cs.length; i < len; i++) {
17808                 cs[i].expand(deep);
17809         }
17810     },
17811
17812     /**
17813      * Collapse all child nodes
17814      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
17815      */
17816     collapseChildNodes : function(deep){
17817         var cs = this.childNodes;
17818         for(var i = 0, len = cs.length; i < len; i++) {
17819                 cs[i].collapse(deep);
17820         }
17821     },
17822
17823     /**
17824      * Disables this node
17825      */
17826     disable : function(){
17827         this.disabled = true;
17828         this.unselect();
17829         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17830             this.ui.onDisableChange(this, true);
17831         }
17832         this.fireEvent("disabledchange", this, true);
17833     },
17834
17835     /**
17836      * Enables this node
17837      */
17838     enable : function(){
17839         this.disabled = false;
17840         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17841             this.ui.onDisableChange(this, false);
17842         }
17843         this.fireEvent("disabledchange", this, false);
17844     },
17845
17846     // private
17847     renderChildren : function(suppressEvent){
17848         if(suppressEvent !== false){
17849             this.fireEvent("beforechildrenrendered", this);
17850         }
17851         var cs = this.childNodes;
17852         for(var i = 0, len = cs.length; i < len; i++){
17853             cs[i].render(true);
17854         }
17855         this.childrenRendered = true;
17856     },
17857
17858     // private
17859     sort : function(fn, scope){
17860         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
17861         if(this.childrenRendered){
17862             var cs = this.childNodes;
17863             for(var i = 0, len = cs.length; i < len; i++){
17864                 cs[i].render(true);
17865             }
17866         }
17867     },
17868
17869     // private
17870     render : function(bulkRender){
17871         this.ui.render(bulkRender);
17872         if(!this.rendered){
17873             this.rendered = true;
17874             if(this.expanded){
17875                 this.expanded = false;
17876                 this.expand(false, false);
17877             }
17878         }
17879     },
17880
17881     // private
17882     renderIndent : function(deep, refresh){
17883         if(refresh){
17884             this.ui.childIndent = null;
17885         }
17886         this.ui.renderIndent();
17887         if(deep === true && this.childrenRendered){
17888             var cs = this.childNodes;
17889             for(var i = 0, len = cs.length; i < len; i++){
17890                 cs[i].renderIndent(true, refresh);
17891             }
17892         }
17893     }
17894 });/*
17895  * Based on:
17896  * Ext JS Library 1.1.1
17897  * Copyright(c) 2006-2007, Ext JS, LLC.
17898  *
17899  * Originally Released Under LGPL - original licence link has changed is not relivant.
17900  *
17901  * Fork - LGPL
17902  * <script type="text/javascript">
17903  */
17904  
17905 /**
17906  * @class Roo.tree.AsyncTreeNode
17907  * @extends Roo.tree.TreeNode
17908  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
17909  * @constructor
17910  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
17911  */
17912  Roo.tree.AsyncTreeNode = function(config){
17913     this.loaded = false;
17914     this.loading = false;
17915     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
17916     /**
17917     * @event beforeload
17918     * Fires before this node is loaded, return false to cancel
17919     * @param {Node} this This node
17920     */
17921     this.addEvents({'beforeload':true, 'load': true});
17922     /**
17923     * @event load
17924     * Fires when this node is loaded
17925     * @param {Node} this This node
17926     */
17927     /**
17928      * The loader used by this node (defaults to using the tree's defined loader)
17929      * @type TreeLoader
17930      * @property loader
17931      */
17932 };
17933 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
17934     expand : function(deep, anim, callback){
17935         if(this.loading){ // if an async load is already running, waiting til it's done
17936             var timer;
17937             var f = function(){
17938                 if(!this.loading){ // done loading
17939                     clearInterval(timer);
17940                     this.expand(deep, anim, callback);
17941                 }
17942             }.createDelegate(this);
17943             timer = setInterval(f, 200);
17944             return;
17945         }
17946         if(!this.loaded){
17947             if(this.fireEvent("beforeload", this) === false){
17948                 return;
17949             }
17950             this.loading = true;
17951             this.ui.beforeLoad(this);
17952             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
17953             if(loader){
17954                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
17955                 return;
17956             }
17957         }
17958         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
17959     },
17960     
17961     /**
17962      * Returns true if this node is currently loading
17963      * @return {Boolean}
17964      */
17965     isLoading : function(){
17966         return this.loading;  
17967     },
17968     
17969     loadComplete : function(deep, anim, callback){
17970         this.loading = false;
17971         this.loaded = true;
17972         this.ui.afterLoad(this);
17973         this.fireEvent("load", this);
17974         this.expand(deep, anim, callback);
17975     },
17976     
17977     /**
17978      * Returns true if this node has been loaded
17979      * @return {Boolean}
17980      */
17981     isLoaded : function(){
17982         return this.loaded;
17983     },
17984     
17985     hasChildNodes : function(){
17986         if(!this.isLeaf() && !this.loaded){
17987             return true;
17988         }else{
17989             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
17990         }
17991     },
17992
17993     /**
17994      * Trigger a reload for this node
17995      * @param {Function} callback
17996      */
17997     reload : function(callback){
17998         this.collapse(false, false);
17999         while(this.firstChild){
18000             this.removeChild(this.firstChild);
18001         }
18002         this.childrenRendered = false;
18003         this.loaded = false;
18004         if(this.isHiddenRoot()){
18005             this.expanded = false;
18006         }
18007         this.expand(false, false, callback);
18008     }
18009 });/*
18010  * Based on:
18011  * Ext JS Library 1.1.1
18012  * Copyright(c) 2006-2007, Ext JS, LLC.
18013  *
18014  * Originally Released Under LGPL - original licence link has changed is not relivant.
18015  *
18016  * Fork - LGPL
18017  * <script type="text/javascript">
18018  */
18019  
18020 /**
18021  * @class Roo.tree.TreeNodeUI
18022  * @constructor
18023  * @param {Object} node The node to render
18024  * The TreeNode UI implementation is separate from the
18025  * tree implementation. Unless you are customizing the tree UI,
18026  * you should never have to use this directly.
18027  */
18028 Roo.tree.TreeNodeUI = function(node){
18029     this.node = node;
18030     this.rendered = false;
18031     this.animating = false;
18032     this.emptyIcon = Roo.BLANK_IMAGE_URL;
18033 };
18034
18035 Roo.tree.TreeNodeUI.prototype = {
18036     removeChild : function(node){
18037         if(this.rendered){
18038             this.ctNode.removeChild(node.ui.getEl());
18039         }
18040     },
18041
18042     beforeLoad : function(){
18043          this.addClass("x-tree-node-loading");
18044     },
18045
18046     afterLoad : function(){
18047          this.removeClass("x-tree-node-loading");
18048     },
18049
18050     onTextChange : function(node, text, oldText){
18051         if(this.rendered){
18052             this.textNode.innerHTML = text;
18053         }
18054     },
18055
18056     onDisableChange : function(node, state){
18057         this.disabled = state;
18058         if(state){
18059             this.addClass("x-tree-node-disabled");
18060         }else{
18061             this.removeClass("x-tree-node-disabled");
18062         }
18063     },
18064
18065     onSelectedChange : function(state){
18066         if(state){
18067             this.focus();
18068             this.addClass("x-tree-selected");
18069         }else{
18070             //this.blur();
18071             this.removeClass("x-tree-selected");
18072         }
18073     },
18074
18075     onMove : function(tree, node, oldParent, newParent, index, refNode){
18076         this.childIndent = null;
18077         if(this.rendered){
18078             var targetNode = newParent.ui.getContainer();
18079             if(!targetNode){//target not rendered
18080                 this.holder = document.createElement("div");
18081                 this.holder.appendChild(this.wrap);
18082                 return;
18083             }
18084             var insertBefore = refNode ? refNode.ui.getEl() : null;
18085             if(insertBefore){
18086                 targetNode.insertBefore(this.wrap, insertBefore);
18087             }else{
18088                 targetNode.appendChild(this.wrap);
18089             }
18090             this.node.renderIndent(true);
18091         }
18092     },
18093
18094     addClass : function(cls){
18095         if(this.elNode){
18096             Roo.fly(this.elNode).addClass(cls);
18097         }
18098     },
18099
18100     removeClass : function(cls){
18101         if(this.elNode){
18102             Roo.fly(this.elNode).removeClass(cls);
18103         }
18104     },
18105
18106     remove : function(){
18107         if(this.rendered){
18108             this.holder = document.createElement("div");
18109             this.holder.appendChild(this.wrap);
18110         }
18111     },
18112
18113     fireEvent : function(){
18114         return this.node.fireEvent.apply(this.node, arguments);
18115     },
18116
18117     initEvents : function(){
18118         this.node.on("move", this.onMove, this);
18119         var E = Roo.EventManager;
18120         var a = this.anchor;
18121
18122         var el = Roo.fly(a, '_treeui');
18123
18124         if(Roo.isOpera){ // opera render bug ignores the CSS
18125             el.setStyle("text-decoration", "none");
18126         }
18127
18128         el.on("click", this.onClick, this);
18129         el.on("dblclick", this.onDblClick, this);
18130
18131         if(this.checkbox){
18132             Roo.EventManager.on(this.checkbox,
18133                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
18134         }
18135
18136         el.on("contextmenu", this.onContextMenu, this);
18137
18138         var icon = Roo.fly(this.iconNode);
18139         icon.on("click", this.onClick, this);
18140         icon.on("dblclick", this.onDblClick, this);
18141         icon.on("contextmenu", this.onContextMenu, this);
18142         E.on(this.ecNode, "click", this.ecClick, this, true);
18143
18144         if(this.node.disabled){
18145             this.addClass("x-tree-node-disabled");
18146         }
18147         if(this.node.hidden){
18148             this.addClass("x-tree-node-disabled");
18149         }
18150         var ot = this.node.getOwnerTree();
18151         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
18152         if(dd && (!this.node.isRoot || ot.rootVisible)){
18153             Roo.dd.Registry.register(this.elNode, {
18154                 node: this.node,
18155                 handles: this.getDDHandles(),
18156                 isHandle: false
18157             });
18158         }
18159     },
18160
18161     getDDHandles : function(){
18162         return [this.iconNode, this.textNode];
18163     },
18164
18165     hide : function(){
18166         if(this.rendered){
18167             this.wrap.style.display = "none";
18168         }
18169     },
18170
18171     show : function(){
18172         if(this.rendered){
18173             this.wrap.style.display = "";
18174         }
18175     },
18176
18177     onContextMenu : function(e){
18178         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
18179             e.preventDefault();
18180             this.focus();
18181             this.fireEvent("contextmenu", this.node, e);
18182         }
18183     },
18184
18185     onClick : function(e){
18186         if(this.dropping){
18187             e.stopEvent();
18188             return;
18189         }
18190         if(this.fireEvent("beforeclick", this.node, e) !== false){
18191             if(!this.disabled && this.node.attributes.href){
18192                 this.fireEvent("click", this.node, e);
18193                 return;
18194             }
18195             e.preventDefault();
18196             if(this.disabled){
18197                 return;
18198             }
18199
18200             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
18201                 this.node.toggle();
18202             }
18203
18204             this.fireEvent("click", this.node, e);
18205         }else{
18206             e.stopEvent();
18207         }
18208     },
18209
18210     onDblClick : function(e){
18211         e.preventDefault();
18212         if(this.disabled){
18213             return;
18214         }
18215         if(this.checkbox){
18216             this.toggleCheck();
18217         }
18218         if(!this.animating && this.node.hasChildNodes()){
18219             this.node.toggle();
18220         }
18221         this.fireEvent("dblclick", this.node, e);
18222     },
18223
18224     onCheckChange : function(){
18225         var checked = this.checkbox.checked;
18226         this.node.attributes.checked = checked;
18227         this.fireEvent('checkchange', this.node, checked);
18228     },
18229
18230     ecClick : function(e){
18231         if(!this.animating && this.node.hasChildNodes()){
18232             this.node.toggle();
18233         }
18234     },
18235
18236     startDrop : function(){
18237         this.dropping = true;
18238     },
18239
18240     // delayed drop so the click event doesn't get fired on a drop
18241     endDrop : function(){
18242        setTimeout(function(){
18243            this.dropping = false;
18244        }.createDelegate(this), 50);
18245     },
18246
18247     expand : function(){
18248         this.updateExpandIcon();
18249         this.ctNode.style.display = "";
18250     },
18251
18252     focus : function(){
18253         if(!this.node.preventHScroll){
18254             try{this.anchor.focus();
18255             }catch(e){}
18256         }else if(!Roo.isIE){
18257             try{
18258                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
18259                 var l = noscroll.scrollLeft;
18260                 this.anchor.focus();
18261                 noscroll.scrollLeft = l;
18262             }catch(e){}
18263         }
18264     },
18265
18266     toggleCheck : function(value){
18267         var cb = this.checkbox;
18268         if(cb){
18269             cb.checked = (value === undefined ? !cb.checked : value);
18270         }
18271     },
18272
18273     blur : function(){
18274         try{
18275             this.anchor.blur();
18276         }catch(e){}
18277     },
18278
18279     animExpand : function(callback){
18280         var ct = Roo.get(this.ctNode);
18281         ct.stopFx();
18282         if(!this.node.hasChildNodes()){
18283             this.updateExpandIcon();
18284             this.ctNode.style.display = "";
18285             Roo.callback(callback);
18286             return;
18287         }
18288         this.animating = true;
18289         this.updateExpandIcon();
18290
18291         ct.slideIn('t', {
18292            callback : function(){
18293                this.animating = false;
18294                Roo.callback(callback);
18295             },
18296             scope: this,
18297             duration: this.node.ownerTree.duration || .25
18298         });
18299     },
18300
18301     highlight : function(){
18302         var tree = this.node.getOwnerTree();
18303         Roo.fly(this.wrap).highlight(
18304             tree.hlColor || "C3DAF9",
18305             {endColor: tree.hlBaseColor}
18306         );
18307     },
18308
18309     collapse : function(){
18310         this.updateExpandIcon();
18311         this.ctNode.style.display = "none";
18312     },
18313
18314     animCollapse : function(callback){
18315         var ct = Roo.get(this.ctNode);
18316         ct.enableDisplayMode('block');
18317         ct.stopFx();
18318
18319         this.animating = true;
18320         this.updateExpandIcon();
18321
18322         ct.slideOut('t', {
18323             callback : function(){
18324                this.animating = false;
18325                Roo.callback(callback);
18326             },
18327             scope: this,
18328             duration: this.node.ownerTree.duration || .25
18329         });
18330     },
18331
18332     getContainer : function(){
18333         return this.ctNode;
18334     },
18335
18336     getEl : function(){
18337         return this.wrap;
18338     },
18339
18340     appendDDGhost : function(ghostNode){
18341         ghostNode.appendChild(this.elNode.cloneNode(true));
18342     },
18343
18344     getDDRepairXY : function(){
18345         return Roo.lib.Dom.getXY(this.iconNode);
18346     },
18347
18348     onRender : function(){
18349         this.render();
18350     },
18351
18352     render : function(bulkRender){
18353         var n = this.node, a = n.attributes;
18354         var targetNode = n.parentNode ?
18355               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
18356
18357         if(!this.rendered){
18358             this.rendered = true;
18359
18360             this.renderElements(n, a, targetNode, bulkRender);
18361
18362             if(a.qtip){
18363                if(this.textNode.setAttributeNS){
18364                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
18365                    if(a.qtipTitle){
18366                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
18367                    }
18368                }else{
18369                    this.textNode.setAttribute("ext:qtip", a.qtip);
18370                    if(a.qtipTitle){
18371                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
18372                    }
18373                }
18374             }else if(a.qtipCfg){
18375                 a.qtipCfg.target = Roo.id(this.textNode);
18376                 Roo.QuickTips.register(a.qtipCfg);
18377             }
18378             this.initEvents();
18379             if(!this.node.expanded){
18380                 this.updateExpandIcon();
18381             }
18382         }else{
18383             if(bulkRender === true) {
18384                 targetNode.appendChild(this.wrap);
18385             }
18386         }
18387     },
18388
18389     renderElements : function(n, a, targetNode, bulkRender)
18390     {
18391         // add some indent caching, this helps performance when rendering a large tree
18392         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
18393         var t = n.getOwnerTree();
18394         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
18395         if (typeof(n.attributes.html) != 'undefined') {
18396             txt = n.attributes.html;
18397         }
18398         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
18399         var cb = typeof a.checked == 'boolean';
18400         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
18401         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
18402             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
18403             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
18404             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
18405             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
18406             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
18407              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
18408                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
18409             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
18410             "</li>"];
18411
18412         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
18413             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
18414                                 n.nextSibling.ui.getEl(), buf.join(""));
18415         }else{
18416             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
18417         }
18418
18419         this.elNode = this.wrap.childNodes[0];
18420         this.ctNode = this.wrap.childNodes[1];
18421         var cs = this.elNode.childNodes;
18422         this.indentNode = cs[0];
18423         this.ecNode = cs[1];
18424         this.iconNode = cs[2];
18425         var index = 3;
18426         if(cb){
18427             this.checkbox = cs[3];
18428             index++;
18429         }
18430         this.anchor = cs[index];
18431         this.textNode = cs[index].firstChild;
18432     },
18433
18434     getAnchor : function(){
18435         return this.anchor;
18436     },
18437
18438     getTextEl : function(){
18439         return this.textNode;
18440     },
18441
18442     getIconEl : function(){
18443         return this.iconNode;
18444     },
18445
18446     isChecked : function(){
18447         return this.checkbox ? this.checkbox.checked : false;
18448     },
18449
18450     updateExpandIcon : function(){
18451         if(this.rendered){
18452             var n = this.node, c1, c2;
18453             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
18454             var hasChild = n.hasChildNodes();
18455             if(hasChild){
18456                 if(n.expanded){
18457                     cls += "-minus";
18458                     c1 = "x-tree-node-collapsed";
18459                     c2 = "x-tree-node-expanded";
18460                 }else{
18461                     cls += "-plus";
18462                     c1 = "x-tree-node-expanded";
18463                     c2 = "x-tree-node-collapsed";
18464                 }
18465                 if(this.wasLeaf){
18466                     this.removeClass("x-tree-node-leaf");
18467                     this.wasLeaf = false;
18468                 }
18469                 if(this.c1 != c1 || this.c2 != c2){
18470                     Roo.fly(this.elNode).replaceClass(c1, c2);
18471                     this.c1 = c1; this.c2 = c2;
18472                 }
18473             }else{
18474                 // this changes non-leafs into leafs if they have no children.
18475                 // it's not very rational behaviour..
18476                 
18477                 if(!this.wasLeaf && this.node.leaf){
18478                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
18479                     delete this.c1;
18480                     delete this.c2;
18481                     this.wasLeaf = true;
18482                 }
18483             }
18484             var ecc = "x-tree-ec-icon "+cls;
18485             if(this.ecc != ecc){
18486                 this.ecNode.className = ecc;
18487                 this.ecc = ecc;
18488             }
18489         }
18490     },
18491
18492     getChildIndent : function(){
18493         if(!this.childIndent){
18494             var buf = [];
18495             var p = this.node;
18496             while(p){
18497                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
18498                     if(!p.isLast()) {
18499                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
18500                     } else {
18501                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
18502                     }
18503                 }
18504                 p = p.parentNode;
18505             }
18506             this.childIndent = buf.join("");
18507         }
18508         return this.childIndent;
18509     },
18510
18511     renderIndent : function(){
18512         if(this.rendered){
18513             var indent = "";
18514             var p = this.node.parentNode;
18515             if(p){
18516                 indent = p.ui.getChildIndent();
18517             }
18518             if(this.indentMarkup != indent){ // don't rerender if not required
18519                 this.indentNode.innerHTML = indent;
18520                 this.indentMarkup = indent;
18521             }
18522             this.updateExpandIcon();
18523         }
18524     }
18525 };
18526
18527 Roo.tree.RootTreeNodeUI = function(){
18528     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
18529 };
18530 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
18531     render : function(){
18532         if(!this.rendered){
18533             var targetNode = this.node.ownerTree.innerCt.dom;
18534             this.node.expanded = true;
18535             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
18536             this.wrap = this.ctNode = targetNode.firstChild;
18537         }
18538     },
18539     collapse : function(){
18540     },
18541     expand : function(){
18542     }
18543 });/*
18544  * Based on:
18545  * Ext JS Library 1.1.1
18546  * Copyright(c) 2006-2007, Ext JS, LLC.
18547  *
18548  * Originally Released Under LGPL - original licence link has changed is not relivant.
18549  *
18550  * Fork - LGPL
18551  * <script type="text/javascript">
18552  */
18553 /**
18554  * @class Roo.tree.TreeLoader
18555  * @extends Roo.util.Observable
18556  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
18557  * nodes from a specified URL. The response must be a javascript Array definition
18558  * who's elements are node definition objects. eg:
18559  * <pre><code>
18560 {  success : true,
18561    data :      [
18562    
18563     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
18564     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
18565     ]
18566 }
18567
18568
18569 </code></pre>
18570  * <br><br>
18571  * The old style respose with just an array is still supported, but not recommended.
18572  * <br><br>
18573  *
18574  * A server request is sent, and child nodes are loaded only when a node is expanded.
18575  * The loading node's id is passed to the server under the parameter name "node" to
18576  * enable the server to produce the correct child nodes.
18577  * <br><br>
18578  * To pass extra parameters, an event handler may be attached to the "beforeload"
18579  * event, and the parameters specified in the TreeLoader's baseParams property:
18580  * <pre><code>
18581     myTreeLoader.on("beforeload", function(treeLoader, node) {
18582         this.baseParams.category = node.attributes.category;
18583     }, this);
18584 </code></pre><
18585  * This would pass an HTTP parameter called "category" to the server containing
18586  * the value of the Node's "category" attribute.
18587  * @constructor
18588  * Creates a new Treeloader.
18589  * @param {Object} config A config object containing config properties.
18590  */
18591 Roo.tree.TreeLoader = function(config){
18592     this.baseParams = {};
18593     this.requestMethod = "POST";
18594     Roo.apply(this, config);
18595
18596     this.addEvents({
18597     
18598         /**
18599          * @event beforeload
18600          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
18601          * @param {Object} This TreeLoader object.
18602          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18603          * @param {Object} callback The callback function specified in the {@link #load} call.
18604          */
18605         beforeload : true,
18606         /**
18607          * @event load
18608          * Fires when the node has been successfuly loaded.
18609          * @param {Object} This TreeLoader object.
18610          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18611          * @param {Object} response The response object containing the data from the server.
18612          */
18613         load : true,
18614         /**
18615          * @event loadexception
18616          * Fires if the network request failed.
18617          * @param {Object} This TreeLoader object.
18618          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18619          * @param {Object} response The response object containing the data from the server.
18620          */
18621         loadexception : true,
18622         /**
18623          * @event create
18624          * Fires before a node is created, enabling you to return custom Node types 
18625          * @param {Object} This TreeLoader object.
18626          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
18627          */
18628         create : true
18629     });
18630
18631     Roo.tree.TreeLoader.superclass.constructor.call(this);
18632 };
18633
18634 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
18635     /**
18636     * @cfg {String} dataUrl The URL from which to request a Json string which
18637     * specifies an array of node definition object representing the child nodes
18638     * to be loaded.
18639     */
18640     /**
18641     * @cfg {String} requestMethod either GET or POST
18642     * defaults to POST (due to BC)
18643     * to be loaded.
18644     */
18645     /**
18646     * @cfg {Object} baseParams (optional) An object containing properties which
18647     * specify HTTP parameters to be passed to each request for child nodes.
18648     */
18649     /**
18650     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
18651     * created by this loader. If the attributes sent by the server have an attribute in this object,
18652     * they take priority.
18653     */
18654     /**
18655     * @cfg {Object} uiProviders (optional) An object containing properties which
18656     * 
18657     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
18658     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
18659     * <i>uiProvider</i> attribute of a returned child node is a string rather
18660     * than a reference to a TreeNodeUI implementation, this that string value
18661     * is used as a property name in the uiProviders object. You can define the provider named
18662     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
18663     */
18664     uiProviders : {},
18665
18666     /**
18667     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
18668     * child nodes before loading.
18669     */
18670     clearOnLoad : true,
18671
18672     /**
18673     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
18674     * property on loading, rather than expecting an array. (eg. more compatible to a standard
18675     * Grid query { data : [ .....] }
18676     */
18677     
18678     root : false,
18679      /**
18680     * @cfg {String} queryParam (optional) 
18681     * Name of the query as it will be passed on the querystring (defaults to 'node')
18682     * eg. the request will be ?node=[id]
18683     */
18684     
18685     
18686     queryParam: false,
18687     
18688     /**
18689      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
18690      * This is called automatically when a node is expanded, but may be used to reload
18691      * a node (or append new children if the {@link #clearOnLoad} option is false.)
18692      * @param {Roo.tree.TreeNode} node
18693      * @param {Function} callback
18694      */
18695     load : function(node, callback){
18696         if(this.clearOnLoad){
18697             while(node.firstChild){
18698                 node.removeChild(node.firstChild);
18699             }
18700         }
18701         if(node.attributes.children){ // preloaded json children
18702             var cs = node.attributes.children;
18703             for(var i = 0, len = cs.length; i < len; i++){
18704                 node.appendChild(this.createNode(cs[i]));
18705             }
18706             if(typeof callback == "function"){
18707                 callback();
18708             }
18709         }else if(this.dataUrl){
18710             this.requestData(node, callback);
18711         }
18712     },
18713
18714     getParams: function(node){
18715         var buf = [], bp = this.baseParams;
18716         for(var key in bp){
18717             if(typeof bp[key] != "function"){
18718                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
18719             }
18720         }
18721         var n = this.queryParam === false ? 'node' : this.queryParam;
18722         buf.push(n + "=", encodeURIComponent(node.id));
18723         return buf.join("");
18724     },
18725
18726     requestData : function(node, callback){
18727         if(this.fireEvent("beforeload", this, node, callback) !== false){
18728             this.transId = Roo.Ajax.request({
18729                 method:this.requestMethod,
18730                 url: this.dataUrl||this.url,
18731                 success: this.handleResponse,
18732                 failure: this.handleFailure,
18733                 scope: this,
18734                 argument: {callback: callback, node: node},
18735                 params: this.getParams(node)
18736             });
18737         }else{
18738             // if the load is cancelled, make sure we notify
18739             // the node that we are done
18740             if(typeof callback == "function"){
18741                 callback();
18742             }
18743         }
18744     },
18745
18746     isLoading : function(){
18747         return this.transId ? true : false;
18748     },
18749
18750     abort : function(){
18751         if(this.isLoading()){
18752             Roo.Ajax.abort(this.transId);
18753         }
18754     },
18755
18756     // private
18757     createNode : function(attr)
18758     {
18759         // apply baseAttrs, nice idea Corey!
18760         if(this.baseAttrs){
18761             Roo.applyIf(attr, this.baseAttrs);
18762         }
18763         if(this.applyLoader !== false){
18764             attr.loader = this;
18765         }
18766         // uiProvider = depreciated..
18767         
18768         if(typeof(attr.uiProvider) == 'string'){
18769            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
18770                 /**  eval:var:attr */ eval(attr.uiProvider);
18771         }
18772         if(typeof(this.uiProviders['default']) != 'undefined') {
18773             attr.uiProvider = this.uiProviders['default'];
18774         }
18775         
18776         this.fireEvent('create', this, attr);
18777         
18778         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
18779         return(attr.leaf ?
18780                         new Roo.tree.TreeNode(attr) :
18781                         new Roo.tree.AsyncTreeNode(attr));
18782     },
18783
18784     processResponse : function(response, node, callback)
18785     {
18786         var json = response.responseText;
18787         try {
18788             
18789             var o = Roo.decode(json);
18790             
18791             if (this.root === false && typeof(o.success) != undefined) {
18792                 this.root = 'data'; // the default behaviour for list like data..
18793                 }
18794                 
18795             if (this.root !== false &&  !o.success) {
18796                 // it's a failure condition.
18797                 var a = response.argument;
18798                 this.fireEvent("loadexception", this, a.node, response);
18799                 Roo.log("Load failed - should have a handler really");
18800                 return;
18801             }
18802             
18803             
18804             
18805             if (this.root !== false) {
18806                  o = o[this.root];
18807             }
18808             
18809             for(var i = 0, len = o.length; i < len; i++){
18810                 var n = this.createNode(o[i]);
18811                 if(n){
18812                     node.appendChild(n);
18813                 }
18814             }
18815             if(typeof callback == "function"){
18816                 callback(this, node);
18817             }
18818         }catch(e){
18819             this.handleFailure(response);
18820         }
18821     },
18822
18823     handleResponse : function(response){
18824         this.transId = false;
18825         var a = response.argument;
18826         this.processResponse(response, a.node, a.callback);
18827         this.fireEvent("load", this, a.node, response);
18828     },
18829
18830     handleFailure : function(response)
18831     {
18832         // should handle failure better..
18833         this.transId = false;
18834         var a = response.argument;
18835         this.fireEvent("loadexception", this, a.node, response);
18836         if(typeof a.callback == "function"){
18837             a.callback(this, a.node);
18838         }
18839     }
18840 });/*
18841  * Based on:
18842  * Ext JS Library 1.1.1
18843  * Copyright(c) 2006-2007, Ext JS, LLC.
18844  *
18845  * Originally Released Under LGPL - original licence link has changed is not relivant.
18846  *
18847  * Fork - LGPL
18848  * <script type="text/javascript">
18849  */
18850
18851 /**
18852 * @class Roo.tree.TreeFilter
18853 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
18854 * @param {TreePanel} tree
18855 * @param {Object} config (optional)
18856  */
18857 Roo.tree.TreeFilter = function(tree, config){
18858     this.tree = tree;
18859     this.filtered = {};
18860     Roo.apply(this, config);
18861 };
18862
18863 Roo.tree.TreeFilter.prototype = {
18864     clearBlank:false,
18865     reverse:false,
18866     autoClear:false,
18867     remove:false,
18868
18869      /**
18870      * Filter the data by a specific attribute.
18871      * @param {String/RegExp} value Either string that the attribute value
18872      * should start with or a RegExp to test against the attribute
18873      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
18874      * @param {TreeNode} startNode (optional) The node to start the filter at.
18875      */
18876     filter : function(value, attr, startNode){
18877         attr = attr || "text";
18878         var f;
18879         if(typeof value == "string"){
18880             var vlen = value.length;
18881             // auto clear empty filter
18882             if(vlen == 0 && this.clearBlank){
18883                 this.clear();
18884                 return;
18885             }
18886             value = value.toLowerCase();
18887             f = function(n){
18888                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
18889             };
18890         }else if(value.exec){ // regex?
18891             f = function(n){
18892                 return value.test(n.attributes[attr]);
18893             };
18894         }else{
18895             throw 'Illegal filter type, must be string or regex';
18896         }
18897         this.filterBy(f, null, startNode);
18898         },
18899
18900     /**
18901      * Filter by a function. The passed function will be called with each
18902      * node in the tree (or from the startNode). If the function returns true, the node is kept
18903      * otherwise it is filtered. If a node is filtered, its children are also filtered.
18904      * @param {Function} fn The filter function
18905      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
18906      */
18907     filterBy : function(fn, scope, startNode){
18908         startNode = startNode || this.tree.root;
18909         if(this.autoClear){
18910             this.clear();
18911         }
18912         var af = this.filtered, rv = this.reverse;
18913         var f = function(n){
18914             if(n == startNode){
18915                 return true;
18916             }
18917             if(af[n.id]){
18918                 return false;
18919             }
18920             var m = fn.call(scope || n, n);
18921             if(!m || rv){
18922                 af[n.id] = n;
18923                 n.ui.hide();
18924                 return false;
18925             }
18926             return true;
18927         };
18928         startNode.cascade(f);
18929         if(this.remove){
18930            for(var id in af){
18931                if(typeof id != "function"){
18932                    var n = af[id];
18933                    if(n && n.parentNode){
18934                        n.parentNode.removeChild(n);
18935                    }
18936                }
18937            }
18938         }
18939     },
18940
18941     /**
18942      * Clears the current filter. Note: with the "remove" option
18943      * set a filter cannot be cleared.
18944      */
18945     clear : function(){
18946         var t = this.tree;
18947         var af = this.filtered;
18948         for(var id in af){
18949             if(typeof id != "function"){
18950                 var n = af[id];
18951                 if(n){
18952                     n.ui.show();
18953                 }
18954             }
18955         }
18956         this.filtered = {};
18957     }
18958 };
18959 /*
18960  * Based on:
18961  * Ext JS Library 1.1.1
18962  * Copyright(c) 2006-2007, Ext JS, LLC.
18963  *
18964  * Originally Released Under LGPL - original licence link has changed is not relivant.
18965  *
18966  * Fork - LGPL
18967  * <script type="text/javascript">
18968  */
18969  
18970
18971 /**
18972  * @class Roo.tree.TreeSorter
18973  * Provides sorting of nodes in a TreePanel
18974  * 
18975  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
18976  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
18977  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
18978  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
18979  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
18980  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
18981  * @constructor
18982  * @param {TreePanel} tree
18983  * @param {Object} config
18984  */
18985 Roo.tree.TreeSorter = function(tree, config){
18986     Roo.apply(this, config);
18987     tree.on("beforechildrenrendered", this.doSort, this);
18988     tree.on("append", this.updateSort, this);
18989     tree.on("insert", this.updateSort, this);
18990     
18991     var dsc = this.dir && this.dir.toLowerCase() == "desc";
18992     var p = this.property || "text";
18993     var sortType = this.sortType;
18994     var fs = this.folderSort;
18995     var cs = this.caseSensitive === true;
18996     var leafAttr = this.leafAttr || 'leaf';
18997
18998     this.sortFn = function(n1, n2){
18999         if(fs){
19000             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
19001                 return 1;
19002             }
19003             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
19004                 return -1;
19005             }
19006         }
19007         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
19008         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
19009         if(v1 < v2){
19010                         return dsc ? +1 : -1;
19011                 }else if(v1 > v2){
19012                         return dsc ? -1 : +1;
19013         }else{
19014                 return 0;
19015         }
19016     };
19017 };
19018
19019 Roo.tree.TreeSorter.prototype = {
19020     doSort : function(node){
19021         node.sort(this.sortFn);
19022     },
19023     
19024     compareNodes : function(n1, n2){
19025         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
19026     },
19027     
19028     updateSort : function(tree, node){
19029         if(node.childrenRendered){
19030             this.doSort.defer(1, this, [node]);
19031         }
19032     }
19033 };/*
19034  * Based on:
19035  * Ext JS Library 1.1.1
19036  * Copyright(c) 2006-2007, Ext JS, LLC.
19037  *
19038  * Originally Released Under LGPL - original licence link has changed is not relivant.
19039  *
19040  * Fork - LGPL
19041  * <script type="text/javascript">
19042  */
19043
19044 if(Roo.dd.DropZone){
19045     
19046 Roo.tree.TreeDropZone = function(tree, config){
19047     this.allowParentInsert = false;
19048     this.allowContainerDrop = false;
19049     this.appendOnly = false;
19050     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
19051     this.tree = tree;
19052     this.lastInsertClass = "x-tree-no-status";
19053     this.dragOverData = {};
19054 };
19055
19056 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
19057     ddGroup : "TreeDD",
19058     scroll:  true,
19059     
19060     expandDelay : 1000,
19061     
19062     expandNode : function(node){
19063         if(node.hasChildNodes() && !node.isExpanded()){
19064             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
19065         }
19066     },
19067     
19068     queueExpand : function(node){
19069         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
19070     },
19071     
19072     cancelExpand : function(){
19073         if(this.expandProcId){
19074             clearTimeout(this.expandProcId);
19075             this.expandProcId = false;
19076         }
19077     },
19078     
19079     isValidDropPoint : function(n, pt, dd, e, data){
19080         if(!n || !data){ return false; }
19081         var targetNode = n.node;
19082         var dropNode = data.node;
19083         // default drop rules
19084         if(!(targetNode && targetNode.isTarget && pt)){
19085             return false;
19086         }
19087         if(pt == "append" && targetNode.allowChildren === false){
19088             return false;
19089         }
19090         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
19091             return false;
19092         }
19093         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
19094             return false;
19095         }
19096         // reuse the object
19097         var overEvent = this.dragOverData;
19098         overEvent.tree = this.tree;
19099         overEvent.target = targetNode;
19100         overEvent.data = data;
19101         overEvent.point = pt;
19102         overEvent.source = dd;
19103         overEvent.rawEvent = e;
19104         overEvent.dropNode = dropNode;
19105         overEvent.cancel = false;  
19106         var result = this.tree.fireEvent("nodedragover", overEvent);
19107         return overEvent.cancel === false && result !== false;
19108     },
19109     
19110     getDropPoint : function(e, n, dd)
19111     {
19112         var tn = n.node;
19113         if(tn.isRoot){
19114             return tn.allowChildren !== false ? "append" : false; // always append for root
19115         }
19116         var dragEl = n.ddel;
19117         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
19118         var y = Roo.lib.Event.getPageY(e);
19119         //var noAppend = tn.allowChildren === false || tn.isLeaf();
19120         
19121         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
19122         var noAppend = tn.allowChildren === false;
19123         if(this.appendOnly || tn.parentNode.allowChildren === false){
19124             return noAppend ? false : "append";
19125         }
19126         var noBelow = false;
19127         if(!this.allowParentInsert){
19128             noBelow = tn.hasChildNodes() && tn.isExpanded();
19129         }
19130         var q = (b - t) / (noAppend ? 2 : 3);
19131         if(y >= t && y < (t + q)){
19132             return "above";
19133         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
19134             return "below";
19135         }else{
19136             return "append";
19137         }
19138     },
19139     
19140     onNodeEnter : function(n, dd, e, data)
19141     {
19142         this.cancelExpand();
19143     },
19144     
19145     onNodeOver : function(n, dd, e, data)
19146     {
19147        
19148         var pt = this.getDropPoint(e, n, dd);
19149         var node = n.node;
19150         
19151         // auto node expand check
19152         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
19153             this.queueExpand(node);
19154         }else if(pt != "append"){
19155             this.cancelExpand();
19156         }
19157         
19158         // set the insert point style on the target node
19159         var returnCls = this.dropNotAllowed;
19160         if(this.isValidDropPoint(n, pt, dd, e, data)){
19161            if(pt){
19162                var el = n.ddel;
19163                var cls;
19164                if(pt == "above"){
19165                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
19166                    cls = "x-tree-drag-insert-above";
19167                }else if(pt == "below"){
19168                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
19169                    cls = "x-tree-drag-insert-below";
19170                }else{
19171                    returnCls = "x-tree-drop-ok-append";
19172                    cls = "x-tree-drag-append";
19173                }
19174                if(this.lastInsertClass != cls){
19175                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
19176                    this.lastInsertClass = cls;
19177                }
19178            }
19179        }
19180        return returnCls;
19181     },
19182     
19183     onNodeOut : function(n, dd, e, data){
19184         
19185         this.cancelExpand();
19186         this.removeDropIndicators(n);
19187     },
19188     
19189     onNodeDrop : function(n, dd, e, data){
19190         var point = this.getDropPoint(e, n, dd);
19191         var targetNode = n.node;
19192         targetNode.ui.startDrop();
19193         if(!this.isValidDropPoint(n, point, dd, e, data)){
19194             targetNode.ui.endDrop();
19195             return false;
19196         }
19197         // first try to find the drop node
19198         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
19199         var dropEvent = {
19200             tree : this.tree,
19201             target: targetNode,
19202             data: data,
19203             point: point,
19204             source: dd,
19205             rawEvent: e,
19206             dropNode: dropNode,
19207             cancel: !dropNode   
19208         };
19209         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
19210         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
19211             targetNode.ui.endDrop();
19212             return false;
19213         }
19214         // allow target changing
19215         targetNode = dropEvent.target;
19216         if(point == "append" && !targetNode.isExpanded()){
19217             targetNode.expand(false, null, function(){
19218                 this.completeDrop(dropEvent);
19219             }.createDelegate(this));
19220         }else{
19221             this.completeDrop(dropEvent);
19222         }
19223         return true;
19224     },
19225     
19226     completeDrop : function(de){
19227         var ns = de.dropNode, p = de.point, t = de.target;
19228         if(!(ns instanceof Array)){
19229             ns = [ns];
19230         }
19231         var n;
19232         for(var i = 0, len = ns.length; i < len; i++){
19233             n = ns[i];
19234             if(p == "above"){
19235                 t.parentNode.insertBefore(n, t);
19236             }else if(p == "below"){
19237                 t.parentNode.insertBefore(n, t.nextSibling);
19238             }else{
19239                 t.appendChild(n);
19240             }
19241         }
19242         n.ui.focus();
19243         if(this.tree.hlDrop){
19244             n.ui.highlight();
19245         }
19246         t.ui.endDrop();
19247         this.tree.fireEvent("nodedrop", de);
19248     },
19249     
19250     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
19251         if(this.tree.hlDrop){
19252             dropNode.ui.focus();
19253             dropNode.ui.highlight();
19254         }
19255         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
19256     },
19257     
19258     getTree : function(){
19259         return this.tree;
19260     },
19261     
19262     removeDropIndicators : function(n){
19263         if(n && n.ddel){
19264             var el = n.ddel;
19265             Roo.fly(el).removeClass([
19266                     "x-tree-drag-insert-above",
19267                     "x-tree-drag-insert-below",
19268                     "x-tree-drag-append"]);
19269             this.lastInsertClass = "_noclass";
19270         }
19271     },
19272     
19273     beforeDragDrop : function(target, e, id){
19274         this.cancelExpand();
19275         return true;
19276     },
19277     
19278     afterRepair : function(data){
19279         if(data && Roo.enableFx){
19280             data.node.ui.highlight();
19281         }
19282         this.hideProxy();
19283     } 
19284     
19285 });
19286
19287 }
19288 /*
19289  * Based on:
19290  * Ext JS Library 1.1.1
19291  * Copyright(c) 2006-2007, Ext JS, LLC.
19292  *
19293  * Originally Released Under LGPL - original licence link has changed is not relivant.
19294  *
19295  * Fork - LGPL
19296  * <script type="text/javascript">
19297  */
19298  
19299
19300 if(Roo.dd.DragZone){
19301 Roo.tree.TreeDragZone = function(tree, config){
19302     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
19303     this.tree = tree;
19304 };
19305
19306 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
19307     ddGroup : "TreeDD",
19308    
19309     onBeforeDrag : function(data, e){
19310         var n = data.node;
19311         return n && n.draggable && !n.disabled;
19312     },
19313      
19314     
19315     onInitDrag : function(e){
19316         var data = this.dragData;
19317         this.tree.getSelectionModel().select(data.node);
19318         this.proxy.update("");
19319         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
19320         this.tree.fireEvent("startdrag", this.tree, data.node, e);
19321     },
19322     
19323     getRepairXY : function(e, data){
19324         return data.node.ui.getDDRepairXY();
19325     },
19326     
19327     onEndDrag : function(data, e){
19328         this.tree.fireEvent("enddrag", this.tree, data.node, e);
19329         
19330         
19331     },
19332     
19333     onValidDrop : function(dd, e, id){
19334         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
19335         this.hideProxy();
19336     },
19337     
19338     beforeInvalidDrop : function(e, id){
19339         // this scrolls the original position back into view
19340         var sm = this.tree.getSelectionModel();
19341         sm.clearSelections();
19342         sm.select(this.dragData.node);
19343     }
19344 });
19345 }/*
19346  * Based on:
19347  * Ext JS Library 1.1.1
19348  * Copyright(c) 2006-2007, Ext JS, LLC.
19349  *
19350  * Originally Released Under LGPL - original licence link has changed is not relivant.
19351  *
19352  * Fork - LGPL
19353  * <script type="text/javascript">
19354  */
19355 /**
19356  * @class Roo.tree.TreeEditor
19357  * @extends Roo.Editor
19358  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
19359  * as the editor field.
19360  * @constructor
19361  * @param {Object} config (used to be the tree panel.)
19362  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
19363  * 
19364  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
19365  * @cfg {Roo.form.TextField|Object} field The field configuration
19366  *
19367  * 
19368  */
19369 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
19370     var tree = config;
19371     var field;
19372     if (oldconfig) { // old style..
19373         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
19374     } else {
19375         // new style..
19376         tree = config.tree;
19377         config.field = config.field  || {};
19378         config.field.xtype = 'TextField';
19379         field = Roo.factory(config.field, Roo.form);
19380     }
19381     config = config || {};
19382     
19383     
19384     this.addEvents({
19385         /**
19386          * @event beforenodeedit
19387          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
19388          * false from the handler of this event.
19389          * @param {Editor} this
19390          * @param {Roo.tree.Node} node 
19391          */
19392         "beforenodeedit" : true
19393     });
19394     
19395     //Roo.log(config);
19396     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
19397
19398     this.tree = tree;
19399
19400     tree.on('beforeclick', this.beforeNodeClick, this);
19401     tree.getTreeEl().on('mousedown', this.hide, this);
19402     this.on('complete', this.updateNode, this);
19403     this.on('beforestartedit', this.fitToTree, this);
19404     this.on('startedit', this.bindScroll, this, {delay:10});
19405     this.on('specialkey', this.onSpecialKey, this);
19406 };
19407
19408 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
19409     /**
19410      * @cfg {String} alignment
19411      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
19412      */
19413     alignment: "l-l",
19414     // inherit
19415     autoSize: false,
19416     /**
19417      * @cfg {Boolean} hideEl
19418      * True to hide the bound element while the editor is displayed (defaults to false)
19419      */
19420     hideEl : false,
19421     /**
19422      * @cfg {String} cls
19423      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
19424      */
19425     cls: "x-small-editor x-tree-editor",
19426     /**
19427      * @cfg {Boolean} shim
19428      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
19429      */
19430     shim:false,
19431     // inherit
19432     shadow:"frame",
19433     /**
19434      * @cfg {Number} maxWidth
19435      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
19436      * the containing tree element's size, it will be automatically limited for you to the container width, taking
19437      * scroll and client offsets into account prior to each edit.
19438      */
19439     maxWidth: 250,
19440
19441     editDelay : 350,
19442
19443     // private
19444     fitToTree : function(ed, el){
19445         var td = this.tree.getTreeEl().dom, nd = el.dom;
19446         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
19447             td.scrollLeft = nd.offsetLeft;
19448         }
19449         var w = Math.min(
19450                 this.maxWidth,
19451                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
19452         this.setSize(w, '');
19453         
19454         return this.fireEvent('beforenodeedit', this, this.editNode);
19455         
19456     },
19457
19458     // private
19459     triggerEdit : function(node){
19460         this.completeEdit();
19461         this.editNode = node;
19462         this.startEdit(node.ui.textNode, node.text);
19463     },
19464
19465     // private
19466     bindScroll : function(){
19467         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
19468     },
19469
19470     // private
19471     beforeNodeClick : function(node, e){
19472         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
19473         this.lastClick = new Date();
19474         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
19475             e.stopEvent();
19476             this.triggerEdit(node);
19477             return false;
19478         }
19479         return true;
19480     },
19481
19482     // private
19483     updateNode : function(ed, value){
19484         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
19485         this.editNode.setText(value);
19486     },
19487
19488     // private
19489     onHide : function(){
19490         Roo.tree.TreeEditor.superclass.onHide.call(this);
19491         if(this.editNode){
19492             this.editNode.ui.focus();
19493         }
19494     },
19495
19496     // private
19497     onSpecialKey : function(field, e){
19498         var k = e.getKey();
19499         if(k == e.ESC){
19500             e.stopEvent();
19501             this.cancelEdit();
19502         }else if(k == e.ENTER && !e.hasModifier()){
19503             e.stopEvent();
19504             this.completeEdit();
19505         }
19506     }
19507 });//<Script type="text/javascript">
19508 /*
19509  * Based on:
19510  * Ext JS Library 1.1.1
19511  * Copyright(c) 2006-2007, Ext JS, LLC.
19512  *
19513  * Originally Released Under LGPL - original licence link has changed is not relivant.
19514  *
19515  * Fork - LGPL
19516  * <script type="text/javascript">
19517  */
19518  
19519 /**
19520  * Not documented??? - probably should be...
19521  */
19522
19523 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
19524     //focus: Roo.emptyFn, // prevent odd scrolling behavior
19525     
19526     renderElements : function(n, a, targetNode, bulkRender){
19527         //consel.log("renderElements?");
19528         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
19529
19530         var t = n.getOwnerTree();
19531         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
19532         
19533         var cols = t.columns;
19534         var bw = t.borderWidth;
19535         var c = cols[0];
19536         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
19537          var cb = typeof a.checked == "boolean";
19538         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19539         var colcls = 'x-t-' + tid + '-c0';
19540         var buf = [
19541             '<li class="x-tree-node">',
19542             
19543                 
19544                 '<div class="x-tree-node-el ', a.cls,'">',
19545                     // extran...
19546                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
19547                 
19548                 
19549                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
19550                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
19551                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
19552                            (a.icon ? ' x-tree-node-inline-icon' : ''),
19553                            (a.iconCls ? ' '+a.iconCls : ''),
19554                            '" unselectable="on" />',
19555                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
19556                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
19557                              
19558                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19559                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
19560                             '<span unselectable="on" qtip="' + tx + '">',
19561                              tx,
19562                              '</span></a>' ,
19563                     '</div>',
19564                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19565                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
19566                  ];
19567         for(var i = 1, len = cols.length; i < len; i++){
19568             c = cols[i];
19569             colcls = 'x-t-' + tid + '-c' +i;
19570             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19571             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
19572                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
19573                       "</div>");
19574          }
19575          
19576          buf.push(
19577             '</a>',
19578             '<div class="x-clear"></div></div>',
19579             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
19580             "</li>");
19581         
19582         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
19583             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
19584                                 n.nextSibling.ui.getEl(), buf.join(""));
19585         }else{
19586             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
19587         }
19588         var el = this.wrap.firstChild;
19589         this.elRow = el;
19590         this.elNode = el.firstChild;
19591         this.ranchor = el.childNodes[1];
19592         this.ctNode = this.wrap.childNodes[1];
19593         var cs = el.firstChild.childNodes;
19594         this.indentNode = cs[0];
19595         this.ecNode = cs[1];
19596         this.iconNode = cs[2];
19597         var index = 3;
19598         if(cb){
19599             this.checkbox = cs[3];
19600             index++;
19601         }
19602         this.anchor = cs[index];
19603         
19604         this.textNode = cs[index].firstChild;
19605         
19606         //el.on("click", this.onClick, this);
19607         //el.on("dblclick", this.onDblClick, this);
19608         
19609         
19610        // console.log(this);
19611     },
19612     initEvents : function(){
19613         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
19614         
19615             
19616         var a = this.ranchor;
19617
19618         var el = Roo.get(a);
19619
19620         if(Roo.isOpera){ // opera render bug ignores the CSS
19621             el.setStyle("text-decoration", "none");
19622         }
19623
19624         el.on("click", this.onClick, this);
19625         el.on("dblclick", this.onDblClick, this);
19626         el.on("contextmenu", this.onContextMenu, this);
19627         
19628     },
19629     
19630     /*onSelectedChange : function(state){
19631         if(state){
19632             this.focus();
19633             this.addClass("x-tree-selected");
19634         }else{
19635             //this.blur();
19636             this.removeClass("x-tree-selected");
19637         }
19638     },*/
19639     addClass : function(cls){
19640         if(this.elRow){
19641             Roo.fly(this.elRow).addClass(cls);
19642         }
19643         
19644     },
19645     
19646     
19647     removeClass : function(cls){
19648         if(this.elRow){
19649             Roo.fly(this.elRow).removeClass(cls);
19650         }
19651     }
19652
19653     
19654     
19655 });//<Script type="text/javascript">
19656
19657 /*
19658  * Based on:
19659  * Ext JS Library 1.1.1
19660  * Copyright(c) 2006-2007, Ext JS, LLC.
19661  *
19662  * Originally Released Under LGPL - original licence link has changed is not relivant.
19663  *
19664  * Fork - LGPL
19665  * <script type="text/javascript">
19666  */
19667  
19668
19669 /**
19670  * @class Roo.tree.ColumnTree
19671  * @extends Roo.data.TreePanel
19672  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
19673  * @cfg {int} borderWidth  compined right/left border allowance
19674  * @constructor
19675  * @param {String/HTMLElement/Element} el The container element
19676  * @param {Object} config
19677  */
19678 Roo.tree.ColumnTree =  function(el, config)
19679 {
19680    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
19681    this.addEvents({
19682         /**
19683         * @event resize
19684         * Fire this event on a container when it resizes
19685         * @param {int} w Width
19686         * @param {int} h Height
19687         */
19688        "resize" : true
19689     });
19690     this.on('resize', this.onResize, this);
19691 };
19692
19693 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
19694     //lines:false,
19695     
19696     
19697     borderWidth: Roo.isBorderBox ? 0 : 2, 
19698     headEls : false,
19699     
19700     render : function(){
19701         // add the header.....
19702        
19703         Roo.tree.ColumnTree.superclass.render.apply(this);
19704         
19705         this.el.addClass('x-column-tree');
19706         
19707         this.headers = this.el.createChild(
19708             {cls:'x-tree-headers'},this.innerCt.dom);
19709    
19710         var cols = this.columns, c;
19711         var totalWidth = 0;
19712         this.headEls = [];
19713         var  len = cols.length;
19714         for(var i = 0; i < len; i++){
19715              c = cols[i];
19716              totalWidth += c.width;
19717             this.headEls.push(this.headers.createChild({
19718                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
19719                  cn: {
19720                      cls:'x-tree-hd-text',
19721                      html: c.header
19722                  },
19723                  style:'width:'+(c.width-this.borderWidth)+'px;'
19724              }));
19725         }
19726         this.headers.createChild({cls:'x-clear'});
19727         // prevent floats from wrapping when clipped
19728         this.headers.setWidth(totalWidth);
19729         //this.innerCt.setWidth(totalWidth);
19730         this.innerCt.setStyle({ overflow: 'auto' });
19731         this.onResize(this.width, this.height);
19732              
19733         
19734     },
19735     onResize : function(w,h)
19736     {
19737         this.height = h;
19738         this.width = w;
19739         // resize cols..
19740         this.innerCt.setWidth(this.width);
19741         this.innerCt.setHeight(this.height-20);
19742         
19743         // headers...
19744         var cols = this.columns, c;
19745         var totalWidth = 0;
19746         var expEl = false;
19747         var len = cols.length;
19748         for(var i = 0; i < len; i++){
19749             c = cols[i];
19750             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
19751                 // it's the expander..
19752                 expEl  = this.headEls[i];
19753                 continue;
19754             }
19755             totalWidth += c.width;
19756             
19757         }
19758         if (expEl) {
19759             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
19760         }
19761         this.headers.setWidth(w-20);
19762
19763         
19764         
19765         
19766     }
19767 });
19768 /*
19769  * Based on:
19770  * Ext JS Library 1.1.1
19771  * Copyright(c) 2006-2007, Ext JS, LLC.
19772  *
19773  * Originally Released Under LGPL - original licence link has changed is not relivant.
19774  *
19775  * Fork - LGPL
19776  * <script type="text/javascript">
19777  */
19778  
19779 /**
19780  * @class Roo.menu.Menu
19781  * @extends Roo.util.Observable
19782  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
19783  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
19784  * @constructor
19785  * Creates a new Menu
19786  * @param {Object} config Configuration options
19787  */
19788 Roo.menu.Menu = function(config){
19789     Roo.apply(this, config);
19790     this.id = this.id || Roo.id();
19791     this.addEvents({
19792         /**
19793          * @event beforeshow
19794          * Fires before this menu is displayed
19795          * @param {Roo.menu.Menu} this
19796          */
19797         beforeshow : true,
19798         /**
19799          * @event beforehide
19800          * Fires before this menu is hidden
19801          * @param {Roo.menu.Menu} this
19802          */
19803         beforehide : true,
19804         /**
19805          * @event show
19806          * Fires after this menu is displayed
19807          * @param {Roo.menu.Menu} this
19808          */
19809         show : true,
19810         /**
19811          * @event hide
19812          * Fires after this menu is hidden
19813          * @param {Roo.menu.Menu} this
19814          */
19815         hide : true,
19816         /**
19817          * @event click
19818          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19819          * @param {Roo.menu.Menu} this
19820          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19821          * @param {Roo.EventObject} e
19822          */
19823         click : true,
19824         /**
19825          * @event mouseover
19826          * Fires when the mouse is hovering over this menu
19827          * @param {Roo.menu.Menu} this
19828          * @param {Roo.EventObject} e
19829          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19830          */
19831         mouseover : true,
19832         /**
19833          * @event mouseout
19834          * Fires when the mouse exits this menu
19835          * @param {Roo.menu.Menu} this
19836          * @param {Roo.EventObject} e
19837          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19838          */
19839         mouseout : true,
19840         /**
19841          * @event itemclick
19842          * Fires when a menu item contained in this menu is clicked
19843          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
19844          * @param {Roo.EventObject} e
19845          */
19846         itemclick: true
19847     });
19848     if (this.registerMenu) {
19849         Roo.menu.MenuMgr.register(this);
19850     }
19851     
19852     var mis = this.items;
19853     this.items = new Roo.util.MixedCollection();
19854     if(mis){
19855         this.add.apply(this, mis);
19856     }
19857 };
19858
19859 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
19860     /**
19861      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
19862      */
19863     minWidth : 120,
19864     /**
19865      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
19866      * for bottom-right shadow (defaults to "sides")
19867      */
19868     shadow : "sides",
19869     /**
19870      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
19871      * this menu (defaults to "tl-tr?")
19872      */
19873     subMenuAlign : "tl-tr?",
19874     /**
19875      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
19876      * relative to its element of origin (defaults to "tl-bl?")
19877      */
19878     defaultAlign : "tl-bl?",
19879     /**
19880      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
19881      */
19882     allowOtherMenus : false,
19883     /**
19884      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
19885      */
19886     registerMenu : true,
19887
19888     hidden:true,
19889
19890     // private
19891     render : function(){
19892         if(this.el){
19893             return;
19894         }
19895         var el = this.el = new Roo.Layer({
19896             cls: "x-menu",
19897             shadow:this.shadow,
19898             constrain: false,
19899             parentEl: this.parentEl || document.body,
19900             zindex:15000
19901         });
19902
19903         this.keyNav = new Roo.menu.MenuNav(this);
19904
19905         if(this.plain){
19906             el.addClass("x-menu-plain");
19907         }
19908         if(this.cls){
19909             el.addClass(this.cls);
19910         }
19911         // generic focus element
19912         this.focusEl = el.createChild({
19913             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
19914         });
19915         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
19916         ul.on("click", this.onClick, this);
19917         ul.on("mouseover", this.onMouseOver, this);
19918         ul.on("mouseout", this.onMouseOut, this);
19919         this.items.each(function(item){
19920             if (item.hidden) {
19921                 return;
19922             }
19923             
19924             var li = document.createElement("li");
19925             li.className = "x-menu-list-item";
19926             ul.dom.appendChild(li);
19927             item.render(li, this);
19928         }, this);
19929         this.ul = ul;
19930         this.autoWidth();
19931     },
19932
19933     // private
19934     autoWidth : function(){
19935         var el = this.el, ul = this.ul;
19936         if(!el){
19937             return;
19938         }
19939         var w = this.width;
19940         if(w){
19941             el.setWidth(w);
19942         }else if(Roo.isIE){
19943             el.setWidth(this.minWidth);
19944             var t = el.dom.offsetWidth; // force recalc
19945             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
19946         }
19947     },
19948
19949     // private
19950     delayAutoWidth : function(){
19951         if(this.rendered){
19952             if(!this.awTask){
19953                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
19954             }
19955             this.awTask.delay(20);
19956         }
19957     },
19958
19959     // private
19960     findTargetItem : function(e){
19961         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
19962         if(t && t.menuItemId){
19963             return this.items.get(t.menuItemId);
19964         }
19965     },
19966
19967     // private
19968     onClick : function(e){
19969         var t;
19970         if(t = this.findTargetItem(e)){
19971             t.onClick(e);
19972             this.fireEvent("click", this, t, e);
19973         }
19974     },
19975
19976     // private
19977     setActiveItem : function(item, autoExpand){
19978         if(item != this.activeItem){
19979             if(this.activeItem){
19980                 this.activeItem.deactivate();
19981             }
19982             this.activeItem = item;
19983             item.activate(autoExpand);
19984         }else if(autoExpand){
19985             item.expandMenu();
19986         }
19987     },
19988
19989     // private
19990     tryActivate : function(start, step){
19991         var items = this.items;
19992         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
19993             var item = items.get(i);
19994             if(!item.disabled && item.canActivate){
19995                 this.setActiveItem(item, false);
19996                 return item;
19997             }
19998         }
19999         return false;
20000     },
20001
20002     // private
20003     onMouseOver : function(e){
20004         var t;
20005         if(t = this.findTargetItem(e)){
20006             if(t.canActivate && !t.disabled){
20007                 this.setActiveItem(t, true);
20008             }
20009         }
20010         this.fireEvent("mouseover", this, e, t);
20011     },
20012
20013     // private
20014     onMouseOut : function(e){
20015         var t;
20016         if(t = this.findTargetItem(e)){
20017             if(t == this.activeItem && t.shouldDeactivate(e)){
20018                 this.activeItem.deactivate();
20019                 delete this.activeItem;
20020             }
20021         }
20022         this.fireEvent("mouseout", this, e, t);
20023     },
20024
20025     /**
20026      * Read-only.  Returns true if the menu is currently displayed, else false.
20027      * @type Boolean
20028      */
20029     isVisible : function(){
20030         return this.el && !this.hidden;
20031     },
20032
20033     /**
20034      * Displays this menu relative to another element
20035      * @param {String/HTMLElement/Roo.Element} element The element to align to
20036      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
20037      * the element (defaults to this.defaultAlign)
20038      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
20039      */
20040     show : function(el, pos, parentMenu){
20041         this.parentMenu = parentMenu;
20042         if(!this.el){
20043             this.render();
20044         }
20045         this.fireEvent("beforeshow", this);
20046         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
20047     },
20048
20049     /**
20050      * Displays this menu at a specific xy position
20051      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
20052      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
20053      */
20054     showAt : function(xy, parentMenu, /* private: */_e){
20055         this.parentMenu = parentMenu;
20056         if(!this.el){
20057             this.render();
20058         }
20059         if(_e !== false){
20060             this.fireEvent("beforeshow", this);
20061             xy = this.el.adjustForConstraints(xy);
20062         }
20063         this.el.setXY(xy);
20064         this.el.show();
20065         this.hidden = false;
20066         this.focus();
20067         this.fireEvent("show", this);
20068     },
20069
20070     focus : function(){
20071         if(!this.hidden){
20072             this.doFocus.defer(50, this);
20073         }
20074     },
20075
20076     doFocus : function(){
20077         if(!this.hidden){
20078             this.focusEl.focus();
20079         }
20080     },
20081
20082     /**
20083      * Hides this menu and optionally all parent menus
20084      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
20085      */
20086     hide : function(deep){
20087         if(this.el && this.isVisible()){
20088             this.fireEvent("beforehide", this);
20089             if(this.activeItem){
20090                 this.activeItem.deactivate();
20091                 this.activeItem = null;
20092             }
20093             this.el.hide();
20094             this.hidden = true;
20095             this.fireEvent("hide", this);
20096         }
20097         if(deep === true && this.parentMenu){
20098             this.parentMenu.hide(true);
20099         }
20100     },
20101
20102     /**
20103      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
20104      * Any of the following are valid:
20105      * <ul>
20106      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
20107      * <li>An HTMLElement object which will be converted to a menu item</li>
20108      * <li>A menu item config object that will be created as a new menu item</li>
20109      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
20110      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
20111      * </ul>
20112      * Usage:
20113      * <pre><code>
20114 // Create the menu
20115 var menu = new Roo.menu.Menu();
20116
20117 // Create a menu item to add by reference
20118 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
20119
20120 // Add a bunch of items at once using different methods.
20121 // Only the last item added will be returned.
20122 var item = menu.add(
20123     menuItem,                // add existing item by ref
20124     'Dynamic Item',          // new TextItem
20125     '-',                     // new separator
20126     { text: 'Config Item' }  // new item by config
20127 );
20128 </code></pre>
20129      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
20130      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
20131      */
20132     add : function(){
20133         var a = arguments, l = a.length, item;
20134         for(var i = 0; i < l; i++){
20135             var el = a[i];
20136             if ((typeof(el) == "object") && el.xtype && el.xns) {
20137                 el = Roo.factory(el, Roo.menu);
20138             }
20139             
20140             if(el.render){ // some kind of Item
20141                 item = this.addItem(el);
20142             }else if(typeof el == "string"){ // string
20143                 if(el == "separator" || el == "-"){
20144                     item = this.addSeparator();
20145                 }else{
20146                     item = this.addText(el);
20147                 }
20148             }else if(el.tagName || el.el){ // element
20149                 item = this.addElement(el);
20150             }else if(typeof el == "object"){ // must be menu item config?
20151                 item = this.addMenuItem(el);
20152             }
20153         }
20154         return item;
20155     },
20156
20157     /**
20158      * Returns this menu's underlying {@link Roo.Element} object
20159      * @return {Roo.Element} The element
20160      */
20161     getEl : function(){
20162         if(!this.el){
20163             this.render();
20164         }
20165         return this.el;
20166     },
20167
20168     /**
20169      * Adds a separator bar to the menu
20170      * @return {Roo.menu.Item} The menu item that was added
20171      */
20172     addSeparator : function(){
20173         return this.addItem(new Roo.menu.Separator());
20174     },
20175
20176     /**
20177      * Adds an {@link Roo.Element} object to the menu
20178      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
20179      * @return {Roo.menu.Item} The menu item that was added
20180      */
20181     addElement : function(el){
20182         return this.addItem(new Roo.menu.BaseItem(el));
20183     },
20184
20185     /**
20186      * Adds an existing object based on {@link Roo.menu.Item} to the menu
20187      * @param {Roo.menu.Item} item The menu item to add
20188      * @return {Roo.menu.Item} The menu item that was added
20189      */
20190     addItem : function(item){
20191         this.items.add(item);
20192         if(this.ul){
20193             var li = document.createElement("li");
20194             li.className = "x-menu-list-item";
20195             this.ul.dom.appendChild(li);
20196             item.render(li, this);
20197             this.delayAutoWidth();
20198         }
20199         return item;
20200     },
20201
20202     /**
20203      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
20204      * @param {Object} config A MenuItem config object
20205      * @return {Roo.menu.Item} The menu item that was added
20206      */
20207     addMenuItem : function(config){
20208         if(!(config instanceof Roo.menu.Item)){
20209             if(typeof config.checked == "boolean"){ // must be check menu item config?
20210                 config = new Roo.menu.CheckItem(config);
20211             }else{
20212                 config = new Roo.menu.Item(config);
20213             }
20214         }
20215         return this.addItem(config);
20216     },
20217
20218     /**
20219      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
20220      * @param {String} text The text to display in the menu item
20221      * @return {Roo.menu.Item} The menu item that was added
20222      */
20223     addText : function(text){
20224         return this.addItem(new Roo.menu.TextItem({ text : text }));
20225     },
20226
20227     /**
20228      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
20229      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
20230      * @param {Roo.menu.Item} item The menu item to add
20231      * @return {Roo.menu.Item} The menu item that was added
20232      */
20233     insert : function(index, item){
20234         this.items.insert(index, item);
20235         if(this.ul){
20236             var li = document.createElement("li");
20237             li.className = "x-menu-list-item";
20238             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
20239             item.render(li, this);
20240             this.delayAutoWidth();
20241         }
20242         return item;
20243     },
20244
20245     /**
20246      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
20247      * @param {Roo.menu.Item} item The menu item to remove
20248      */
20249     remove : function(item){
20250         this.items.removeKey(item.id);
20251         item.destroy();
20252     },
20253
20254     /**
20255      * Removes and destroys all items in the menu
20256      */
20257     removeAll : function(){
20258         var f;
20259         while(f = this.items.first()){
20260             this.remove(f);
20261         }
20262     }
20263 });
20264
20265 // MenuNav is a private utility class used internally by the Menu
20266 Roo.menu.MenuNav = function(menu){
20267     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
20268     this.scope = this.menu = menu;
20269 };
20270
20271 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
20272     doRelay : function(e, h){
20273         var k = e.getKey();
20274         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
20275             this.menu.tryActivate(0, 1);
20276             return false;
20277         }
20278         return h.call(this.scope || this, e, this.menu);
20279     },
20280
20281     up : function(e, m){
20282         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
20283             m.tryActivate(m.items.length-1, -1);
20284         }
20285     },
20286
20287     down : function(e, m){
20288         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
20289             m.tryActivate(0, 1);
20290         }
20291     },
20292
20293     right : function(e, m){
20294         if(m.activeItem){
20295             m.activeItem.expandMenu(true);
20296         }
20297     },
20298
20299     left : function(e, m){
20300         m.hide();
20301         if(m.parentMenu && m.parentMenu.activeItem){
20302             m.parentMenu.activeItem.activate();
20303         }
20304     },
20305
20306     enter : function(e, m){
20307         if(m.activeItem){
20308             e.stopPropagation();
20309             m.activeItem.onClick(e);
20310             m.fireEvent("click", this, m.activeItem);
20311             return true;
20312         }
20313     }
20314 });/*
20315  * Based on:
20316  * Ext JS Library 1.1.1
20317  * Copyright(c) 2006-2007, Ext JS, LLC.
20318  *
20319  * Originally Released Under LGPL - original licence link has changed is not relivant.
20320  *
20321  * Fork - LGPL
20322  * <script type="text/javascript">
20323  */
20324  
20325 /**
20326  * @class Roo.menu.MenuMgr
20327  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
20328  * @singleton
20329  */
20330 Roo.menu.MenuMgr = function(){
20331    var menus, active, groups = {}, attached = false, lastShow = new Date();
20332
20333    // private - called when first menu is created
20334    function init(){
20335        menus = {};
20336        active = new Roo.util.MixedCollection();
20337        Roo.get(document).addKeyListener(27, function(){
20338            if(active.length > 0){
20339                hideAll();
20340            }
20341        });
20342    }
20343
20344    // private
20345    function hideAll(){
20346        if(active && active.length > 0){
20347            var c = active.clone();
20348            c.each(function(m){
20349                m.hide();
20350            });
20351        }
20352    }
20353
20354    // private
20355    function onHide(m){
20356        active.remove(m);
20357        if(active.length < 1){
20358            Roo.get(document).un("mousedown", onMouseDown);
20359            attached = false;
20360        }
20361    }
20362
20363    // private
20364    function onShow(m){
20365        var last = active.last();
20366        lastShow = new Date();
20367        active.add(m);
20368        if(!attached){
20369            Roo.get(document).on("mousedown", onMouseDown);
20370            attached = true;
20371        }
20372        if(m.parentMenu){
20373           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
20374           m.parentMenu.activeChild = m;
20375        }else if(last && last.isVisible()){
20376           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
20377        }
20378    }
20379
20380    // private
20381    function onBeforeHide(m){
20382        if(m.activeChild){
20383            m.activeChild.hide();
20384        }
20385        if(m.autoHideTimer){
20386            clearTimeout(m.autoHideTimer);
20387            delete m.autoHideTimer;
20388        }
20389    }
20390
20391    // private
20392    function onBeforeShow(m){
20393        var pm = m.parentMenu;
20394        if(!pm && !m.allowOtherMenus){
20395            hideAll();
20396        }else if(pm && pm.activeChild && active != m){
20397            pm.activeChild.hide();
20398        }
20399    }
20400
20401    // private
20402    function onMouseDown(e){
20403        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
20404            hideAll();
20405        }
20406    }
20407
20408    // private
20409    function onBeforeCheck(mi, state){
20410        if(state){
20411            var g = groups[mi.group];
20412            for(var i = 0, l = g.length; i < l; i++){
20413                if(g[i] != mi){
20414                    g[i].setChecked(false);
20415                }
20416            }
20417        }
20418    }
20419
20420    return {
20421
20422        /**
20423         * Hides all menus that are currently visible
20424         */
20425        hideAll : function(){
20426             hideAll();  
20427        },
20428
20429        // private
20430        register : function(menu){
20431            if(!menus){
20432                init();
20433            }
20434            menus[menu.id] = menu;
20435            menu.on("beforehide", onBeforeHide);
20436            menu.on("hide", onHide);
20437            menu.on("beforeshow", onBeforeShow);
20438            menu.on("show", onShow);
20439            var g = menu.group;
20440            if(g && menu.events["checkchange"]){
20441                if(!groups[g]){
20442                    groups[g] = [];
20443                }
20444                groups[g].push(menu);
20445                menu.on("checkchange", onCheck);
20446            }
20447        },
20448
20449         /**
20450          * Returns a {@link Roo.menu.Menu} object
20451          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
20452          * be used to generate and return a new Menu instance.
20453          */
20454        get : function(menu){
20455            if(typeof menu == "string"){ // menu id
20456                return menus[menu];
20457            }else if(menu.events){  // menu instance
20458                return menu;
20459            }else if(typeof menu.length == 'number'){ // array of menu items?
20460                return new Roo.menu.Menu({items:menu});
20461            }else{ // otherwise, must be a config
20462                return new Roo.menu.Menu(menu);
20463            }
20464        },
20465
20466        // private
20467        unregister : function(menu){
20468            delete menus[menu.id];
20469            menu.un("beforehide", onBeforeHide);
20470            menu.un("hide", onHide);
20471            menu.un("beforeshow", onBeforeShow);
20472            menu.un("show", onShow);
20473            var g = menu.group;
20474            if(g && menu.events["checkchange"]){
20475                groups[g].remove(menu);
20476                menu.un("checkchange", onCheck);
20477            }
20478        },
20479
20480        // private
20481        registerCheckable : function(menuItem){
20482            var g = menuItem.group;
20483            if(g){
20484                if(!groups[g]){
20485                    groups[g] = [];
20486                }
20487                groups[g].push(menuItem);
20488                menuItem.on("beforecheckchange", onBeforeCheck);
20489            }
20490        },
20491
20492        // private
20493        unregisterCheckable : function(menuItem){
20494            var g = menuItem.group;
20495            if(g){
20496                groups[g].remove(menuItem);
20497                menuItem.un("beforecheckchange", onBeforeCheck);
20498            }
20499        }
20500    };
20501 }();/*
20502  * Based on:
20503  * Ext JS Library 1.1.1
20504  * Copyright(c) 2006-2007, Ext JS, LLC.
20505  *
20506  * Originally Released Under LGPL - original licence link has changed is not relivant.
20507  *
20508  * Fork - LGPL
20509  * <script type="text/javascript">
20510  */
20511  
20512
20513 /**
20514  * @class Roo.menu.BaseItem
20515  * @extends Roo.Component
20516  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
20517  * management and base configuration options shared by all menu components.
20518  * @constructor
20519  * Creates a new BaseItem
20520  * @param {Object} config Configuration options
20521  */
20522 Roo.menu.BaseItem = function(config){
20523     Roo.menu.BaseItem.superclass.constructor.call(this, config);
20524
20525     this.addEvents({
20526         /**
20527          * @event click
20528          * Fires when this item is clicked
20529          * @param {Roo.menu.BaseItem} this
20530          * @param {Roo.EventObject} e
20531          */
20532         click: true,
20533         /**
20534          * @event activate
20535          * Fires when this item is activated
20536          * @param {Roo.menu.BaseItem} this
20537          */
20538         activate : true,
20539         /**
20540          * @event deactivate
20541          * Fires when this item is deactivated
20542          * @param {Roo.menu.BaseItem} this
20543          */
20544         deactivate : true
20545     });
20546
20547     if(this.handler){
20548         this.on("click", this.handler, this.scope, true);
20549     }
20550 };
20551
20552 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
20553     /**
20554      * @cfg {Function} handler
20555      * A function that will handle the click event of this menu item (defaults to undefined)
20556      */
20557     /**
20558      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
20559      */
20560     canActivate : false,
20561     
20562      /**
20563      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
20564      */
20565     hidden: false,
20566     
20567     /**
20568      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
20569      */
20570     activeClass : "x-menu-item-active",
20571     /**
20572      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
20573      */
20574     hideOnClick : true,
20575     /**
20576      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
20577      */
20578     hideDelay : 100,
20579
20580     // private
20581     ctype: "Roo.menu.BaseItem",
20582
20583     // private
20584     actionMode : "container",
20585
20586     // private
20587     render : function(container, parentMenu){
20588         this.parentMenu = parentMenu;
20589         Roo.menu.BaseItem.superclass.render.call(this, container);
20590         this.container.menuItemId = this.id;
20591     },
20592
20593     // private
20594     onRender : function(container, position){
20595         this.el = Roo.get(this.el);
20596         container.dom.appendChild(this.el.dom);
20597     },
20598
20599     // private
20600     onClick : function(e){
20601         if(!this.disabled && this.fireEvent("click", this, e) !== false
20602                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
20603             this.handleClick(e);
20604         }else{
20605             e.stopEvent();
20606         }
20607     },
20608
20609     // private
20610     activate : function(){
20611         if(this.disabled){
20612             return false;
20613         }
20614         var li = this.container;
20615         li.addClass(this.activeClass);
20616         this.region = li.getRegion().adjust(2, 2, -2, -2);
20617         this.fireEvent("activate", this);
20618         return true;
20619     },
20620
20621     // private
20622     deactivate : function(){
20623         this.container.removeClass(this.activeClass);
20624         this.fireEvent("deactivate", this);
20625     },
20626
20627     // private
20628     shouldDeactivate : function(e){
20629         return !this.region || !this.region.contains(e.getPoint());
20630     },
20631
20632     // private
20633     handleClick : function(e){
20634         if(this.hideOnClick){
20635             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
20636         }
20637     },
20638
20639     // private
20640     expandMenu : function(autoActivate){
20641         // do nothing
20642     },
20643
20644     // private
20645     hideMenu : function(){
20646         // do nothing
20647     }
20648 });/*
20649  * Based on:
20650  * Ext JS Library 1.1.1
20651  * Copyright(c) 2006-2007, Ext JS, LLC.
20652  *
20653  * Originally Released Under LGPL - original licence link has changed is not relivant.
20654  *
20655  * Fork - LGPL
20656  * <script type="text/javascript">
20657  */
20658  
20659 /**
20660  * @class Roo.menu.Adapter
20661  * @extends Roo.menu.BaseItem
20662  * 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.
20663  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
20664  * @constructor
20665  * Creates a new Adapter
20666  * @param {Object} config Configuration options
20667  */
20668 Roo.menu.Adapter = function(component, config){
20669     Roo.menu.Adapter.superclass.constructor.call(this, config);
20670     this.component = component;
20671 };
20672 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
20673     // private
20674     canActivate : true,
20675
20676     // private
20677     onRender : function(container, position){
20678         this.component.render(container);
20679         this.el = this.component.getEl();
20680     },
20681
20682     // private
20683     activate : function(){
20684         if(this.disabled){
20685             return false;
20686         }
20687         this.component.focus();
20688         this.fireEvent("activate", this);
20689         return true;
20690     },
20691
20692     // private
20693     deactivate : function(){
20694         this.fireEvent("deactivate", this);
20695     },
20696
20697     // private
20698     disable : function(){
20699         this.component.disable();
20700         Roo.menu.Adapter.superclass.disable.call(this);
20701     },
20702
20703     // private
20704     enable : function(){
20705         this.component.enable();
20706         Roo.menu.Adapter.superclass.enable.call(this);
20707     }
20708 });/*
20709  * Based on:
20710  * Ext JS Library 1.1.1
20711  * Copyright(c) 2006-2007, Ext JS, LLC.
20712  *
20713  * Originally Released Under LGPL - original licence link has changed is not relivant.
20714  *
20715  * Fork - LGPL
20716  * <script type="text/javascript">
20717  */
20718
20719 /**
20720  * @class Roo.menu.TextItem
20721  * @extends Roo.menu.BaseItem
20722  * Adds a static text string to a menu, usually used as either a heading or group separator.
20723  * Note: old style constructor with text is still supported.
20724  * 
20725  * @constructor
20726  * Creates a new TextItem
20727  * @param {Object} cfg Configuration
20728  */
20729 Roo.menu.TextItem = function(cfg){
20730     if (typeof(cfg) == 'string') {
20731         this.text = cfg;
20732     } else {
20733         Roo.apply(this,cfg);
20734     }
20735     
20736     Roo.menu.TextItem.superclass.constructor.call(this);
20737 };
20738
20739 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
20740     /**
20741      * @cfg {Boolean} text Text to show on item.
20742      */
20743     text : '',
20744     
20745     /**
20746      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20747      */
20748     hideOnClick : false,
20749     /**
20750      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
20751      */
20752     itemCls : "x-menu-text",
20753
20754     // private
20755     onRender : function(){
20756         var s = document.createElement("span");
20757         s.className = this.itemCls;
20758         s.innerHTML = this.text;
20759         this.el = s;
20760         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
20761     }
20762 });/*
20763  * Based on:
20764  * Ext JS Library 1.1.1
20765  * Copyright(c) 2006-2007, Ext JS, LLC.
20766  *
20767  * Originally Released Under LGPL - original licence link has changed is not relivant.
20768  *
20769  * Fork - LGPL
20770  * <script type="text/javascript">
20771  */
20772
20773 /**
20774  * @class Roo.menu.Separator
20775  * @extends Roo.menu.BaseItem
20776  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
20777  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
20778  * @constructor
20779  * @param {Object} config Configuration options
20780  */
20781 Roo.menu.Separator = function(config){
20782     Roo.menu.Separator.superclass.constructor.call(this, config);
20783 };
20784
20785 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
20786     /**
20787      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
20788      */
20789     itemCls : "x-menu-sep",
20790     /**
20791      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20792      */
20793     hideOnClick : false,
20794
20795     // private
20796     onRender : function(li){
20797         var s = document.createElement("span");
20798         s.className = this.itemCls;
20799         s.innerHTML = "&#160;";
20800         this.el = s;
20801         li.addClass("x-menu-sep-li");
20802         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
20803     }
20804 });/*
20805  * Based on:
20806  * Ext JS Library 1.1.1
20807  * Copyright(c) 2006-2007, Ext JS, LLC.
20808  *
20809  * Originally Released Under LGPL - original licence link has changed is not relivant.
20810  *
20811  * Fork - LGPL
20812  * <script type="text/javascript">
20813  */
20814 /**
20815  * @class Roo.menu.Item
20816  * @extends Roo.menu.BaseItem
20817  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
20818  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
20819  * activation and click handling.
20820  * @constructor
20821  * Creates a new Item
20822  * @param {Object} config Configuration options
20823  */
20824 Roo.menu.Item = function(config){
20825     Roo.menu.Item.superclass.constructor.call(this, config);
20826     if(this.menu){
20827         this.menu = Roo.menu.MenuMgr.get(this.menu);
20828     }
20829 };
20830 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
20831     
20832     /**
20833      * @cfg {String} text
20834      * The text to show on the menu item.
20835      */
20836     text: '',
20837      /**
20838      * @cfg {String} HTML to render in menu
20839      * The text to show on the menu item (HTML version).
20840      */
20841     html: '',
20842     /**
20843      * @cfg {String} icon
20844      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
20845      */
20846     icon: undefined,
20847     /**
20848      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
20849      */
20850     itemCls : "x-menu-item",
20851     /**
20852      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
20853      */
20854     canActivate : true,
20855     /**
20856      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
20857      */
20858     showDelay: 200,
20859     // doc'd in BaseItem
20860     hideDelay: 200,
20861
20862     // private
20863     ctype: "Roo.menu.Item",
20864     
20865     // private
20866     onRender : function(container, position){
20867         var el = document.createElement("a");
20868         el.hideFocus = true;
20869         el.unselectable = "on";
20870         el.href = this.href || "#";
20871         if(this.hrefTarget){
20872             el.target = this.hrefTarget;
20873         }
20874         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
20875         
20876         var html = this.html.length ? this.html  : String.format('{0}',this.text);
20877         
20878         el.innerHTML = String.format(
20879                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
20880                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
20881         this.el = el;
20882         Roo.menu.Item.superclass.onRender.call(this, container, position);
20883     },
20884
20885     /**
20886      * Sets the text to display in this menu item
20887      * @param {String} text The text to display
20888      * @param {Boolean} isHTML true to indicate text is pure html.
20889      */
20890     setText : function(text, isHTML){
20891         if (isHTML) {
20892             this.html = text;
20893         } else {
20894             this.text = text;
20895             this.html = '';
20896         }
20897         if(this.rendered){
20898             var html = this.html.length ? this.html  : String.format('{0}',this.text);
20899      
20900             this.el.update(String.format(
20901                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
20902                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
20903             this.parentMenu.autoWidth();
20904         }
20905     },
20906
20907     // private
20908     handleClick : function(e){
20909         if(!this.href){ // if no link defined, stop the event automatically
20910             e.stopEvent();
20911         }
20912         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
20913     },
20914
20915     // private
20916     activate : function(autoExpand){
20917         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
20918             this.focus();
20919             if(autoExpand){
20920                 this.expandMenu();
20921             }
20922         }
20923         return true;
20924     },
20925
20926     // private
20927     shouldDeactivate : function(e){
20928         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
20929             if(this.menu && this.menu.isVisible()){
20930                 return !this.menu.getEl().getRegion().contains(e.getPoint());
20931             }
20932             return true;
20933         }
20934         return false;
20935     },
20936
20937     // private
20938     deactivate : function(){
20939         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
20940         this.hideMenu();
20941     },
20942
20943     // private
20944     expandMenu : function(autoActivate){
20945         if(!this.disabled && this.menu){
20946             clearTimeout(this.hideTimer);
20947             delete this.hideTimer;
20948             if(!this.menu.isVisible() && !this.showTimer){
20949                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
20950             }else if (this.menu.isVisible() && autoActivate){
20951                 this.menu.tryActivate(0, 1);
20952             }
20953         }
20954     },
20955
20956     // private
20957     deferExpand : function(autoActivate){
20958         delete this.showTimer;
20959         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
20960         if(autoActivate){
20961             this.menu.tryActivate(0, 1);
20962         }
20963     },
20964
20965     // private
20966     hideMenu : function(){
20967         clearTimeout(this.showTimer);
20968         delete this.showTimer;
20969         if(!this.hideTimer && this.menu && this.menu.isVisible()){
20970             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
20971         }
20972     },
20973
20974     // private
20975     deferHide : function(){
20976         delete this.hideTimer;
20977         this.menu.hide();
20978     }
20979 });/*
20980  * Based on:
20981  * Ext JS Library 1.1.1
20982  * Copyright(c) 2006-2007, Ext JS, LLC.
20983  *
20984  * Originally Released Under LGPL - original licence link has changed is not relivant.
20985  *
20986  * Fork - LGPL
20987  * <script type="text/javascript">
20988  */
20989  
20990 /**
20991  * @class Roo.menu.CheckItem
20992  * @extends Roo.menu.Item
20993  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
20994  * @constructor
20995  * Creates a new CheckItem
20996  * @param {Object} config Configuration options
20997  */
20998 Roo.menu.CheckItem = function(config){
20999     Roo.menu.CheckItem.superclass.constructor.call(this, config);
21000     this.addEvents({
21001         /**
21002          * @event beforecheckchange
21003          * Fires before the checked value is set, providing an opportunity to cancel if needed
21004          * @param {Roo.menu.CheckItem} this
21005          * @param {Boolean} checked The new checked value that will be set
21006          */
21007         "beforecheckchange" : true,
21008         /**
21009          * @event checkchange
21010          * Fires after the checked value has been set
21011          * @param {Roo.menu.CheckItem} this
21012          * @param {Boolean} checked The checked value that was set
21013          */
21014         "checkchange" : true
21015     });
21016     if(this.checkHandler){
21017         this.on('checkchange', this.checkHandler, this.scope);
21018     }
21019 };
21020 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
21021     /**
21022      * @cfg {String} group
21023      * All check items with the same group name will automatically be grouped into a single-select
21024      * radio button group (defaults to '')
21025      */
21026     /**
21027      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
21028      */
21029     itemCls : "x-menu-item x-menu-check-item",
21030     /**
21031      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
21032      */
21033     groupClass : "x-menu-group-item",
21034
21035     /**
21036      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
21037      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
21038      * initialized with checked = true will be rendered as checked.
21039      */
21040     checked: false,
21041
21042     // private
21043     ctype: "Roo.menu.CheckItem",
21044
21045     // private
21046     onRender : function(c){
21047         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
21048         if(this.group){
21049             this.el.addClass(this.groupClass);
21050         }
21051         Roo.menu.MenuMgr.registerCheckable(this);
21052         if(this.checked){
21053             this.checked = false;
21054             this.setChecked(true, true);
21055         }
21056     },
21057
21058     // private
21059     destroy : function(){
21060         if(this.rendered){
21061             Roo.menu.MenuMgr.unregisterCheckable(this);
21062         }
21063         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
21064     },
21065
21066     /**
21067      * Set the checked state of this item
21068      * @param {Boolean} checked The new checked value
21069      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
21070      */
21071     setChecked : function(state, suppressEvent){
21072         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
21073             if(this.container){
21074                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
21075             }
21076             this.checked = state;
21077             if(suppressEvent !== true){
21078                 this.fireEvent("checkchange", this, state);
21079             }
21080         }
21081     },
21082
21083     // private
21084     handleClick : function(e){
21085        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
21086            this.setChecked(!this.checked);
21087        }
21088        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
21089     }
21090 });/*
21091  * Based on:
21092  * Ext JS Library 1.1.1
21093  * Copyright(c) 2006-2007, Ext JS, LLC.
21094  *
21095  * Originally Released Under LGPL - original licence link has changed is not relivant.
21096  *
21097  * Fork - LGPL
21098  * <script type="text/javascript">
21099  */
21100  
21101 /**
21102  * @class Roo.menu.DateItem
21103  * @extends Roo.menu.Adapter
21104  * A menu item that wraps the {@link Roo.DatPicker} component.
21105  * @constructor
21106  * Creates a new DateItem
21107  * @param {Object} config Configuration options
21108  */
21109 Roo.menu.DateItem = function(config){
21110     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
21111     /** The Roo.DatePicker object @type Roo.DatePicker */
21112     this.picker = this.component;
21113     this.addEvents({select: true});
21114     
21115     this.picker.on("render", function(picker){
21116         picker.getEl().swallowEvent("click");
21117         picker.container.addClass("x-menu-date-item");
21118     });
21119
21120     this.picker.on("select", this.onSelect, this);
21121 };
21122
21123 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
21124     // private
21125     onSelect : function(picker, date){
21126         this.fireEvent("select", this, date, picker);
21127         Roo.menu.DateItem.superclass.handleClick.call(this);
21128     }
21129 });/*
21130  * Based on:
21131  * Ext JS Library 1.1.1
21132  * Copyright(c) 2006-2007, Ext JS, LLC.
21133  *
21134  * Originally Released Under LGPL - original licence link has changed is not relivant.
21135  *
21136  * Fork - LGPL
21137  * <script type="text/javascript">
21138  */
21139  
21140 /**
21141  * @class Roo.menu.ColorItem
21142  * @extends Roo.menu.Adapter
21143  * A menu item that wraps the {@link Roo.ColorPalette} component.
21144  * @constructor
21145  * Creates a new ColorItem
21146  * @param {Object} config Configuration options
21147  */
21148 Roo.menu.ColorItem = function(config){
21149     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
21150     /** The Roo.ColorPalette object @type Roo.ColorPalette */
21151     this.palette = this.component;
21152     this.relayEvents(this.palette, ["select"]);
21153     if(this.selectHandler){
21154         this.on('select', this.selectHandler, this.scope);
21155     }
21156 };
21157 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
21158  * Based on:
21159  * Ext JS Library 1.1.1
21160  * Copyright(c) 2006-2007, Ext JS, LLC.
21161  *
21162  * Originally Released Under LGPL - original licence link has changed is not relivant.
21163  *
21164  * Fork - LGPL
21165  * <script type="text/javascript">
21166  */
21167  
21168
21169 /**
21170  * @class Roo.menu.DateMenu
21171  * @extends Roo.menu.Menu
21172  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
21173  * @constructor
21174  * Creates a new DateMenu
21175  * @param {Object} config Configuration options
21176  */
21177 Roo.menu.DateMenu = function(config){
21178     Roo.menu.DateMenu.superclass.constructor.call(this, config);
21179     this.plain = true;
21180     var di = new Roo.menu.DateItem(config);
21181     this.add(di);
21182     /**
21183      * The {@link Roo.DatePicker} instance for this DateMenu
21184      * @type DatePicker
21185      */
21186     this.picker = di.picker;
21187     /**
21188      * @event select
21189      * @param {DatePicker} picker
21190      * @param {Date} date
21191      */
21192     this.relayEvents(di, ["select"]);
21193     this.on('beforeshow', function(){
21194         if(this.picker){
21195             this.picker.hideMonthPicker(false);
21196         }
21197     }, this);
21198 };
21199 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
21200     cls:'x-date-menu'
21201 });/*
21202  * Based on:
21203  * Ext JS Library 1.1.1
21204  * Copyright(c) 2006-2007, Ext JS, LLC.
21205  *
21206  * Originally Released Under LGPL - original licence link has changed is not relivant.
21207  *
21208  * Fork - LGPL
21209  * <script type="text/javascript">
21210  */
21211  
21212
21213 /**
21214  * @class Roo.menu.ColorMenu
21215  * @extends Roo.menu.Menu
21216  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
21217  * @constructor
21218  * Creates a new ColorMenu
21219  * @param {Object} config Configuration options
21220  */
21221 Roo.menu.ColorMenu = function(config){
21222     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
21223     this.plain = true;
21224     var ci = new Roo.menu.ColorItem(config);
21225     this.add(ci);
21226     /**
21227      * The {@link Roo.ColorPalette} instance for this ColorMenu
21228      * @type ColorPalette
21229      */
21230     this.palette = ci.palette;
21231     /**
21232      * @event select
21233      * @param {ColorPalette} palette
21234      * @param {String} color
21235      */
21236     this.relayEvents(ci, ["select"]);
21237 };
21238 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
21239  * Based on:
21240  * Ext JS Library 1.1.1
21241  * Copyright(c) 2006-2007, Ext JS, LLC.
21242  *
21243  * Originally Released Under LGPL - original licence link has changed is not relivant.
21244  *
21245  * Fork - LGPL
21246  * <script type="text/javascript">
21247  */
21248  
21249 /**
21250  * @class Roo.form.Field
21251  * @extends Roo.BoxComponent
21252  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
21253  * @constructor
21254  * Creates a new Field
21255  * @param {Object} config Configuration options
21256  */
21257 Roo.form.Field = function(config){
21258     Roo.form.Field.superclass.constructor.call(this, config);
21259 };
21260
21261 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
21262     /**
21263      * @cfg {String} fieldLabel Label to use when rendering a form.
21264      */
21265        /**
21266      * @cfg {String} qtip Mouse over tip
21267      */
21268      
21269     /**
21270      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
21271      */
21272     invalidClass : "x-form-invalid",
21273     /**
21274      * @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")
21275      */
21276     invalidText : "The value in this field is invalid",
21277     /**
21278      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
21279      */
21280     focusClass : "x-form-focus",
21281     /**
21282      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
21283       automatic validation (defaults to "keyup").
21284      */
21285     validationEvent : "keyup",
21286     /**
21287      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
21288      */
21289     validateOnBlur : true,
21290     /**
21291      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
21292      */
21293     validationDelay : 250,
21294     /**
21295      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21296      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
21297      */
21298     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
21299     /**
21300      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
21301      */
21302     fieldClass : "x-form-field",
21303     /**
21304      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
21305      *<pre>
21306 Value         Description
21307 -----------   ----------------------------------------------------------------------
21308 qtip          Display a quick tip when the user hovers over the field
21309 title         Display a default browser title attribute popup
21310 under         Add a block div beneath the field containing the error text
21311 side          Add an error icon to the right of the field with a popup on hover
21312 [element id]  Add the error text directly to the innerHTML of the specified element
21313 </pre>
21314      */
21315     msgTarget : 'qtip',
21316     /**
21317      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
21318      */
21319     msgFx : 'normal',
21320
21321     /**
21322      * @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.
21323      */
21324     readOnly : false,
21325
21326     /**
21327      * @cfg {Boolean} disabled True to disable the field (defaults to false).
21328      */
21329     disabled : false,
21330
21331     /**
21332      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
21333      */
21334     inputType : undefined,
21335     
21336     /**
21337      * @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).
21338          */
21339         tabIndex : undefined,
21340         
21341     // private
21342     isFormField : true,
21343
21344     // private
21345     hasFocus : false,
21346     /**
21347      * @property {Roo.Element} fieldEl
21348      * Element Containing the rendered Field (with label etc.)
21349      */
21350     /**
21351      * @cfg {Mixed} value A value to initialize this field with.
21352      */
21353     value : undefined,
21354
21355     /**
21356      * @cfg {String} name The field's HTML name attribute.
21357      */
21358     /**
21359      * @cfg {String} cls A CSS class to apply to the field's underlying element.
21360      */
21361
21362         // private ??
21363         initComponent : function(){
21364         Roo.form.Field.superclass.initComponent.call(this);
21365         this.addEvents({
21366             /**
21367              * @event focus
21368              * Fires when this field receives input focus.
21369              * @param {Roo.form.Field} this
21370              */
21371             focus : true,
21372             /**
21373              * @event blur
21374              * Fires when this field loses input focus.
21375              * @param {Roo.form.Field} this
21376              */
21377             blur : true,
21378             /**
21379              * @event specialkey
21380              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
21381              * {@link Roo.EventObject#getKey} to determine which key was pressed.
21382              * @param {Roo.form.Field} this
21383              * @param {Roo.EventObject} e The event object
21384              */
21385             specialkey : true,
21386             /**
21387              * @event change
21388              * Fires just before the field blurs if the field value has changed.
21389              * @param {Roo.form.Field} this
21390              * @param {Mixed} newValue The new value
21391              * @param {Mixed} oldValue The original value
21392              */
21393             change : true,
21394             /**
21395              * @event invalid
21396              * Fires after the field has been marked as invalid.
21397              * @param {Roo.form.Field} this
21398              * @param {String} msg The validation message
21399              */
21400             invalid : true,
21401             /**
21402              * @event valid
21403              * Fires after the field has been validated with no errors.
21404              * @param {Roo.form.Field} this
21405              */
21406             valid : true,
21407              /**
21408              * @event keyup
21409              * Fires after the key up
21410              * @param {Roo.form.Field} this
21411              * @param {Roo.EventObject}  e The event Object
21412              */
21413             keyup : true
21414         });
21415     },
21416
21417     /**
21418      * Returns the name attribute of the field if available
21419      * @return {String} name The field name
21420      */
21421     getName: function(){
21422          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
21423     },
21424
21425     // private
21426     onRender : function(ct, position){
21427         Roo.form.Field.superclass.onRender.call(this, ct, position);
21428         if(!this.el){
21429             var cfg = this.getAutoCreate();
21430             if(!cfg.name){
21431                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
21432             }
21433             if (!cfg.name.length) {
21434                 delete cfg.name;
21435             }
21436             if(this.inputType){
21437                 cfg.type = this.inputType;
21438             }
21439             this.el = ct.createChild(cfg, position);
21440         }
21441         var type = this.el.dom.type;
21442         if(type){
21443             if(type == 'password'){
21444                 type = 'text';
21445             }
21446             this.el.addClass('x-form-'+type);
21447         }
21448         if(this.readOnly){
21449             this.el.dom.readOnly = true;
21450         }
21451         if(this.tabIndex !== undefined){
21452             this.el.dom.setAttribute('tabIndex', this.tabIndex);
21453         }
21454
21455         this.el.addClass([this.fieldClass, this.cls]);
21456         this.initValue();
21457     },
21458
21459     /**
21460      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
21461      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
21462      * @return {Roo.form.Field} this
21463      */
21464     applyTo : function(target){
21465         this.allowDomMove = false;
21466         this.el = Roo.get(target);
21467         this.render(this.el.dom.parentNode);
21468         return this;
21469     },
21470
21471     // private
21472     initValue : function(){
21473         if(this.value !== undefined){
21474             this.setValue(this.value);
21475         }else if(this.el.dom.value.length > 0){
21476             this.setValue(this.el.dom.value);
21477         }
21478     },
21479
21480     /**
21481      * Returns true if this field has been changed since it was originally loaded and is not disabled.
21482      */
21483     isDirty : function() {
21484         if(this.disabled) {
21485             return false;
21486         }
21487         return String(this.getValue()) !== String(this.originalValue);
21488     },
21489
21490     // private
21491     afterRender : function(){
21492         Roo.form.Field.superclass.afterRender.call(this);
21493         this.initEvents();
21494     },
21495
21496     // private
21497     fireKey : function(e){
21498         //Roo.log('field ' + e.getKey());
21499         if(e.isNavKeyPress()){
21500             this.fireEvent("specialkey", this, e);
21501         }
21502     },
21503
21504     /**
21505      * Resets the current field value to the originally loaded value and clears any validation messages
21506      */
21507     reset : function(){
21508         this.setValue(this.originalValue);
21509         this.clearInvalid();
21510     },
21511
21512     // private
21513     initEvents : function(){
21514         // safari killled keypress - so keydown is now used..
21515         this.el.on("keydown" , this.fireKey,  this);
21516         this.el.on("focus", this.onFocus,  this);
21517         this.el.on("blur", this.onBlur,  this);
21518         this.el.relayEvent('keyup', this);
21519
21520         // reference to original value for reset
21521         this.originalValue = this.getValue();
21522     },
21523
21524     // private
21525     onFocus : function(){
21526         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21527             this.el.addClass(this.focusClass);
21528         }
21529         if(!this.hasFocus){
21530             this.hasFocus = true;
21531             this.startValue = this.getValue();
21532             this.fireEvent("focus", this);
21533         }
21534     },
21535
21536     beforeBlur : Roo.emptyFn,
21537
21538     // private
21539     onBlur : function(){
21540         this.beforeBlur();
21541         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21542             this.el.removeClass(this.focusClass);
21543         }
21544         this.hasFocus = false;
21545         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
21546             this.validate();
21547         }
21548         var v = this.getValue();
21549         if(String(v) !== String(this.startValue)){
21550             this.fireEvent('change', this, v, this.startValue);
21551         }
21552         this.fireEvent("blur", this);
21553     },
21554
21555     /**
21556      * Returns whether or not the field value is currently valid
21557      * @param {Boolean} preventMark True to disable marking the field invalid
21558      * @return {Boolean} True if the value is valid, else false
21559      */
21560     isValid : function(preventMark){
21561         if(this.disabled){
21562             return true;
21563         }
21564         var restore = this.preventMark;
21565         this.preventMark = preventMark === true;
21566         var v = this.validateValue(this.processValue(this.getRawValue()));
21567         this.preventMark = restore;
21568         return v;
21569     },
21570
21571     /**
21572      * Validates the field value
21573      * @return {Boolean} True if the value is valid, else false
21574      */
21575     validate : function(){
21576         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
21577             this.clearInvalid();
21578             return true;
21579         }
21580         return false;
21581     },
21582
21583     processValue : function(value){
21584         return value;
21585     },
21586
21587     // private
21588     // Subclasses should provide the validation implementation by overriding this
21589     validateValue : function(value){
21590         return true;
21591     },
21592
21593     /**
21594      * Mark this field as invalid
21595      * @param {String} msg The validation message
21596      */
21597     markInvalid : function(msg){
21598         if(!this.rendered || this.preventMark){ // not rendered
21599             return;
21600         }
21601         this.el.addClass(this.invalidClass);
21602         msg = msg || this.invalidText;
21603         switch(this.msgTarget){
21604             case 'qtip':
21605                 this.el.dom.qtip = msg;
21606                 this.el.dom.qclass = 'x-form-invalid-tip';
21607                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
21608                     Roo.QuickTips.enable();
21609                 }
21610                 break;
21611             case 'title':
21612                 this.el.dom.title = msg;
21613                 break;
21614             case 'under':
21615                 if(!this.errorEl){
21616                     var elp = this.el.findParent('.x-form-element', 5, true);
21617                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
21618                     this.errorEl.setWidth(elp.getWidth(true)-20);
21619                 }
21620                 this.errorEl.update(msg);
21621                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
21622                 break;
21623             case 'side':
21624                 if(!this.errorIcon){
21625                     var elp = this.el.findParent('.x-form-element', 5, true);
21626                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
21627                 }
21628                 this.alignErrorIcon();
21629                 this.errorIcon.dom.qtip = msg;
21630                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
21631                 this.errorIcon.show();
21632                 this.on('resize', this.alignErrorIcon, this);
21633                 break;
21634             default:
21635                 var t = Roo.getDom(this.msgTarget);
21636                 t.innerHTML = msg;
21637                 t.style.display = this.msgDisplay;
21638                 break;
21639         }
21640         this.fireEvent('invalid', this, msg);
21641     },
21642
21643     // private
21644     alignErrorIcon : function(){
21645         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
21646     },
21647
21648     /**
21649      * Clear any invalid styles/messages for this field
21650      */
21651     clearInvalid : function(){
21652         if(!this.rendered || this.preventMark){ // not rendered
21653             return;
21654         }
21655         this.el.removeClass(this.invalidClass);
21656         switch(this.msgTarget){
21657             case 'qtip':
21658                 this.el.dom.qtip = '';
21659                 break;
21660             case 'title':
21661                 this.el.dom.title = '';
21662                 break;
21663             case 'under':
21664                 if(this.errorEl){
21665                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
21666                 }
21667                 break;
21668             case 'side':
21669                 if(this.errorIcon){
21670                     this.errorIcon.dom.qtip = '';
21671                     this.errorIcon.hide();
21672                     this.un('resize', this.alignErrorIcon, this);
21673                 }
21674                 break;
21675             default:
21676                 var t = Roo.getDom(this.msgTarget);
21677                 t.innerHTML = '';
21678                 t.style.display = 'none';
21679                 break;
21680         }
21681         this.fireEvent('valid', this);
21682     },
21683
21684     /**
21685      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
21686      * @return {Mixed} value The field value
21687      */
21688     getRawValue : function(){
21689         var v = this.el.getValue();
21690         
21691         return v;
21692     },
21693
21694     /**
21695      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
21696      * @return {Mixed} value The field value
21697      */
21698     getValue : function(){
21699         var v = this.el.getValue();
21700          
21701         return v;
21702     },
21703
21704     /**
21705      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
21706      * @param {Mixed} value The value to set
21707      */
21708     setRawValue : function(v){
21709         return this.el.dom.value = (v === null || v === undefined ? '' : v);
21710     },
21711
21712     /**
21713      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
21714      * @param {Mixed} value The value to set
21715      */
21716     setValue : function(v){
21717         this.value = v;
21718         if(this.rendered){
21719             this.el.dom.value = (v === null || v === undefined ? '' : v);
21720              this.validate();
21721         }
21722     },
21723
21724     adjustSize : function(w, h){
21725         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
21726         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
21727         return s;
21728     },
21729
21730     adjustWidth : function(tag, w){
21731         tag = tag.toLowerCase();
21732         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
21733             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
21734                 if(tag == 'input'){
21735                     return w + 2;
21736                 }
21737                 if(tag == 'textarea'){
21738                     return w-2;
21739                 }
21740             }else if(Roo.isOpera){
21741                 if(tag == 'input'){
21742                     return w + 2;
21743                 }
21744                 if(tag == 'textarea'){
21745                     return w-2;
21746                 }
21747             }
21748         }
21749         return w;
21750     }
21751 });
21752
21753
21754 // anything other than normal should be considered experimental
21755 Roo.form.Field.msgFx = {
21756     normal : {
21757         show: function(msgEl, f){
21758             msgEl.setDisplayed('block');
21759         },
21760
21761         hide : function(msgEl, f){
21762             msgEl.setDisplayed(false).update('');
21763         }
21764     },
21765
21766     slide : {
21767         show: function(msgEl, f){
21768             msgEl.slideIn('t', {stopFx:true});
21769         },
21770
21771         hide : function(msgEl, f){
21772             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
21773         }
21774     },
21775
21776     slideRight : {
21777         show: function(msgEl, f){
21778             msgEl.fixDisplay();
21779             msgEl.alignTo(f.el, 'tl-tr');
21780             msgEl.slideIn('l', {stopFx:true});
21781         },
21782
21783         hide : function(msgEl, f){
21784             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
21785         }
21786     }
21787 };/*
21788  * Based on:
21789  * Ext JS Library 1.1.1
21790  * Copyright(c) 2006-2007, Ext JS, LLC.
21791  *
21792  * Originally Released Under LGPL - original licence link has changed is not relivant.
21793  *
21794  * Fork - LGPL
21795  * <script type="text/javascript">
21796  */
21797  
21798
21799 /**
21800  * @class Roo.form.TextField
21801  * @extends Roo.form.Field
21802  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
21803  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
21804  * @constructor
21805  * Creates a new TextField
21806  * @param {Object} config Configuration options
21807  */
21808 Roo.form.TextField = function(config){
21809     Roo.form.TextField.superclass.constructor.call(this, config);
21810     this.addEvents({
21811         /**
21812          * @event autosize
21813          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
21814          * according to the default logic, but this event provides a hook for the developer to apply additional
21815          * logic at runtime to resize the field if needed.
21816              * @param {Roo.form.Field} this This text field
21817              * @param {Number} width The new field width
21818              */
21819         autosize : true
21820     });
21821 };
21822
21823 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
21824     /**
21825      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
21826      */
21827     grow : false,
21828     /**
21829      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
21830      */
21831     growMin : 30,
21832     /**
21833      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
21834      */
21835     growMax : 800,
21836     /**
21837      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
21838      */
21839     vtype : null,
21840     /**
21841      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
21842      */
21843     maskRe : null,
21844     /**
21845      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
21846      */
21847     disableKeyFilter : false,
21848     /**
21849      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
21850      */
21851     allowBlank : true,
21852     /**
21853      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
21854      */
21855     minLength : 0,
21856     /**
21857      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
21858      */
21859     maxLength : Number.MAX_VALUE,
21860     /**
21861      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
21862      */
21863     minLengthText : "The minimum length for this field is {0}",
21864     /**
21865      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
21866      */
21867     maxLengthText : "The maximum length for this field is {0}",
21868     /**
21869      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
21870      */
21871     selectOnFocus : false,
21872     /**
21873      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
21874      */
21875     blankText : "This field is required",
21876     /**
21877      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
21878      * If available, this function will be called only after the basic validators all return true, and will be passed the
21879      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
21880      */
21881     validator : null,
21882     /**
21883      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
21884      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
21885      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
21886      */
21887     regex : null,
21888     /**
21889      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
21890      */
21891     regexText : "",
21892     /**
21893      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
21894      */
21895     emptyText : null,
21896    
21897
21898     // private
21899     initEvents : function()
21900     {
21901         if (this.emptyText) {
21902             this.el.attr('placeholder', this.emptyText);
21903         }
21904         
21905         Roo.form.TextField.superclass.initEvents.call(this);
21906         if(this.validationEvent == 'keyup'){
21907             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
21908             this.el.on('keyup', this.filterValidation, this);
21909         }
21910         else if(this.validationEvent !== false){
21911             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
21912         }
21913         
21914         if(this.selectOnFocus){
21915             this.on("focus", this.preFocus, this);
21916             
21917         }
21918         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
21919             this.el.on("keypress", this.filterKeys, this);
21920         }
21921         if(this.grow){
21922             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
21923             this.el.on("click", this.autoSize,  this);
21924         }
21925         if(this.el.is('input[type=password]') && Roo.isSafari){
21926             this.el.on('keydown', this.SafariOnKeyDown, this);
21927         }
21928     },
21929
21930     processValue : function(value){
21931         if(this.stripCharsRe){
21932             var newValue = value.replace(this.stripCharsRe, '');
21933             if(newValue !== value){
21934                 this.setRawValue(newValue);
21935                 return newValue;
21936             }
21937         }
21938         return value;
21939     },
21940
21941     filterValidation : function(e){
21942         if(!e.isNavKeyPress()){
21943             this.validationTask.delay(this.validationDelay);
21944         }
21945     },
21946
21947     // private
21948     onKeyUp : function(e){
21949         if(!e.isNavKeyPress()){
21950             this.autoSize();
21951         }
21952     },
21953
21954     /**
21955      * Resets the current field value to the originally-loaded value and clears any validation messages.
21956      *  
21957      */
21958     reset : function(){
21959         Roo.form.TextField.superclass.reset.call(this);
21960        
21961     },
21962
21963     
21964     // private
21965     preFocus : function(){
21966         
21967         if(this.selectOnFocus){
21968             this.el.dom.select();
21969         }
21970     },
21971
21972     
21973     // private
21974     filterKeys : function(e){
21975         var k = e.getKey();
21976         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
21977             return;
21978         }
21979         var c = e.getCharCode(), cc = String.fromCharCode(c);
21980         if(Roo.isIE && (e.isSpecialKey() || !cc)){
21981             return;
21982         }
21983         if(!this.maskRe.test(cc)){
21984             e.stopEvent();
21985         }
21986     },
21987
21988     setValue : function(v){
21989         
21990         Roo.form.TextField.superclass.setValue.apply(this, arguments);
21991         
21992         this.autoSize();
21993     },
21994
21995     /**
21996      * Validates a value according to the field's validation rules and marks the field as invalid
21997      * if the validation fails
21998      * @param {Mixed} value The value to validate
21999      * @return {Boolean} True if the value is valid, else false
22000      */
22001     validateValue : function(value){
22002         if(value.length < 1)  { // if it's blank
22003              if(this.allowBlank){
22004                 this.clearInvalid();
22005                 return true;
22006              }else{
22007                 this.markInvalid(this.blankText);
22008                 return false;
22009              }
22010         }
22011         if(value.length < this.minLength){
22012             this.markInvalid(String.format(this.minLengthText, this.minLength));
22013             return false;
22014         }
22015         if(value.length > this.maxLength){
22016             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
22017             return false;
22018         }
22019         if(this.vtype){
22020             var vt = Roo.form.VTypes;
22021             if(!vt[this.vtype](value, this)){
22022                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
22023                 return false;
22024             }
22025         }
22026         if(typeof this.validator == "function"){
22027             var msg = this.validator(value);
22028             if(msg !== true){
22029                 this.markInvalid(msg);
22030                 return false;
22031             }
22032         }
22033         if(this.regex && !this.regex.test(value)){
22034             this.markInvalid(this.regexText);
22035             return false;
22036         }
22037         return true;
22038     },
22039
22040     /**
22041      * Selects text in this field
22042      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
22043      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
22044      */
22045     selectText : function(start, end){
22046         var v = this.getRawValue();
22047         if(v.length > 0){
22048             start = start === undefined ? 0 : start;
22049             end = end === undefined ? v.length : end;
22050             var d = this.el.dom;
22051             if(d.setSelectionRange){
22052                 d.setSelectionRange(start, end);
22053             }else if(d.createTextRange){
22054                 var range = d.createTextRange();
22055                 range.moveStart("character", start);
22056                 range.moveEnd("character", v.length-end);
22057                 range.select();
22058             }
22059         }
22060     },
22061
22062     /**
22063      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
22064      * This only takes effect if grow = true, and fires the autosize event.
22065      */
22066     autoSize : function(){
22067         if(!this.grow || !this.rendered){
22068             return;
22069         }
22070         if(!this.metrics){
22071             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
22072         }
22073         var el = this.el;
22074         var v = el.dom.value;
22075         var d = document.createElement('div');
22076         d.appendChild(document.createTextNode(v));
22077         v = d.innerHTML;
22078         d = null;
22079         v += "&#160;";
22080         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
22081         this.el.setWidth(w);
22082         this.fireEvent("autosize", this, w);
22083     },
22084     
22085     // private
22086     SafariOnKeyDown : function(event)
22087     {
22088         // this is a workaround for a password hang bug on chrome/ webkit.
22089         
22090         var isSelectAll = false;
22091         
22092         if(this.el.dom.selectionEnd > 0){
22093             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
22094         }
22095         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
22096             event.preventDefault();
22097             this.setValue('');
22098             return;
22099         }
22100         
22101         if(isSelectAll){ // backspace and delete key
22102             
22103             event.preventDefault();
22104             // this is very hacky as keydown always get's upper case.
22105             //
22106             var cc = String.fromCharCode(event.getCharCode());
22107             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
22108             
22109         }
22110         
22111         
22112     }
22113 });/*
22114  * Based on:
22115  * Ext JS Library 1.1.1
22116  * Copyright(c) 2006-2007, Ext JS, LLC.
22117  *
22118  * Originally Released Under LGPL - original licence link has changed is not relivant.
22119  *
22120  * Fork - LGPL
22121  * <script type="text/javascript">
22122  */
22123  
22124 /**
22125  * @class Roo.form.Hidden
22126  * @extends Roo.form.TextField
22127  * Simple Hidden element used on forms 
22128  * 
22129  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
22130  * 
22131  * @constructor
22132  * Creates a new Hidden form element.
22133  * @param {Object} config Configuration options
22134  */
22135
22136
22137
22138 // easy hidden field...
22139 Roo.form.Hidden = function(config){
22140     Roo.form.Hidden.superclass.constructor.call(this, config);
22141 };
22142   
22143 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
22144     fieldLabel:      '',
22145     inputType:      'hidden',
22146     width:          50,
22147     allowBlank:     true,
22148     labelSeparator: '',
22149     hidden:         true,
22150     itemCls :       'x-form-item-display-none'
22151
22152
22153 });
22154
22155
22156 /*
22157  * Based on:
22158  * Ext JS Library 1.1.1
22159  * Copyright(c) 2006-2007, Ext JS, LLC.
22160  *
22161  * Originally Released Under LGPL - original licence link has changed is not relivant.
22162  *
22163  * Fork - LGPL
22164  * <script type="text/javascript">
22165  */
22166  
22167 /**
22168  * @class Roo.form.TriggerField
22169  * @extends Roo.form.TextField
22170  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
22171  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
22172  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
22173  * for which you can provide a custom implementation.  For example:
22174  * <pre><code>
22175 var trigger = new Roo.form.TriggerField();
22176 trigger.onTriggerClick = myTriggerFn;
22177 trigger.applyTo('my-field');
22178 </code></pre>
22179  *
22180  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
22181  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
22182  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
22183  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
22184  * @constructor
22185  * Create a new TriggerField.
22186  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
22187  * to the base TextField)
22188  */
22189 Roo.form.TriggerField = function(config){
22190     this.mimicing = false;
22191     Roo.form.TriggerField.superclass.constructor.call(this, config);
22192 };
22193
22194 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
22195     /**
22196      * @cfg {String} triggerClass A CSS class to apply to the trigger
22197      */
22198     /**
22199      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
22200      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
22201      */
22202     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
22203     /**
22204      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
22205      */
22206     hideTrigger:false,
22207
22208     /** @cfg {Boolean} grow @hide */
22209     /** @cfg {Number} growMin @hide */
22210     /** @cfg {Number} growMax @hide */
22211
22212     /**
22213      * @hide 
22214      * @method
22215      */
22216     autoSize: Roo.emptyFn,
22217     // private
22218     monitorTab : true,
22219     // private
22220     deferHeight : true,
22221
22222     
22223     actionMode : 'wrap',
22224     // private
22225     onResize : function(w, h){
22226         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
22227         if(typeof w == 'number'){
22228             var x = w - this.trigger.getWidth();
22229             this.el.setWidth(this.adjustWidth('input', x));
22230             this.trigger.setStyle('left', x+'px');
22231         }
22232     },
22233
22234     // private
22235     adjustSize : Roo.BoxComponent.prototype.adjustSize,
22236
22237     // private
22238     getResizeEl : function(){
22239         return this.wrap;
22240     },
22241
22242     // private
22243     getPositionEl : function(){
22244         return this.wrap;
22245     },
22246
22247     // private
22248     alignErrorIcon : function(){
22249         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
22250     },
22251
22252     // private
22253     onRender : function(ct, position){
22254         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
22255         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
22256         this.trigger = this.wrap.createChild(this.triggerConfig ||
22257                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
22258         if(this.hideTrigger){
22259             this.trigger.setDisplayed(false);
22260         }
22261         this.initTrigger();
22262         if(!this.width){
22263             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
22264         }
22265     },
22266
22267     // private
22268     initTrigger : function(){
22269         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
22270         this.trigger.addClassOnOver('x-form-trigger-over');
22271         this.trigger.addClassOnClick('x-form-trigger-click');
22272     },
22273
22274     // private
22275     onDestroy : function(){
22276         if(this.trigger){
22277             this.trigger.removeAllListeners();
22278             this.trigger.remove();
22279         }
22280         if(this.wrap){
22281             this.wrap.remove();
22282         }
22283         Roo.form.TriggerField.superclass.onDestroy.call(this);
22284     },
22285
22286     // private
22287     onFocus : function(){
22288         Roo.form.TriggerField.superclass.onFocus.call(this);
22289         if(!this.mimicing){
22290             this.wrap.addClass('x-trigger-wrap-focus');
22291             this.mimicing = true;
22292             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
22293             if(this.monitorTab){
22294                 this.el.on("keydown", this.checkTab, this);
22295             }
22296         }
22297     },
22298
22299     // private
22300     checkTab : function(e){
22301         if(e.getKey() == e.TAB){
22302             this.triggerBlur();
22303         }
22304     },
22305
22306     // private
22307     onBlur : function(){
22308         // do nothing
22309     },
22310
22311     // private
22312     mimicBlur : function(e, t){
22313         if(!this.wrap.contains(t) && this.validateBlur()){
22314             this.triggerBlur();
22315         }
22316     },
22317
22318     // private
22319     triggerBlur : function(){
22320         this.mimicing = false;
22321         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
22322         if(this.monitorTab){
22323             this.el.un("keydown", this.checkTab, this);
22324         }
22325         this.wrap.removeClass('x-trigger-wrap-focus');
22326         Roo.form.TriggerField.superclass.onBlur.call(this);
22327     },
22328
22329     // private
22330     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
22331     validateBlur : function(e, t){
22332         return true;
22333     },
22334
22335     // private
22336     onDisable : function(){
22337         Roo.form.TriggerField.superclass.onDisable.call(this);
22338         if(this.wrap){
22339             this.wrap.addClass('x-item-disabled');
22340         }
22341     },
22342
22343     // private
22344     onEnable : function(){
22345         Roo.form.TriggerField.superclass.onEnable.call(this);
22346         if(this.wrap){
22347             this.wrap.removeClass('x-item-disabled');
22348         }
22349     },
22350
22351     // private
22352     onShow : function(){
22353         var ae = this.getActionEl();
22354         
22355         if(ae){
22356             ae.dom.style.display = '';
22357             ae.dom.style.visibility = 'visible';
22358         }
22359     },
22360
22361     // private
22362     
22363     onHide : function(){
22364         var ae = this.getActionEl();
22365         ae.dom.style.display = 'none';
22366     },
22367
22368     /**
22369      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
22370      * by an implementing function.
22371      * @method
22372      * @param {EventObject} e
22373      */
22374     onTriggerClick : Roo.emptyFn
22375 });
22376
22377 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
22378 // to be extended by an implementing class.  For an example of implementing this class, see the custom
22379 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
22380 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
22381     initComponent : function(){
22382         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
22383
22384         this.triggerConfig = {
22385             tag:'span', cls:'x-form-twin-triggers', cn:[
22386             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
22387             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
22388         ]};
22389     },
22390
22391     getTrigger : function(index){
22392         return this.triggers[index];
22393     },
22394
22395     initTrigger : function(){
22396         var ts = this.trigger.select('.x-form-trigger', true);
22397         this.wrap.setStyle('overflow', 'hidden');
22398         var triggerField = this;
22399         ts.each(function(t, all, index){
22400             t.hide = function(){
22401                 var w = triggerField.wrap.getWidth();
22402                 this.dom.style.display = 'none';
22403                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
22404             };
22405             t.show = function(){
22406                 var w = triggerField.wrap.getWidth();
22407                 this.dom.style.display = '';
22408                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
22409             };
22410             var triggerIndex = 'Trigger'+(index+1);
22411
22412             if(this['hide'+triggerIndex]){
22413                 t.dom.style.display = 'none';
22414             }
22415             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
22416             t.addClassOnOver('x-form-trigger-over');
22417             t.addClassOnClick('x-form-trigger-click');
22418         }, this);
22419         this.triggers = ts.elements;
22420     },
22421
22422     onTrigger1Click : Roo.emptyFn,
22423     onTrigger2Click : Roo.emptyFn
22424 });/*
22425  * Based on:
22426  * Ext JS Library 1.1.1
22427  * Copyright(c) 2006-2007, Ext JS, LLC.
22428  *
22429  * Originally Released Under LGPL - original licence link has changed is not relivant.
22430  *
22431  * Fork - LGPL
22432  * <script type="text/javascript">
22433  */
22434  
22435 /**
22436  * @class Roo.form.TextArea
22437  * @extends Roo.form.TextField
22438  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
22439  * support for auto-sizing.
22440  * @constructor
22441  * Creates a new TextArea
22442  * @param {Object} config Configuration options
22443  */
22444 Roo.form.TextArea = function(config){
22445     Roo.form.TextArea.superclass.constructor.call(this, config);
22446     // these are provided exchanges for backwards compat
22447     // minHeight/maxHeight were replaced by growMin/growMax to be
22448     // compatible with TextField growing config values
22449     if(this.minHeight !== undefined){
22450         this.growMin = this.minHeight;
22451     }
22452     if(this.maxHeight !== undefined){
22453         this.growMax = this.maxHeight;
22454     }
22455 };
22456
22457 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
22458     /**
22459      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
22460      */
22461     growMin : 60,
22462     /**
22463      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
22464      */
22465     growMax: 1000,
22466     /**
22467      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
22468      * in the field (equivalent to setting overflow: hidden, defaults to false)
22469      */
22470     preventScrollbars: false,
22471     /**
22472      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
22473      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
22474      */
22475
22476     // private
22477     onRender : function(ct, position){
22478         if(!this.el){
22479             this.defaultAutoCreate = {
22480                 tag: "textarea",
22481                 style:"width:300px;height:60px;",
22482                 autocomplete: "off"
22483             };
22484         }
22485         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
22486         if(this.grow){
22487             this.textSizeEl = Roo.DomHelper.append(document.body, {
22488                 tag: "pre", cls: "x-form-grow-sizer"
22489             });
22490             if(this.preventScrollbars){
22491                 this.el.setStyle("overflow", "hidden");
22492             }
22493             this.el.setHeight(this.growMin);
22494         }
22495     },
22496
22497     onDestroy : function(){
22498         if(this.textSizeEl){
22499             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
22500         }
22501         Roo.form.TextArea.superclass.onDestroy.call(this);
22502     },
22503
22504     // private
22505     onKeyUp : function(e){
22506         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
22507             this.autoSize();
22508         }
22509     },
22510
22511     /**
22512      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
22513      * This only takes effect if grow = true, and fires the autosize event if the height changes.
22514      */
22515     autoSize : function(){
22516         if(!this.grow || !this.textSizeEl){
22517             return;
22518         }
22519         var el = this.el;
22520         var v = el.dom.value;
22521         var ts = this.textSizeEl;
22522
22523         ts.innerHTML = '';
22524         ts.appendChild(document.createTextNode(v));
22525         v = ts.innerHTML;
22526
22527         Roo.fly(ts).setWidth(this.el.getWidth());
22528         if(v.length < 1){
22529             v = "&#160;&#160;";
22530         }else{
22531             if(Roo.isIE){
22532                 v = v.replace(/\n/g, '<p>&#160;</p>');
22533             }
22534             v += "&#160;\n&#160;";
22535         }
22536         ts.innerHTML = v;
22537         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
22538         if(h != this.lastHeight){
22539             this.lastHeight = h;
22540             this.el.setHeight(h);
22541             this.fireEvent("autosize", this, h);
22542         }
22543     }
22544 });/*
22545  * Based on:
22546  * Ext JS Library 1.1.1
22547  * Copyright(c) 2006-2007, Ext JS, LLC.
22548  *
22549  * Originally Released Under LGPL - original licence link has changed is not relivant.
22550  *
22551  * Fork - LGPL
22552  * <script type="text/javascript">
22553  */
22554  
22555
22556 /**
22557  * @class Roo.form.NumberField
22558  * @extends Roo.form.TextField
22559  * Numeric text field that provides automatic keystroke filtering and numeric validation.
22560  * @constructor
22561  * Creates a new NumberField
22562  * @param {Object} config Configuration options
22563  */
22564 Roo.form.NumberField = function(config){
22565     Roo.form.NumberField.superclass.constructor.call(this, config);
22566 };
22567
22568 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
22569     /**
22570      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
22571      */
22572     fieldClass: "x-form-field x-form-num-field",
22573     /**
22574      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
22575      */
22576     allowDecimals : true,
22577     /**
22578      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
22579      */
22580     decimalSeparator : ".",
22581     /**
22582      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
22583      */
22584     decimalPrecision : 2,
22585     /**
22586      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
22587      */
22588     allowNegative : true,
22589     /**
22590      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
22591      */
22592     minValue : Number.NEGATIVE_INFINITY,
22593     /**
22594      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
22595      */
22596     maxValue : Number.MAX_VALUE,
22597     /**
22598      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
22599      */
22600     minText : "The minimum value for this field is {0}",
22601     /**
22602      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
22603      */
22604     maxText : "The maximum value for this field is {0}",
22605     /**
22606      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
22607      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
22608      */
22609     nanText : "{0} is not a valid number",
22610
22611     // private
22612     initEvents : function(){
22613         Roo.form.NumberField.superclass.initEvents.call(this);
22614         var allowed = "0123456789";
22615         if(this.allowDecimals){
22616             allowed += this.decimalSeparator;
22617         }
22618         if(this.allowNegative){
22619             allowed += "-";
22620         }
22621         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
22622         var keyPress = function(e){
22623             var k = e.getKey();
22624             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
22625                 return;
22626             }
22627             var c = e.getCharCode();
22628             if(allowed.indexOf(String.fromCharCode(c)) === -1){
22629                 e.stopEvent();
22630             }
22631         };
22632         this.el.on("keypress", keyPress, this);
22633     },
22634
22635     // private
22636     validateValue : function(value){
22637         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
22638             return false;
22639         }
22640         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22641              return true;
22642         }
22643         var num = this.parseValue(value);
22644         if(isNaN(num)){
22645             this.markInvalid(String.format(this.nanText, value));
22646             return false;
22647         }
22648         if(num < this.minValue){
22649             this.markInvalid(String.format(this.minText, this.minValue));
22650             return false;
22651         }
22652         if(num > this.maxValue){
22653             this.markInvalid(String.format(this.maxText, this.maxValue));
22654             return false;
22655         }
22656         return true;
22657     },
22658
22659     getValue : function(){
22660         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
22661     },
22662
22663     // private
22664     parseValue : function(value){
22665         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
22666         return isNaN(value) ? '' : value;
22667     },
22668
22669     // private
22670     fixPrecision : function(value){
22671         var nan = isNaN(value);
22672         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
22673             return nan ? '' : value;
22674         }
22675         return parseFloat(value).toFixed(this.decimalPrecision);
22676     },
22677
22678     setValue : function(v){
22679         v = this.fixPrecision(v);
22680         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
22681     },
22682
22683     // private
22684     decimalPrecisionFcn : function(v){
22685         return Math.floor(v);
22686     },
22687
22688     beforeBlur : function(){
22689         var v = this.parseValue(this.getRawValue());
22690         if(v){
22691             this.setValue(v);
22692         }
22693     }
22694 });/*
22695  * Based on:
22696  * Ext JS Library 1.1.1
22697  * Copyright(c) 2006-2007, Ext JS, LLC.
22698  *
22699  * Originally Released Under LGPL - original licence link has changed is not relivant.
22700  *
22701  * Fork - LGPL
22702  * <script type="text/javascript">
22703  */
22704  
22705 /**
22706  * @class Roo.form.DateField
22707  * @extends Roo.form.TriggerField
22708  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22709 * @constructor
22710 * Create a new DateField
22711 * @param {Object} config
22712  */
22713 Roo.form.DateField = function(config){
22714     Roo.form.DateField.superclass.constructor.call(this, config);
22715     
22716       this.addEvents({
22717          
22718         /**
22719          * @event select
22720          * Fires when a date is selected
22721              * @param {Roo.form.DateField} combo This combo box
22722              * @param {Date} date The date selected
22723              */
22724         'select' : true
22725          
22726     });
22727     
22728     
22729     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22730     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22731     this.ddMatch = null;
22732     if(this.disabledDates){
22733         var dd = this.disabledDates;
22734         var re = "(?:";
22735         for(var i = 0; i < dd.length; i++){
22736             re += dd[i];
22737             if(i != dd.length-1) re += "|";
22738         }
22739         this.ddMatch = new RegExp(re + ")");
22740     }
22741 };
22742
22743 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
22744     /**
22745      * @cfg {String} format
22746      * The default date format string which can be overriden for localization support.  The format must be
22747      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22748      */
22749     format : "m/d/y",
22750     /**
22751      * @cfg {String} altFormats
22752      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22753      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22754      */
22755     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22756     /**
22757      * @cfg {Array} disabledDays
22758      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22759      */
22760     disabledDays : null,
22761     /**
22762      * @cfg {String} disabledDaysText
22763      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22764      */
22765     disabledDaysText : "Disabled",
22766     /**
22767      * @cfg {Array} disabledDates
22768      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22769      * expression so they are very powerful. Some examples:
22770      * <ul>
22771      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22772      * <li>["03/08", "09/16"] would disable those days for every year</li>
22773      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22774      * <li>["03/../2006"] would disable every day in March 2006</li>
22775      * <li>["^03"] would disable every day in every March</li>
22776      * </ul>
22777      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22778      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22779      */
22780     disabledDates : null,
22781     /**
22782      * @cfg {String} disabledDatesText
22783      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22784      */
22785     disabledDatesText : "Disabled",
22786     /**
22787      * @cfg {Date/String} minValue
22788      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22789      * valid format (defaults to null).
22790      */
22791     minValue : null,
22792     /**
22793      * @cfg {Date/String} maxValue
22794      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22795      * valid format (defaults to null).
22796      */
22797     maxValue : null,
22798     /**
22799      * @cfg {String} minText
22800      * The error text to display when the date in the cell is before minValue (defaults to
22801      * 'The date in this field must be after {minValue}').
22802      */
22803     minText : "The date in this field must be equal to or after {0}",
22804     /**
22805      * @cfg {String} maxText
22806      * The error text to display when the date in the cell is after maxValue (defaults to
22807      * 'The date in this field must be before {maxValue}').
22808      */
22809     maxText : "The date in this field must be equal to or before {0}",
22810     /**
22811      * @cfg {String} invalidText
22812      * The error text to display when the date in the field is invalid (defaults to
22813      * '{value} is not a valid date - it must be in the format {format}').
22814      */
22815     invalidText : "{0} is not a valid date - it must be in the format {1}",
22816     /**
22817      * @cfg {String} triggerClass
22818      * An additional CSS class used to style the trigger button.  The trigger will always get the
22819      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22820      * which displays a calendar icon).
22821      */
22822     triggerClass : 'x-form-date-trigger',
22823     
22824
22825     /**
22826      * @cfg {Boolean} useIso
22827      * if enabled, then the date field will use a hidden field to store the 
22828      * real value as iso formated date. default (false)
22829      */ 
22830     useIso : false,
22831     /**
22832      * @cfg {String/Object} autoCreate
22833      * A DomHelper element spec, or true for a default element spec (defaults to
22834      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22835      */ 
22836     // private
22837     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
22838     
22839     // private
22840     hiddenField: false,
22841     
22842     onRender : function(ct, position)
22843     {
22844         Roo.form.DateField.superclass.onRender.call(this, ct, position);
22845         if (this.useIso) {
22846             //this.el.dom.removeAttribute('name'); 
22847             Roo.log("Changing name?");
22848             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
22849             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22850                     'before', true);
22851             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
22852             // prevent input submission
22853             this.hiddenName = this.name;
22854         }
22855             
22856             
22857     },
22858     
22859     // private
22860     validateValue : function(value)
22861     {
22862         value = this.formatDate(value);
22863         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
22864             Roo.log('super failed');
22865             return false;
22866         }
22867         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22868              return true;
22869         }
22870         var svalue = value;
22871         value = this.parseDate(value);
22872         if(!value){
22873             Roo.log('parse date failed' + svalue);
22874             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22875             return false;
22876         }
22877         var time = value.getTime();
22878         if(this.minValue && time < this.minValue.getTime()){
22879             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22880             return false;
22881         }
22882         if(this.maxValue && time > this.maxValue.getTime()){
22883             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22884             return false;
22885         }
22886         if(this.disabledDays){
22887             var day = value.getDay();
22888             for(var i = 0; i < this.disabledDays.length; i++) {
22889                 if(day === this.disabledDays[i]){
22890                     this.markInvalid(this.disabledDaysText);
22891                     return false;
22892                 }
22893             }
22894         }
22895         var fvalue = this.formatDate(value);
22896         if(this.ddMatch && this.ddMatch.test(fvalue)){
22897             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22898             return false;
22899         }
22900         return true;
22901     },
22902
22903     // private
22904     // Provides logic to override the default TriggerField.validateBlur which just returns true
22905     validateBlur : function(){
22906         return !this.menu || !this.menu.isVisible();
22907     },
22908     
22909     getName: function()
22910     {
22911         // returns hidden if it's set..
22912         if (!this.rendered) {return ''};
22913         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
22914         
22915     },
22916
22917     /**
22918      * Returns the current date value of the date field.
22919      * @return {Date} The date value
22920      */
22921     getValue : function(){
22922         
22923         return  this.hiddenField ?
22924                 this.hiddenField.value :
22925                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
22926     },
22927
22928     /**
22929      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22930      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
22931      * (the default format used is "m/d/y").
22932      * <br />Usage:
22933      * <pre><code>
22934 //All of these calls set the same date value (May 4, 2006)
22935
22936 //Pass a date object:
22937 var dt = new Date('5/4/06');
22938 dateField.setValue(dt);
22939
22940 //Pass a date string (default format):
22941 dateField.setValue('5/4/06');
22942
22943 //Pass a date string (custom format):
22944 dateField.format = 'Y-m-d';
22945 dateField.setValue('2006-5-4');
22946 </code></pre>
22947      * @param {String/Date} date The date or valid date string
22948      */
22949     setValue : function(date){
22950         if (this.hiddenField) {
22951             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22952         }
22953         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22954         // make sure the value field is always stored as a date..
22955         this.value = this.parseDate(date);
22956         
22957         
22958     },
22959
22960     // private
22961     parseDate : function(value){
22962         if(!value || value instanceof Date){
22963             return value;
22964         }
22965         var v = Date.parseDate(value, this.format);
22966          if (!v && this.useIso) {
22967             v = Date.parseDate(value, 'Y-m-d');
22968         }
22969         if(!v && this.altFormats){
22970             if(!this.altFormatsArray){
22971                 this.altFormatsArray = this.altFormats.split("|");
22972             }
22973             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22974                 v = Date.parseDate(value, this.altFormatsArray[i]);
22975             }
22976         }
22977         return v;
22978     },
22979
22980     // private
22981     formatDate : function(date, fmt){
22982         return (!date || !(date instanceof Date)) ?
22983                date : date.dateFormat(fmt || this.format);
22984     },
22985
22986     // private
22987     menuListeners : {
22988         select: function(m, d){
22989             
22990             this.setValue(d);
22991             this.fireEvent('select', this, d);
22992         },
22993         show : function(){ // retain focus styling
22994             this.onFocus();
22995         },
22996         hide : function(){
22997             this.focus.defer(10, this);
22998             var ml = this.menuListeners;
22999             this.menu.un("select", ml.select,  this);
23000             this.menu.un("show", ml.show,  this);
23001             this.menu.un("hide", ml.hide,  this);
23002         }
23003     },
23004
23005     // private
23006     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
23007     onTriggerClick : function(){
23008         if(this.disabled){
23009             return;
23010         }
23011         if(this.menu == null){
23012             this.menu = new Roo.menu.DateMenu();
23013         }
23014         Roo.apply(this.menu.picker,  {
23015             showClear: this.allowBlank,
23016             minDate : this.minValue,
23017             maxDate : this.maxValue,
23018             disabledDatesRE : this.ddMatch,
23019             disabledDatesText : this.disabledDatesText,
23020             disabledDays : this.disabledDays,
23021             disabledDaysText : this.disabledDaysText,
23022             format : this.useIso ? 'Y-m-d' : this.format,
23023             minText : String.format(this.minText, this.formatDate(this.minValue)),
23024             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
23025         });
23026         this.menu.on(Roo.apply({}, this.menuListeners, {
23027             scope:this
23028         }));
23029         this.menu.picker.setValue(this.getValue() || new Date());
23030         this.menu.show(this.el, "tl-bl?");
23031     },
23032
23033     beforeBlur : function(){
23034         var v = this.parseDate(this.getRawValue());
23035         if(v){
23036             this.setValue(v);
23037         }
23038     }
23039
23040     /** @cfg {Boolean} grow @hide */
23041     /** @cfg {Number} growMin @hide */
23042     /** @cfg {Number} growMax @hide */
23043     /**
23044      * @hide
23045      * @method autoSize
23046      */
23047 });/*
23048  * Based on:
23049  * Ext JS Library 1.1.1
23050  * Copyright(c) 2006-2007, Ext JS, LLC.
23051  *
23052  * Originally Released Under LGPL - original licence link has changed is not relivant.
23053  *
23054  * Fork - LGPL
23055  * <script type="text/javascript">
23056  */
23057  
23058 /**
23059  * @class Roo.form.MonthField
23060  * @extends Roo.form.TriggerField
23061  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
23062 * @constructor
23063 * Create a new MonthField
23064 * @param {Object} config
23065  */
23066 Roo.form.MonthField = function(config){
23067     
23068     Roo.form.MonthField.superclass.constructor.call(this, config);
23069     
23070       this.addEvents({
23071          
23072         /**
23073          * @event select
23074          * Fires when a date is selected
23075              * @param {Roo.form.MonthFieeld} combo This combo box
23076              * @param {Date} date The date selected
23077              */
23078         'select' : true
23079          
23080     });
23081     
23082     
23083     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
23084     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
23085     this.ddMatch = null;
23086     if(this.disabledDates){
23087         var dd = this.disabledDates;
23088         var re = "(?:";
23089         for(var i = 0; i < dd.length; i++){
23090             re += dd[i];
23091             if(i != dd.length-1) re += "|";
23092         }
23093         this.ddMatch = new RegExp(re + ")");
23094     }
23095 };
23096
23097 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
23098     /**
23099      * @cfg {String} format
23100      * The default date format string which can be overriden for localization support.  The format must be
23101      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
23102      */
23103     format : "M Y",
23104     /**
23105      * @cfg {String} altFormats
23106      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
23107      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
23108      */
23109     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
23110     /**
23111      * @cfg {Array} disabledDays
23112      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
23113      */
23114     disabledDays : [0,1,2,3,4,5,6],
23115     /**
23116      * @cfg {String} disabledDaysText
23117      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
23118      */
23119     disabledDaysText : "Disabled",
23120     /**
23121      * @cfg {Array} disabledDates
23122      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
23123      * expression so they are very powerful. Some examples:
23124      * <ul>
23125      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
23126      * <li>["03/08", "09/16"] would disable those days for every year</li>
23127      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
23128      * <li>["03/../2006"] would disable every day in March 2006</li>
23129      * <li>["^03"] would disable every day in every March</li>
23130      * </ul>
23131      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
23132      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
23133      */
23134     disabledDates : null,
23135     /**
23136      * @cfg {String} disabledDatesText
23137      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
23138      */
23139     disabledDatesText : "Disabled",
23140     /**
23141      * @cfg {Date/String} minValue
23142      * The minimum allowed date. Can be either a Javascript date object or a string date in a
23143      * valid format (defaults to null).
23144      */
23145     minValue : null,
23146     /**
23147      * @cfg {Date/String} maxValue
23148      * The maximum allowed date. Can be either a Javascript date object or a string date in a
23149      * valid format (defaults to null).
23150      */
23151     maxValue : null,
23152     /**
23153      * @cfg {String} minText
23154      * The error text to display when the date in the cell is before minValue (defaults to
23155      * 'The date in this field must be after {minValue}').
23156      */
23157     minText : "The date in this field must be equal to or after {0}",
23158     /**
23159      * @cfg {String} maxTextf
23160      * The error text to display when the date in the cell is after maxValue (defaults to
23161      * 'The date in this field must be before {maxValue}').
23162      */
23163     maxText : "The date in this field must be equal to or before {0}",
23164     /**
23165      * @cfg {String} invalidText
23166      * The error text to display when the date in the field is invalid (defaults to
23167      * '{value} is not a valid date - it must be in the format {format}').
23168      */
23169     invalidText : "{0} is not a valid date - it must be in the format {1}",
23170     /**
23171      * @cfg {String} triggerClass
23172      * An additional CSS class used to style the trigger button.  The trigger will always get the
23173      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
23174      * which displays a calendar icon).
23175      */
23176     triggerClass : 'x-form-date-trigger',
23177     
23178
23179     /**
23180      * @cfg {Boolean} useIso
23181      * if enabled, then the date field will use a hidden field to store the 
23182      * real value as iso formated date. default (true)
23183      */ 
23184     useIso : true,
23185     /**
23186      * @cfg {String/Object} autoCreate
23187      * A DomHelper element spec, or true for a default element spec (defaults to
23188      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
23189      */ 
23190     // private
23191     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
23192     
23193     // private
23194     hiddenField: false,
23195     
23196     hideMonthPicker : false,
23197     
23198     onRender : function(ct, position)
23199     {
23200         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
23201         if (this.useIso) {
23202             this.el.dom.removeAttribute('name'); 
23203             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
23204                     'before', true);
23205             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
23206             // prevent input submission
23207             this.hiddenName = this.name;
23208         }
23209             
23210             
23211     },
23212     
23213     // private
23214     validateValue : function(value)
23215     {
23216         value = this.formatDate(value);
23217         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
23218             return false;
23219         }
23220         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
23221              return true;
23222         }
23223         var svalue = value;
23224         value = this.parseDate(value);
23225         if(!value){
23226             this.markInvalid(String.format(this.invalidText, svalue, this.format));
23227             return false;
23228         }
23229         var time = value.getTime();
23230         if(this.minValue && time < this.minValue.getTime()){
23231             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
23232             return false;
23233         }
23234         if(this.maxValue && time > this.maxValue.getTime()){
23235             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
23236             return false;
23237         }
23238         /*if(this.disabledDays){
23239             var day = value.getDay();
23240             for(var i = 0; i < this.disabledDays.length; i++) {
23241                 if(day === this.disabledDays[i]){
23242                     this.markInvalid(this.disabledDaysText);
23243                     return false;
23244                 }
23245             }
23246         }
23247         */
23248         var fvalue = this.formatDate(value);
23249         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
23250             this.markInvalid(String.format(this.disabledDatesText, fvalue));
23251             return false;
23252         }
23253         */
23254         return true;
23255     },
23256
23257     // private
23258     // Provides logic to override the default TriggerField.validateBlur which just returns true
23259     validateBlur : function(){
23260         return !this.menu || !this.menu.isVisible();
23261     },
23262
23263     /**
23264      * Returns the current date value of the date field.
23265      * @return {Date} The date value
23266      */
23267     getValue : function(){
23268         
23269         
23270         
23271         return  this.hiddenField ?
23272                 this.hiddenField.value :
23273                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
23274     },
23275
23276     /**
23277      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
23278      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
23279      * (the default format used is "m/d/y").
23280      * <br />Usage:
23281      * <pre><code>
23282 //All of these calls set the same date value (May 4, 2006)
23283
23284 //Pass a date object:
23285 var dt = new Date('5/4/06');
23286 monthField.setValue(dt);
23287
23288 //Pass a date string (default format):
23289 monthField.setValue('5/4/06');
23290
23291 //Pass a date string (custom format):
23292 monthField.format = 'Y-m-d';
23293 monthField.setValue('2006-5-4');
23294 </code></pre>
23295      * @param {String/Date} date The date or valid date string
23296      */
23297     setValue : function(date){
23298         Roo.log('month setValue' + date);
23299         // can only be first of month..
23300         
23301         var val = this.parseDate(date);
23302         
23303         if (this.hiddenField) {
23304             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
23305         }
23306         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
23307         this.value = this.parseDate(date);
23308     },
23309
23310     // private
23311     parseDate : function(value){
23312         if(!value || value instanceof Date){
23313             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
23314             return value;
23315         }
23316         var v = Date.parseDate(value, this.format);
23317         if (!v && this.useIso) {
23318             v = Date.parseDate(value, 'Y-m-d');
23319         }
23320         if (v) {
23321             // 
23322             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
23323         }
23324         
23325         
23326         if(!v && this.altFormats){
23327             if(!this.altFormatsArray){
23328                 this.altFormatsArray = this.altFormats.split("|");
23329             }
23330             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
23331                 v = Date.parseDate(value, this.altFormatsArray[i]);
23332             }
23333         }
23334         return v;
23335     },
23336
23337     // private
23338     formatDate : function(date, fmt){
23339         return (!date || !(date instanceof Date)) ?
23340                date : date.dateFormat(fmt || this.format);
23341     },
23342
23343     // private
23344     menuListeners : {
23345         select: function(m, d){
23346             this.setValue(d);
23347             this.fireEvent('select', this, d);
23348         },
23349         show : function(){ // retain focus styling
23350             this.onFocus();
23351         },
23352         hide : function(){
23353             this.focus.defer(10, this);
23354             var ml = this.menuListeners;
23355             this.menu.un("select", ml.select,  this);
23356             this.menu.un("show", ml.show,  this);
23357             this.menu.un("hide", ml.hide,  this);
23358         }
23359     },
23360     // private
23361     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
23362     onTriggerClick : function(){
23363         if(this.disabled){
23364             return;
23365         }
23366         if(this.menu == null){
23367             this.menu = new Roo.menu.DateMenu();
23368            
23369         }
23370         
23371         Roo.apply(this.menu.picker,  {
23372             
23373             showClear: this.allowBlank,
23374             minDate : this.minValue,
23375             maxDate : this.maxValue,
23376             disabledDatesRE : this.ddMatch,
23377             disabledDatesText : this.disabledDatesText,
23378             
23379             format : this.useIso ? 'Y-m-d' : this.format,
23380             minText : String.format(this.minText, this.formatDate(this.minValue)),
23381             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
23382             
23383         });
23384          this.menu.on(Roo.apply({}, this.menuListeners, {
23385             scope:this
23386         }));
23387        
23388         
23389         var m = this.menu;
23390         var p = m.picker;
23391         
23392         // hide month picker get's called when we called by 'before hide';
23393         
23394         var ignorehide = true;
23395         p.hideMonthPicker  = function(disableAnim){
23396             if (ignorehide) {
23397                 return;
23398             }
23399              if(this.monthPicker){
23400                 Roo.log("hideMonthPicker called");
23401                 if(disableAnim === true){
23402                     this.monthPicker.hide();
23403                 }else{
23404                     this.monthPicker.slideOut('t', {duration:.2});
23405                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
23406                     p.fireEvent("select", this, this.value);
23407                     m.hide();
23408                 }
23409             }
23410         }
23411         
23412         Roo.log('picker set value');
23413         Roo.log(this.getValue());
23414         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
23415         m.show(this.el, 'tl-bl?');
23416         ignorehide  = false;
23417         // this will trigger hideMonthPicker..
23418         
23419         
23420         // hidden the day picker
23421         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
23422         
23423         
23424         
23425       
23426         
23427         p.showMonthPicker.defer(100, p);
23428     
23429         
23430        
23431     },
23432
23433     beforeBlur : function(){
23434         var v = this.parseDate(this.getRawValue());
23435         if(v){
23436             this.setValue(v);
23437         }
23438     }
23439
23440     /** @cfg {Boolean} grow @hide */
23441     /** @cfg {Number} growMin @hide */
23442     /** @cfg {Number} growMax @hide */
23443     /**
23444      * @hide
23445      * @method autoSize
23446      */
23447 });/*
23448  * Based on:
23449  * Ext JS Library 1.1.1
23450  * Copyright(c) 2006-2007, Ext JS, LLC.
23451  *
23452  * Originally Released Under LGPL - original licence link has changed is not relivant.
23453  *
23454  * Fork - LGPL
23455  * <script type="text/javascript">
23456  */
23457  
23458
23459 /**
23460  * @class Roo.form.ComboBox
23461  * @extends Roo.form.TriggerField
23462  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
23463  * @constructor
23464  * Create a new ComboBox.
23465  * @param {Object} config Configuration options
23466  */
23467 Roo.form.ComboBox = function(config){
23468     Roo.form.ComboBox.superclass.constructor.call(this, config);
23469     this.addEvents({
23470         /**
23471          * @event expand
23472          * Fires when the dropdown list is expanded
23473              * @param {Roo.form.ComboBox} combo This combo box
23474              */
23475         'expand' : true,
23476         /**
23477          * @event collapse
23478          * Fires when the dropdown list is collapsed
23479              * @param {Roo.form.ComboBox} combo This combo box
23480              */
23481         'collapse' : true,
23482         /**
23483          * @event beforeselect
23484          * Fires before a list item is selected. Return false to cancel the selection.
23485              * @param {Roo.form.ComboBox} combo This combo box
23486              * @param {Roo.data.Record} record The data record returned from the underlying store
23487              * @param {Number} index The index of the selected item in the dropdown list
23488              */
23489         'beforeselect' : true,
23490         /**
23491          * @event select
23492          * Fires when a list item is selected
23493              * @param {Roo.form.ComboBox} combo This combo box
23494              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
23495              * @param {Number} index The index of the selected item in the dropdown list
23496              */
23497         'select' : true,
23498         /**
23499          * @event beforequery
23500          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
23501          * The event object passed has these properties:
23502              * @param {Roo.form.ComboBox} combo This combo box
23503              * @param {String} query The query
23504              * @param {Boolean} forceAll true to force "all" query
23505              * @param {Boolean} cancel true to cancel the query
23506              * @param {Object} e The query event object
23507              */
23508         'beforequery': true,
23509          /**
23510          * @event add
23511          * Fires when the 'add' icon is pressed (add a listener to enable add button)
23512              * @param {Roo.form.ComboBox} combo This combo box
23513              */
23514         'add' : true,
23515         /**
23516          * @event edit
23517          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
23518              * @param {Roo.form.ComboBox} combo This combo box
23519              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
23520              */
23521         'edit' : true
23522         
23523         
23524     });
23525     if(this.transform){
23526         this.allowDomMove = false;
23527         var s = Roo.getDom(this.transform);
23528         if(!this.hiddenName){
23529             this.hiddenName = s.name;
23530         }
23531         if(!this.store){
23532             this.mode = 'local';
23533             var d = [], opts = s.options;
23534             for(var i = 0, len = opts.length;i < len; i++){
23535                 var o = opts[i];
23536                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
23537                 if(o.selected) {
23538                     this.value = value;
23539                 }
23540                 d.push([value, o.text]);
23541             }
23542             this.store = new Roo.data.SimpleStore({
23543                 'id': 0,
23544                 fields: ['value', 'text'],
23545                 data : d
23546             });
23547             this.valueField = 'value';
23548             this.displayField = 'text';
23549         }
23550         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
23551         if(!this.lazyRender){
23552             this.target = true;
23553             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
23554             s.parentNode.removeChild(s); // remove it
23555             this.render(this.el.parentNode);
23556         }else{
23557             s.parentNode.removeChild(s); // remove it
23558         }
23559
23560     }
23561     if (this.store) {
23562         this.store = Roo.factory(this.store, Roo.data);
23563     }
23564     
23565     this.selectedIndex = -1;
23566     if(this.mode == 'local'){
23567         if(config.queryDelay === undefined){
23568             this.queryDelay = 10;
23569         }
23570         if(config.minChars === undefined){
23571             this.minChars = 0;
23572         }
23573     }
23574 };
23575
23576 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
23577     /**
23578      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
23579      */
23580     /**
23581      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
23582      * rendering into an Roo.Editor, defaults to false)
23583      */
23584     /**
23585      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
23586      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
23587      */
23588     /**
23589      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
23590      */
23591     /**
23592      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
23593      * the dropdown list (defaults to undefined, with no header element)
23594      */
23595
23596      /**
23597      * @cfg {String/Roo.Template} tpl The template to use to render the output
23598      */
23599      
23600     // private
23601     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
23602     /**
23603      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
23604      */
23605     listWidth: undefined,
23606     /**
23607      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
23608      * mode = 'remote' or 'text' if mode = 'local')
23609      */
23610     displayField: undefined,
23611     /**
23612      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
23613      * mode = 'remote' or 'value' if mode = 'local'). 
23614      * Note: use of a valueField requires the user make a selection
23615      * in order for a value to be mapped.
23616      */
23617     valueField: undefined,
23618     
23619     
23620     /**
23621      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
23622      * field's data value (defaults to the underlying DOM element's name)
23623      */
23624     hiddenName: undefined,
23625     /**
23626      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
23627      */
23628     listClass: '',
23629     /**
23630      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
23631      */
23632     selectedClass: 'x-combo-selected',
23633     /**
23634      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
23635      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
23636      * which displays a downward arrow icon).
23637      */
23638     triggerClass : 'x-form-arrow-trigger',
23639     /**
23640      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
23641      */
23642     shadow:'sides',
23643     /**
23644      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
23645      * anchor positions (defaults to 'tl-bl')
23646      */
23647     listAlign: 'tl-bl?',
23648     /**
23649      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
23650      */
23651     maxHeight: 300,
23652     /**
23653      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
23654      * query specified by the allQuery config option (defaults to 'query')
23655      */
23656     triggerAction: 'query',
23657     /**
23658      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
23659      * (defaults to 4, does not apply if editable = false)
23660      */
23661     minChars : 4,
23662     /**
23663      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
23664      * delay (typeAheadDelay) if it matches a known value (defaults to false)
23665      */
23666     typeAhead: false,
23667     /**
23668      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
23669      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
23670      */
23671     queryDelay: 500,
23672     /**
23673      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
23674      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
23675      */
23676     pageSize: 0,
23677     /**
23678      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
23679      * when editable = true (defaults to false)
23680      */
23681     selectOnFocus:false,
23682     /**
23683      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
23684      */
23685     queryParam: 'query',
23686     /**
23687      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
23688      * when mode = 'remote' (defaults to 'Loading...')
23689      */
23690     loadingText: 'Loading...',
23691     /**
23692      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
23693      */
23694     resizable: false,
23695     /**
23696      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
23697      */
23698     handleHeight : 8,
23699     /**
23700      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
23701      * traditional select (defaults to true)
23702      */
23703     editable: true,
23704     /**
23705      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
23706      */
23707     allQuery: '',
23708     /**
23709      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
23710      */
23711     mode: 'remote',
23712     /**
23713      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
23714      * listWidth has a higher value)
23715      */
23716     minListWidth : 70,
23717     /**
23718      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
23719      * allow the user to set arbitrary text into the field (defaults to false)
23720      */
23721     forceSelection:false,
23722     /**
23723      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
23724      * if typeAhead = true (defaults to 250)
23725      */
23726     typeAheadDelay : 250,
23727     /**
23728      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
23729      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
23730      */
23731     valueNotFoundText : undefined,
23732     /**
23733      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
23734      */
23735     blockFocus : false,
23736     
23737     /**
23738      * @cfg {Boolean} disableClear Disable showing of clear button.
23739      */
23740     disableClear : false,
23741     /**
23742      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
23743      */
23744     alwaysQuery : false,
23745     
23746     //private
23747     addicon : false,
23748     editicon: false,
23749     
23750     // element that contains real text value.. (when hidden is used..)
23751      
23752     // private
23753     onRender : function(ct, position){
23754         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
23755         if(this.hiddenName){
23756             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
23757                     'before', true);
23758             this.hiddenField.value =
23759                 this.hiddenValue !== undefined ? this.hiddenValue :
23760                 this.value !== undefined ? this.value : '';
23761
23762             // prevent input submission
23763             this.el.dom.removeAttribute('name');
23764              
23765              
23766         }
23767         if(Roo.isGecko){
23768             this.el.dom.setAttribute('autocomplete', 'off');
23769         }
23770
23771         var cls = 'x-combo-list';
23772
23773         this.list = new Roo.Layer({
23774             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
23775         });
23776
23777         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
23778         this.list.setWidth(lw);
23779         this.list.swallowEvent('mousewheel');
23780         this.assetHeight = 0;
23781
23782         if(this.title){
23783             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
23784             this.assetHeight += this.header.getHeight();
23785         }
23786
23787         this.innerList = this.list.createChild({cls:cls+'-inner'});
23788         this.innerList.on('mouseover', this.onViewOver, this);
23789         this.innerList.on('mousemove', this.onViewMove, this);
23790         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23791         
23792         if(this.allowBlank && !this.pageSize && !this.disableClear){
23793             this.footer = this.list.createChild({cls:cls+'-ft'});
23794             this.pageTb = new Roo.Toolbar(this.footer);
23795            
23796         }
23797         if(this.pageSize){
23798             this.footer = this.list.createChild({cls:cls+'-ft'});
23799             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
23800                     {pageSize: this.pageSize});
23801             
23802         }
23803         
23804         if (this.pageTb && this.allowBlank && !this.disableClear) {
23805             var _this = this;
23806             this.pageTb.add(new Roo.Toolbar.Fill(), {
23807                 cls: 'x-btn-icon x-btn-clear',
23808                 text: '&#160;',
23809                 handler: function()
23810                 {
23811                     _this.collapse();
23812                     _this.clearValue();
23813                     _this.onSelect(false, -1);
23814                 }
23815             });
23816         }
23817         if (this.footer) {
23818             this.assetHeight += this.footer.getHeight();
23819         }
23820         
23821
23822         if(!this.tpl){
23823             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
23824         }
23825
23826         this.view = new Roo.View(this.innerList, this.tpl, {
23827             singleSelect:true, store: this.store, selectedClass: this.selectedClass
23828         });
23829
23830         this.view.on('click', this.onViewClick, this);
23831
23832         this.store.on('beforeload', this.onBeforeLoad, this);
23833         this.store.on('load', this.onLoad, this);
23834         this.store.on('loadexception', this.onLoadException, this);
23835
23836         if(this.resizable){
23837             this.resizer = new Roo.Resizable(this.list,  {
23838                pinned:true, handles:'se'
23839             });
23840             this.resizer.on('resize', function(r, w, h){
23841                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
23842                 this.listWidth = w;
23843                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
23844                 this.restrictHeight();
23845             }, this);
23846             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
23847         }
23848         if(!this.editable){
23849             this.editable = true;
23850             this.setEditable(false);
23851         }  
23852         
23853         
23854         if (typeof(this.events.add.listeners) != 'undefined') {
23855             
23856             this.addicon = this.wrap.createChild(
23857                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
23858        
23859             this.addicon.on('click', function(e) {
23860                 this.fireEvent('add', this);
23861             }, this);
23862         }
23863         if (typeof(this.events.edit.listeners) != 'undefined') {
23864             
23865             this.editicon = this.wrap.createChild(
23866                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
23867             if (this.addicon) {
23868                 this.editicon.setStyle('margin-left', '40px');
23869             }
23870             this.editicon.on('click', function(e) {
23871                 
23872                 // we fire even  if inothing is selected..
23873                 this.fireEvent('edit', this, this.lastData );
23874                 
23875             }, this);
23876         }
23877         
23878         
23879         
23880     },
23881
23882     // private
23883     initEvents : function(){
23884         Roo.form.ComboBox.superclass.initEvents.call(this);
23885
23886         this.keyNav = new Roo.KeyNav(this.el, {
23887             "up" : function(e){
23888                 this.inKeyMode = true;
23889                 this.selectPrev();
23890             },
23891
23892             "down" : function(e){
23893                 if(!this.isExpanded()){
23894                     this.onTriggerClick();
23895                 }else{
23896                     this.inKeyMode = true;
23897                     this.selectNext();
23898                 }
23899             },
23900
23901             "enter" : function(e){
23902                 this.onViewClick();
23903                 //return true;
23904             },
23905
23906             "esc" : function(e){
23907                 this.collapse();
23908             },
23909
23910             "tab" : function(e){
23911                 this.onViewClick(false);
23912                 this.fireEvent("specialkey", this, e);
23913                 return true;
23914             },
23915
23916             scope : this,
23917
23918             doRelay : function(foo, bar, hname){
23919                 if(hname == 'down' || this.scope.isExpanded()){
23920                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
23921                 }
23922                 return true;
23923             },
23924
23925             forceKeyDown: true
23926         });
23927         this.queryDelay = Math.max(this.queryDelay || 10,
23928                 this.mode == 'local' ? 10 : 250);
23929         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
23930         if(this.typeAhead){
23931             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
23932         }
23933         if(this.editable !== false){
23934             this.el.on("keyup", this.onKeyUp, this);
23935         }
23936         if(this.forceSelection){
23937             this.on('blur', this.doForce, this);
23938         }
23939     },
23940
23941     onDestroy : function(){
23942         if(this.view){
23943             this.view.setStore(null);
23944             this.view.el.removeAllListeners();
23945             this.view.el.remove();
23946             this.view.purgeListeners();
23947         }
23948         if(this.list){
23949             this.list.destroy();
23950         }
23951         if(this.store){
23952             this.store.un('beforeload', this.onBeforeLoad, this);
23953             this.store.un('load', this.onLoad, this);
23954             this.store.un('loadexception', this.onLoadException, this);
23955         }
23956         Roo.form.ComboBox.superclass.onDestroy.call(this);
23957     },
23958
23959     // private
23960     fireKey : function(e){
23961         if(e.isNavKeyPress() && !this.list.isVisible()){
23962             this.fireEvent("specialkey", this, e);
23963         }
23964     },
23965
23966     // private
23967     onResize: function(w, h){
23968         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
23969         
23970         if(typeof w != 'number'){
23971             // we do not handle it!?!?
23972             return;
23973         }
23974         var tw = this.trigger.getWidth();
23975         tw += this.addicon ? this.addicon.getWidth() : 0;
23976         tw += this.editicon ? this.editicon.getWidth() : 0;
23977         var x = w - tw;
23978         this.el.setWidth( this.adjustWidth('input', x));
23979             
23980         this.trigger.setStyle('left', x+'px');
23981         
23982         if(this.list && this.listWidth === undefined){
23983             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
23984             this.list.setWidth(lw);
23985             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23986         }
23987         
23988     
23989         
23990     },
23991
23992     /**
23993      * Allow or prevent the user from directly editing the field text.  If false is passed,
23994      * the user will only be able to select from the items defined in the dropdown list.  This method
23995      * is the runtime equivalent of setting the 'editable' config option at config time.
23996      * @param {Boolean} value True to allow the user to directly edit the field text
23997      */
23998     setEditable : function(value){
23999         if(value == this.editable){
24000             return;
24001         }
24002         this.editable = value;
24003         if(!value){
24004             this.el.dom.setAttribute('readOnly', true);
24005             this.el.on('mousedown', this.onTriggerClick,  this);
24006             this.el.addClass('x-combo-noedit');
24007         }else{
24008             this.el.dom.setAttribute('readOnly', false);
24009             this.el.un('mousedown', this.onTriggerClick,  this);
24010             this.el.removeClass('x-combo-noedit');
24011         }
24012     },
24013
24014     // private
24015     onBeforeLoad : function(){
24016         if(!this.hasFocus){
24017             return;
24018         }
24019         this.innerList.update(this.loadingText ?
24020                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
24021         this.restrictHeight();
24022         this.selectedIndex = -1;
24023     },
24024
24025     // private
24026     onLoad : function(){
24027         if(!this.hasFocus){
24028             return;
24029         }
24030         if(this.store.getCount() > 0){
24031             this.expand();
24032             this.restrictHeight();
24033             if(this.lastQuery == this.allQuery){
24034                 if(this.editable){
24035                     this.el.dom.select();
24036                 }
24037                 if(!this.selectByValue(this.value, true)){
24038                     this.select(0, true);
24039                 }
24040             }else{
24041                 this.selectNext();
24042                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
24043                     this.taTask.delay(this.typeAheadDelay);
24044                 }
24045             }
24046         }else{
24047             this.onEmptyResults();
24048         }
24049         //this.el.focus();
24050     },
24051     // private
24052     onLoadException : function()
24053     {
24054         this.collapse();
24055         Roo.log(this.store.reader.jsonData);
24056         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
24057             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
24058         }
24059         
24060         
24061     },
24062     // private
24063     onTypeAhead : function(){
24064         if(this.store.getCount() > 0){
24065             var r = this.store.getAt(0);
24066             var newValue = r.data[this.displayField];
24067             var len = newValue.length;
24068             var selStart = this.getRawValue().length;
24069             if(selStart != len){
24070                 this.setRawValue(newValue);
24071                 this.selectText(selStart, newValue.length);
24072             }
24073         }
24074     },
24075
24076     // private
24077     onSelect : function(record, index){
24078         if(this.fireEvent('beforeselect', this, record, index) !== false){
24079             this.setFromData(index > -1 ? record.data : false);
24080             this.collapse();
24081             this.fireEvent('select', this, record, index);
24082         }
24083     },
24084
24085     /**
24086      * Returns the currently selected field value or empty string if no value is set.
24087      * @return {String} value The selected value
24088      */
24089     getValue : function(){
24090         if(this.valueField){
24091             return typeof this.value != 'undefined' ? this.value : '';
24092         }else{
24093             return Roo.form.ComboBox.superclass.getValue.call(this);
24094         }
24095     },
24096
24097     /**
24098      * Clears any text/value currently set in the field
24099      */
24100     clearValue : function(){
24101         if(this.hiddenField){
24102             this.hiddenField.value = '';
24103         }
24104         this.value = '';
24105         this.setRawValue('');
24106         this.lastSelectionText = '';
24107         
24108     },
24109
24110     /**
24111      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
24112      * will be displayed in the field.  If the value does not match the data value of an existing item,
24113      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
24114      * Otherwise the field will be blank (although the value will still be set).
24115      * @param {String} value The value to match
24116      */
24117     setValue : function(v){
24118         var text = v;
24119         if(this.valueField){
24120             var r = this.findRecord(this.valueField, v);
24121             if(r){
24122                 text = r.data[this.displayField];
24123             }else if(this.valueNotFoundText !== undefined){
24124                 text = this.valueNotFoundText;
24125             }
24126         }
24127         this.lastSelectionText = text;
24128         if(this.hiddenField){
24129             this.hiddenField.value = v;
24130         }
24131         Roo.form.ComboBox.superclass.setValue.call(this, text);
24132         this.value = v;
24133     },
24134     /**
24135      * @property {Object} the last set data for the element
24136      */
24137     
24138     lastData : false,
24139     /**
24140      * Sets the value of the field based on a object which is related to the record format for the store.
24141      * @param {Object} value the value to set as. or false on reset?
24142      */
24143     setFromData : function(o){
24144         var dv = ''; // display value
24145         var vv = ''; // value value..
24146         this.lastData = o;
24147         if (this.displayField) {
24148             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
24149         } else {
24150             // this is an error condition!!!
24151             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
24152         }
24153         
24154         if(this.valueField){
24155             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
24156         }
24157         if(this.hiddenField){
24158             this.hiddenField.value = vv;
24159             
24160             this.lastSelectionText = dv;
24161             Roo.form.ComboBox.superclass.setValue.call(this, dv);
24162             this.value = vv;
24163             return;
24164         }
24165         // no hidden field.. - we store the value in 'value', but still display
24166         // display field!!!!
24167         this.lastSelectionText = dv;
24168         Roo.form.ComboBox.superclass.setValue.call(this, dv);
24169         this.value = vv;
24170         
24171         
24172     },
24173     // private
24174     reset : function(){
24175         // overridden so that last data is reset..
24176         this.setValue(this.originalValue);
24177         this.clearInvalid();
24178         this.lastData = false;
24179         if (this.view) {
24180             this.view.clearSelections();
24181         }
24182     },
24183     // private
24184     findRecord : function(prop, value){
24185         var record;
24186         if(this.store.getCount() > 0){
24187             this.store.each(function(r){
24188                 if(r.data[prop] == value){
24189                     record = r;
24190                     return false;
24191                 }
24192                 return true;
24193             });
24194         }
24195         return record;
24196     },
24197     
24198     getName: function()
24199     {
24200         // returns hidden if it's set..
24201         if (!this.rendered) {return ''};
24202         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
24203         
24204     },
24205     // private
24206     onViewMove : function(e, t){
24207         this.inKeyMode = false;
24208     },
24209
24210     // private
24211     onViewOver : function(e, t){
24212         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
24213             return;
24214         }
24215         var item = this.view.findItemFromChild(t);
24216         if(item){
24217             var index = this.view.indexOf(item);
24218             this.select(index, false);
24219         }
24220     },
24221
24222     // private
24223     onViewClick : function(doFocus)
24224     {
24225         var index = this.view.getSelectedIndexes()[0];
24226         var r = this.store.getAt(index);
24227         if(r){
24228             this.onSelect(r, index);
24229         }
24230         if(doFocus !== false && !this.blockFocus){
24231             this.el.focus();
24232         }
24233     },
24234
24235     // private
24236     restrictHeight : function(){
24237         this.innerList.dom.style.height = '';
24238         var inner = this.innerList.dom;
24239         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
24240         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
24241         this.list.beginUpdate();
24242         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
24243         this.list.alignTo(this.el, this.listAlign);
24244         this.list.endUpdate();
24245     },
24246
24247     // private
24248     onEmptyResults : function(){
24249         this.collapse();
24250     },
24251
24252     /**
24253      * Returns true if the dropdown list is expanded, else false.
24254      */
24255     isExpanded : function(){
24256         return this.list.isVisible();
24257     },
24258
24259     /**
24260      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
24261      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
24262      * @param {String} value The data value of the item to select
24263      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
24264      * selected item if it is not currently in view (defaults to true)
24265      * @return {Boolean} True if the value matched an item in the list, else false
24266      */
24267     selectByValue : function(v, scrollIntoView){
24268         if(v !== undefined && v !== null){
24269             var r = this.findRecord(this.valueField || this.displayField, v);
24270             if(r){
24271                 this.select(this.store.indexOf(r), scrollIntoView);
24272                 return true;
24273             }
24274         }
24275         return false;
24276     },
24277
24278     /**
24279      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
24280      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
24281      * @param {Number} index The zero-based index of the list item to select
24282      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
24283      * selected item if it is not currently in view (defaults to true)
24284      */
24285     select : function(index, scrollIntoView){
24286         this.selectedIndex = index;
24287         this.view.select(index);
24288         if(scrollIntoView !== false){
24289             var el = this.view.getNode(index);
24290             if(el){
24291                 this.innerList.scrollChildIntoView(el, false);
24292             }
24293         }
24294     },
24295
24296     // private
24297     selectNext : function(){
24298         var ct = this.store.getCount();
24299         if(ct > 0){
24300             if(this.selectedIndex == -1){
24301                 this.select(0);
24302             }else if(this.selectedIndex < ct-1){
24303                 this.select(this.selectedIndex+1);
24304             }
24305         }
24306     },
24307
24308     // private
24309     selectPrev : function(){
24310         var ct = this.store.getCount();
24311         if(ct > 0){
24312             if(this.selectedIndex == -1){
24313                 this.select(0);
24314             }else if(this.selectedIndex != 0){
24315                 this.select(this.selectedIndex-1);
24316             }
24317         }
24318     },
24319
24320     // private
24321     onKeyUp : function(e){
24322         if(this.editable !== false && !e.isSpecialKey()){
24323             this.lastKey = e.getKey();
24324             this.dqTask.delay(this.queryDelay);
24325         }
24326     },
24327
24328     // private
24329     validateBlur : function(){
24330         return !this.list || !this.list.isVisible();   
24331     },
24332
24333     // private
24334     initQuery : function(){
24335         this.doQuery(this.getRawValue());
24336     },
24337
24338     // private
24339     doForce : function(){
24340         if(this.el.dom.value.length > 0){
24341             this.el.dom.value =
24342                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
24343              
24344         }
24345     },
24346
24347     /**
24348      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
24349      * query allowing the query action to be canceled if needed.
24350      * @param {String} query The SQL query to execute
24351      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
24352      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
24353      * saved in the current store (defaults to false)
24354      */
24355     doQuery : function(q, forceAll){
24356         if(q === undefined || q === null){
24357             q = '';
24358         }
24359         var qe = {
24360             query: q,
24361             forceAll: forceAll,
24362             combo: this,
24363             cancel:false
24364         };
24365         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
24366             return false;
24367         }
24368         q = qe.query;
24369         forceAll = qe.forceAll;
24370         if(forceAll === true || (q.length >= this.minChars)){
24371             if(this.lastQuery != q || this.alwaysQuery){
24372                 this.lastQuery = q;
24373                 if(this.mode == 'local'){
24374                     this.selectedIndex = -1;
24375                     if(forceAll){
24376                         this.store.clearFilter();
24377                     }else{
24378                         this.store.filter(this.displayField, q);
24379                     }
24380                     this.onLoad();
24381                 }else{
24382                     this.store.baseParams[this.queryParam] = q;
24383                     this.store.load({
24384                         params: this.getParams(q)
24385                     });
24386                     this.expand();
24387                 }
24388             }else{
24389                 this.selectedIndex = -1;
24390                 this.onLoad();   
24391             }
24392         }
24393     },
24394
24395     // private
24396     getParams : function(q){
24397         var p = {};
24398         //p[this.queryParam] = q;
24399         if(this.pageSize){
24400             p.start = 0;
24401             p.limit = this.pageSize;
24402         }
24403         return p;
24404     },
24405
24406     /**
24407      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
24408      */
24409     collapse : function(){
24410         if(!this.isExpanded()){
24411             return;
24412         }
24413         this.list.hide();
24414         Roo.get(document).un('mousedown', this.collapseIf, this);
24415         Roo.get(document).un('mousewheel', this.collapseIf, this);
24416         if (!this.editable) {
24417             Roo.get(document).un('keydown', this.listKeyPress, this);
24418         }
24419         this.fireEvent('collapse', this);
24420     },
24421
24422     // private
24423     collapseIf : function(e){
24424         if(!e.within(this.wrap) && !e.within(this.list)){
24425             this.collapse();
24426         }
24427     },
24428
24429     /**
24430      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
24431      */
24432     expand : function(){
24433         if(this.isExpanded() || !this.hasFocus){
24434             return;
24435         }
24436         this.list.alignTo(this.el, this.listAlign);
24437         this.list.show();
24438         Roo.get(document).on('mousedown', this.collapseIf, this);
24439         Roo.get(document).on('mousewheel', this.collapseIf, this);
24440         if (!this.editable) {
24441             Roo.get(document).on('keydown', this.listKeyPress, this);
24442         }
24443         
24444         this.fireEvent('expand', this);
24445     },
24446
24447     // private
24448     // Implements the default empty TriggerField.onTriggerClick function
24449     onTriggerClick : function(){
24450         if(this.disabled){
24451             return;
24452         }
24453         if(this.isExpanded()){
24454             this.collapse();
24455             if (!this.blockFocus) {
24456                 this.el.focus();
24457             }
24458             
24459         }else {
24460             this.hasFocus = true;
24461             if(this.triggerAction == 'all') {
24462                 this.doQuery(this.allQuery, true);
24463             } else {
24464                 this.doQuery(this.getRawValue());
24465             }
24466             if (!this.blockFocus) {
24467                 this.el.focus();
24468             }
24469         }
24470     },
24471     listKeyPress : function(e)
24472     {
24473         //Roo.log('listkeypress');
24474         // scroll to first matching element based on key pres..
24475         if (e.isSpecialKey()) {
24476             return false;
24477         }
24478         var k = String.fromCharCode(e.getKey()).toUpperCase();
24479         //Roo.log(k);
24480         var match  = false;
24481         var csel = this.view.getSelectedNodes();
24482         var cselitem = false;
24483         if (csel.length) {
24484             var ix = this.view.indexOf(csel[0]);
24485             cselitem  = this.store.getAt(ix);
24486             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
24487                 cselitem = false;
24488             }
24489             
24490         }
24491         
24492         this.store.each(function(v) { 
24493             if (cselitem) {
24494                 // start at existing selection.
24495                 if (cselitem.id == v.id) {
24496                     cselitem = false;
24497                 }
24498                 return;
24499             }
24500                 
24501             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
24502                 match = this.store.indexOf(v);
24503                 return false;
24504             }
24505         }, this);
24506         
24507         if (match === false) {
24508             return true; // no more action?
24509         }
24510         // scroll to?
24511         this.view.select(match);
24512         var sn = Roo.get(this.view.getSelectedNodes()[0])
24513         sn.scrollIntoView(sn.dom.parentNode, false);
24514     }
24515
24516     /** 
24517     * @cfg {Boolean} grow 
24518     * @hide 
24519     */
24520     /** 
24521     * @cfg {Number} growMin 
24522     * @hide 
24523     */
24524     /** 
24525     * @cfg {Number} growMax 
24526     * @hide 
24527     */
24528     /**
24529      * @hide
24530      * @method autoSize
24531      */
24532 });/*
24533  * Copyright(c) 2010-2012, Roo J Solutions Limited
24534  *
24535  * Licence LGPL
24536  *
24537  */
24538
24539 /**
24540  * @class Roo.form.ComboBoxArray
24541  * @extends Roo.form.TextField
24542  * A facebook style adder... for lists of email / people / countries  etc...
24543  * pick multiple items from a combo box, and shows each one.
24544  *
24545  *  Fred [x]  Brian [x]  [Pick another |v]
24546  *
24547  *
24548  *  For this to work: it needs various extra information
24549  *    - normal combo problay has
24550  *      name, hiddenName
24551  *    + displayField, valueField
24552  *
24553  *    For our purpose...
24554  *
24555  *
24556  *   If we change from 'extends' to wrapping...
24557  *   
24558  *  
24559  *
24560  
24561  
24562  * @constructor
24563  * Create a new ComboBoxArray.
24564  * @param {Object} config Configuration options
24565  */
24566  
24567
24568 Roo.form.ComboBoxArray = function(config)
24569 {
24570     
24571     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
24572     
24573     this.items = new Roo.util.MixedCollection(false);
24574     
24575     // construct the child combo...
24576     
24577     
24578     
24579     
24580    
24581     
24582 }
24583
24584  
24585 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
24586
24587     /**
24588      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
24589      */
24590     
24591     lastData : false,
24592     
24593     // behavies liek a hiddne field
24594     inputType:      'hidden',
24595     /**
24596      * @cfg {Number} width The width of the box that displays the selected element
24597      */ 
24598     width:          300,
24599
24600     
24601     
24602     /**
24603      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
24604      */
24605     name : false,
24606     /**
24607      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
24608      */
24609     hiddenName : false,
24610     
24611     
24612     // private the array of items that are displayed..
24613     items  : false,
24614     // private - the hidden field el.
24615     hiddenEl : false,
24616     // private - the filed el..
24617     el : false,
24618     
24619     //validateValue : function() { return true; }, // all values are ok!
24620     //onAddClick: function() { },
24621     
24622     onRender : function(ct, position) 
24623     {
24624         
24625         // create the standard hidden element
24626         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
24627         
24628         
24629         // give fake names to child combo;
24630         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
24631         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
24632         
24633         this.combo = Roo.factory(this.combo, Roo.form);
24634         this.combo.onRender(ct, position);
24635         if (typeof(this.combo.width) != 'undefined') {
24636             this.combo.onResize(this.combo.width,0);
24637         }
24638         
24639         this.combo.initEvents();
24640         
24641         // assigned so form know we need to do this..
24642         this.store          = this.combo.store;
24643         this.valueField     = this.combo.valueField;
24644         this.displayField   = this.combo.displayField ;
24645         
24646         
24647         this.combo.wrap.addClass('x-cbarray-grp');
24648         
24649         var cbwrap = this.combo.wrap.createChild(
24650             {tag: 'div', cls: 'x-cbarray-cb'},
24651             this.combo.el.dom
24652         );
24653         
24654              
24655         this.hiddenEl = this.combo.wrap.createChild({
24656             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
24657         });
24658         this.el = this.combo.wrap.createChild({
24659             tag: 'input',  type:'hidden' , name: this.name, value : ''
24660         });
24661          //   this.el.dom.removeAttribute("name");
24662         
24663         
24664         this.outerWrap = this.combo.wrap;
24665         this.wrap = cbwrap;
24666         
24667         this.outerWrap.setWidth(this.width);
24668         this.outerWrap.dom.removeChild(this.el.dom);
24669         
24670         this.wrap.dom.appendChild(this.el.dom);
24671         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
24672         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
24673         
24674         this.combo.trigger.setStyle('position','relative');
24675         this.combo.trigger.setStyle('left', '0px');
24676         this.combo.trigger.setStyle('top', '2px');
24677         
24678         this.combo.el.setStyle('vertical-align', 'text-bottom');
24679         
24680         //this.trigger.setStyle('vertical-align', 'top');
24681         
24682         // this should use the code from combo really... on('add' ....)
24683         if (this.adder) {
24684             
24685         
24686             this.adder = this.outerWrap.createChild(
24687                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
24688             var _t = this;
24689             this.adder.on('click', function(e) {
24690                 _t.fireEvent('adderclick', this, e);
24691             }, _t);
24692         }
24693         //var _t = this;
24694         //this.adder.on('click', this.onAddClick, _t);
24695         
24696         
24697         this.combo.on('select', function(cb, rec, ix) {
24698             this.addItem(rec.data);
24699             
24700             cb.setValue('');
24701             cb.el.dom.value = '';
24702             //cb.lastData = rec.data;
24703             // add to list
24704             
24705         }, this);
24706         
24707         
24708     },
24709     
24710     
24711     getName: function()
24712     {
24713         // returns hidden if it's set..
24714         if (!this.rendered) {return ''};
24715         return  this.hiddenName ? this.hiddenName : this.name;
24716         
24717     },
24718     
24719     
24720     onResize: function(w, h){
24721         
24722         return;
24723         // not sure if this is needed..
24724         //this.combo.onResize(w,h);
24725         
24726         if(typeof w != 'number'){
24727             // we do not handle it!?!?
24728             return;
24729         }
24730         var tw = this.combo.trigger.getWidth();
24731         tw += this.addicon ? this.addicon.getWidth() : 0;
24732         tw += this.editicon ? this.editicon.getWidth() : 0;
24733         var x = w - tw;
24734         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
24735             
24736         this.combo.trigger.setStyle('left', '0px');
24737         
24738         if(this.list && this.listWidth === undefined){
24739             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
24740             this.list.setWidth(lw);
24741             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
24742         }
24743         
24744     
24745         
24746     },
24747     
24748     addItem: function(rec)
24749     {
24750         var valueField = this.combo.valueField;
24751         var displayField = this.combo.displayField;
24752         if (this.items.indexOfKey(rec[valueField]) > -1) {
24753             //console.log("GOT " + rec.data.id);
24754             return;
24755         }
24756         
24757         var x = new Roo.form.ComboBoxArray.Item({
24758             //id : rec[this.idField],
24759             data : rec,
24760             displayField : displayField ,
24761             tipField : displayField ,
24762             cb : this
24763         });
24764         // use the 
24765         this.items.add(rec[valueField],x);
24766         // add it before the element..
24767         this.updateHiddenEl();
24768         x.render(this.outerWrap, this.wrap.dom);
24769         // add the image handler..
24770     },
24771     
24772     updateHiddenEl : function()
24773     {
24774         this.validate();
24775         if (!this.hiddenEl) {
24776             return;
24777         }
24778         var ar = [];
24779         var idField = this.combo.valueField;
24780         
24781         this.items.each(function(f) {
24782             ar.push(f.data[idField]);
24783            
24784         });
24785         this.hiddenEl.dom.value = ar.join(',');
24786         this.validate();
24787     },
24788     
24789     reset : function()
24790     {
24791         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
24792         this.items.each(function(f) {
24793            f.remove(); 
24794         });
24795         this.el.dom.value = '';
24796         if (this.hiddenEl) {
24797             this.hiddenEl.dom.value = '';
24798         }
24799         
24800     },
24801     getValue: function()
24802     {
24803         return this.hiddenEl ? this.hiddenEl.dom.value : '';
24804     },
24805     setValue: function(v) // not a valid action - must use addItems..
24806     {
24807          
24808         this.reset();
24809         
24810         
24811         
24812         if (this.store.isLocal && (typeof(v) == 'string')) {
24813             // then we can use the store to find the values..
24814             // comma seperated at present.. this needs to allow JSON based encoding..
24815             this.hiddenEl.value  = v;
24816             var v_ar = [];
24817             Roo.each(v.split(','), function(k) {
24818                 Roo.log("CHECK " + this.valueField + ',' + k);
24819                 var li = this.store.query(this.valueField, k);
24820                 if (!li.length) {
24821                     return;
24822                 }
24823                 var add = {};
24824                 add[this.valueField] = k;
24825                 add[this.displayField] = li.item(0).data[this.displayField];
24826                 
24827                 this.addItem(add);
24828             }, this) 
24829              
24830         }
24831         if (typeof(v) == 'object') {
24832             // then let's assume it's an array of objects..
24833             Roo.each(v, function(l) {
24834                 this.addItem(l);
24835             }, this);
24836              
24837         }
24838         
24839         
24840     },
24841     setFromData: function(v)
24842     {
24843         // this recieves an object, if setValues is called.
24844         this.reset();
24845         this.el.dom.value = v[this.displayField];
24846         this.hiddenEl.dom.value = v[this.valueField];
24847         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
24848             return;
24849         }
24850         var kv = v[this.valueField];
24851         var dv = v[this.displayField];
24852         kv = typeof(kv) != 'string' ? '' : kv;
24853         dv = typeof(dv) != 'string' ? '' : dv;
24854         
24855         
24856         var keys = kv.split(',');
24857         var display = dv.split(',');
24858         for (var i = 0 ; i < keys.length; i++) {
24859             
24860             add = {};
24861             add[this.valueField] = keys[i];
24862             add[this.displayField] = display[i];
24863             this.addItem(add);
24864         }
24865       
24866         
24867     },
24868     
24869     
24870     validateValue : function(value){
24871         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
24872         
24873     }
24874     
24875 });
24876
24877
24878
24879 /**
24880  * @class Roo.form.ComboBoxArray.Item
24881  * @extends Roo.BoxComponent
24882  * A selected item in the list
24883  *  Fred [x]  Brian [x]  [Pick another |v]
24884  * 
24885  * @constructor
24886  * Create a new item.
24887  * @param {Object} config Configuration options
24888  */
24889  
24890 Roo.form.ComboBoxArray.Item = function(config) {
24891     config.id = Roo.id();
24892     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
24893 }
24894
24895 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
24896     data : {},
24897     cb: false,
24898     displayField : false,
24899     tipField : false,
24900     
24901     
24902     defaultAutoCreate : {
24903         tag: 'div',
24904         cls: 'x-cbarray-item',
24905         cn : [ 
24906             { tag: 'div' },
24907             {
24908                 tag: 'img',
24909                 width:16,
24910                 height : 16,
24911                 src : Roo.BLANK_IMAGE_URL ,
24912                 align: 'center'
24913             }
24914         ]
24915         
24916     },
24917     
24918  
24919     onRender : function(ct, position)
24920     {
24921         Roo.form.Field.superclass.onRender.call(this, ct, position);
24922         
24923         if(!this.el){
24924             var cfg = this.getAutoCreate();
24925             this.el = ct.createChild(cfg, position);
24926         }
24927         
24928         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
24929         
24930         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
24931             this.cb.renderer(this.data) :
24932             String.format('{0}',this.data[this.displayField]);
24933         
24934             
24935         this.el.child('div').dom.setAttribute('qtip',
24936                         String.format('{0}',this.data[this.tipField])
24937         );
24938         
24939         this.el.child('img').on('click', this.remove, this);
24940         
24941     },
24942    
24943     remove : function()
24944     {
24945         
24946         this.cb.items.remove(this);
24947         this.el.child('img').un('click', this.remove, this);
24948         this.el.remove();
24949         this.cb.updateHiddenEl();
24950     }
24951     
24952     
24953 });/*
24954  * Based on:
24955  * Ext JS Library 1.1.1
24956  * Copyright(c) 2006-2007, Ext JS, LLC.
24957  *
24958  * Originally Released Under LGPL - original licence link has changed is not relivant.
24959  *
24960  * Fork - LGPL
24961  * <script type="text/javascript">
24962  */
24963 /**
24964  * @class Roo.form.Checkbox
24965  * @extends Roo.form.Field
24966  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
24967  * @constructor
24968  * Creates a new Checkbox
24969  * @param {Object} config Configuration options
24970  */
24971 Roo.form.Checkbox = function(config){
24972     Roo.form.Checkbox.superclass.constructor.call(this, config);
24973     this.addEvents({
24974         /**
24975          * @event check
24976          * Fires when the checkbox is checked or unchecked.
24977              * @param {Roo.form.Checkbox} this This checkbox
24978              * @param {Boolean} checked The new checked value
24979              */
24980         check : true
24981     });
24982 };
24983
24984 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
24985     /**
24986      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
24987      */
24988     focusClass : undefined,
24989     /**
24990      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
24991      */
24992     fieldClass: "x-form-field",
24993     /**
24994      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
24995      */
24996     checked: false,
24997     /**
24998      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
24999      * {tag: "input", type: "checkbox", autocomplete: "off"})
25000      */
25001     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
25002     /**
25003      * @cfg {String} boxLabel The text that appears beside the checkbox
25004      */
25005     boxLabel : "",
25006     /**
25007      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
25008      */  
25009     inputValue : '1',
25010     /**
25011      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
25012      */
25013      valueOff: '0', // value when not checked..
25014
25015     actionMode : 'viewEl', 
25016     //
25017     // private
25018     itemCls : 'x-menu-check-item x-form-item',
25019     groupClass : 'x-menu-group-item',
25020     inputType : 'hidden',
25021     
25022     
25023     inSetChecked: false, // check that we are not calling self...
25024     
25025     inputElement: false, // real input element?
25026     basedOn: false, // ????
25027     
25028     isFormField: true, // not sure where this is needed!!!!
25029
25030     onResize : function(){
25031         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
25032         if(!this.boxLabel){
25033             this.el.alignTo(this.wrap, 'c-c');
25034         }
25035     },
25036
25037     initEvents : function(){
25038         Roo.form.Checkbox.superclass.initEvents.call(this);
25039         this.el.on("click", this.onClick,  this);
25040         this.el.on("change", this.onClick,  this);
25041     },
25042
25043
25044     getResizeEl : function(){
25045         return this.wrap;
25046     },
25047
25048     getPositionEl : function(){
25049         return this.wrap;
25050     },
25051
25052     // private
25053     onRender : function(ct, position){
25054         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
25055         /*
25056         if(this.inputValue !== undefined){
25057             this.el.dom.value = this.inputValue;
25058         }
25059         */
25060         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
25061         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
25062         var viewEl = this.wrap.createChild({ 
25063             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
25064         this.viewEl = viewEl;   
25065         this.wrap.on('click', this.onClick,  this); 
25066         
25067         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
25068         this.el.on('propertychange', this.setFromHidden,  this);  //ie
25069         
25070         
25071         
25072         if(this.boxLabel){
25073             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
25074         //    viewEl.on('click', this.onClick,  this); 
25075         }
25076         //if(this.checked){
25077             this.setChecked(this.checked);
25078         //}else{
25079             //this.checked = this.el.dom;
25080         //}
25081
25082     },
25083
25084     // private
25085     initValue : Roo.emptyFn,
25086
25087     /**
25088      * Returns the checked state of the checkbox.
25089      * @return {Boolean} True if checked, else false
25090      */
25091     getValue : function(){
25092         if(this.el){
25093             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
25094         }
25095         return this.valueOff;
25096         
25097     },
25098
25099         // private
25100     onClick : function(){ 
25101         this.setChecked(!this.checked);
25102
25103         //if(this.el.dom.checked != this.checked){
25104         //    this.setValue(this.el.dom.checked);
25105        // }
25106     },
25107
25108     /**
25109      * Sets the checked state of the checkbox.
25110      * On is always based on a string comparison between inputValue and the param.
25111      * @param {Boolean/String} value - the value to set 
25112      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
25113      */
25114     setValue : function(v,suppressEvent){
25115         
25116         
25117         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
25118         //if(this.el && this.el.dom){
25119         //    this.el.dom.checked = this.checked;
25120         //    this.el.dom.defaultChecked = this.checked;
25121         //}
25122         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
25123         //this.fireEvent("check", this, this.checked);
25124     },
25125     // private..
25126     setChecked : function(state,suppressEvent)
25127     {
25128         if (this.inSetChecked) {
25129             this.checked = state;
25130             return;
25131         }
25132         
25133     
25134         if(this.wrap){
25135             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
25136         }
25137         this.checked = state;
25138         if(suppressEvent !== true){
25139             this.fireEvent('check', this, state);
25140         }
25141         this.inSetChecked = true;
25142         this.el.dom.value = state ? this.inputValue : this.valueOff;
25143         this.inSetChecked = false;
25144         
25145     },
25146     // handle setting of hidden value by some other method!!?!?
25147     setFromHidden: function()
25148     {
25149         if(!this.el){
25150             return;
25151         }
25152         //console.log("SET FROM HIDDEN");
25153         //alert('setFrom hidden');
25154         this.setValue(this.el.dom.value);
25155     },
25156     
25157     onDestroy : function()
25158     {
25159         if(this.viewEl){
25160             Roo.get(this.viewEl).remove();
25161         }
25162          
25163         Roo.form.Checkbox.superclass.onDestroy.call(this);
25164     }
25165
25166 });/*
25167  * Based on:
25168  * Ext JS Library 1.1.1
25169  * Copyright(c) 2006-2007, Ext JS, LLC.
25170  *
25171  * Originally Released Under LGPL - original licence link has changed is not relivant.
25172  *
25173  * Fork - LGPL
25174  * <script type="text/javascript">
25175  */
25176  
25177 /**
25178  * @class Roo.form.Radio
25179  * @extends Roo.form.Checkbox
25180  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
25181  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
25182  * @constructor
25183  * Creates a new Radio
25184  * @param {Object} config Configuration options
25185  */
25186 Roo.form.Radio = function(){
25187     Roo.form.Radio.superclass.constructor.apply(this, arguments);
25188 };
25189 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
25190     inputType: 'radio',
25191
25192     /**
25193      * If this radio is part of a group, it will return the selected value
25194      * @return {String}
25195      */
25196     getGroupValue : function(){
25197         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
25198     },
25199     
25200     
25201     onRender : function(ct, position){
25202         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
25203         
25204         if(this.inputValue !== undefined){
25205             this.el.dom.value = this.inputValue;
25206         }
25207          
25208         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
25209         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
25210         //var viewEl = this.wrap.createChild({ 
25211         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
25212         //this.viewEl = viewEl;   
25213         //this.wrap.on('click', this.onClick,  this); 
25214         
25215         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
25216         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
25217         
25218         
25219         
25220         if(this.boxLabel){
25221             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
25222         //    viewEl.on('click', this.onClick,  this); 
25223         }
25224          if(this.checked){
25225             this.el.dom.checked =   'checked' ;
25226         }
25227          
25228     } 
25229     
25230     
25231 });//<script type="text/javascript">
25232
25233 /*
25234  * Ext JS Library 1.1.1
25235  * Copyright(c) 2006-2007, Ext JS, LLC.
25236  * licensing@extjs.com
25237  * 
25238  * http://www.extjs.com/license
25239  */
25240  
25241  /*
25242   * 
25243   * Known bugs:
25244   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
25245   * - IE ? - no idea how much works there.
25246   * 
25247   * 
25248   * 
25249   */
25250  
25251
25252 /**
25253  * @class Ext.form.HtmlEditor
25254  * @extends Ext.form.Field
25255  * Provides a lightweight HTML Editor component.
25256  *
25257  * This has been tested on Fireforx / Chrome.. IE may not be so great..
25258  * 
25259  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
25260  * supported by this editor.</b><br/><br/>
25261  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
25262  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25263  */
25264 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
25265       /**
25266      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25267      */
25268     toolbars : false,
25269     /**
25270      * @cfg {String} createLinkText The default text for the create link prompt
25271      */
25272     createLinkText : 'Please enter the URL for the link:',
25273     /**
25274      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
25275      */
25276     defaultLinkValue : 'http:/'+'/',
25277    
25278      /**
25279      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25280      *                        Roo.resizable.
25281      */
25282     resizable : false,
25283      /**
25284      * @cfg {Number} height (in pixels)
25285      */   
25286     height: 300,
25287    /**
25288      * @cfg {Number} width (in pixels)
25289      */   
25290     width: 500,
25291     
25292     /**
25293      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25294      * 
25295      */
25296     stylesheets: false,
25297     
25298     // id of frame..
25299     frameId: false,
25300     
25301     // private properties
25302     validationEvent : false,
25303     deferHeight: true,
25304     initialized : false,
25305     activated : false,
25306     sourceEditMode : false,
25307     onFocus : Roo.emptyFn,
25308     iframePad:3,
25309     hideMode:'offsets',
25310     
25311     defaultAutoCreate : { // modified by initCompnoent..
25312         tag: "textarea",
25313         style:"width:500px;height:300px;",
25314         autocomplete: "off"
25315     },
25316
25317     // private
25318     initComponent : function(){
25319         this.addEvents({
25320             /**
25321              * @event initialize
25322              * Fires when the editor is fully initialized (including the iframe)
25323              * @param {HtmlEditor} this
25324              */
25325             initialize: true,
25326             /**
25327              * @event activate
25328              * Fires when the editor is first receives the focus. Any insertion must wait
25329              * until after this event.
25330              * @param {HtmlEditor} this
25331              */
25332             activate: true,
25333              /**
25334              * @event beforesync
25335              * Fires before the textarea is updated with content from the editor iframe. Return false
25336              * to cancel the sync.
25337              * @param {HtmlEditor} this
25338              * @param {String} html
25339              */
25340             beforesync: true,
25341              /**
25342              * @event beforepush
25343              * Fires before the iframe editor is updated with content from the textarea. Return false
25344              * to cancel the push.
25345              * @param {HtmlEditor} this
25346              * @param {String} html
25347              */
25348             beforepush: true,
25349              /**
25350              * @event sync
25351              * Fires when the textarea is updated with content from the editor iframe.
25352              * @param {HtmlEditor} this
25353              * @param {String} html
25354              */
25355             sync: true,
25356              /**
25357              * @event push
25358              * Fires when the iframe editor is updated with content from the textarea.
25359              * @param {HtmlEditor} this
25360              * @param {String} html
25361              */
25362             push: true,
25363              /**
25364              * @event editmodechange
25365              * Fires when the editor switches edit modes
25366              * @param {HtmlEditor} this
25367              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25368              */
25369             editmodechange: true,
25370             /**
25371              * @event editorevent
25372              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25373              * @param {HtmlEditor} this
25374              */
25375             editorevent: true
25376         });
25377         this.defaultAutoCreate =  {
25378             tag: "textarea",
25379             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
25380             autocomplete: "off"
25381         };
25382     },
25383
25384     /**
25385      * Protected method that will not generally be called directly. It
25386      * is called when the editor creates its toolbar. Override this method if you need to
25387      * add custom toolbar buttons.
25388      * @param {HtmlEditor} editor
25389      */
25390     createToolbar : function(editor){
25391         if (!editor.toolbars || !editor.toolbars.length) {
25392             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
25393         }
25394         
25395         for (var i =0 ; i < editor.toolbars.length;i++) {
25396             editor.toolbars[i] = Roo.factory(
25397                     typeof(editor.toolbars[i]) == 'string' ?
25398                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
25399                 Roo.form.HtmlEditor);
25400             editor.toolbars[i].init(editor);
25401         }
25402          
25403         
25404     },
25405
25406     /**
25407      * Protected method that will not generally be called directly. It
25408      * is called when the editor initializes the iframe with HTML contents. Override this method if you
25409      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25410      */
25411     getDocMarkup : function(){
25412         // body styles..
25413         var st = '';
25414         if (this.stylesheets === false) {
25415             
25416             Roo.get(document.head).select('style').each(function(node) {
25417                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25418             });
25419             
25420             Roo.get(document.head).select('link').each(function(node) { 
25421                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25422             });
25423             
25424         } else if (!this.stylesheets.length) {
25425                 // simple..
25426                 st = '<style type="text/css">' +
25427                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25428                    '</style>';
25429         } else {
25430             Roo.each(this.stylesheets, function(s) {
25431                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
25432             });
25433             
25434         }
25435         
25436         st +=  '<style type="text/css">' +
25437             'IMG { cursor: pointer } ' +
25438         '</style>';
25439
25440         
25441         return '<html><head>' + st  +
25442             //<style type="text/css">' +
25443             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25444             //'</style>' +
25445             ' </head><body class="roo-htmleditor-body"></body></html>';
25446     },
25447
25448     // private
25449     onRender : function(ct, position)
25450     {
25451         var _t = this;
25452         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
25453         this.el.dom.style.border = '0 none';
25454         this.el.dom.setAttribute('tabIndex', -1);
25455         this.el.addClass('x-hidden');
25456         if(Roo.isIE){ // fix IE 1px bogus margin
25457             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25458         }
25459         this.wrap = this.el.wrap({
25460             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25461         });
25462         
25463         if (this.resizable) {
25464             this.resizeEl = new Roo.Resizable(this.wrap, {
25465                 pinned : true,
25466                 wrap: true,
25467                 dynamic : true,
25468                 minHeight : this.height,
25469                 height: this.height,
25470                 handles : this.resizable,
25471                 width: this.width,
25472                 listeners : {
25473                     resize : function(r, w, h) {
25474                         _t.onResize(w,h); // -something
25475                     }
25476                 }
25477             });
25478             
25479         }
25480
25481         this.frameId = Roo.id();
25482         
25483         this.createToolbar(this);
25484         
25485       
25486         
25487         var iframe = this.wrap.createChild({
25488             tag: 'iframe',
25489             id: this.frameId,
25490             name: this.frameId,
25491             frameBorder : 'no',
25492             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
25493         }, this.el
25494         );
25495         
25496        // console.log(iframe);
25497         //this.wrap.dom.appendChild(iframe);
25498
25499         this.iframe = iframe.dom;
25500
25501          this.assignDocWin();
25502         
25503         this.doc.designMode = 'on';
25504        
25505         this.doc.open();
25506         this.doc.write(this.getDocMarkup());
25507         this.doc.close();
25508
25509         
25510         var task = { // must defer to wait for browser to be ready
25511             run : function(){
25512                 //console.log("run task?" + this.doc.readyState);
25513                 this.assignDocWin();
25514                 if(this.doc.body || this.doc.readyState == 'complete'){
25515                     try {
25516                         this.doc.designMode="on";
25517                     } catch (e) {
25518                         return;
25519                     }
25520                     Roo.TaskMgr.stop(task);
25521                     this.initEditor.defer(10, this);
25522                 }
25523             },
25524             interval : 10,
25525             duration:10000,
25526             scope: this
25527         };
25528         Roo.TaskMgr.start(task);
25529
25530         if(!this.width){
25531             this.setSize(this.wrap.getSize());
25532         }
25533         if (this.resizeEl) {
25534             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
25535             // should trigger onReize..
25536         }
25537     },
25538
25539     // private
25540     onResize : function(w, h)
25541     {
25542         //Roo.log('resize: ' +w + ',' + h );
25543         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
25544         if(this.el && this.iframe){
25545             if(typeof w == 'number'){
25546                 var aw = w - this.wrap.getFrameWidth('lr');
25547                 this.el.setWidth(this.adjustWidth('textarea', aw));
25548                 this.iframe.style.width = aw + 'px';
25549             }
25550             if(typeof h == 'number'){
25551                 var tbh = 0;
25552                 for (var i =0; i < this.toolbars.length;i++) {
25553                     // fixme - ask toolbars for heights?
25554                     tbh += this.toolbars[i].tb.el.getHeight();
25555                     if (this.toolbars[i].footer) {
25556                         tbh += this.toolbars[i].footer.el.getHeight();
25557                     }
25558                 }
25559                 
25560                 
25561                 
25562                 
25563                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
25564                 ah -= 5; // knock a few pixes off for look..
25565                 this.el.setHeight(this.adjustWidth('textarea', ah));
25566                 this.iframe.style.height = ah + 'px';
25567                 if(this.doc){
25568                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
25569                 }
25570             }
25571         }
25572     },
25573
25574     /**
25575      * Toggles the editor between standard and source edit mode.
25576      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25577      */
25578     toggleSourceEdit : function(sourceEditMode){
25579         
25580         this.sourceEditMode = sourceEditMode === true;
25581         
25582         if(this.sourceEditMode){
25583 //            Roo.log('in');
25584 //            Roo.log(this.syncValue());
25585             this.syncValue();
25586             this.iframe.className = 'x-hidden';
25587             this.el.removeClass('x-hidden');
25588             this.el.dom.removeAttribute('tabIndex');
25589             this.el.focus();
25590         }else{
25591 //            Roo.log('out')
25592 //            Roo.log(this.pushValue()); 
25593             this.pushValue();
25594             this.iframe.className = '';
25595             this.el.addClass('x-hidden');
25596             this.el.dom.setAttribute('tabIndex', -1);
25597             this.deferFocus();
25598         }
25599         this.setSize(this.wrap.getSize());
25600         this.fireEvent('editmodechange', this, this.sourceEditMode);
25601     },
25602
25603     // private used internally
25604     createLink : function(){
25605         var url = prompt(this.createLinkText, this.defaultLinkValue);
25606         if(url && url != 'http:/'+'/'){
25607             this.relayCmd('createlink', url);
25608         }
25609     },
25610
25611     // private (for BoxComponent)
25612     adjustSize : Roo.BoxComponent.prototype.adjustSize,
25613
25614     // private (for BoxComponent)
25615     getResizeEl : function(){
25616         return this.wrap;
25617     },
25618
25619     // private (for BoxComponent)
25620     getPositionEl : function(){
25621         return this.wrap;
25622     },
25623
25624     // private
25625     initEvents : function(){
25626         this.originalValue = this.getValue();
25627     },
25628
25629     /**
25630      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25631      * @method
25632      */
25633     markInvalid : Roo.emptyFn,
25634     /**
25635      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25636      * @method
25637      */
25638     clearInvalid : Roo.emptyFn,
25639
25640     setValue : function(v){
25641         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
25642         this.pushValue();
25643     },
25644
25645     /**
25646      * Protected method that will not generally be called directly. If you need/want
25647      * custom HTML cleanup, this is the method you should override.
25648      * @param {String} html The HTML to be cleaned
25649      * return {String} The cleaned HTML
25650      */
25651     cleanHtml : function(html){
25652         html = String(html);
25653         if(html.length > 5){
25654             if(Roo.isSafari){ // strip safari nonsense
25655                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25656             }
25657         }
25658         if(html == '&nbsp;'){
25659             html = '';
25660         }
25661         return html;
25662     },
25663
25664     /**
25665      * Protected method that will not generally be called directly. Syncs the contents
25666      * of the editor iframe with the textarea.
25667      */
25668     syncValue : function(){
25669         if(this.initialized){
25670             var bd = (this.doc.body || this.doc.documentElement);
25671             //this.cleanUpPaste(); -- this is done else where and causes havoc..
25672             var html = bd.innerHTML;
25673             if(Roo.isSafari){
25674                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25675                 var m = bs.match(/text-align:(.*?);/i);
25676                 if(m && m[1]){
25677                     html = '<div style="'+m[0]+'">' + html + '</div>';
25678                 }
25679             }
25680             html = this.cleanHtml(html);
25681             // fix up the special chars.. normaly like back quotes in word...
25682             // however we do not want to do this with chinese..
25683             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
25684                 var cc = b.charCodeAt();
25685                 if (
25686                     (cc >= 0x4E00 && cc < 0xA000 ) ||
25687                     (cc >= 0x3400 && cc < 0x4E00 ) ||
25688                     (cc >= 0xf900 && cc < 0xfb00 )
25689                 ) {
25690                         return b;
25691                 }
25692                 return "&#"+cc+";" 
25693             });
25694             if(this.fireEvent('beforesync', this, html) !== false){
25695                 this.el.dom.value = html;
25696                 this.fireEvent('sync', this, html);
25697             }
25698         }
25699     },
25700
25701     /**
25702      * Protected method that will not generally be called directly. Pushes the value of the textarea
25703      * into the iframe editor.
25704      */
25705     pushValue : function(){
25706         if(this.initialized){
25707             var v = this.el.dom.value;
25708             
25709             if(v.length < 1){
25710                 v = '&#160;';
25711             }
25712             
25713             if(this.fireEvent('beforepush', this, v) !== false){
25714                 var d = (this.doc.body || this.doc.documentElement);
25715                 d.innerHTML = v;
25716                 this.cleanUpPaste();
25717                 this.el.dom.value = d.innerHTML;
25718                 this.fireEvent('push', this, v);
25719             }
25720         }
25721     },
25722
25723     // private
25724     deferFocus : function(){
25725         this.focus.defer(10, this);
25726     },
25727
25728     // doc'ed in Field
25729     focus : function(){
25730         if(this.win && !this.sourceEditMode){
25731             this.win.focus();
25732         }else{
25733             this.el.focus();
25734         }
25735     },
25736     
25737     assignDocWin: function()
25738     {
25739         var iframe = this.iframe;
25740         
25741          if(Roo.isIE){
25742             this.doc = iframe.contentWindow.document;
25743             this.win = iframe.contentWindow;
25744         } else {
25745             if (!Roo.get(this.frameId)) {
25746                 return;
25747             }
25748             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25749             this.win = Roo.get(this.frameId).dom.contentWindow;
25750         }
25751     },
25752     
25753     // private
25754     initEditor : function(){
25755         //console.log("INIT EDITOR");
25756         this.assignDocWin();
25757         
25758         
25759         
25760         this.doc.designMode="on";
25761         this.doc.open();
25762         this.doc.write(this.getDocMarkup());
25763         this.doc.close();
25764         
25765         var dbody = (this.doc.body || this.doc.documentElement);
25766         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25767         // this copies styles from the containing element into thsi one..
25768         // not sure why we need all of this..
25769         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25770         ss['background-attachment'] = 'fixed'; // w3c
25771         dbody.bgProperties = 'fixed'; // ie
25772         Roo.DomHelper.applyStyles(dbody, ss);
25773         Roo.EventManager.on(this.doc, {
25774             //'mousedown': this.onEditorEvent,
25775             'mouseup': this.onEditorEvent,
25776             'dblclick': this.onEditorEvent,
25777             'click': this.onEditorEvent,
25778             'keyup': this.onEditorEvent,
25779             buffer:100,
25780             scope: this
25781         });
25782         if(Roo.isGecko){
25783             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25784         }
25785         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25786             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25787         }
25788         this.initialized = true;
25789
25790         this.fireEvent('initialize', this);
25791         this.pushValue();
25792     },
25793
25794     // private
25795     onDestroy : function(){
25796         
25797         
25798         
25799         if(this.rendered){
25800             
25801             for (var i =0; i < this.toolbars.length;i++) {
25802                 // fixme - ask toolbars for heights?
25803                 this.toolbars[i].onDestroy();
25804             }
25805             
25806             this.wrap.dom.innerHTML = '';
25807             this.wrap.remove();
25808         }
25809     },
25810
25811     // private
25812     onFirstFocus : function(){
25813         
25814         this.assignDocWin();
25815         
25816         
25817         this.activated = true;
25818         for (var i =0; i < this.toolbars.length;i++) {
25819             this.toolbars[i].onFirstFocus();
25820         }
25821        
25822         if(Roo.isGecko){ // prevent silly gecko errors
25823             this.win.focus();
25824             var s = this.win.getSelection();
25825             if(!s.focusNode || s.focusNode.nodeType != 3){
25826                 var r = s.getRangeAt(0);
25827                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
25828                 r.collapse(true);
25829                 this.deferFocus();
25830             }
25831             try{
25832                 this.execCmd('useCSS', true);
25833                 this.execCmd('styleWithCSS', false);
25834             }catch(e){}
25835         }
25836         this.fireEvent('activate', this);
25837     },
25838
25839     // private
25840     adjustFont: function(btn){
25841         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
25842         //if(Roo.isSafari){ // safari
25843         //    adjust *= 2;
25844        // }
25845         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
25846         if(Roo.isSafari){ // safari
25847             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
25848             v =  (v < 10) ? 10 : v;
25849             v =  (v > 48) ? 48 : v;
25850             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
25851             
25852         }
25853         
25854         
25855         v = Math.max(1, v+adjust);
25856         
25857         this.execCmd('FontSize', v  );
25858     },
25859
25860     onEditorEvent : function(e){
25861         this.fireEvent('editorevent', this, e);
25862       //  this.updateToolbar();
25863         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
25864     },
25865
25866     insertTag : function(tg)
25867     {
25868         // could be a bit smarter... -> wrap the current selected tRoo..
25869         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
25870             
25871             range = this.createRange(this.getSelection());
25872             var wrappingNode = this.doc.createElement(tg.toLowerCase());
25873             wrappingNode.appendChild(range.extractContents());
25874             range.insertNode(wrappingNode);
25875
25876             return;
25877             
25878             
25879             
25880         }
25881         this.execCmd("formatblock",   tg);
25882         
25883     },
25884     
25885     insertText : function(txt)
25886     {
25887         
25888         
25889         var range = this.createRange();
25890         range.deleteContents();
25891                //alert(Sender.getAttribute('label'));
25892                
25893         range.insertNode(this.doc.createTextNode(txt));
25894     } ,
25895     
25896     // private
25897     relayBtnCmd : function(btn){
25898         this.relayCmd(btn.cmd);
25899     },
25900
25901     /**
25902      * Executes a Midas editor command on the editor document and performs necessary focus and
25903      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25904      * @param {String} cmd The Midas command
25905      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25906      */
25907     relayCmd : function(cmd, value){
25908         this.win.focus();
25909         this.execCmd(cmd, value);
25910         this.fireEvent('editorevent', this);
25911         //this.updateToolbar();
25912         this.deferFocus();
25913     },
25914
25915     /**
25916      * Executes a Midas editor command directly on the editor document.
25917      * For visual commands, you should use {@link #relayCmd} instead.
25918      * <b>This should only be called after the editor is initialized.</b>
25919      * @param {String} cmd The Midas command
25920      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25921      */
25922     execCmd : function(cmd, value){
25923         this.doc.execCommand(cmd, false, value === undefined ? null : value);
25924         this.syncValue();
25925     },
25926  
25927  
25928    
25929     /**
25930      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25931      * to insert tRoo.
25932      * @param {String} text | dom node.. 
25933      */
25934     insertAtCursor : function(text)
25935     {
25936         
25937         
25938         
25939         if(!this.activated){
25940             return;
25941         }
25942         /*
25943         if(Roo.isIE){
25944             this.win.focus();
25945             var r = this.doc.selection.createRange();
25946             if(r){
25947                 r.collapse(true);
25948                 r.pasteHTML(text);
25949                 this.syncValue();
25950                 this.deferFocus();
25951             
25952             }
25953             return;
25954         }
25955         */
25956         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25957             this.win.focus();
25958             
25959             
25960             // from jquery ui (MIT licenced)
25961             var range, node;
25962             var win = this.win;
25963             
25964             if (win.getSelection && win.getSelection().getRangeAt) {
25965                 range = win.getSelection().getRangeAt(0);
25966                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25967                 range.insertNode(node);
25968             } else if (win.document.selection && win.document.selection.createRange) {
25969                 // no firefox support
25970                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25971                 win.document.selection.createRange().pasteHTML(txt);
25972             } else {
25973                 // no firefox support
25974                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25975                 this.execCmd('InsertHTML', txt);
25976             } 
25977             
25978             this.syncValue();
25979             
25980             this.deferFocus();
25981         }
25982     },
25983  // private
25984     mozKeyPress : function(e){
25985         if(e.ctrlKey){
25986             var c = e.getCharCode(), cmd;
25987           
25988             if(c > 0){
25989                 c = String.fromCharCode(c).toLowerCase();
25990                 switch(c){
25991                     case 'b':
25992                         cmd = 'bold';
25993                         break;
25994                     case 'i':
25995                         cmd = 'italic';
25996                         break;
25997                     
25998                     case 'u':
25999                         cmd = 'underline';
26000                         break;
26001                     
26002                     case 'v':
26003                         this.cleanUpPaste.defer(100, this);
26004                         return;
26005                         
26006                 }
26007                 if(cmd){
26008                     this.win.focus();
26009                     this.execCmd(cmd);
26010                     this.deferFocus();
26011                     e.preventDefault();
26012                 }
26013                 
26014             }
26015         }
26016     },
26017
26018     // private
26019     fixKeys : function(){ // load time branching for fastest keydown performance
26020         if(Roo.isIE){
26021             return function(e){
26022                 var k = e.getKey(), r;
26023                 if(k == e.TAB){
26024                     e.stopEvent();
26025                     r = this.doc.selection.createRange();
26026                     if(r){
26027                         r.collapse(true);
26028                         r.pasteHTML('&#160;&#160;&#160;&#160;');
26029                         this.deferFocus();
26030                     }
26031                     return;
26032                 }
26033                 
26034                 if(k == e.ENTER){
26035                     r = this.doc.selection.createRange();
26036                     if(r){
26037                         var target = r.parentElement();
26038                         if(!target || target.tagName.toLowerCase() != 'li'){
26039                             e.stopEvent();
26040                             r.pasteHTML('<br />');
26041                             r.collapse(false);
26042                             r.select();
26043                         }
26044                     }
26045                 }
26046                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26047                     this.cleanUpPaste.defer(100, this);
26048                     return;
26049                 }
26050                 
26051                 
26052             };
26053         }else if(Roo.isOpera){
26054             return function(e){
26055                 var k = e.getKey();
26056                 if(k == e.TAB){
26057                     e.stopEvent();
26058                     this.win.focus();
26059                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
26060                     this.deferFocus();
26061                 }
26062                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26063                     this.cleanUpPaste.defer(100, this);
26064                     return;
26065                 }
26066                 
26067             };
26068         }else if(Roo.isSafari){
26069             return function(e){
26070                 var k = e.getKey();
26071                 
26072                 if(k == e.TAB){
26073                     e.stopEvent();
26074                     this.execCmd('InsertText','\t');
26075                     this.deferFocus();
26076                     return;
26077                 }
26078                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26079                     this.cleanUpPaste.defer(100, this);
26080                     return;
26081                 }
26082                 
26083              };
26084         }
26085     }(),
26086     
26087     getAllAncestors: function()
26088     {
26089         var p = this.getSelectedNode();
26090         var a = [];
26091         if (!p) {
26092             a.push(p); // push blank onto stack..
26093             p = this.getParentElement();
26094         }
26095         
26096         
26097         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26098             a.push(p);
26099             p = p.parentNode;
26100         }
26101         a.push(this.doc.body);
26102         return a;
26103     },
26104     lastSel : false,
26105     lastSelNode : false,
26106     
26107     
26108     getSelection : function() 
26109     {
26110         this.assignDocWin();
26111         return Roo.isIE ? this.doc.selection : this.win.getSelection();
26112     },
26113     
26114     getSelectedNode: function() 
26115     {
26116         // this may only work on Gecko!!!
26117         
26118         // should we cache this!!!!
26119         
26120         
26121         
26122          
26123         var range = this.createRange(this.getSelection()).cloneRange();
26124         
26125         if (Roo.isIE) {
26126             var parent = range.parentElement();
26127             while (true) {
26128                 var testRange = range.duplicate();
26129                 testRange.moveToElementText(parent);
26130                 if (testRange.inRange(range)) {
26131                     break;
26132                 }
26133                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26134                     break;
26135                 }
26136                 parent = parent.parentElement;
26137             }
26138             return parent;
26139         }
26140         
26141         // is ancestor a text element.
26142         var ac =  range.commonAncestorContainer;
26143         if (ac.nodeType == 3) {
26144             ac = ac.parentNode;
26145         }
26146         
26147         var ar = ac.childNodes;
26148          
26149         var nodes = [];
26150         var other_nodes = [];
26151         var has_other_nodes = false;
26152         for (var i=0;i<ar.length;i++) {
26153             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
26154                 continue;
26155             }
26156             // fullly contained node.
26157             
26158             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26159                 nodes.push(ar[i]);
26160                 continue;
26161             }
26162             
26163             // probably selected..
26164             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26165                 other_nodes.push(ar[i]);
26166                 continue;
26167             }
26168             // outer..
26169             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
26170                 continue;
26171             }
26172             
26173             
26174             has_other_nodes = true;
26175         }
26176         if (!nodes.length && other_nodes.length) {
26177             nodes= other_nodes;
26178         }
26179         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26180             return false;
26181         }
26182         
26183         return nodes[0];
26184     },
26185     createRange: function(sel)
26186     {
26187         // this has strange effects when using with 
26188         // top toolbar - not sure if it's a great idea.
26189         //this.editor.contentWindow.focus();
26190         if (typeof sel != "undefined") {
26191             try {
26192                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26193             } catch(e) {
26194                 return this.doc.createRange();
26195             }
26196         } else {
26197             return this.doc.createRange();
26198         }
26199     },
26200     getParentElement: function()
26201     {
26202         
26203         this.assignDocWin();
26204         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26205         
26206         var range = this.createRange(sel);
26207          
26208         try {
26209             var p = range.commonAncestorContainer;
26210             while (p.nodeType == 3) { // text node
26211                 p = p.parentNode;
26212             }
26213             return p;
26214         } catch (e) {
26215             return null;
26216         }
26217     
26218     },
26219     /***
26220      *
26221      * Range intersection.. the hard stuff...
26222      *  '-1' = before
26223      *  '0' = hits..
26224      *  '1' = after.
26225      *         [ -- selected range --- ]
26226      *   [fail]                        [fail]
26227      *
26228      *    basically..
26229      *      if end is before start or  hits it. fail.
26230      *      if start is after end or hits it fail.
26231      *
26232      *   if either hits (but other is outside. - then it's not 
26233      *   
26234      *    
26235      **/
26236     
26237     
26238     // @see http://www.thismuchiknow.co.uk/?p=64.
26239     rangeIntersectsNode : function(range, node)
26240     {
26241         var nodeRange = node.ownerDocument.createRange();
26242         try {
26243             nodeRange.selectNode(node);
26244         } catch (e) {
26245             nodeRange.selectNodeContents(node);
26246         }
26247     
26248         var rangeStartRange = range.cloneRange();
26249         rangeStartRange.collapse(true);
26250     
26251         var rangeEndRange = range.cloneRange();
26252         rangeEndRange.collapse(false);
26253     
26254         var nodeStartRange = nodeRange.cloneRange();
26255         nodeStartRange.collapse(true);
26256     
26257         var nodeEndRange = nodeRange.cloneRange();
26258         nodeEndRange.collapse(false);
26259     
26260         return rangeStartRange.compareBoundaryPoints(
26261                  Range.START_TO_START, nodeEndRange) == -1 &&
26262                rangeEndRange.compareBoundaryPoints(
26263                  Range.START_TO_START, nodeStartRange) == 1;
26264         
26265          
26266     },
26267     rangeCompareNode : function(range, node)
26268     {
26269         var nodeRange = node.ownerDocument.createRange();
26270         try {
26271             nodeRange.selectNode(node);
26272         } catch (e) {
26273             nodeRange.selectNodeContents(node);
26274         }
26275         
26276         
26277         range.collapse(true);
26278     
26279         nodeRange.collapse(true);
26280      
26281         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26282         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
26283          
26284         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26285         
26286         var nodeIsBefore   =  ss == 1;
26287         var nodeIsAfter    = ee == -1;
26288         
26289         if (nodeIsBefore && nodeIsAfter)
26290             return 0; // outer
26291         if (!nodeIsBefore && nodeIsAfter)
26292             return 1; //right trailed.
26293         
26294         if (nodeIsBefore && !nodeIsAfter)
26295             return 2;  // left trailed.
26296         // fully contined.
26297         return 3;
26298     },
26299
26300     // private? - in a new class?
26301     cleanUpPaste :  function()
26302     {
26303         // cleans up the whole document..
26304          Roo.log('cleanuppaste');
26305         this.cleanUpChildren(this.doc.body);
26306         var clean = this.cleanWordChars(this.doc.body.innerHTML);
26307         if (clean != this.doc.body.innerHTML) {
26308             this.doc.body.innerHTML = clean;
26309         }
26310         
26311     },
26312     
26313     cleanWordChars : function(input) {// change the chars to hex code
26314         var he = Roo.form.HtmlEditor;
26315         
26316         var output = input;
26317         Roo.each(he.swapCodes, function(sw) { 
26318             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26319             
26320             output = output.replace(swapper, sw[1]);
26321         });
26322         
26323         return output;
26324     },
26325     
26326     
26327     cleanUpChildren : function (n)
26328     {
26329         if (!n.childNodes.length) {
26330             return;
26331         }
26332         for (var i = n.childNodes.length-1; i > -1 ; i--) {
26333            this.cleanUpChild(n.childNodes[i]);
26334         }
26335     },
26336     
26337     
26338         
26339     
26340     cleanUpChild : function (node)
26341     {
26342         var ed = this;
26343         //console.log(node);
26344         if (node.nodeName == "#text") {
26345             // clean up silly Windows -- stuff?
26346             return; 
26347         }
26348         if (node.nodeName == "#comment") {
26349             node.parentNode.removeChild(node);
26350             // clean up silly Windows -- stuff?
26351             return; 
26352         }
26353         
26354         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
26355             // remove node.
26356             node.parentNode.removeChild(node);
26357             return;
26358             
26359         }
26360         
26361         var remove_keep_children= Roo.form.HtmlEditor.remove.indexOf(node.tagName.toLowerCase()) > -1;
26362         
26363         // remove <a name=....> as rendering on yahoo mailer is borked with this.
26364         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
26365         
26366         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
26367         //    remove_keep_children = true;
26368         //}
26369         
26370         if (remove_keep_children) {
26371             this.cleanUpChildren(node);
26372             // inserts everything just before this node...
26373             while (node.childNodes.length) {
26374                 var cn = node.childNodes[0];
26375                 node.removeChild(cn);
26376                 node.parentNode.insertBefore(cn, node);
26377             }
26378             node.parentNode.removeChild(node);
26379             return;
26380         }
26381         
26382         if (!node.attributes || !node.attributes.length) {
26383             this.cleanUpChildren(node);
26384             return;
26385         }
26386         
26387         function cleanAttr(n,v)
26388         {
26389             
26390             if (v.match(/^\./) || v.match(/^\//)) {
26391                 return;
26392             }
26393             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
26394                 return;
26395             }
26396             if (v.match(/^#/)) {
26397                 return;
26398             }
26399 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26400             node.removeAttribute(n);
26401             
26402         }
26403         
26404         function cleanStyle(n,v)
26405         {
26406             if (v.match(/expression/)) { //XSS?? should we even bother..
26407                 node.removeAttribute(n);
26408                 return;
26409             }
26410             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.form.HtmlEditor.cwhite : ed.cwhite;
26411             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.form.HtmlEditor.cblack : ed.cblack;
26412             
26413             
26414             var parts = v.split(/;/);
26415             var clean = [];
26416             
26417             Roo.each(parts, function(p) {
26418                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26419                 if (!p.length) {
26420                     return true;
26421                 }
26422                 var l = p.split(':').shift().replace(/\s+/g,'');
26423                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26424                 
26425                 
26426                 if ( cblack.indexOf(l) > -1) {
26427 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26428                     //node.removeAttribute(n);
26429                     return true;
26430                 }
26431                 //Roo.log()
26432                 // only allow 'c whitelisted system attributes'
26433                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
26434 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26435                     //node.removeAttribute(n);
26436                     return true;
26437                 }
26438                 
26439                 
26440                  
26441                 
26442                 clean.push(p);
26443                 return true;
26444             });
26445             if (clean.length) { 
26446                 node.setAttribute(n, clean.join(';'));
26447             } else {
26448                 node.removeAttribute(n);
26449             }
26450             
26451         }
26452         
26453         
26454         for (var i = node.attributes.length-1; i > -1 ; i--) {
26455             var a = node.attributes[i];
26456             //console.log(a);
26457             
26458             if (a.name.toLowerCase().substr(0,2)=='on')  {
26459                 node.removeAttribute(a.name);
26460                 continue;
26461             }
26462             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
26463                 node.removeAttribute(a.name);
26464                 continue;
26465             }
26466             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
26467                 cleanAttr(a.name,a.value); // fixme..
26468                 continue;
26469             }
26470             if (a.name == 'style') {
26471                 cleanStyle(a.name,a.value);
26472                 continue;
26473             }
26474             /// clean up MS crap..
26475             // tecnically this should be a list of valid class'es..
26476             
26477             
26478             if (a.name == 'class') {
26479                 if (a.value.match(/^Mso/)) {
26480                     node.className = '';
26481                 }
26482                 
26483                 if (a.value.match(/body/)) {
26484                     node.className = '';
26485                 }
26486                 continue;
26487             }
26488             
26489             // style cleanup!?
26490             // class cleanup?
26491             
26492         }
26493         
26494         
26495         this.cleanUpChildren(node);
26496         
26497         
26498     }
26499     
26500     
26501     // hide stuff that is not compatible
26502     /**
26503      * @event blur
26504      * @hide
26505      */
26506     /**
26507      * @event change
26508      * @hide
26509      */
26510     /**
26511      * @event focus
26512      * @hide
26513      */
26514     /**
26515      * @event specialkey
26516      * @hide
26517      */
26518     /**
26519      * @cfg {String} fieldClass @hide
26520      */
26521     /**
26522      * @cfg {String} focusClass @hide
26523      */
26524     /**
26525      * @cfg {String} autoCreate @hide
26526      */
26527     /**
26528      * @cfg {String} inputType @hide
26529      */
26530     /**
26531      * @cfg {String} invalidClass @hide
26532      */
26533     /**
26534      * @cfg {String} invalidText @hide
26535      */
26536     /**
26537      * @cfg {String} msgFx @hide
26538      */
26539     /**
26540      * @cfg {String} validateOnBlur @hide
26541      */
26542 });
26543
26544 Roo.form.HtmlEditor.white = [
26545         'area', 'br', 'img', 'input', 'hr', 'wbr',
26546         
26547        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
26548        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
26549        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
26550        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
26551        'table',   'ul',         'xmp', 
26552        
26553        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
26554       'thead',   'tr', 
26555      
26556       'dir', 'menu', 'ol', 'ul', 'dl',
26557        
26558       'embed',  'object'
26559 ];
26560
26561
26562 Roo.form.HtmlEditor.black = [
26563     //    'embed',  'object', // enable - backend responsiblity to clean thiese
26564         'applet', // 
26565         'base',   'basefont', 'bgsound', 'blink',  'body', 
26566         'frame',  'frameset', 'head',    'html',   'ilayer', 
26567         'iframe', 'layer',  'link',     'meta',    'object',   
26568         'script', 'style' ,'title',  'xml' // clean later..
26569 ];
26570 Roo.form.HtmlEditor.clean = [
26571     'script', 'style', 'title', 'xml'
26572 ];
26573 Roo.form.HtmlEditor.remove = [
26574     'font'
26575 ];
26576 // attributes..
26577
26578 Roo.form.HtmlEditor.ablack = [
26579     'on'
26580 ];
26581     
26582 Roo.form.HtmlEditor.aclean = [ 
26583     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
26584 ];
26585
26586 // protocols..
26587 Roo.form.HtmlEditor.pwhite= [
26588         'http',  'https',  'mailto'
26589 ];
26590
26591 // white listed style attributes.
26592 Roo.form.HtmlEditor.cwhite= [
26593       //  'text-align', /// default is to allow most things..
26594       
26595          
26596 //        'font-size'//??
26597 ];
26598
26599 // black listed style attributes.
26600 Roo.form.HtmlEditor.cblack= [
26601       //  'font-size' -- this can be set by the project 
26602 ];
26603
26604
26605 Roo.form.HtmlEditor.swapCodes   =[ 
26606     [    8211, "--" ], 
26607     [    8212, "--" ], 
26608     [    8216,  "'" ],  
26609     [    8217, "'" ],  
26610     [    8220, '"' ],  
26611     [    8221, '"' ],  
26612     [    8226, "*" ],  
26613     [    8230, "..." ]
26614 ]; 
26615
26616     // <script type="text/javascript">
26617 /*
26618  * Based on
26619  * Ext JS Library 1.1.1
26620  * Copyright(c) 2006-2007, Ext JS, LLC.
26621  *  
26622  
26623  */
26624
26625 /**
26626  * @class Roo.form.HtmlEditorToolbar1
26627  * Basic Toolbar
26628  * 
26629  * Usage:
26630  *
26631  new Roo.form.HtmlEditor({
26632     ....
26633     toolbars : [
26634         new Roo.form.HtmlEditorToolbar1({
26635             disable : { fonts: 1 , format: 1, ..., ... , ...],
26636             btns : [ .... ]
26637         })
26638     }
26639      
26640  * 
26641  * @cfg {Object} disable List of elements to disable..
26642  * @cfg {Array} btns List of additional buttons.
26643  * 
26644  * 
26645  * NEEDS Extra CSS? 
26646  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26647  */
26648  
26649 Roo.form.HtmlEditor.ToolbarStandard = function(config)
26650 {
26651     
26652     Roo.apply(this, config);
26653     
26654     // default disabled, based on 'good practice'..
26655     this.disable = this.disable || {};
26656     Roo.applyIf(this.disable, {
26657         fontSize : true,
26658         colors : true,
26659         specialElements : true
26660     });
26661     
26662     
26663     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26664     // dont call parent... till later.
26665 }
26666
26667 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
26668     
26669     tb: false,
26670     
26671     rendered: false,
26672     
26673     editor : false,
26674     /**
26675      * @cfg {Object} disable  List of toolbar elements to disable
26676          
26677      */
26678     disable : false,
26679       /**
26680      * @cfg {Array} fontFamilies An array of available font families
26681      */
26682     fontFamilies : [
26683         'Arial',
26684         'Courier New',
26685         'Tahoma',
26686         'Times New Roman',
26687         'Verdana'
26688     ],
26689     
26690     specialChars : [
26691            "&#169;",
26692           "&#174;",     
26693           "&#8482;",    
26694           "&#163;" ,    
26695          // "&#8212;",    
26696           "&#8230;",    
26697           "&#247;" ,    
26698         //  "&#225;" ,     ?? a acute?
26699            "&#8364;"    , //Euro
26700        //   "&#8220;"    ,
26701         //  "&#8221;"    ,
26702         //  "&#8226;"    ,
26703           "&#176;"  //   , // degrees
26704
26705          // "&#233;"     , // e ecute
26706          // "&#250;"     , // u ecute?
26707     ],
26708     
26709     specialElements : [
26710         {
26711             text: "Insert Table",
26712             xtype: 'MenuItem',
26713             xns : Roo.Menu,
26714             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
26715                 
26716         },
26717         {    
26718             text: "Insert Image",
26719             xtype: 'MenuItem',
26720             xns : Roo.Menu,
26721             ihtml : '<img src="about:blank"/>'
26722             
26723         }
26724         
26725          
26726     ],
26727     
26728     
26729     inputElements : [ 
26730             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
26731             "input:submit", "input:button", "select", "textarea", "label" ],
26732     formats : [
26733         ["p"] ,  
26734         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
26735         ["pre"],[ "code"], 
26736         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
26737         ['div'],['span']
26738     ],
26739     
26740     cleanStyles : [
26741         "font-size"
26742     ],
26743      /**
26744      * @cfg {String} defaultFont default font to use.
26745      */
26746     defaultFont: 'tahoma',
26747    
26748     fontSelect : false,
26749     
26750     
26751     formatCombo : false,
26752     
26753     init : function(editor)
26754     {
26755         this.editor = editor;
26756         
26757         
26758         var fid = editor.frameId;
26759         var etb = this;
26760         function btn(id, toggle, handler){
26761             var xid = fid + '-'+ id ;
26762             return {
26763                 id : xid,
26764                 cmd : id,
26765                 cls : 'x-btn-icon x-edit-'+id,
26766                 enableToggle:toggle !== false,
26767                 scope: editor, // was editor...
26768                 handler:handler||editor.relayBtnCmd,
26769                 clickEvent:'mousedown',
26770                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
26771                 tabIndex:-1
26772             };
26773         }
26774         
26775         
26776         
26777         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
26778         this.tb = tb;
26779          // stop form submits
26780         tb.el.on('click', function(e){
26781             e.preventDefault(); // what does this do?
26782         });
26783
26784         if(!this.disable.font) { // && !Roo.isSafari){
26785             /* why no safari for fonts 
26786             editor.fontSelect = tb.el.createChild({
26787                 tag:'select',
26788                 tabIndex: -1,
26789                 cls:'x-font-select',
26790                 html: this.createFontOptions()
26791             });
26792             
26793             editor.fontSelect.on('change', function(){
26794                 var font = editor.fontSelect.dom.value;
26795                 editor.relayCmd('fontname', font);
26796                 editor.deferFocus();
26797             }, editor);
26798             
26799             tb.add(
26800                 editor.fontSelect.dom,
26801                 '-'
26802             );
26803             */
26804             
26805         };
26806         if(!this.disable.formats){
26807             this.formatCombo = new Roo.form.ComboBox({
26808                 store: new Roo.data.SimpleStore({
26809                     id : 'tag',
26810                     fields: ['tag'],
26811                     data : this.formats // from states.js
26812                 }),
26813                 blockFocus : true,
26814                 name : '',
26815                 //autoCreate : {tag: "div",  size: "20"},
26816                 displayField:'tag',
26817                 typeAhead: false,
26818                 mode: 'local',
26819                 editable : false,
26820                 triggerAction: 'all',
26821                 emptyText:'Add tag',
26822                 selectOnFocus:true,
26823                 width:135,
26824                 listeners : {
26825                     'select': function(c, r, i) {
26826                         editor.insertTag(r.get('tag'));
26827                         editor.focus();
26828                     }
26829                 }
26830
26831             });
26832             tb.addField(this.formatCombo);
26833             
26834         }
26835         
26836         if(!this.disable.format){
26837             tb.add(
26838                 btn('bold'),
26839                 btn('italic'),
26840                 btn('underline')
26841             );
26842         };
26843         if(!this.disable.fontSize){
26844             tb.add(
26845                 '-',
26846                 
26847                 
26848                 btn('increasefontsize', false, editor.adjustFont),
26849                 btn('decreasefontsize', false, editor.adjustFont)
26850             );
26851         };
26852         
26853         
26854         if(!this.disable.colors){
26855             tb.add(
26856                 '-', {
26857                     id:editor.frameId +'-forecolor',
26858                     cls:'x-btn-icon x-edit-forecolor',
26859                     clickEvent:'mousedown',
26860                     tooltip: this.buttonTips['forecolor'] || undefined,
26861                     tabIndex:-1,
26862                     menu : new Roo.menu.ColorMenu({
26863                         allowReselect: true,
26864                         focus: Roo.emptyFn,
26865                         value:'000000',
26866                         plain:true,
26867                         selectHandler: function(cp, color){
26868                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
26869                             editor.deferFocus();
26870                         },
26871                         scope: editor,
26872                         clickEvent:'mousedown'
26873                     })
26874                 }, {
26875                     id:editor.frameId +'backcolor',
26876                     cls:'x-btn-icon x-edit-backcolor',
26877                     clickEvent:'mousedown',
26878                     tooltip: this.buttonTips['backcolor'] || undefined,
26879                     tabIndex:-1,
26880                     menu : new Roo.menu.ColorMenu({
26881                         focus: Roo.emptyFn,
26882                         value:'FFFFFF',
26883                         plain:true,
26884                         allowReselect: true,
26885                         selectHandler: function(cp, color){
26886                             if(Roo.isGecko){
26887                                 editor.execCmd('useCSS', false);
26888                                 editor.execCmd('hilitecolor', color);
26889                                 editor.execCmd('useCSS', true);
26890                                 editor.deferFocus();
26891                             }else{
26892                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
26893                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
26894                                 editor.deferFocus();
26895                             }
26896                         },
26897                         scope:editor,
26898                         clickEvent:'mousedown'
26899                     })
26900                 }
26901             );
26902         };
26903         // now add all the items...
26904         
26905
26906         if(!this.disable.alignments){
26907             tb.add(
26908                 '-',
26909                 btn('justifyleft'),
26910                 btn('justifycenter'),
26911                 btn('justifyright')
26912             );
26913         };
26914
26915         //if(!Roo.isSafari){
26916             if(!this.disable.links){
26917                 tb.add(
26918                     '-',
26919                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
26920                 );
26921             };
26922
26923             if(!this.disable.lists){
26924                 tb.add(
26925                     '-',
26926                     btn('insertorderedlist'),
26927                     btn('insertunorderedlist')
26928                 );
26929             }
26930             if(!this.disable.sourceEdit){
26931                 tb.add(
26932                     '-',
26933                     btn('sourceedit', true, function(btn){
26934                         this.toggleSourceEdit(btn.pressed);
26935                     })
26936                 );
26937             }
26938         //}
26939         
26940         var smenu = { };
26941         // special menu.. - needs to be tidied up..
26942         if (!this.disable.special) {
26943             smenu = {
26944                 text: "&#169;",
26945                 cls: 'x-edit-none',
26946                 
26947                 menu : {
26948                     items : []
26949                 }
26950             };
26951             for (var i =0; i < this.specialChars.length; i++) {
26952                 smenu.menu.items.push({
26953                     
26954                     html: this.specialChars[i],
26955                     handler: function(a,b) {
26956                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
26957                         //editor.insertAtCursor(a.html);
26958                         
26959                     },
26960                     tabIndex:-1
26961                 });
26962             }
26963             
26964             
26965             tb.add(smenu);
26966             
26967             
26968         }
26969         
26970         var cmenu = { };
26971         if (!this.disable.cleanStyles) {
26972             cmenu = {
26973                 cls: 'x-btn-icon x-btn-clear',
26974                 
26975                 menu : {
26976                     items : []
26977                 }
26978             };
26979             for (var i =0; i < this.cleanStyles.length; i++) {
26980                 cmenu.menu.items.push({
26981                     actiontype : this.cleanStyles[i],
26982                     html: 'Remove ' + this.cleanStyles[i],
26983                     handler: function(a,b) {
26984                         Roo.log(a);
26985                         Roo.log(b);
26986                         var c = Roo.get(editor.doc.body);
26987                         c.select('[style]').each(function(s) {
26988                             s.dom.style.removeProperty(a.actiontype);
26989                         });
26990                         
26991                     },
26992                     tabIndex:-1
26993                 });
26994             }
26995             
26996             tb.add(cmenu);
26997         }
26998          
26999         if (!this.disable.specialElements) {
27000             var semenu = {
27001                 text: "Other;",
27002                 cls: 'x-edit-none',
27003                 menu : {
27004                     items : []
27005                 }
27006             };
27007             for (var i =0; i < this.specialElements.length; i++) {
27008                 semenu.menu.items.push(
27009                     Roo.apply({ 
27010                         handler: function(a,b) {
27011                             editor.insertAtCursor(this.ihtml);
27012                         }
27013                     }, this.specialElements[i])
27014                 );
27015                     
27016             }
27017             
27018             tb.add(semenu);
27019             
27020             
27021         }
27022          
27023         
27024         if (this.btns) {
27025             for(var i =0; i< this.btns.length;i++) {
27026                 var b = Roo.factory(this.btns[i],Roo.form);
27027                 b.cls =  'x-edit-none';
27028                 b.scope = editor;
27029                 tb.add(b);
27030             }
27031         
27032         }
27033         
27034         
27035         
27036         // disable everything...
27037         
27038         this.tb.items.each(function(item){
27039            if(item.id != editor.frameId+ '-sourceedit'){
27040                 item.disable();
27041             }
27042         });
27043         this.rendered = true;
27044         
27045         // the all the btns;
27046         editor.on('editorevent', this.updateToolbar, this);
27047         // other toolbars need to implement this..
27048         //editor.on('editmodechange', this.updateToolbar, this);
27049     },
27050     
27051     
27052     
27053     /**
27054      * Protected method that will not generally be called directly. It triggers
27055      * a toolbar update by reading the markup state of the current selection in the editor.
27056      */
27057     updateToolbar: function(){
27058
27059         if(!this.editor.activated){
27060             this.editor.onFirstFocus();
27061             return;
27062         }
27063
27064         var btns = this.tb.items.map, 
27065             doc = this.editor.doc,
27066             frameId = this.editor.frameId;
27067
27068         if(!this.disable.font && !Roo.isSafari){
27069             /*
27070             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
27071             if(name != this.fontSelect.dom.value){
27072                 this.fontSelect.dom.value = name;
27073             }
27074             */
27075         }
27076         if(!this.disable.format){
27077             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
27078             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
27079             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
27080         }
27081         if(!this.disable.alignments){
27082             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
27083             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
27084             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
27085         }
27086         if(!Roo.isSafari && !this.disable.lists){
27087             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
27088             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
27089         }
27090         
27091         var ans = this.editor.getAllAncestors();
27092         if (this.formatCombo) {
27093             
27094             
27095             var store = this.formatCombo.store;
27096             this.formatCombo.setValue("");
27097             for (var i =0; i < ans.length;i++) {
27098                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27099                     // select it..
27100                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27101                     break;
27102                 }
27103             }
27104         }
27105         
27106         
27107         
27108         // hides menus... - so this cant be on a menu...
27109         Roo.menu.MenuMgr.hideAll();
27110
27111         //this.editorsyncValue();
27112     },
27113    
27114     
27115     createFontOptions : function(){
27116         var buf = [], fs = this.fontFamilies, ff, lc;
27117         
27118         
27119         
27120         for(var i = 0, len = fs.length; i< len; i++){
27121             ff = fs[i];
27122             lc = ff.toLowerCase();
27123             buf.push(
27124                 '<option value="',lc,'" style="font-family:',ff,';"',
27125                     (this.defaultFont == lc ? ' selected="true">' : '>'),
27126                     ff,
27127                 '</option>'
27128             );
27129         }
27130         return buf.join('');
27131     },
27132     
27133     toggleSourceEdit : function(sourceEditMode){
27134         if(sourceEditMode === undefined){
27135             sourceEditMode = !this.sourceEditMode;
27136         }
27137         this.sourceEditMode = sourceEditMode === true;
27138         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
27139         // just toggle the button?
27140         if(btn.pressed !== this.editor.sourceEditMode){
27141             btn.toggle(this.editor.sourceEditMode);
27142             return;
27143         }
27144         
27145         if(this.sourceEditMode){
27146             this.tb.items.each(function(item){
27147                 if(item.cmd != 'sourceedit'){
27148                     item.disable();
27149                 }
27150             });
27151           
27152         }else{
27153             if(this.initialized){
27154                 this.tb.items.each(function(item){
27155                     item.enable();
27156                 });
27157             }
27158             
27159         }
27160         // tell the editor that it's been pressed..
27161         this.editor.toggleSourceEdit(sourceEditMode);
27162        
27163     },
27164      /**
27165      * Object collection of toolbar tooltips for the buttons in the editor. The key
27166      * is the command id associated with that button and the value is a valid QuickTips object.
27167      * For example:
27168 <pre><code>
27169 {
27170     bold : {
27171         title: 'Bold (Ctrl+B)',
27172         text: 'Make the selected text bold.',
27173         cls: 'x-html-editor-tip'
27174     },
27175     italic : {
27176         title: 'Italic (Ctrl+I)',
27177         text: 'Make the selected text italic.',
27178         cls: 'x-html-editor-tip'
27179     },
27180     ...
27181 </code></pre>
27182     * @type Object
27183      */
27184     buttonTips : {
27185         bold : {
27186             title: 'Bold (Ctrl+B)',
27187             text: 'Make the selected text bold.',
27188             cls: 'x-html-editor-tip'
27189         },
27190         italic : {
27191             title: 'Italic (Ctrl+I)',
27192             text: 'Make the selected text italic.',
27193             cls: 'x-html-editor-tip'
27194         },
27195         underline : {
27196             title: 'Underline (Ctrl+U)',
27197             text: 'Underline the selected text.',
27198             cls: 'x-html-editor-tip'
27199         },
27200         increasefontsize : {
27201             title: 'Grow Text',
27202             text: 'Increase the font size.',
27203             cls: 'x-html-editor-tip'
27204         },
27205         decreasefontsize : {
27206             title: 'Shrink Text',
27207             text: 'Decrease the font size.',
27208             cls: 'x-html-editor-tip'
27209         },
27210         backcolor : {
27211             title: 'Text Highlight Color',
27212             text: 'Change the background color of the selected text.',
27213             cls: 'x-html-editor-tip'
27214         },
27215         forecolor : {
27216             title: 'Font Color',
27217             text: 'Change the color of the selected text.',
27218             cls: 'x-html-editor-tip'
27219         },
27220         justifyleft : {
27221             title: 'Align Text Left',
27222             text: 'Align text to the left.',
27223             cls: 'x-html-editor-tip'
27224         },
27225         justifycenter : {
27226             title: 'Center Text',
27227             text: 'Center text in the editor.',
27228             cls: 'x-html-editor-tip'
27229         },
27230         justifyright : {
27231             title: 'Align Text Right',
27232             text: 'Align text to the right.',
27233             cls: 'x-html-editor-tip'
27234         },
27235         insertunorderedlist : {
27236             title: 'Bullet List',
27237             text: 'Start a bulleted list.',
27238             cls: 'x-html-editor-tip'
27239         },
27240         insertorderedlist : {
27241             title: 'Numbered List',
27242             text: 'Start a numbered list.',
27243             cls: 'x-html-editor-tip'
27244         },
27245         createlink : {
27246             title: 'Hyperlink',
27247             text: 'Make the selected text a hyperlink.',
27248             cls: 'x-html-editor-tip'
27249         },
27250         sourceedit : {
27251             title: 'Source Edit',
27252             text: 'Switch to source editing mode.',
27253             cls: 'x-html-editor-tip'
27254         }
27255     },
27256     // private
27257     onDestroy : function(){
27258         if(this.rendered){
27259             
27260             this.tb.items.each(function(item){
27261                 if(item.menu){
27262                     item.menu.removeAll();
27263                     if(item.menu.el){
27264                         item.menu.el.destroy();
27265                     }
27266                 }
27267                 item.destroy();
27268             });
27269              
27270         }
27271     },
27272     onFirstFocus: function() {
27273         this.tb.items.each(function(item){
27274            item.enable();
27275         });
27276     }
27277 });
27278
27279
27280
27281
27282 // <script type="text/javascript">
27283 /*
27284  * Based on
27285  * Ext JS Library 1.1.1
27286  * Copyright(c) 2006-2007, Ext JS, LLC.
27287  *  
27288  
27289  */
27290
27291  
27292 /**
27293  * @class Roo.form.HtmlEditor.ToolbarContext
27294  * Context Toolbar
27295  * 
27296  * Usage:
27297  *
27298  new Roo.form.HtmlEditor({
27299     ....
27300     toolbars : [
27301         { xtype: 'ToolbarStandard', styles : {} }
27302         { xtype: 'ToolbarContext', disable : {} }
27303     ]
27304 })
27305
27306      
27307  * 
27308  * @config : {Object} disable List of elements to disable.. (not done yet.)
27309  * @config : {Object} styles  Map of styles available.
27310  * 
27311  */
27312
27313 Roo.form.HtmlEditor.ToolbarContext = function(config)
27314 {
27315     
27316     Roo.apply(this, config);
27317     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27318     // dont call parent... till later.
27319     this.styles = this.styles || {};
27320 }
27321
27322  
27323
27324 Roo.form.HtmlEditor.ToolbarContext.types = {
27325     'IMG' : {
27326         width : {
27327             title: "Width",
27328             width: 40
27329         },
27330         height:  {
27331             title: "Height",
27332             width: 40
27333         },
27334         align: {
27335             title: "Align",
27336             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
27337             width : 80
27338             
27339         },
27340         border: {
27341             title: "Border",
27342             width: 40
27343         },
27344         alt: {
27345             title: "Alt",
27346             width: 120
27347         },
27348         src : {
27349             title: "Src",
27350             width: 220
27351         }
27352         
27353     },
27354     'A' : {
27355         name : {
27356             title: "Name",
27357             width: 50
27358         },
27359         href:  {
27360             title: "Href",
27361             width: 220
27362         } // border?
27363         
27364     },
27365     'TABLE' : {
27366         rows : {
27367             title: "Rows",
27368             width: 20
27369         },
27370         cols : {
27371             title: "Cols",
27372             width: 20
27373         },
27374         width : {
27375             title: "Width",
27376             width: 40
27377         },
27378         height : {
27379             title: "Height",
27380             width: 40
27381         },
27382         border : {
27383             title: "Border",
27384             width: 20
27385         }
27386     },
27387     'TD' : {
27388         width : {
27389             title: "Width",
27390             width: 40
27391         },
27392         height : {
27393             title: "Height",
27394             width: 40
27395         },   
27396         align: {
27397             title: "Align",
27398             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
27399             width: 80
27400         },
27401         valign: {
27402             title: "Valign",
27403             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
27404             width: 80
27405         },
27406         colspan: {
27407             title: "Colspan",
27408             width: 20
27409             
27410         },
27411          'font-family'  : {
27412             title : "Font",
27413             style : 'fontFamily',
27414             displayField: 'display',
27415             optname : 'font-family',
27416             width: 140
27417         }
27418     },
27419     'INPUT' : {
27420         name : {
27421             title: "name",
27422             width: 120
27423         },
27424         value : {
27425             title: "Value",
27426             width: 120
27427         },
27428         width : {
27429             title: "Width",
27430             width: 40
27431         }
27432     },
27433     'LABEL' : {
27434         'for' : {
27435             title: "For",
27436             width: 120
27437         }
27438     },
27439     'TEXTAREA' : {
27440           name : {
27441             title: "name",
27442             width: 120
27443         },
27444         rows : {
27445             title: "Rows",
27446             width: 20
27447         },
27448         cols : {
27449             title: "Cols",
27450             width: 20
27451         }
27452     },
27453     'SELECT' : {
27454         name : {
27455             title: "name",
27456             width: 120
27457         },
27458         selectoptions : {
27459             title: "Options",
27460             width: 200
27461         }
27462     },
27463     
27464     // should we really allow this??
27465     // should this just be 
27466     'BODY' : {
27467         title : {
27468             title: "Title",
27469             width: 200,
27470             disabled : true
27471         }
27472     },
27473     'SPAN' : {
27474         'font-family'  : {
27475             title : "Font",
27476             style : 'fontFamily',
27477             displayField: 'display',
27478             optname : 'font-family',
27479             width: 140
27480         }
27481     },
27482     'DIV' : {
27483         'font-family'  : {
27484             title : "Font",
27485             style : 'fontFamily',
27486             displayField: 'display',
27487             optname : 'font-family',
27488             width: 140
27489         }
27490     },
27491      'P' : {
27492         'font-family'  : {
27493             title : "Font",
27494             style : 'fontFamily',
27495             displayField: 'display',
27496             optname : 'font-family',
27497             width: 140
27498         }
27499     },
27500     
27501     '*' : {
27502         // empty..
27503     }
27504
27505 };
27506
27507 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
27508 Roo.form.HtmlEditor.ToolbarContext.stores = false;
27509
27510 Roo.form.HtmlEditor.ToolbarContext.options = {
27511         'font-family'  : [ 
27512                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
27513                 [ 'Courier New', 'Courier New'],
27514                 [ 'Tahoma', 'Tahoma'],
27515                 [ 'Times New Roman,serif', 'Times'],
27516                 [ 'Verdana','Verdana' ]
27517         ]
27518 };
27519
27520 // fixme - these need to be configurable..
27521  
27522
27523 Roo.form.HtmlEditor.ToolbarContext.types
27524
27525
27526 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
27527     
27528     tb: false,
27529     
27530     rendered: false,
27531     
27532     editor : false,
27533     /**
27534      * @cfg {Object} disable  List of toolbar elements to disable
27535          
27536      */
27537     disable : false,
27538     /**
27539      * @cfg {Object} styles List of styles 
27540      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
27541      *
27542      * These must be defined in the page, so they get rendered correctly..
27543      * .headline { }
27544      * TD.underline { }
27545      * 
27546      */
27547     styles : false,
27548     
27549     options: false,
27550     
27551     toolbars : false,
27552     
27553     init : function(editor)
27554     {
27555         this.editor = editor;
27556         
27557         
27558         var fid = editor.frameId;
27559         var etb = this;
27560         function btn(id, toggle, handler){
27561             var xid = fid + '-'+ id ;
27562             return {
27563                 id : xid,
27564                 cmd : id,
27565                 cls : 'x-btn-icon x-edit-'+id,
27566                 enableToggle:toggle !== false,
27567                 scope: editor, // was editor...
27568                 handler:handler||editor.relayBtnCmd,
27569                 clickEvent:'mousedown',
27570                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27571                 tabIndex:-1
27572             };
27573         }
27574         // create a new element.
27575         var wdiv = editor.wrap.createChild({
27576                 tag: 'div'
27577             }, editor.wrap.dom.firstChild.nextSibling, true);
27578         
27579         // can we do this more than once??
27580         
27581          // stop form submits
27582       
27583  
27584         // disable everything...
27585         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
27586         this.toolbars = {};
27587            
27588         for (var i in  ty) {
27589           
27590             this.toolbars[i] = this.buildToolbar(ty[i],i);
27591         }
27592         this.tb = this.toolbars.BODY;
27593         this.tb.el.show();
27594         this.buildFooter();
27595         this.footer.show();
27596         editor.on('hide', function( ) { this.footer.hide() }, this);
27597         editor.on('show', function( ) { this.footer.show() }, this);
27598         
27599          
27600         this.rendered = true;
27601         
27602         // the all the btns;
27603         editor.on('editorevent', this.updateToolbar, this);
27604         // other toolbars need to implement this..
27605         //editor.on('editmodechange', this.updateToolbar, this);
27606     },
27607     
27608     
27609     
27610     /**
27611      * Protected method that will not generally be called directly. It triggers
27612      * a toolbar update by reading the markup state of the current selection in the editor.
27613      */
27614     updateToolbar: function(editor,ev,sel){
27615
27616         //Roo.log(ev);
27617         // capture mouse up - this is handy for selecting images..
27618         // perhaps should go somewhere else...
27619         if(!this.editor.activated){
27620              this.editor.onFirstFocus();
27621             return;
27622         }
27623         
27624         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
27625         // selectNode - might want to handle IE?
27626         if (ev &&
27627             (ev.type == 'mouseup' || ev.type == 'click' ) &&
27628             ev.target && ev.target.tagName == 'IMG') {
27629             // they have click on an image...
27630             // let's see if we can change the selection...
27631             sel = ev.target;
27632          
27633               var nodeRange = sel.ownerDocument.createRange();
27634             try {
27635                 nodeRange.selectNode(sel);
27636             } catch (e) {
27637                 nodeRange.selectNodeContents(sel);
27638             }
27639             //nodeRange.collapse(true);
27640             var s = editor.win.getSelection();
27641             s.removeAllRanges();
27642             s.addRange(nodeRange);
27643         }  
27644         
27645       
27646         var updateFooter = sel ? false : true;
27647         
27648         
27649         var ans = this.editor.getAllAncestors();
27650         
27651         // pick
27652         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
27653         
27654         if (!sel) { 
27655             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
27656             sel = sel ? sel : this.editor.doc.body;
27657             sel = sel.tagName.length ? sel : this.editor.doc.body;
27658             
27659         }
27660         // pick a menu that exists..
27661         var tn = sel.tagName.toUpperCase();
27662         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
27663         
27664         tn = sel.tagName.toUpperCase();
27665         
27666         var lastSel = this.tb.selectedNode
27667         
27668         this.tb.selectedNode = sel;
27669         
27670         // if current menu does not match..
27671         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
27672                 
27673             this.tb.el.hide();
27674             ///console.log("show: " + tn);
27675             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
27676             this.tb.el.show();
27677             // update name
27678             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
27679             
27680             
27681             // update attributes
27682             if (this.tb.fields) {
27683                 this.tb.fields.each(function(e) {
27684                     if (e.stylename) {
27685                         e.setValue(sel.style[e.stylename]);
27686                         return;
27687                     } 
27688                    e.setValue(sel.getAttribute(e.attrname));
27689                 });
27690             }
27691             
27692             var hasStyles = false;
27693             for(var i in this.styles) {
27694                 hasStyles = true;
27695                 break;
27696             }
27697             
27698             // update styles
27699             if (hasStyles) { 
27700                 var st = this.tb.fields.item(0);
27701                 
27702                 st.store.removeAll();
27703                
27704                 
27705                 var cn = sel.className.split(/\s+/);
27706                 
27707                 var avs = [];
27708                 if (this.styles['*']) {
27709                     
27710                     Roo.each(this.styles['*'], function(v) {
27711                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
27712                     });
27713                 }
27714                 if (this.styles[tn]) { 
27715                     Roo.each(this.styles[tn], function(v) {
27716                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
27717                     });
27718                 }
27719                 
27720                 st.store.loadData(avs);
27721                 st.collapse();
27722                 st.setValue(cn);
27723             }
27724             // flag our selected Node.
27725             this.tb.selectedNode = sel;
27726            
27727            
27728             Roo.menu.MenuMgr.hideAll();
27729
27730         }
27731         
27732         if (!updateFooter) {
27733             //this.footDisp.dom.innerHTML = ''; 
27734             return;
27735         }
27736         // update the footer
27737         //
27738         var html = '';
27739         
27740         this.footerEls = ans.reverse();
27741         Roo.each(this.footerEls, function(a,i) {
27742             if (!a) { return; }
27743             html += html.length ? ' &gt; '  :  '';
27744             
27745             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
27746             
27747         });
27748        
27749         // 
27750         var sz = this.footDisp.up('td').getSize();
27751         this.footDisp.dom.style.width = (sz.width -10) + 'px';
27752         this.footDisp.dom.style.marginLeft = '5px';
27753         
27754         this.footDisp.dom.style.overflow = 'hidden';
27755         
27756         this.footDisp.dom.innerHTML = html;
27757             
27758         //this.editorsyncValue();
27759     },
27760      
27761     
27762    
27763        
27764     // private
27765     onDestroy : function(){
27766         if(this.rendered){
27767             
27768             this.tb.items.each(function(item){
27769                 if(item.menu){
27770                     item.menu.removeAll();
27771                     if(item.menu.el){
27772                         item.menu.el.destroy();
27773                     }
27774                 }
27775                 item.destroy();
27776             });
27777              
27778         }
27779     },
27780     onFirstFocus: function() {
27781         // need to do this for all the toolbars..
27782         this.tb.items.each(function(item){
27783            item.enable();
27784         });
27785     },
27786     buildToolbar: function(tlist, nm)
27787     {
27788         var editor = this.editor;
27789          // create a new element.
27790         var wdiv = editor.wrap.createChild({
27791                 tag: 'div'
27792             }, editor.wrap.dom.firstChild.nextSibling, true);
27793         
27794        
27795         var tb = new Roo.Toolbar(wdiv);
27796         // add the name..
27797         
27798         tb.add(nm+ ":&nbsp;");
27799         
27800         var styles = [];
27801         for(var i in this.styles) {
27802             styles.push(i);
27803         }
27804         
27805         // styles...
27806         if (styles && styles.length) {
27807             
27808             // this needs a multi-select checkbox...
27809             tb.addField( new Roo.form.ComboBox({
27810                 store: new Roo.data.SimpleStore({
27811                     id : 'val',
27812                     fields: ['val', 'selected'],
27813                     data : [] 
27814                 }),
27815                 name : '-roo-edit-className',
27816                 attrname : 'className',
27817                 displayField: 'val',
27818                 typeAhead: false,
27819                 mode: 'local',
27820                 editable : false,
27821                 triggerAction: 'all',
27822                 emptyText:'Select Style',
27823                 selectOnFocus:true,
27824                 width: 130,
27825                 listeners : {
27826                     'select': function(c, r, i) {
27827                         // initial support only for on class per el..
27828                         tb.selectedNode.className =  r ? r.get('val') : '';
27829                         editor.syncValue();
27830                     }
27831                 }
27832     
27833             }));
27834         }
27835         
27836         var tbc = Roo.form.HtmlEditor.ToolbarContext;
27837         var tbops = tbc.options;
27838         
27839         for (var i in tlist) {
27840             
27841             var item = tlist[i];
27842             tb.add(item.title + ":&nbsp;");
27843             
27844             
27845             //optname == used so you can configure the options available..
27846             var opts = item.opts ? item.opts : false;
27847             if (item.optname) {
27848                 opts = tbops[item.optname];
27849            
27850             }
27851             
27852             if (opts) {
27853                 // opts == pulldown..
27854                 tb.addField( new Roo.form.ComboBox({
27855                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
27856                         id : 'val',
27857                         fields: ['val', 'display'],
27858                         data : opts  
27859                     }),
27860                     name : '-roo-edit-' + i,
27861                     attrname : i,
27862                     stylename : item.style ? item.style : false,
27863                     displayField: item.displayField ? item.displayField : 'val',
27864                     valueField :  'val',
27865                     typeAhead: false,
27866                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
27867                     editable : false,
27868                     triggerAction: 'all',
27869                     emptyText:'Select',
27870                     selectOnFocus:true,
27871                     width: item.width ? item.width  : 130,
27872                     listeners : {
27873                         'select': function(c, r, i) {
27874                             if (c.stylename) {
27875                                 tb.selectedNode.style[c.stylename] =  r.get('val');
27876                                 return;
27877                             }
27878                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
27879                         }
27880                     }
27881
27882                 }));
27883                 continue;
27884                     
27885                  
27886                 
27887                 tb.addField( new Roo.form.TextField({
27888                     name: i,
27889                     width: 100,
27890                     //allowBlank:false,
27891                     value: ''
27892                 }));
27893                 continue;
27894             }
27895             tb.addField( new Roo.form.TextField({
27896                 name: '-roo-edit-' + i,
27897                 attrname : i,
27898                 
27899                 width: item.width,
27900                 //allowBlank:true,
27901                 value: '',
27902                 listeners: {
27903                     'change' : function(f, nv, ov) {
27904                         tb.selectedNode.setAttribute(f.attrname, nv);
27905                     }
27906                 }
27907             }));
27908              
27909         }
27910         tb.addFill();
27911         var _this = this;
27912         tb.addButton( {
27913             text: 'Remove Tag',
27914     
27915             listeners : {
27916                 click : function ()
27917                 {
27918                     // remove
27919                     // undo does not work.
27920                      
27921                     var sn = tb.selectedNode;
27922                     
27923                     var pn = sn.parentNode;
27924                     
27925                     var stn =  sn.childNodes[0];
27926                     var en = sn.childNodes[sn.childNodes.length - 1 ];
27927                     while (sn.childNodes.length) {
27928                         var node = sn.childNodes[0];
27929                         sn.removeChild(node);
27930                         //Roo.log(node);
27931                         pn.insertBefore(node, sn);
27932                         
27933                     }
27934                     pn.removeChild(sn);
27935                     var range = editor.createRange();
27936         
27937                     range.setStart(stn,0);
27938                     range.setEnd(en,0); //????
27939                     //range.selectNode(sel);
27940                     
27941                     
27942                     var selection = editor.getSelection();
27943                     selection.removeAllRanges();
27944                     selection.addRange(range);
27945                     
27946                     
27947                     
27948                     //_this.updateToolbar(null, null, pn);
27949                     _this.updateToolbar(null, null, null);
27950                     _this.footDisp.dom.innerHTML = ''; 
27951                 }
27952             }
27953             
27954                     
27955                 
27956             
27957         });
27958         
27959         
27960         tb.el.on('click', function(e){
27961             e.preventDefault(); // what does this do?
27962         });
27963         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
27964         tb.el.hide();
27965         tb.name = nm;
27966         // dont need to disable them... as they will get hidden
27967         return tb;
27968          
27969         
27970     },
27971     buildFooter : function()
27972     {
27973         
27974         var fel = this.editor.wrap.createChild();
27975         this.footer = new Roo.Toolbar(fel);
27976         // toolbar has scrolly on left / right?
27977         var footDisp= new Roo.Toolbar.Fill();
27978         var _t = this;
27979         this.footer.add(
27980             {
27981                 text : '&lt;',
27982                 xtype: 'Button',
27983                 handler : function() {
27984                     _t.footDisp.scrollTo('left',0,true)
27985                 }
27986             }
27987         );
27988         this.footer.add( footDisp );
27989         this.footer.add( 
27990             {
27991                 text : '&gt;',
27992                 xtype: 'Button',
27993                 handler : function() {
27994                     // no animation..
27995                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
27996                 }
27997             }
27998         );
27999         var fel = Roo.get(footDisp.el);
28000         fel.addClass('x-editor-context');
28001         this.footDispWrap = fel; 
28002         this.footDispWrap.overflow  = 'hidden';
28003         
28004         this.footDisp = fel.createChild();
28005         this.footDispWrap.on('click', this.onContextClick, this)
28006         
28007         
28008     },
28009     onContextClick : function (ev,dom)
28010     {
28011         ev.preventDefault();
28012         var  cn = dom.className;
28013         //Roo.log(cn);
28014         if (!cn.match(/x-ed-loc-/)) {
28015             return;
28016         }
28017         var n = cn.split('-').pop();
28018         var ans = this.footerEls;
28019         var sel = ans[n];
28020         
28021          // pick
28022         var range = this.editor.createRange();
28023         
28024         range.selectNodeContents(sel);
28025         //range.selectNode(sel);
28026         
28027         
28028         var selection = this.editor.getSelection();
28029         selection.removeAllRanges();
28030         selection.addRange(range);
28031         
28032         
28033         
28034         this.updateToolbar(null, null, sel);
28035         
28036         
28037     }
28038     
28039     
28040     
28041     
28042     
28043 });
28044
28045
28046
28047
28048
28049 /*
28050  * Based on:
28051  * Ext JS Library 1.1.1
28052  * Copyright(c) 2006-2007, Ext JS, LLC.
28053  *
28054  * Originally Released Under LGPL - original licence link has changed is not relivant.
28055  *
28056  * Fork - LGPL
28057  * <script type="text/javascript">
28058  */
28059  
28060 /**
28061  * @class Roo.form.BasicForm
28062  * @extends Roo.util.Observable
28063  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
28064  * @constructor
28065  * @param {String/HTMLElement/Roo.Element} el The form element or its id
28066  * @param {Object} config Configuration options
28067  */
28068 Roo.form.BasicForm = function(el, config){
28069     this.allItems = [];
28070     this.childForms = [];
28071     Roo.apply(this, config);
28072     /*
28073      * The Roo.form.Field items in this form.
28074      * @type MixedCollection
28075      */
28076      
28077      
28078     this.items = new Roo.util.MixedCollection(false, function(o){
28079         return o.id || (o.id = Roo.id());
28080     });
28081     this.addEvents({
28082         /**
28083          * @event beforeaction
28084          * Fires before any action is performed. Return false to cancel the action.
28085          * @param {Form} this
28086          * @param {Action} action The action to be performed
28087          */
28088         beforeaction: true,
28089         /**
28090          * @event actionfailed
28091          * Fires when an action fails.
28092          * @param {Form} this
28093          * @param {Action} action The action that failed
28094          */
28095         actionfailed : true,
28096         /**
28097          * @event actioncomplete
28098          * Fires when an action is completed.
28099          * @param {Form} this
28100          * @param {Action} action The action that completed
28101          */
28102         actioncomplete : true
28103     });
28104     if(el){
28105         this.initEl(el);
28106     }
28107     Roo.form.BasicForm.superclass.constructor.call(this);
28108 };
28109
28110 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
28111     /**
28112      * @cfg {String} method
28113      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
28114      */
28115     /**
28116      * @cfg {DataReader} reader
28117      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
28118      * This is optional as there is built-in support for processing JSON.
28119      */
28120     /**
28121      * @cfg {DataReader} errorReader
28122      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
28123      * This is completely optional as there is built-in support for processing JSON.
28124      */
28125     /**
28126      * @cfg {String} url
28127      * The URL to use for form actions if one isn't supplied in the action options.
28128      */
28129     /**
28130      * @cfg {Boolean} fileUpload
28131      * Set to true if this form is a file upload.
28132      */
28133      
28134     /**
28135      * @cfg {Object} baseParams
28136      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
28137      */
28138      /**
28139      
28140     /**
28141      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
28142      */
28143     timeout: 30,
28144
28145     // private
28146     activeAction : null,
28147
28148     /**
28149      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
28150      * or setValues() data instead of when the form was first created.
28151      */
28152     trackResetOnLoad : false,
28153     
28154     
28155     /**
28156      * childForms - used for multi-tab forms
28157      * @type {Array}
28158      */
28159     childForms : false,
28160     
28161     /**
28162      * allItems - full list of fields.
28163      * @type {Array}
28164      */
28165     allItems : false,
28166     
28167     /**
28168      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
28169      * element by passing it or its id or mask the form itself by passing in true.
28170      * @type Mixed
28171      */
28172     waitMsgTarget : false,
28173
28174     // private
28175     initEl : function(el){
28176         this.el = Roo.get(el);
28177         this.id = this.el.id || Roo.id();
28178         this.el.on('submit', this.onSubmit, this);
28179         this.el.addClass('x-form');
28180     },
28181
28182     // private
28183     onSubmit : function(e){
28184         e.stopEvent();
28185     },
28186
28187     /**
28188      * Returns true if client-side validation on the form is successful.
28189      * @return Boolean
28190      */
28191     isValid : function(){
28192         var valid = true;
28193         this.items.each(function(f){
28194            if(!f.validate()){
28195                valid = false;
28196            }
28197         });
28198         return valid;
28199     },
28200
28201     /**
28202      * Returns true if any fields in this form have changed since their original load.
28203      * @return Boolean
28204      */
28205     isDirty : function(){
28206         var dirty = false;
28207         this.items.each(function(f){
28208            if(f.isDirty()){
28209                dirty = true;
28210                return false;
28211            }
28212         });
28213         return dirty;
28214     },
28215
28216     /**
28217      * Performs a predefined action (submit or load) or custom actions you define on this form.
28218      * @param {String} actionName The name of the action type
28219      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
28220      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
28221      * accept other config options):
28222      * <pre>
28223 Property          Type             Description
28224 ----------------  ---------------  ----------------------------------------------------------------------------------
28225 url               String           The url for the action (defaults to the form's url)
28226 method            String           The form method to use (defaults to the form's method, or POST if not defined)
28227 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
28228 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
28229                                    validate the form on the client (defaults to false)
28230      * </pre>
28231      * @return {BasicForm} this
28232      */
28233     doAction : function(action, options){
28234         if(typeof action == 'string'){
28235             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
28236         }
28237         if(this.fireEvent('beforeaction', this, action) !== false){
28238             this.beforeAction(action);
28239             action.run.defer(100, action);
28240         }
28241         return this;
28242     },
28243
28244     /**
28245      * Shortcut to do a submit action.
28246      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
28247      * @return {BasicForm} this
28248      */
28249     submit : function(options){
28250         this.doAction('submit', options);
28251         return this;
28252     },
28253
28254     /**
28255      * Shortcut to do a load action.
28256      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
28257      * @return {BasicForm} this
28258      */
28259     load : function(options){
28260         this.doAction('load', options);
28261         return this;
28262     },
28263
28264     /**
28265      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
28266      * @param {Record} record The record to edit
28267      * @return {BasicForm} this
28268      */
28269     updateRecord : function(record){
28270         record.beginEdit();
28271         var fs = record.fields;
28272         fs.each(function(f){
28273             var field = this.findField(f.name);
28274             if(field){
28275                 record.set(f.name, field.getValue());
28276             }
28277         }, this);
28278         record.endEdit();
28279         return this;
28280     },
28281
28282     /**
28283      * Loads an Roo.data.Record into this form.
28284      * @param {Record} record The record to load
28285      * @return {BasicForm} this
28286      */
28287     loadRecord : function(record){
28288         this.setValues(record.data);
28289         return this;
28290     },
28291
28292     // private
28293     beforeAction : function(action){
28294         var o = action.options;
28295         
28296        
28297         if(this.waitMsgTarget === true){
28298             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
28299         }else if(this.waitMsgTarget){
28300             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
28301             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
28302         }else {
28303             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
28304         }
28305          
28306     },
28307
28308     // private
28309     afterAction : function(action, success){
28310         this.activeAction = null;
28311         var o = action.options;
28312         
28313         if(this.waitMsgTarget === true){
28314             this.el.unmask();
28315         }else if(this.waitMsgTarget){
28316             this.waitMsgTarget.unmask();
28317         }else{
28318             Roo.MessageBox.updateProgress(1);
28319             Roo.MessageBox.hide();
28320         }
28321          
28322         if(success){
28323             if(o.reset){
28324                 this.reset();
28325             }
28326             Roo.callback(o.success, o.scope, [this, action]);
28327             this.fireEvent('actioncomplete', this, action);
28328             
28329         }else{
28330             
28331             // failure condition..
28332             // we have a scenario where updates need confirming.
28333             // eg. if a locking scenario exists..
28334             // we look for { errors : { needs_confirm : true }} in the response.
28335             if (
28336                 (typeof(action.result) != 'undefined')  &&
28337                 (typeof(action.result.errors) != 'undefined')  &&
28338                 (typeof(action.result.errors.needs_confirm) != 'undefined')
28339            ){
28340                 var _t = this;
28341                 Roo.MessageBox.confirm(
28342                     "Change requires confirmation",
28343                     action.result.errorMsg,
28344                     function(r) {
28345                         if (r != 'yes') {
28346                             return;
28347                         }
28348                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
28349                     }
28350                     
28351                 );
28352                 
28353                 
28354                 
28355                 return;
28356             }
28357             
28358             Roo.callback(o.failure, o.scope, [this, action]);
28359             // show an error message if no failed handler is set..
28360             if (!this.hasListener('actionfailed')) {
28361                 Roo.MessageBox.alert("Error",
28362                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
28363                         action.result.errorMsg :
28364                         "Saving Failed, please check your entries or try again"
28365                 );
28366             }
28367             
28368             this.fireEvent('actionfailed', this, action);
28369         }
28370         
28371     },
28372
28373     /**
28374      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
28375      * @param {String} id The value to search for
28376      * @return Field
28377      */
28378     findField : function(id){
28379         var field = this.items.get(id);
28380         if(!field){
28381             this.items.each(function(f){
28382                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
28383                     field = f;
28384                     return false;
28385                 }
28386             });
28387         }
28388         return field || null;
28389     },
28390
28391     /**
28392      * Add a secondary form to this one, 
28393      * Used to provide tabbed forms. One form is primary, with hidden values 
28394      * which mirror the elements from the other forms.
28395      * 
28396      * @param {Roo.form.Form} form to add.
28397      * 
28398      */
28399     addForm : function(form)
28400     {
28401        
28402         if (this.childForms.indexOf(form) > -1) {
28403             // already added..
28404             return;
28405         }
28406         this.childForms.push(form);
28407         var n = '';
28408         Roo.each(form.allItems, function (fe) {
28409             
28410             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
28411             if (this.findField(n)) { // already added..
28412                 return;
28413             }
28414             var add = new Roo.form.Hidden({
28415                 name : n
28416             });
28417             add.render(this.el);
28418             
28419             this.add( add );
28420         }, this);
28421         
28422     },
28423     /**
28424      * Mark fields in this form invalid in bulk.
28425      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
28426      * @return {BasicForm} this
28427      */
28428     markInvalid : function(errors){
28429         if(errors instanceof Array){
28430             for(var i = 0, len = errors.length; i < len; i++){
28431                 var fieldError = errors[i];
28432                 var f = this.findField(fieldError.id);
28433                 if(f){
28434                     f.markInvalid(fieldError.msg);
28435                 }
28436             }
28437         }else{
28438             var field, id;
28439             for(id in errors){
28440                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
28441                     field.markInvalid(errors[id]);
28442                 }
28443             }
28444         }
28445         Roo.each(this.childForms || [], function (f) {
28446             f.markInvalid(errors);
28447         });
28448         
28449         return this;
28450     },
28451
28452     /**
28453      * Set values for fields in this form in bulk.
28454      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
28455      * @return {BasicForm} this
28456      */
28457     setValues : function(values){
28458         if(values instanceof Array){ // array of objects
28459             for(var i = 0, len = values.length; i < len; i++){
28460                 var v = values[i];
28461                 var f = this.findField(v.id);
28462                 if(f){
28463                     f.setValue(v.value);
28464                     if(this.trackResetOnLoad){
28465                         f.originalValue = f.getValue();
28466                     }
28467                 }
28468             }
28469         }else{ // object hash
28470             var field, id;
28471             for(id in values){
28472                 if(typeof values[id] != 'function' && (field = this.findField(id))){
28473                     
28474                     if (field.setFromData && 
28475                         field.valueField && 
28476                         field.displayField &&
28477                         // combos' with local stores can 
28478                         // be queried via setValue()
28479                         // to set their value..
28480                         (field.store && !field.store.isLocal)
28481                         ) {
28482                         // it's a combo
28483                         var sd = { };
28484                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
28485                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
28486                         field.setFromData(sd);
28487                         
28488                     } else {
28489                         field.setValue(values[id]);
28490                     }
28491                     
28492                     
28493                     if(this.trackResetOnLoad){
28494                         field.originalValue = field.getValue();
28495                     }
28496                 }
28497             }
28498         }
28499          
28500         Roo.each(this.childForms || [], function (f) {
28501             f.setValues(values);
28502         });
28503                 
28504         return this;
28505     },
28506
28507     /**
28508      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
28509      * they are returned as an array.
28510      * @param {Boolean} asString
28511      * @return {Object}
28512      */
28513     getValues : function(asString){
28514         if (this.childForms) {
28515             // copy values from the child forms
28516             Roo.each(this.childForms, function (f) {
28517                 this.setValues(f.getValues());
28518             }, this);
28519         }
28520         
28521         
28522         
28523         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
28524         if(asString === true){
28525             return fs;
28526         }
28527         return Roo.urlDecode(fs);
28528     },
28529     
28530     /**
28531      * Returns the fields in this form as an object with key/value pairs. 
28532      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
28533      * @return {Object}
28534      */
28535     getFieldValues : function(with_hidden)
28536     {
28537         if (this.childForms) {
28538             // copy values from the child forms
28539             // should this call getFieldValues - probably not as we do not currently copy
28540             // hidden fields when we generate..
28541             Roo.each(this.childForms, function (f) {
28542                 this.setValues(f.getValues());
28543             }, this);
28544         }
28545         
28546         var ret = {};
28547         this.items.each(function(f){
28548             if (!f.getName()) {
28549                 return;
28550             }
28551             var v = f.getValue();
28552             if (f.inputType =='radio') {
28553                 if (typeof(ret[f.getName()]) == 'undefined') {
28554                     ret[f.getName()] = ''; // empty..
28555                 }
28556                 
28557                 if (!f.el.dom.checked) {
28558                     return;
28559                     
28560                 }
28561                 v = f.el.dom.value;
28562                 
28563             }
28564             
28565             // not sure if this supported any more..
28566             if ((typeof(v) == 'object') && f.getRawValue) {
28567                 v = f.getRawValue() ; // dates..
28568             }
28569             // combo boxes where name != hiddenName...
28570             if (f.name != f.getName()) {
28571                 ret[f.name] = f.getRawValue();
28572             }
28573             ret[f.getName()] = v;
28574         });
28575         
28576         return ret;
28577     },
28578
28579     /**
28580      * Clears all invalid messages in this form.
28581      * @return {BasicForm} this
28582      */
28583     clearInvalid : function(){
28584         this.items.each(function(f){
28585            f.clearInvalid();
28586         });
28587         
28588         Roo.each(this.childForms || [], function (f) {
28589             f.clearInvalid();
28590         });
28591         
28592         
28593         return this;
28594     },
28595
28596     /**
28597      * Resets this form.
28598      * @return {BasicForm} this
28599      */
28600     reset : function(){
28601         this.items.each(function(f){
28602             f.reset();
28603         });
28604         
28605         Roo.each(this.childForms || [], function (f) {
28606             f.reset();
28607         });
28608        
28609         
28610         return this;
28611     },
28612
28613     /**
28614      * Add Roo.form components to this form.
28615      * @param {Field} field1
28616      * @param {Field} field2 (optional)
28617      * @param {Field} etc (optional)
28618      * @return {BasicForm} this
28619      */
28620     add : function(){
28621         this.items.addAll(Array.prototype.slice.call(arguments, 0));
28622         return this;
28623     },
28624
28625
28626     /**
28627      * Removes a field from the items collection (does NOT remove its markup).
28628      * @param {Field} field
28629      * @return {BasicForm} this
28630      */
28631     remove : function(field){
28632         this.items.remove(field);
28633         return this;
28634     },
28635
28636     /**
28637      * Looks at the fields in this form, checks them for an id attribute,
28638      * and calls applyTo on the existing dom element with that id.
28639      * @return {BasicForm} this
28640      */
28641     render : function(){
28642         this.items.each(function(f){
28643             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
28644                 f.applyTo(f.id);
28645             }
28646         });
28647         return this;
28648     },
28649
28650     /**
28651      * Calls {@link Ext#apply} for all fields in this form with the passed object.
28652      * @param {Object} values
28653      * @return {BasicForm} this
28654      */
28655     applyToFields : function(o){
28656         this.items.each(function(f){
28657            Roo.apply(f, o);
28658         });
28659         return this;
28660     },
28661
28662     /**
28663      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
28664      * @param {Object} values
28665      * @return {BasicForm} this
28666      */
28667     applyIfToFields : function(o){
28668         this.items.each(function(f){
28669            Roo.applyIf(f, o);
28670         });
28671         return this;
28672     }
28673 });
28674
28675 // back compat
28676 Roo.BasicForm = Roo.form.BasicForm;/*
28677  * Based on:
28678  * Ext JS Library 1.1.1
28679  * Copyright(c) 2006-2007, Ext JS, LLC.
28680  *
28681  * Originally Released Under LGPL - original licence link has changed is not relivant.
28682  *
28683  * Fork - LGPL
28684  * <script type="text/javascript">
28685  */
28686
28687 /**
28688  * @class Roo.form.Form
28689  * @extends Roo.form.BasicForm
28690  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
28691  * @constructor
28692  * @param {Object} config Configuration options
28693  */
28694 Roo.form.Form = function(config){
28695     var xitems =  [];
28696     if (config.items) {
28697         xitems = config.items;
28698         delete config.items;
28699     }
28700    
28701     
28702     Roo.form.Form.superclass.constructor.call(this, null, config);
28703     this.url = this.url || this.action;
28704     if(!this.root){
28705         this.root = new Roo.form.Layout(Roo.applyIf({
28706             id: Roo.id()
28707         }, config));
28708     }
28709     this.active = this.root;
28710     /**
28711      * Array of all the buttons that have been added to this form via {@link addButton}
28712      * @type Array
28713      */
28714     this.buttons = [];
28715     this.allItems = [];
28716     this.addEvents({
28717         /**
28718          * @event clientvalidation
28719          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
28720          * @param {Form} this
28721          * @param {Boolean} valid true if the form has passed client-side validation
28722          */
28723         clientvalidation: true,
28724         /**
28725          * @event rendered
28726          * Fires when the form is rendered
28727          * @param {Roo.form.Form} form
28728          */
28729         rendered : true
28730     });
28731     
28732     if (this.progressUrl) {
28733             // push a hidden field onto the list of fields..
28734             this.addxtype( {
28735                     xns: Roo.form, 
28736                     xtype : 'Hidden', 
28737                     name : 'UPLOAD_IDENTIFIER' 
28738             });
28739         }
28740         
28741     
28742     Roo.each(xitems, this.addxtype, this);
28743     
28744     
28745     
28746 };
28747
28748 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
28749     /**
28750      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
28751      */
28752     /**
28753      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
28754      */
28755     /**
28756      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
28757      */
28758     buttonAlign:'center',
28759
28760     /**
28761      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
28762      */
28763     minButtonWidth:75,
28764
28765     /**
28766      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
28767      * This property cascades to child containers if not set.
28768      */
28769     labelAlign:'left',
28770
28771     /**
28772      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
28773      * fires a looping event with that state. This is required to bind buttons to the valid
28774      * state using the config value formBind:true on the button.
28775      */
28776     monitorValid : false,
28777
28778     /**
28779      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
28780      */
28781     monitorPoll : 200,
28782     
28783     /**
28784      * @cfg {String} progressUrl - Url to return progress data 
28785      */
28786     
28787     progressUrl : false,
28788   
28789     /**
28790      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
28791      * fields are added and the column is closed. If no fields are passed the column remains open
28792      * until end() is called.
28793      * @param {Object} config The config to pass to the column
28794      * @param {Field} field1 (optional)
28795      * @param {Field} field2 (optional)
28796      * @param {Field} etc (optional)
28797      * @return Column The column container object
28798      */
28799     column : function(c){
28800         var col = new Roo.form.Column(c);
28801         this.start(col);
28802         if(arguments.length > 1){ // duplicate code required because of Opera
28803             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28804             this.end();
28805         }
28806         return col;
28807     },
28808
28809     /**
28810      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
28811      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
28812      * until end() is called.
28813      * @param {Object} config The config to pass to the fieldset
28814      * @param {Field} field1 (optional)
28815      * @param {Field} field2 (optional)
28816      * @param {Field} etc (optional)
28817      * @return FieldSet The fieldset container object
28818      */
28819     fieldset : function(c){
28820         var fs = new Roo.form.FieldSet(c);
28821         this.start(fs);
28822         if(arguments.length > 1){ // duplicate code required because of Opera
28823             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28824             this.end();
28825         }
28826         return fs;
28827     },
28828
28829     /**
28830      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
28831      * fields are added and the container is closed. If no fields are passed the container remains open
28832      * until end() is called.
28833      * @param {Object} config The config to pass to the Layout
28834      * @param {Field} field1 (optional)
28835      * @param {Field} field2 (optional)
28836      * @param {Field} etc (optional)
28837      * @return Layout The container object
28838      */
28839     container : function(c){
28840         var l = new Roo.form.Layout(c);
28841         this.start(l);
28842         if(arguments.length > 1){ // duplicate code required because of Opera
28843             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28844             this.end();
28845         }
28846         return l;
28847     },
28848
28849     /**
28850      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
28851      * @param {Object} container A Roo.form.Layout or subclass of Layout
28852      * @return {Form} this
28853      */
28854     start : function(c){
28855         // cascade label info
28856         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
28857         this.active.stack.push(c);
28858         c.ownerCt = this.active;
28859         this.active = c;
28860         return this;
28861     },
28862
28863     /**
28864      * Closes the current open container
28865      * @return {Form} this
28866      */
28867     end : function(){
28868         if(this.active == this.root){
28869             return this;
28870         }
28871         this.active = this.active.ownerCt;
28872         return this;
28873     },
28874
28875     /**
28876      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
28877      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
28878      * as the label of the field.
28879      * @param {Field} field1
28880      * @param {Field} field2 (optional)
28881      * @param {Field} etc. (optional)
28882      * @return {Form} this
28883      */
28884     add : function(){
28885         this.active.stack.push.apply(this.active.stack, arguments);
28886         this.allItems.push.apply(this.allItems,arguments);
28887         var r = [];
28888         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
28889             if(a[i].isFormField){
28890                 r.push(a[i]);
28891             }
28892         }
28893         if(r.length > 0){
28894             Roo.form.Form.superclass.add.apply(this, r);
28895         }
28896         return this;
28897     },
28898     
28899
28900     
28901     
28902     
28903      /**
28904      * Find any element that has been added to a form, using it's ID or name
28905      * This can include framesets, columns etc. along with regular fields..
28906      * @param {String} id - id or name to find.
28907      
28908      * @return {Element} e - or false if nothing found.
28909      */
28910     findbyId : function(id)
28911     {
28912         var ret = false;
28913         if (!id) {
28914             return ret;
28915         }
28916         Roo.each(this.allItems, function(f){
28917             if (f.id == id || f.name == id ){
28918                 ret = f;
28919                 return false;
28920             }
28921         });
28922         return ret;
28923     },
28924
28925     
28926     
28927     /**
28928      * Render this form into the passed container. This should only be called once!
28929      * @param {String/HTMLElement/Element} container The element this component should be rendered into
28930      * @return {Form} this
28931      */
28932     render : function(ct)
28933     {
28934         
28935         
28936         
28937         ct = Roo.get(ct);
28938         var o = this.autoCreate || {
28939             tag: 'form',
28940             method : this.method || 'POST',
28941             id : this.id || Roo.id()
28942         };
28943         this.initEl(ct.createChild(o));
28944
28945         this.root.render(this.el);
28946         
28947        
28948              
28949         this.items.each(function(f){
28950             f.render('x-form-el-'+f.id);
28951         });
28952
28953         if(this.buttons.length > 0){
28954             // tables are required to maintain order and for correct IE layout
28955             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
28956                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
28957                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
28958             }}, null, true);
28959             var tr = tb.getElementsByTagName('tr')[0];
28960             for(var i = 0, len = this.buttons.length; i < len; i++) {
28961                 var b = this.buttons[i];
28962                 var td = document.createElement('td');
28963                 td.className = 'x-form-btn-td';
28964                 b.render(tr.appendChild(td));
28965             }
28966         }
28967         if(this.monitorValid){ // initialize after render
28968             this.startMonitoring();
28969         }
28970         this.fireEvent('rendered', this);
28971         return this;
28972     },
28973
28974     /**
28975      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
28976      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
28977      * object or a valid Roo.DomHelper element config
28978      * @param {Function} handler The function called when the button is clicked
28979      * @param {Object} scope (optional) The scope of the handler function
28980      * @return {Roo.Button}
28981      */
28982     addButton : function(config, handler, scope){
28983         var bc = {
28984             handler: handler,
28985             scope: scope,
28986             minWidth: this.minButtonWidth,
28987             hideParent:true
28988         };
28989         if(typeof config == "string"){
28990             bc.text = config;
28991         }else{
28992             Roo.apply(bc, config);
28993         }
28994         var btn = new Roo.Button(null, bc);
28995         this.buttons.push(btn);
28996         return btn;
28997     },
28998
28999      /**
29000      * Adds a series of form elements (using the xtype property as the factory method.
29001      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
29002      * @param {Object} config 
29003      */
29004     
29005     addxtype : function()
29006     {
29007         var ar = Array.prototype.slice.call(arguments, 0);
29008         var ret = false;
29009         for(var i = 0; i < ar.length; i++) {
29010             if (!ar[i]) {
29011                 continue; // skip -- if this happends something invalid got sent, we 
29012                 // should ignore it, as basically that interface element will not show up
29013                 // and that should be pretty obvious!!
29014             }
29015             
29016             if (Roo.form[ar[i].xtype]) {
29017                 ar[i].form = this;
29018                 var fe = Roo.factory(ar[i], Roo.form);
29019                 if (!ret) {
29020                     ret = fe;
29021                 }
29022                 fe.form = this;
29023                 if (fe.store) {
29024                     fe.store.form = this;
29025                 }
29026                 if (fe.isLayout) {  
29027                          
29028                     this.start(fe);
29029                     this.allItems.push(fe);
29030                     if (fe.items && fe.addxtype) {
29031                         fe.addxtype.apply(fe, fe.items);
29032                         delete fe.items;
29033                     }
29034                      this.end();
29035                     continue;
29036                 }
29037                 
29038                 
29039                  
29040                 this.add(fe);
29041               //  console.log('adding ' + ar[i].xtype);
29042             }
29043             if (ar[i].xtype == 'Button') {  
29044                 //console.log('adding button');
29045                 //console.log(ar[i]);
29046                 this.addButton(ar[i]);
29047                 this.allItems.push(fe);
29048                 continue;
29049             }
29050             
29051             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
29052                 alert('end is not supported on xtype any more, use items');
29053             //    this.end();
29054             //    //console.log('adding end');
29055             }
29056             
29057         }
29058         return ret;
29059     },
29060     
29061     /**
29062      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
29063      * option "monitorValid"
29064      */
29065     startMonitoring : function(){
29066         if(!this.bound){
29067             this.bound = true;
29068             Roo.TaskMgr.start({
29069                 run : this.bindHandler,
29070                 interval : this.monitorPoll || 200,
29071                 scope: this
29072             });
29073         }
29074     },
29075
29076     /**
29077      * Stops monitoring of the valid state of this form
29078      */
29079     stopMonitoring : function(){
29080         this.bound = false;
29081     },
29082
29083     // private
29084     bindHandler : function(){
29085         if(!this.bound){
29086             return false; // stops binding
29087         }
29088         var valid = true;
29089         this.items.each(function(f){
29090             if(!f.isValid(true)){
29091                 valid = false;
29092                 return false;
29093             }
29094         });
29095         for(var i = 0, len = this.buttons.length; i < len; i++){
29096             var btn = this.buttons[i];
29097             if(btn.formBind === true && btn.disabled === valid){
29098                 btn.setDisabled(!valid);
29099             }
29100         }
29101         this.fireEvent('clientvalidation', this, valid);
29102     }
29103     
29104     
29105     
29106     
29107     
29108     
29109     
29110     
29111 });
29112
29113
29114 // back compat
29115 Roo.Form = Roo.form.Form;
29116 /*
29117  * Based on:
29118  * Ext JS Library 1.1.1
29119  * Copyright(c) 2006-2007, Ext JS, LLC.
29120  *
29121  * Originally Released Under LGPL - original licence link has changed is not relivant.
29122  *
29123  * Fork - LGPL
29124  * <script type="text/javascript">
29125  */
29126  
29127  /**
29128  * @class Roo.form.Action
29129  * Internal Class used to handle form actions
29130  * @constructor
29131  * @param {Roo.form.BasicForm} el The form element or its id
29132  * @param {Object} config Configuration options
29133  */
29134  
29135  
29136 // define the action interface
29137 Roo.form.Action = function(form, options){
29138     this.form = form;
29139     this.options = options || {};
29140 };
29141 /**
29142  * Client Validation Failed
29143  * @const 
29144  */
29145 Roo.form.Action.CLIENT_INVALID = 'client';
29146 /**
29147  * Server Validation Failed
29148  * @const 
29149  */
29150  Roo.form.Action.SERVER_INVALID = 'server';
29151  /**
29152  * Connect to Server Failed
29153  * @const 
29154  */
29155 Roo.form.Action.CONNECT_FAILURE = 'connect';
29156 /**
29157  * Reading Data from Server Failed
29158  * @const 
29159  */
29160 Roo.form.Action.LOAD_FAILURE = 'load';
29161
29162 Roo.form.Action.prototype = {
29163     type : 'default',
29164     failureType : undefined,
29165     response : undefined,
29166     result : undefined,
29167
29168     // interface method
29169     run : function(options){
29170
29171     },
29172
29173     // interface method
29174     success : function(response){
29175
29176     },
29177
29178     // interface method
29179     handleResponse : function(response){
29180
29181     },
29182
29183     // default connection failure
29184     failure : function(response){
29185         
29186         this.response = response;
29187         this.failureType = Roo.form.Action.CONNECT_FAILURE;
29188         this.form.afterAction(this, false);
29189     },
29190
29191     processResponse : function(response){
29192         this.response = response;
29193         if(!response.responseText){
29194             return true;
29195         }
29196         this.result = this.handleResponse(response);
29197         return this.result;
29198     },
29199
29200     // utility functions used internally
29201     getUrl : function(appendParams){
29202         var url = this.options.url || this.form.url || this.form.el.dom.action;
29203         if(appendParams){
29204             var p = this.getParams();
29205             if(p){
29206                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
29207             }
29208         }
29209         return url;
29210     },
29211
29212     getMethod : function(){
29213         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
29214     },
29215
29216     getParams : function(){
29217         var bp = this.form.baseParams;
29218         var p = this.options.params;
29219         if(p){
29220             if(typeof p == "object"){
29221                 p = Roo.urlEncode(Roo.applyIf(p, bp));
29222             }else if(typeof p == 'string' && bp){
29223                 p += '&' + Roo.urlEncode(bp);
29224             }
29225         }else if(bp){
29226             p = Roo.urlEncode(bp);
29227         }
29228         return p;
29229     },
29230
29231     createCallback : function(){
29232         return {
29233             success: this.success,
29234             failure: this.failure,
29235             scope: this,
29236             timeout: (this.form.timeout*1000),
29237             upload: this.form.fileUpload ? this.success : undefined
29238         };
29239     }
29240 };
29241
29242 Roo.form.Action.Submit = function(form, options){
29243     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
29244 };
29245
29246 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
29247     type : 'submit',
29248
29249     haveProgress : false,
29250     uploadComplete : false,
29251     
29252     // uploadProgress indicator.
29253     uploadProgress : function()
29254     {
29255         if (!this.form.progressUrl) {
29256             return;
29257         }
29258         
29259         if (!this.haveProgress) {
29260             Roo.MessageBox.progress("Uploading", "Uploading");
29261         }
29262         if (this.uploadComplete) {
29263            Roo.MessageBox.hide();
29264            return;
29265         }
29266         
29267         this.haveProgress = true;
29268    
29269         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
29270         
29271         var c = new Roo.data.Connection();
29272         c.request({
29273             url : this.form.progressUrl,
29274             params: {
29275                 id : uid
29276             },
29277             method: 'GET',
29278             success : function(req){
29279                //console.log(data);
29280                 var rdata = false;
29281                 var edata;
29282                 try  {
29283                    rdata = Roo.decode(req.responseText)
29284                 } catch (e) {
29285                     Roo.log("Invalid data from server..");
29286                     Roo.log(edata);
29287                     return;
29288                 }
29289                 if (!rdata || !rdata.success) {
29290                     Roo.log(rdata);
29291                     Roo.MessageBox.alert(Roo.encode(rdata));
29292                     return;
29293                 }
29294                 var data = rdata.data;
29295                 
29296                 if (this.uploadComplete) {
29297                    Roo.MessageBox.hide();
29298                    return;
29299                 }
29300                    
29301                 if (data){
29302                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
29303                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
29304                     );
29305                 }
29306                 this.uploadProgress.defer(2000,this);
29307             },
29308        
29309             failure: function(data) {
29310                 Roo.log('progress url failed ');
29311                 Roo.log(data);
29312             },
29313             scope : this
29314         });
29315            
29316     },
29317     
29318     
29319     run : function()
29320     {
29321         // run get Values on the form, so it syncs any secondary forms.
29322         this.form.getValues();
29323         
29324         var o = this.options;
29325         var method = this.getMethod();
29326         var isPost = method == 'POST';
29327         if(o.clientValidation === false || this.form.isValid()){
29328             
29329             if (this.form.progressUrl) {
29330                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
29331                     (new Date() * 1) + '' + Math.random());
29332                     
29333             } 
29334             
29335             
29336             Roo.Ajax.request(Roo.apply(this.createCallback(), {
29337                 form:this.form.el.dom,
29338                 url:this.getUrl(!isPost),
29339                 method: method,
29340                 params:isPost ? this.getParams() : null,
29341                 isUpload: this.form.fileUpload
29342             }));
29343             
29344             this.uploadProgress();
29345
29346         }else if (o.clientValidation !== false){ // client validation failed
29347             this.failureType = Roo.form.Action.CLIENT_INVALID;
29348             this.form.afterAction(this, false);
29349         }
29350     },
29351
29352     success : function(response)
29353     {
29354         this.uploadComplete= true;
29355         if (this.haveProgress) {
29356             Roo.MessageBox.hide();
29357         }
29358         
29359         
29360         var result = this.processResponse(response);
29361         if(result === true || result.success){
29362             this.form.afterAction(this, true);
29363             return;
29364         }
29365         if(result.errors){
29366             this.form.markInvalid(result.errors);
29367             this.failureType = Roo.form.Action.SERVER_INVALID;
29368         }
29369         this.form.afterAction(this, false);
29370     },
29371     failure : function(response)
29372     {
29373         this.uploadComplete= true;
29374         if (this.haveProgress) {
29375             Roo.MessageBox.hide();
29376         }
29377         
29378         this.response = response;
29379         this.failureType = Roo.form.Action.CONNECT_FAILURE;
29380         this.form.afterAction(this, false);
29381     },
29382     
29383     handleResponse : function(response){
29384         if(this.form.errorReader){
29385             var rs = this.form.errorReader.read(response);
29386             var errors = [];
29387             if(rs.records){
29388                 for(var i = 0, len = rs.records.length; i < len; i++) {
29389                     var r = rs.records[i];
29390                     errors[i] = r.data;
29391                 }
29392             }
29393             if(errors.length < 1){
29394                 errors = null;
29395             }
29396             return {
29397                 success : rs.success,
29398                 errors : errors
29399             };
29400         }
29401         var ret = false;
29402         try {
29403             ret = Roo.decode(response.responseText);
29404         } catch (e) {
29405             ret = {
29406                 success: false,
29407                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
29408                 errors : []
29409             };
29410         }
29411         return ret;
29412         
29413     }
29414 });
29415
29416
29417 Roo.form.Action.Load = function(form, options){
29418     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
29419     this.reader = this.form.reader;
29420 };
29421
29422 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
29423     type : 'load',
29424
29425     run : function(){
29426         
29427         Roo.Ajax.request(Roo.apply(
29428                 this.createCallback(), {
29429                     method:this.getMethod(),
29430                     url:this.getUrl(false),
29431                     params:this.getParams()
29432         }));
29433     },
29434
29435     success : function(response){
29436         
29437         var result = this.processResponse(response);
29438         if(result === true || !result.success || !result.data){
29439             this.failureType = Roo.form.Action.LOAD_FAILURE;
29440             this.form.afterAction(this, false);
29441             return;
29442         }
29443         this.form.clearInvalid();
29444         this.form.setValues(result.data);
29445         this.form.afterAction(this, true);
29446     },
29447
29448     handleResponse : function(response){
29449         if(this.form.reader){
29450             var rs = this.form.reader.read(response);
29451             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
29452             return {
29453                 success : rs.success,
29454                 data : data
29455             };
29456         }
29457         return Roo.decode(response.responseText);
29458     }
29459 });
29460
29461 Roo.form.Action.ACTION_TYPES = {
29462     'load' : Roo.form.Action.Load,
29463     'submit' : Roo.form.Action.Submit
29464 };/*
29465  * Based on:
29466  * Ext JS Library 1.1.1
29467  * Copyright(c) 2006-2007, Ext JS, LLC.
29468  *
29469  * Originally Released Under LGPL - original licence link has changed is not relivant.
29470  *
29471  * Fork - LGPL
29472  * <script type="text/javascript">
29473  */
29474  
29475 /**
29476  * @class Roo.form.Layout
29477  * @extends Roo.Component
29478  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
29479  * @constructor
29480  * @param {Object} config Configuration options
29481  */
29482 Roo.form.Layout = function(config){
29483     var xitems = [];
29484     if (config.items) {
29485         xitems = config.items;
29486         delete config.items;
29487     }
29488     Roo.form.Layout.superclass.constructor.call(this, config);
29489     this.stack = [];
29490     Roo.each(xitems, this.addxtype, this);
29491      
29492 };
29493
29494 Roo.extend(Roo.form.Layout, Roo.Component, {
29495     /**
29496      * @cfg {String/Object} autoCreate
29497      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
29498      */
29499     /**
29500      * @cfg {String/Object/Function} style
29501      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
29502      * a function which returns such a specification.
29503      */
29504     /**
29505      * @cfg {String} labelAlign
29506      * Valid values are "left," "top" and "right" (defaults to "left")
29507      */
29508     /**
29509      * @cfg {Number} labelWidth
29510      * Fixed width in pixels of all field labels (defaults to undefined)
29511      */
29512     /**
29513      * @cfg {Boolean} clear
29514      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
29515      */
29516     clear : true,
29517     /**
29518      * @cfg {String} labelSeparator
29519      * The separator to use after field labels (defaults to ':')
29520      */
29521     labelSeparator : ':',
29522     /**
29523      * @cfg {Boolean} hideLabels
29524      * True to suppress the display of field labels in this layout (defaults to false)
29525      */
29526     hideLabels : false,
29527
29528     // private
29529     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
29530     
29531     isLayout : true,
29532     
29533     // private
29534     onRender : function(ct, position){
29535         if(this.el){ // from markup
29536             this.el = Roo.get(this.el);
29537         }else {  // generate
29538             var cfg = this.getAutoCreate();
29539             this.el = ct.createChild(cfg, position);
29540         }
29541         if(this.style){
29542             this.el.applyStyles(this.style);
29543         }
29544         if(this.labelAlign){
29545             this.el.addClass('x-form-label-'+this.labelAlign);
29546         }
29547         if(this.hideLabels){
29548             this.labelStyle = "display:none";
29549             this.elementStyle = "padding-left:0;";
29550         }else{
29551             if(typeof this.labelWidth == 'number'){
29552                 this.labelStyle = "width:"+this.labelWidth+"px;";
29553                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
29554             }
29555             if(this.labelAlign == 'top'){
29556                 this.labelStyle = "width:auto;";
29557                 this.elementStyle = "padding-left:0;";
29558             }
29559         }
29560         var stack = this.stack;
29561         var slen = stack.length;
29562         if(slen > 0){
29563             if(!this.fieldTpl){
29564                 var t = new Roo.Template(
29565                     '<div class="x-form-item {5}">',
29566                         '<label for="{0}" style="{2}">{1}{4}</label>',
29567                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
29568                         '</div>',
29569                     '</div><div class="x-form-clear-left"></div>'
29570                 );
29571                 t.disableFormats = true;
29572                 t.compile();
29573                 Roo.form.Layout.prototype.fieldTpl = t;
29574             }
29575             for(var i = 0; i < slen; i++) {
29576                 if(stack[i].isFormField){
29577                     this.renderField(stack[i]);
29578                 }else{
29579                     this.renderComponent(stack[i]);
29580                 }
29581             }
29582         }
29583         if(this.clear){
29584             this.el.createChild({cls:'x-form-clear'});
29585         }
29586     },
29587
29588     // private
29589     renderField : function(f){
29590         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
29591                f.id, //0
29592                f.fieldLabel, //1
29593                f.labelStyle||this.labelStyle||'', //2
29594                this.elementStyle||'', //3
29595                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
29596                f.itemCls||this.itemCls||''  //5
29597        ], true).getPrevSibling());
29598     },
29599
29600     // private
29601     renderComponent : function(c){
29602         c.render(c.isLayout ? this.el : this.el.createChild());    
29603     },
29604     /**
29605      * Adds a object form elements (using the xtype property as the factory method.)
29606      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
29607      * @param {Object} config 
29608      */
29609     addxtype : function(o)
29610     {
29611         // create the lement.
29612         o.form = this.form;
29613         var fe = Roo.factory(o, Roo.form);
29614         this.form.allItems.push(fe);
29615         this.stack.push(fe);
29616         
29617         if (fe.isFormField) {
29618             this.form.items.add(fe);
29619         }
29620          
29621         return fe;
29622     }
29623 });
29624
29625 /**
29626  * @class Roo.form.Column
29627  * @extends Roo.form.Layout
29628  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
29629  * @constructor
29630  * @param {Object} config Configuration options
29631  */
29632 Roo.form.Column = function(config){
29633     Roo.form.Column.superclass.constructor.call(this, config);
29634 };
29635
29636 Roo.extend(Roo.form.Column, Roo.form.Layout, {
29637     /**
29638      * @cfg {Number/String} width
29639      * The fixed width of the column in pixels or CSS value (defaults to "auto")
29640      */
29641     /**
29642      * @cfg {String/Object} autoCreate
29643      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
29644      */
29645
29646     // private
29647     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
29648
29649     // private
29650     onRender : function(ct, position){
29651         Roo.form.Column.superclass.onRender.call(this, ct, position);
29652         if(this.width){
29653             this.el.setWidth(this.width);
29654         }
29655     }
29656 });
29657
29658
29659 /**
29660  * @class Roo.form.Row
29661  * @extends Roo.form.Layout
29662  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
29663  * @constructor
29664  * @param {Object} config Configuration options
29665  */
29666
29667  
29668 Roo.form.Row = function(config){
29669     Roo.form.Row.superclass.constructor.call(this, config);
29670 };
29671  
29672 Roo.extend(Roo.form.Row, Roo.form.Layout, {
29673       /**
29674      * @cfg {Number/String} width
29675      * The fixed width of the column in pixels or CSS value (defaults to "auto")
29676      */
29677     /**
29678      * @cfg {Number/String} height
29679      * The fixed height of the column in pixels or CSS value (defaults to "auto")
29680      */
29681     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
29682     
29683     padWidth : 20,
29684     // private
29685     onRender : function(ct, position){
29686         //console.log('row render');
29687         if(!this.rowTpl){
29688             var t = new Roo.Template(
29689                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
29690                     '<label for="{0}" style="{2}">{1}{4}</label>',
29691                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
29692                     '</div>',
29693                 '</div>'
29694             );
29695             t.disableFormats = true;
29696             t.compile();
29697             Roo.form.Layout.prototype.rowTpl = t;
29698         }
29699         this.fieldTpl = this.rowTpl;
29700         
29701         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
29702         var labelWidth = 100;
29703         
29704         if ((this.labelAlign != 'top')) {
29705             if (typeof this.labelWidth == 'number') {
29706                 labelWidth = this.labelWidth
29707             }
29708             this.padWidth =  20 + labelWidth;
29709             
29710         }
29711         
29712         Roo.form.Column.superclass.onRender.call(this, ct, position);
29713         if(this.width){
29714             this.el.setWidth(this.width);
29715         }
29716         if(this.height){
29717             this.el.setHeight(this.height);
29718         }
29719     },
29720     
29721     // private
29722     renderField : function(f){
29723         f.fieldEl = this.fieldTpl.append(this.el, [
29724                f.id, f.fieldLabel,
29725                f.labelStyle||this.labelStyle||'',
29726                this.elementStyle||'',
29727                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
29728                f.itemCls||this.itemCls||'',
29729                f.width ? f.width + this.padWidth : 160 + this.padWidth
29730        ],true);
29731     }
29732 });
29733  
29734
29735 /**
29736  * @class Roo.form.FieldSet
29737  * @extends Roo.form.Layout
29738  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
29739  * @constructor
29740  * @param {Object} config Configuration options
29741  */
29742 Roo.form.FieldSet = function(config){
29743     Roo.form.FieldSet.superclass.constructor.call(this, config);
29744 };
29745
29746 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
29747     /**
29748      * @cfg {String} legend
29749      * The text to display as the legend for the FieldSet (defaults to '')
29750      */
29751     /**
29752      * @cfg {String/Object} autoCreate
29753      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
29754      */
29755
29756     // private
29757     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
29758
29759     // private
29760     onRender : function(ct, position){
29761         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
29762         if(this.legend){
29763             this.setLegend(this.legend);
29764         }
29765     },
29766
29767     // private
29768     setLegend : function(text){
29769         if(this.rendered){
29770             this.el.child('legend').update(text);
29771         }
29772     }
29773 });/*
29774  * Based on:
29775  * Ext JS Library 1.1.1
29776  * Copyright(c) 2006-2007, Ext JS, LLC.
29777  *
29778  * Originally Released Under LGPL - original licence link has changed is not relivant.
29779  *
29780  * Fork - LGPL
29781  * <script type="text/javascript">
29782  */
29783 /**
29784  * @class Roo.form.VTypes
29785  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
29786  * @singleton
29787  */
29788 Roo.form.VTypes = function(){
29789     // closure these in so they are only created once.
29790     var alpha = /^[a-zA-Z_]+$/;
29791     var alphanum = /^[a-zA-Z0-9_]+$/;
29792     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
29793     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
29794
29795     // All these messages and functions are configurable
29796     return {
29797         /**
29798          * The function used to validate email addresses
29799          * @param {String} value The email address
29800          */
29801         'email' : function(v){
29802             return email.test(v);
29803         },
29804         /**
29805          * The error text to display when the email validation function returns false
29806          * @type String
29807          */
29808         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
29809         /**
29810          * The keystroke filter mask to be applied on email input
29811          * @type RegExp
29812          */
29813         'emailMask' : /[a-z0-9_\.\-@]/i,
29814
29815         /**
29816          * The function used to validate URLs
29817          * @param {String} value The URL
29818          */
29819         'url' : function(v){
29820             return url.test(v);
29821         },
29822         /**
29823          * The error text to display when the url validation function returns false
29824          * @type String
29825          */
29826         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
29827         
29828         /**
29829          * The function used to validate alpha values
29830          * @param {String} value The value
29831          */
29832         'alpha' : function(v){
29833             return alpha.test(v);
29834         },
29835         /**
29836          * The error text to display when the alpha validation function returns false
29837          * @type String
29838          */
29839         'alphaText' : 'This field should only contain letters and _',
29840         /**
29841          * The keystroke filter mask to be applied on alpha input
29842          * @type RegExp
29843          */
29844         'alphaMask' : /[a-z_]/i,
29845
29846         /**
29847          * The function used to validate alphanumeric values
29848          * @param {String} value The value
29849          */
29850         'alphanum' : function(v){
29851             return alphanum.test(v);
29852         },
29853         /**
29854          * The error text to display when the alphanumeric validation function returns false
29855          * @type String
29856          */
29857         'alphanumText' : 'This field should only contain letters, numbers and _',
29858         /**
29859          * The keystroke filter mask to be applied on alphanumeric input
29860          * @type RegExp
29861          */
29862         'alphanumMask' : /[a-z0-9_]/i
29863     };
29864 }();//<script type="text/javascript">
29865
29866 /**
29867  * @class Roo.form.FCKeditor
29868  * @extends Roo.form.TextArea
29869  * Wrapper around the FCKEditor http://www.fckeditor.net
29870  * @constructor
29871  * Creates a new FCKeditor
29872  * @param {Object} config Configuration options
29873  */
29874 Roo.form.FCKeditor = function(config){
29875     Roo.form.FCKeditor.superclass.constructor.call(this, config);
29876     this.addEvents({
29877          /**
29878          * @event editorinit
29879          * Fired when the editor is initialized - you can add extra handlers here..
29880          * @param {FCKeditor} this
29881          * @param {Object} the FCK object.
29882          */
29883         editorinit : true
29884     });
29885     
29886     
29887 };
29888 Roo.form.FCKeditor.editors = { };
29889 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
29890 {
29891     //defaultAutoCreate : {
29892     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
29893     //},
29894     // private
29895     /**
29896      * @cfg {Object} fck options - see fck manual for details.
29897      */
29898     fckconfig : false,
29899     
29900     /**
29901      * @cfg {Object} fck toolbar set (Basic or Default)
29902      */
29903     toolbarSet : 'Basic',
29904     /**
29905      * @cfg {Object} fck BasePath
29906      */ 
29907     basePath : '/fckeditor/',
29908     
29909     
29910     frame : false,
29911     
29912     value : '',
29913     
29914    
29915     onRender : function(ct, position)
29916     {
29917         if(!this.el){
29918             this.defaultAutoCreate = {
29919                 tag: "textarea",
29920                 style:"width:300px;height:60px;",
29921                 autocomplete: "off"
29922             };
29923         }
29924         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
29925         /*
29926         if(this.grow){
29927             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
29928             if(this.preventScrollbars){
29929                 this.el.setStyle("overflow", "hidden");
29930             }
29931             this.el.setHeight(this.growMin);
29932         }
29933         */
29934         //console.log('onrender' + this.getId() );
29935         Roo.form.FCKeditor.editors[this.getId()] = this;
29936          
29937
29938         this.replaceTextarea() ;
29939         
29940     },
29941     
29942     getEditor : function() {
29943         return this.fckEditor;
29944     },
29945     /**
29946      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
29947      * @param {Mixed} value The value to set
29948      */
29949     
29950     
29951     setValue : function(value)
29952     {
29953         //console.log('setValue: ' + value);
29954         
29955         if(typeof(value) == 'undefined') { // not sure why this is happending...
29956             return;
29957         }
29958         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
29959         
29960         //if(!this.el || !this.getEditor()) {
29961         //    this.value = value;
29962             //this.setValue.defer(100,this,[value]);    
29963         //    return;
29964         //} 
29965         
29966         if(!this.getEditor()) {
29967             return;
29968         }
29969         
29970         this.getEditor().SetData(value);
29971         
29972         //
29973
29974     },
29975
29976     /**
29977      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
29978      * @return {Mixed} value The field value
29979      */
29980     getValue : function()
29981     {
29982         
29983         if (this.frame && this.frame.dom.style.display == 'none') {
29984             return Roo.form.FCKeditor.superclass.getValue.call(this);
29985         }
29986         
29987         if(!this.el || !this.getEditor()) {
29988            
29989            // this.getValue.defer(100,this); 
29990             return this.value;
29991         }
29992        
29993         
29994         var value=this.getEditor().GetData();
29995         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
29996         return Roo.form.FCKeditor.superclass.getValue.call(this);
29997         
29998
29999     },
30000
30001     /**
30002      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
30003      * @return {Mixed} value The field value
30004      */
30005     getRawValue : function()
30006     {
30007         if (this.frame && this.frame.dom.style.display == 'none') {
30008             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
30009         }
30010         
30011         if(!this.el || !this.getEditor()) {
30012             //this.getRawValue.defer(100,this); 
30013             return this.value;
30014             return;
30015         }
30016         
30017         
30018         
30019         var value=this.getEditor().GetData();
30020         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
30021         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
30022          
30023     },
30024     
30025     setSize : function(w,h) {
30026         
30027         
30028         
30029         //if (this.frame && this.frame.dom.style.display == 'none') {
30030         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
30031         //    return;
30032         //}
30033         //if(!this.el || !this.getEditor()) {
30034         //    this.setSize.defer(100,this, [w,h]); 
30035         //    return;
30036         //}
30037         
30038         
30039         
30040         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
30041         
30042         this.frame.dom.setAttribute('width', w);
30043         this.frame.dom.setAttribute('height', h);
30044         this.frame.setSize(w,h);
30045         
30046     },
30047     
30048     toggleSourceEdit : function(value) {
30049         
30050       
30051          
30052         this.el.dom.style.display = value ? '' : 'none';
30053         this.frame.dom.style.display = value ?  'none' : '';
30054         
30055     },
30056     
30057     
30058     focus: function(tag)
30059     {
30060         if (this.frame.dom.style.display == 'none') {
30061             return Roo.form.FCKeditor.superclass.focus.call(this);
30062         }
30063         if(!this.el || !this.getEditor()) {
30064             this.focus.defer(100,this, [tag]); 
30065             return;
30066         }
30067         
30068         
30069         
30070         
30071         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
30072         this.getEditor().Focus();
30073         if (tgs.length) {
30074             if (!this.getEditor().Selection.GetSelection()) {
30075                 this.focus.defer(100,this, [tag]); 
30076                 return;
30077             }
30078             
30079             
30080             var r = this.getEditor().EditorDocument.createRange();
30081             r.setStart(tgs[0],0);
30082             r.setEnd(tgs[0],0);
30083             this.getEditor().Selection.GetSelection().removeAllRanges();
30084             this.getEditor().Selection.GetSelection().addRange(r);
30085             this.getEditor().Focus();
30086         }
30087         
30088     },
30089     
30090     
30091     
30092     replaceTextarea : function()
30093     {
30094         if ( document.getElementById( this.getId() + '___Frame' ) )
30095             return ;
30096         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
30097         //{
30098             // We must check the elements firstly using the Id and then the name.
30099         var oTextarea = document.getElementById( this.getId() );
30100         
30101         var colElementsByName = document.getElementsByName( this.getId() ) ;
30102          
30103         oTextarea.style.display = 'none' ;
30104
30105         if ( oTextarea.tabIndex ) {            
30106             this.TabIndex = oTextarea.tabIndex ;
30107         }
30108         
30109         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
30110         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
30111         this.frame = Roo.get(this.getId() + '___Frame')
30112     },
30113     
30114     _getConfigHtml : function()
30115     {
30116         var sConfig = '' ;
30117
30118         for ( var o in this.fckconfig ) {
30119             sConfig += sConfig.length > 0  ? '&amp;' : '';
30120             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
30121         }
30122
30123         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
30124     },
30125     
30126     
30127     _getIFrameHtml : function()
30128     {
30129         var sFile = 'fckeditor.html' ;
30130         /* no idea what this is about..
30131         try
30132         {
30133             if ( (/fcksource=true/i).test( window.top.location.search ) )
30134                 sFile = 'fckeditor.original.html' ;
30135         }
30136         catch (e) { 
30137         */
30138
30139         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
30140         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
30141         
30142         
30143         var html = '<iframe id="' + this.getId() +
30144             '___Frame" src="' + sLink +
30145             '" width="' + this.width +
30146             '" height="' + this.height + '"' +
30147             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
30148             ' frameborder="0" scrolling="no"></iframe>' ;
30149
30150         return html ;
30151     },
30152     
30153     _insertHtmlBefore : function( html, element )
30154     {
30155         if ( element.insertAdjacentHTML )       {
30156             // IE
30157             element.insertAdjacentHTML( 'beforeBegin', html ) ;
30158         } else { // Gecko
30159             var oRange = document.createRange() ;
30160             oRange.setStartBefore( element ) ;
30161             var oFragment = oRange.createContextualFragment( html );
30162             element.parentNode.insertBefore( oFragment, element ) ;
30163         }
30164     }
30165     
30166     
30167   
30168     
30169     
30170     
30171     
30172
30173 });
30174
30175 //Roo.reg('fckeditor', Roo.form.FCKeditor);
30176
30177 function FCKeditor_OnComplete(editorInstance){
30178     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
30179     f.fckEditor = editorInstance;
30180     //console.log("loaded");
30181     f.fireEvent('editorinit', f, editorInstance);
30182
30183   
30184
30185  
30186
30187
30188
30189
30190
30191
30192
30193
30194
30195
30196
30197
30198
30199
30200
30201 //<script type="text/javascript">
30202 /**
30203  * @class Roo.form.GridField
30204  * @extends Roo.form.Field
30205  * Embed a grid (or editable grid into a form)
30206  * STATUS ALPHA
30207  * 
30208  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
30209  * it needs 
30210  * xgrid.store = Roo.data.Store
30211  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
30212  * xgrid.store.reader = Roo.data.JsonReader 
30213  * 
30214  * 
30215  * @constructor
30216  * Creates a new GridField
30217  * @param {Object} config Configuration options
30218  */
30219 Roo.form.GridField = function(config){
30220     Roo.form.GridField.superclass.constructor.call(this, config);
30221      
30222 };
30223
30224 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
30225     /**
30226      * @cfg {Number} width  - used to restrict width of grid..
30227      */
30228     width : 100,
30229     /**
30230      * @cfg {Number} height - used to restrict height of grid..
30231      */
30232     height : 50,
30233      /**
30234      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
30235          * 
30236          *}
30237      */
30238     xgrid : false, 
30239     /**
30240      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30241      * {tag: "input", type: "checkbox", autocomplete: "off"})
30242      */
30243    // defaultAutoCreate : { tag: 'div' },
30244     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
30245     /**
30246      * @cfg {String} addTitle Text to include for adding a title.
30247      */
30248     addTitle : false,
30249     //
30250     onResize : function(){
30251         Roo.form.Field.superclass.onResize.apply(this, arguments);
30252     },
30253
30254     initEvents : function(){
30255         // Roo.form.Checkbox.superclass.initEvents.call(this);
30256         // has no events...
30257        
30258     },
30259
30260
30261     getResizeEl : function(){
30262         return this.wrap;
30263     },
30264
30265     getPositionEl : function(){
30266         return this.wrap;
30267     },
30268
30269     // private
30270     onRender : function(ct, position){
30271         
30272         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
30273         var style = this.style;
30274         delete this.style;
30275         
30276         Roo.form.GridField.superclass.onRender.call(this, ct, position);
30277         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
30278         this.viewEl = this.wrap.createChild({ tag: 'div' });
30279         if (style) {
30280             this.viewEl.applyStyles(style);
30281         }
30282         if (this.width) {
30283             this.viewEl.setWidth(this.width);
30284         }
30285         if (this.height) {
30286             this.viewEl.setHeight(this.height);
30287         }
30288         //if(this.inputValue !== undefined){
30289         //this.setValue(this.value);
30290         
30291         
30292         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
30293         
30294         
30295         this.grid.render();
30296         this.grid.getDataSource().on('remove', this.refreshValue, this);
30297         this.grid.getDataSource().on('update', this.refreshValue, this);
30298         this.grid.on('afteredit', this.refreshValue, this);
30299  
30300     },
30301      
30302     
30303     /**
30304      * Sets the value of the item. 
30305      * @param {String} either an object  or a string..
30306      */
30307     setValue : function(v){
30308         //this.value = v;
30309         v = v || []; // empty set..
30310         // this does not seem smart - it really only affects memoryproxy grids..
30311         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
30312             var ds = this.grid.getDataSource();
30313             // assumes a json reader..
30314             var data = {}
30315             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
30316             ds.loadData( data);
30317         }
30318         // clear selection so it does not get stale.
30319         if (this.grid.sm) { 
30320             this.grid.sm.clearSelections();
30321         }
30322         
30323         Roo.form.GridField.superclass.setValue.call(this, v);
30324         this.refreshValue();
30325         // should load data in the grid really....
30326     },
30327     
30328     // private
30329     refreshValue: function() {
30330          var val = [];
30331         this.grid.getDataSource().each(function(r) {
30332             val.push(r.data);
30333         });
30334         this.el.dom.value = Roo.encode(val);
30335     }
30336     
30337      
30338     
30339     
30340 });/*
30341  * Based on:
30342  * Ext JS Library 1.1.1
30343  * Copyright(c) 2006-2007, Ext JS, LLC.
30344  *
30345  * Originally Released Under LGPL - original licence link has changed is not relivant.
30346  *
30347  * Fork - LGPL
30348  * <script type="text/javascript">
30349  */
30350 /**
30351  * @class Roo.form.DisplayField
30352  * @extends Roo.form.Field
30353  * A generic Field to display non-editable data.
30354  * @constructor
30355  * Creates a new Display Field item.
30356  * @param {Object} config Configuration options
30357  */
30358 Roo.form.DisplayField = function(config){
30359     Roo.form.DisplayField.superclass.constructor.call(this, config);
30360     
30361 };
30362
30363 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
30364     inputType:      'hidden',
30365     allowBlank:     true,
30366     readOnly:         true,
30367     
30368  
30369     /**
30370      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
30371      */
30372     focusClass : undefined,
30373     /**
30374      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
30375      */
30376     fieldClass: 'x-form-field',
30377     
30378      /**
30379      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
30380      */
30381     valueRenderer: undefined,
30382     
30383     width: 100,
30384     /**
30385      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30386      * {tag: "input", type: "checkbox", autocomplete: "off"})
30387      */
30388      
30389  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
30390
30391     onResize : function(){
30392         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
30393         
30394     },
30395
30396     initEvents : function(){
30397         // Roo.form.Checkbox.superclass.initEvents.call(this);
30398         // has no events...
30399        
30400     },
30401
30402
30403     getResizeEl : function(){
30404         return this.wrap;
30405     },
30406
30407     getPositionEl : function(){
30408         return this.wrap;
30409     },
30410
30411     // private
30412     onRender : function(ct, position){
30413         
30414         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
30415         //if(this.inputValue !== undefined){
30416         this.wrap = this.el.wrap();
30417         
30418         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
30419         
30420         if (this.bodyStyle) {
30421             this.viewEl.applyStyles(this.bodyStyle);
30422         }
30423         //this.viewEl.setStyle('padding', '2px');
30424         
30425         this.setValue(this.value);
30426         
30427     },
30428 /*
30429     // private
30430     initValue : Roo.emptyFn,
30431
30432   */
30433
30434         // private
30435     onClick : function(){
30436         
30437     },
30438
30439     /**
30440      * Sets the checked state of the checkbox.
30441      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
30442      */
30443     setValue : function(v){
30444         this.value = v;
30445         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
30446         // this might be called before we have a dom element..
30447         if (!this.viewEl) {
30448             return;
30449         }
30450         this.viewEl.dom.innerHTML = html;
30451         Roo.form.DisplayField.superclass.setValue.call(this, v);
30452
30453     }
30454 });/*
30455  * 
30456  * Licence- LGPL
30457  * 
30458  */
30459
30460 /**
30461  * @class Roo.form.DayPicker
30462  * @extends Roo.form.Field
30463  * A Day picker show [M] [T] [W] ....
30464  * @constructor
30465  * Creates a new Day Picker
30466  * @param {Object} config Configuration options
30467  */
30468 Roo.form.DayPicker= function(config){
30469     Roo.form.DayPicker.superclass.constructor.call(this, config);
30470      
30471 };
30472
30473 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
30474     /**
30475      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
30476      */
30477     focusClass : undefined,
30478     /**
30479      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
30480      */
30481     fieldClass: "x-form-field",
30482    
30483     /**
30484      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30485      * {tag: "input", type: "checkbox", autocomplete: "off"})
30486      */
30487     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
30488     
30489    
30490     actionMode : 'viewEl', 
30491     //
30492     // private
30493  
30494     inputType : 'hidden',
30495     
30496      
30497     inputElement: false, // real input element?
30498     basedOn: false, // ????
30499     
30500     isFormField: true, // not sure where this is needed!!!!
30501
30502     onResize : function(){
30503         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
30504         if(!this.boxLabel){
30505             this.el.alignTo(this.wrap, 'c-c');
30506         }
30507     },
30508
30509     initEvents : function(){
30510         Roo.form.Checkbox.superclass.initEvents.call(this);
30511         this.el.on("click", this.onClick,  this);
30512         this.el.on("change", this.onClick,  this);
30513     },
30514
30515
30516     getResizeEl : function(){
30517         return this.wrap;
30518     },
30519
30520     getPositionEl : function(){
30521         return this.wrap;
30522     },
30523
30524     
30525     // private
30526     onRender : function(ct, position){
30527         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
30528        
30529         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
30530         
30531         var r1 = '<table><tr>';
30532         var r2 = '<tr class="x-form-daypick-icons">';
30533         for (var i=0; i < 7; i++) {
30534             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
30535             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
30536         }
30537         
30538         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
30539         viewEl.select('img').on('click', this.onClick, this);
30540         this.viewEl = viewEl;   
30541         
30542         
30543         // this will not work on Chrome!!!
30544         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
30545         this.el.on('propertychange', this.setFromHidden,  this);  //ie
30546         
30547         
30548           
30549
30550     },
30551
30552     // private
30553     initValue : Roo.emptyFn,
30554
30555     /**
30556      * Returns the checked state of the checkbox.
30557      * @return {Boolean} True if checked, else false
30558      */
30559     getValue : function(){
30560         return this.el.dom.value;
30561         
30562     },
30563
30564         // private
30565     onClick : function(e){ 
30566         //this.setChecked(!this.checked);
30567         Roo.get(e.target).toggleClass('x-menu-item-checked');
30568         this.refreshValue();
30569         //if(this.el.dom.checked != this.checked){
30570         //    this.setValue(this.el.dom.checked);
30571        // }
30572     },
30573     
30574     // private
30575     refreshValue : function()
30576     {
30577         var val = '';
30578         this.viewEl.select('img',true).each(function(e,i,n)  {
30579             val += e.is(".x-menu-item-checked") ? String(n) : '';
30580         });
30581         this.setValue(val, true);
30582     },
30583
30584     /**
30585      * Sets the checked state of the checkbox.
30586      * On is always based on a string comparison between inputValue and the param.
30587      * @param {Boolean/String} value - the value to set 
30588      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
30589      */
30590     setValue : function(v,suppressEvent){
30591         if (!this.el.dom) {
30592             return;
30593         }
30594         var old = this.el.dom.value ;
30595         this.el.dom.value = v;
30596         if (suppressEvent) {
30597             return ;
30598         }
30599          
30600         // update display..
30601         this.viewEl.select('img',true).each(function(e,i,n)  {
30602             
30603             var on = e.is(".x-menu-item-checked");
30604             var newv = v.indexOf(String(n)) > -1;
30605             if (on != newv) {
30606                 e.toggleClass('x-menu-item-checked');
30607             }
30608             
30609         });
30610         
30611         
30612         this.fireEvent('change', this, v, old);
30613         
30614         
30615     },
30616    
30617     // handle setting of hidden value by some other method!!?!?
30618     setFromHidden: function()
30619     {
30620         if(!this.el){
30621             return;
30622         }
30623         //console.log("SET FROM HIDDEN");
30624         //alert('setFrom hidden');
30625         this.setValue(this.el.dom.value);
30626     },
30627     
30628     onDestroy : function()
30629     {
30630         if(this.viewEl){
30631             Roo.get(this.viewEl).remove();
30632         }
30633          
30634         Roo.form.DayPicker.superclass.onDestroy.call(this);
30635     }
30636
30637 });/*
30638  * RooJS Library 1.1.1
30639  * Copyright(c) 2008-2011  Alan Knowles
30640  *
30641  * License - LGPL
30642  */
30643  
30644
30645 /**
30646  * @class Roo.form.ComboCheck
30647  * @extends Roo.form.ComboBox
30648  * A combobox for multiple select items.
30649  *
30650  * FIXME - could do with a reset button..
30651  * 
30652  * @constructor
30653  * Create a new ComboCheck
30654  * @param {Object} config Configuration options
30655  */
30656 Roo.form.ComboCheck = function(config){
30657     Roo.form.ComboCheck.superclass.constructor.call(this, config);
30658     // should verify some data...
30659     // like
30660     // hiddenName = required..
30661     // displayField = required
30662     // valudField == required
30663     var req= [ 'hiddenName', 'displayField', 'valueField' ];
30664     var _t = this;
30665     Roo.each(req, function(e) {
30666         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
30667             throw "Roo.form.ComboCheck : missing value for: " + e;
30668         }
30669     });
30670     
30671     
30672 };
30673
30674 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
30675      
30676      
30677     editable : false,
30678      
30679     selectedClass: 'x-menu-item-checked', 
30680     
30681     // private
30682     onRender : function(ct, position){
30683         var _t = this;
30684         
30685         
30686         
30687         if(!this.tpl){
30688             var cls = 'x-combo-list';
30689
30690             
30691             this.tpl =  new Roo.Template({
30692                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
30693                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
30694                    '<span>{' + this.displayField + '}</span>' +
30695                     '</div>' 
30696                 
30697             });
30698         }
30699  
30700         
30701         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
30702         this.view.singleSelect = false;
30703         this.view.multiSelect = true;
30704         this.view.toggleSelect = true;
30705         this.pageTb.add(new Roo.Toolbar.Fill(), {
30706             
30707             text: 'Done',
30708             handler: function()
30709             {
30710                 _t.collapse();
30711             }
30712         });
30713     },
30714     
30715     onViewOver : function(e, t){
30716         // do nothing...
30717         return;
30718         
30719     },
30720     
30721     onViewClick : function(doFocus,index){
30722         return;
30723         
30724     },
30725     select: function () {
30726         //Roo.log("SELECT CALLED");
30727     },
30728      
30729     selectByValue : function(xv, scrollIntoView){
30730         var ar = this.getValueArray();
30731         var sels = [];
30732         
30733         Roo.each(ar, function(v) {
30734             if(v === undefined || v === null){
30735                 return;
30736             }
30737             var r = this.findRecord(this.valueField, v);
30738             if(r){
30739                 sels.push(this.store.indexOf(r))
30740                 
30741             }
30742         },this);
30743         this.view.select(sels);
30744         return false;
30745     },
30746     
30747     
30748     
30749     onSelect : function(record, index){
30750        // Roo.log("onselect Called");
30751        // this is only called by the clear button now..
30752         this.view.clearSelections();
30753         this.setValue('[]');
30754         if (this.value != this.valueBefore) {
30755             this.fireEvent('change', this, this.value, this.valueBefore);
30756             this.valueBefore = this.value;
30757         }
30758     },
30759     getValueArray : function()
30760     {
30761         var ar = [] ;
30762         
30763         try {
30764             //Roo.log(this.value);
30765             if (typeof(this.value) == 'undefined') {
30766                 return [];
30767             }
30768             var ar = Roo.decode(this.value);
30769             return  ar instanceof Array ? ar : []; //?? valid?
30770             
30771         } catch(e) {
30772             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
30773             return [];
30774         }
30775          
30776     },
30777     expand : function ()
30778     {
30779         
30780         Roo.form.ComboCheck.superclass.expand.call(this);
30781         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
30782         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
30783         
30784
30785     },
30786     
30787     collapse : function(){
30788         Roo.form.ComboCheck.superclass.collapse.call(this);
30789         var sl = this.view.getSelectedIndexes();
30790         var st = this.store;
30791         var nv = [];
30792         var tv = [];
30793         var r;
30794         Roo.each(sl, function(i) {
30795             r = st.getAt(i);
30796             nv.push(r.get(this.valueField));
30797         },this);
30798         this.setValue(Roo.encode(nv));
30799         if (this.value != this.valueBefore) {
30800
30801             this.fireEvent('change', this, this.value, this.valueBefore);
30802             this.valueBefore = this.value;
30803         }
30804         
30805     },
30806     
30807     setValue : function(v){
30808         // Roo.log(v);
30809         this.value = v;
30810         
30811         var vals = this.getValueArray();
30812         var tv = [];
30813         Roo.each(vals, function(k) {
30814             var r = this.findRecord(this.valueField, k);
30815             if(r){
30816                 tv.push(r.data[this.displayField]);
30817             }else if(this.valueNotFoundText !== undefined){
30818                 tv.push( this.valueNotFoundText );
30819             }
30820         },this);
30821        // Roo.log(tv);
30822         
30823         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
30824         this.hiddenField.value = v;
30825         this.value = v;
30826     }
30827     
30828 });/*
30829  * Based on:
30830  * Ext JS Library 1.1.1
30831  * Copyright(c) 2006-2007, Ext JS, LLC.
30832  *
30833  * Originally Released Under LGPL - original licence link has changed is not relivant.
30834  *
30835  * Fork - LGPL
30836  * <script type="text/javascript">
30837  */
30838  
30839 /**
30840  * @class Roo.form.Signature
30841  * @extends Roo.form.Field
30842  * Signature field.  
30843  * @constructor
30844  * 
30845  * @param {Object} config Configuration options
30846  */
30847
30848 Roo.form.Signature = function(config){
30849     Roo.form.Signature.superclass.constructor.call(this, config);
30850     
30851     this.addEvents({// not in used??
30852          /**
30853          * @event confirm
30854          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
30855              * @param {Roo.form.Signature} combo This combo box
30856              */
30857         'confirm' : true,
30858         /**
30859          * @event reset
30860          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
30861              * @param {Roo.form.ComboBox} combo This combo box
30862              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
30863              */
30864         'reset' : true
30865     });
30866 };
30867
30868 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
30869     /**
30870      * @cfg {Object} labels Label to use when rendering a form.
30871      * defaults to 
30872      * labels : { 
30873      *      clear : "Clear",
30874      *      confirm : "Confirm"
30875      *  }
30876      */
30877     labels : { 
30878         clear : "Clear",
30879         confirm : "Confirm"
30880     },
30881     /**
30882      * @cfg {Number} width The signature panel width (defaults to 300)
30883      */
30884     width: 300,
30885     /**
30886      * @cfg {Number} height The signature panel height (defaults to 100)
30887      */
30888     height : 100,
30889     /**
30890      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
30891      */
30892     allowBlank : false,
30893     
30894     //private
30895     // {Object} signPanel The signature SVG panel element (defaults to {})
30896     signPanel : {},
30897     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
30898     isMouseDown : false,
30899     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
30900     isConfirmed : false,
30901     // {String} signatureTmp SVG mapping string (defaults to empty string)
30902     signatureTmp : '',
30903     
30904     
30905     defaultAutoCreate : { // modified by initCompnoent..
30906         tag: "input",
30907         type:"hidden"
30908     },
30909
30910     // private
30911     onRender : function(ct, position){
30912         
30913         Roo.form.Signature.superclass.onRender.call(this, ct, position);
30914         
30915         this.wrap = this.el.wrap({
30916             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
30917         });
30918         
30919         this.createToolbar(this);
30920         this.signPanel = this.wrap.createChild({
30921                 tag: 'div',
30922                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
30923             }, this.el
30924         );
30925             
30926         this.svgID = Roo.id();
30927         this.svgEl = this.signPanel.createChild({
30928               xmlns : 'http://www.w3.org/2000/svg',
30929               tag : 'svg',
30930               id : this.svgID + "-svg",
30931               width: this.width,
30932               height: this.height,
30933               viewBox: '0 0 '+this.width+' '+this.height,
30934               cn : [
30935                 {
30936                     tag: "rect",
30937                     id: this.svgID + "-svg-r",
30938                     width: this.width,
30939                     height: this.height,
30940                     fill: "#ffa"
30941                 },
30942                 {
30943                     tag: "line",
30944                     id: this.svgID + "-svg-l",
30945                     x1: "0", // start
30946                     y1: (this.height*0.8), // start set the line in 80% of height
30947                     x2: this.width, // end
30948                     y2: (this.height*0.8), // end set the line in 80% of height
30949                     'stroke': "#666",
30950                     'stroke-width': "1",
30951                     'stroke-dasharray': "3",
30952                     'shape-rendering': "crispEdges",
30953                     'pointer-events': "none"
30954                 },
30955                 {
30956                     tag: "path",
30957                     id: this.svgID + "-svg-p",
30958                     'stroke': "navy",
30959                     'stroke-width': "3",
30960                     'fill': "none",
30961                     'pointer-events': 'none'
30962                 }
30963               ]
30964         });
30965         this.createSVG();
30966         this.svgBox = this.svgEl.dom.getScreenCTM();
30967     },
30968     createSVG : function(){ 
30969         var svg = this.signPanel;
30970         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
30971         var t = this;
30972
30973         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
30974         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
30975         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
30976         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
30977         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
30978         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
30979         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
30980         
30981     },
30982     isTouchEvent : function(e){
30983         return e.type.match(/^touch/);
30984     },
30985     getCoords : function (e) {
30986         var pt    = this.svgEl.dom.createSVGPoint();
30987         pt.x = e.clientX; 
30988         pt.y = e.clientY;
30989         if (this.isTouchEvent(e)) {
30990             pt.x =  e.targetTouches[0].clientX 
30991             pt.y = e.targetTouches[0].clientY;
30992         }
30993         var a = this.svgEl.dom.getScreenCTM();
30994         var b = a.inverse();
30995         var mx = pt.matrixTransform(b);
30996         return mx.x + ',' + mx.y;
30997     },
30998     //mouse event headler 
30999     down : function (e) {
31000         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
31001         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
31002         
31003         this.isMouseDown = true;
31004         
31005         e.preventDefault();
31006     },
31007     move : function (e) {
31008         if (this.isMouseDown) {
31009             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
31010             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
31011         }
31012         
31013         e.preventDefault();
31014     },
31015     up : function (e) {
31016         this.isMouseDown = false;
31017         var sp = this.signatureTmp.split(' ');
31018         
31019         if(sp.length > 1){
31020             if(!sp[sp.length-2].match(/^L/)){
31021                 sp.pop();
31022                 sp.pop();
31023                 sp.push("");
31024                 this.signatureTmp = sp.join(" ");
31025             }
31026         }
31027         if(this.getValue() != this.signatureTmp){
31028             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31029             this.isConfirmed = false;
31030         }
31031         e.preventDefault();
31032     },
31033     
31034     /**
31035      * Protected method that will not generally be called directly. It
31036      * is called when the editor creates its toolbar. Override this method if you need to
31037      * add custom toolbar buttons.
31038      * @param {HtmlEditor} editor
31039      */
31040     createToolbar : function(editor){
31041          function btn(id, toggle, handler){
31042             var xid = fid + '-'+ id ;
31043             return {
31044                 id : xid,
31045                 cmd : id,
31046                 cls : 'x-btn-icon x-edit-'+id,
31047                 enableToggle:toggle !== false,
31048                 scope: editor, // was editor...
31049                 handler:handler||editor.relayBtnCmd,
31050                 clickEvent:'mousedown',
31051                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
31052                 tabIndex:-1
31053             };
31054         }
31055         
31056         
31057         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
31058         this.tb = tb;
31059         this.tb.add(
31060            {
31061                 cls : ' x-signature-btn x-signature-'+id,
31062                 scope: editor, // was editor...
31063                 handler: this.reset,
31064                 clickEvent:'mousedown',
31065                 text: this.labels.clear
31066             },
31067             {
31068                  xtype : 'Fill',
31069                  xns: Roo.Toolbar
31070             }, 
31071             {
31072                 cls : '  x-signature-btn x-signature-'+id,
31073                 scope: editor, // was editor...
31074                 handler: this.confirmHandler,
31075                 clickEvent:'mousedown',
31076                 text: this.labels.confirm
31077             }
31078         );
31079     
31080     },
31081     //public
31082     /**
31083      * when user is clicked confirm then show this image.....
31084      * 
31085      * @return {String} Image Data URI
31086      */
31087     getImageDataURI : function(){
31088         var svg = this.svgEl.dom.parentNode.innerHTML;
31089         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
31090         return src; 
31091     },
31092     /**
31093      * 
31094      * @return {Boolean} this.isConfirmed
31095      */
31096     getConfirmed : function(){
31097         return this.isConfirmed;
31098     },
31099     /**
31100      * 
31101      * @return {Number} this.width
31102      */
31103     getWidth : function(){
31104         return this.width;
31105     },
31106     /**
31107      * 
31108      * @return {Number} this.height
31109      */
31110     getHeight : function(){
31111         return this.height;
31112     },
31113     // private
31114     getSignature : function(){
31115         return this.signatureTmp;
31116     },
31117     // private
31118     reset : function(){
31119         this.signatureTmp = '';
31120         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31121         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
31122         this.isConfirmed = false;
31123         Roo.form.Signature.superclass.reset.call(this);
31124     },
31125     setSignature : function(s){
31126         this.signatureTmp = s;
31127         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31128         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
31129         this.setValue(s);
31130         this.isConfirmed = false;
31131         Roo.form.Signature.superclass.reset.call(this);
31132     }, 
31133     test : function(){
31134 //        Roo.log(this.signPanel.dom.contentWindow.up())
31135     },
31136     //private
31137     setConfirmed : function(){
31138         
31139         
31140         
31141 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
31142     },
31143     // private
31144     confirmHandler : function(){
31145         if(!this.getSignature()){
31146             return;
31147         }
31148         
31149         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
31150         this.setValue(this.getSignature());
31151         this.isConfirmed = true;
31152         
31153         this.fireEvent('confirm', this);
31154     },
31155     // private
31156     // Subclasses should provide the validation implementation by overriding this
31157     validateValue : function(value){
31158         if(this.allowBlank){
31159             return true;
31160         }
31161         
31162         if(this.isConfirmed){
31163             return true;
31164         }
31165         return false;
31166     }
31167 });/*
31168  * Based on:
31169  * Ext JS Library 1.1.1
31170  * Copyright(c) 2006-2007, Ext JS, LLC.
31171  *
31172  * Originally Released Under LGPL - original licence link has changed is not relivant.
31173  *
31174  * Fork - LGPL
31175  * <script type="text/javascript">
31176  */
31177  
31178
31179 /**
31180  * @class Roo.form.ComboBox
31181  * @extends Roo.form.TriggerField
31182  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
31183  * @constructor
31184  * Create a new ComboBox.
31185  * @param {Object} config Configuration options
31186  */
31187 Roo.form.Select = function(config){
31188     Roo.form.Select.superclass.constructor.call(this, config);
31189      
31190 };
31191
31192 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
31193     /**
31194      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
31195      */
31196     /**
31197      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
31198      * rendering into an Roo.Editor, defaults to false)
31199      */
31200     /**
31201      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
31202      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
31203      */
31204     /**
31205      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
31206      */
31207     /**
31208      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
31209      * the dropdown list (defaults to undefined, with no header element)
31210      */
31211
31212      /**
31213      * @cfg {String/Roo.Template} tpl The template to use to render the output
31214      */
31215      
31216     // private
31217     defaultAutoCreate : {tag: "select"  },
31218     /**
31219      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
31220      */
31221     listWidth: undefined,
31222     /**
31223      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
31224      * mode = 'remote' or 'text' if mode = 'local')
31225      */
31226     displayField: undefined,
31227     /**
31228      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
31229      * mode = 'remote' or 'value' if mode = 'local'). 
31230      * Note: use of a valueField requires the user make a selection
31231      * in order for a value to be mapped.
31232      */
31233     valueField: undefined,
31234     
31235     
31236     /**
31237      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
31238      * field's data value (defaults to the underlying DOM element's name)
31239      */
31240     hiddenName: undefined,
31241     /**
31242      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
31243      */
31244     listClass: '',
31245     /**
31246      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
31247      */
31248     selectedClass: 'x-combo-selected',
31249     /**
31250      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
31251      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
31252      * which displays a downward arrow icon).
31253      */
31254     triggerClass : 'x-form-arrow-trigger',
31255     /**
31256      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
31257      */
31258     shadow:'sides',
31259     /**
31260      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
31261      * anchor positions (defaults to 'tl-bl')
31262      */
31263     listAlign: 'tl-bl?',
31264     /**
31265      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
31266      */
31267     maxHeight: 300,
31268     /**
31269      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
31270      * query specified by the allQuery config option (defaults to 'query')
31271      */
31272     triggerAction: 'query',
31273     /**
31274      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
31275      * (defaults to 4, does not apply if editable = false)
31276      */
31277     minChars : 4,
31278     /**
31279      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
31280      * delay (typeAheadDelay) if it matches a known value (defaults to false)
31281      */
31282     typeAhead: false,
31283     /**
31284      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
31285      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
31286      */
31287     queryDelay: 500,
31288     /**
31289      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
31290      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
31291      */
31292     pageSize: 0,
31293     /**
31294      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
31295      * when editable = true (defaults to false)
31296      */
31297     selectOnFocus:false,
31298     /**
31299      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
31300      */
31301     queryParam: 'query',
31302     /**
31303      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
31304      * when mode = 'remote' (defaults to 'Loading...')
31305      */
31306     loadingText: 'Loading...',
31307     /**
31308      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
31309      */
31310     resizable: false,
31311     /**
31312      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
31313      */
31314     handleHeight : 8,
31315     /**
31316      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
31317      * traditional select (defaults to true)
31318      */
31319     editable: true,
31320     /**
31321      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
31322      */
31323     allQuery: '',
31324     /**
31325      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
31326      */
31327     mode: 'remote',
31328     /**
31329      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
31330      * listWidth has a higher value)
31331      */
31332     minListWidth : 70,
31333     /**
31334      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
31335      * allow the user to set arbitrary text into the field (defaults to false)
31336      */
31337     forceSelection:false,
31338     /**
31339      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
31340      * if typeAhead = true (defaults to 250)
31341      */
31342     typeAheadDelay : 250,
31343     /**
31344      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
31345      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
31346      */
31347     valueNotFoundText : undefined,
31348     
31349     /**
31350      * @cfg {String} defaultValue The value displayed after loading the store.
31351      */
31352     defaultValue: '',
31353     
31354     /**
31355      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
31356      */
31357     blockFocus : false,
31358     
31359     /**
31360      * @cfg {Boolean} disableClear Disable showing of clear button.
31361      */
31362     disableClear : false,
31363     /**
31364      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
31365      */
31366     alwaysQuery : false,
31367     
31368     //private
31369     addicon : false,
31370     editicon: false,
31371     
31372     // element that contains real text value.. (when hidden is used..)
31373      
31374     // private
31375     onRender : function(ct, position){
31376         Roo.form.Field.prototype.onRender.call(this, ct, position);
31377         
31378         if(this.store){
31379             this.store.on('beforeload', this.onBeforeLoad, this);
31380             this.store.on('load', this.onLoad, this);
31381             this.store.on('loadexception', this.onLoadException, this);
31382             this.store.load({});
31383         }
31384         
31385         
31386         
31387     },
31388
31389     // private
31390     initEvents : function(){
31391         //Roo.form.ComboBox.superclass.initEvents.call(this);
31392  
31393     },
31394
31395     onDestroy : function(){
31396        
31397         if(this.store){
31398             this.store.un('beforeload', this.onBeforeLoad, this);
31399             this.store.un('load', this.onLoad, this);
31400             this.store.un('loadexception', this.onLoadException, this);
31401         }
31402         //Roo.form.ComboBox.superclass.onDestroy.call(this);
31403     },
31404
31405     // private
31406     fireKey : function(e){
31407         if(e.isNavKeyPress() && !this.list.isVisible()){
31408             this.fireEvent("specialkey", this, e);
31409         }
31410     },
31411
31412     // private
31413     onResize: function(w, h){
31414         
31415         return; 
31416     
31417         
31418     },
31419
31420     /**
31421      * Allow or prevent the user from directly editing the field text.  If false is passed,
31422      * the user will only be able to select from the items defined in the dropdown list.  This method
31423      * is the runtime equivalent of setting the 'editable' config option at config time.
31424      * @param {Boolean} value True to allow the user to directly edit the field text
31425      */
31426     setEditable : function(value){
31427          
31428     },
31429
31430     // private
31431     onBeforeLoad : function(){
31432         
31433         Roo.log("Select before load");
31434         return;
31435     
31436         this.innerList.update(this.loadingText ?
31437                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
31438         //this.restrictHeight();
31439         this.selectedIndex = -1;
31440     },
31441
31442     // private
31443     onLoad : function(){
31444
31445     
31446         var dom = this.el.dom;
31447         dom.innerHTML = '';
31448          var od = dom.ownerDocument;
31449          
31450         if (this.emptyText) {
31451             var op = od.createElement('option');
31452             op.setAttribute('value', '');
31453             op.innerHTML = String.format('{0}', this.emptyText);
31454             dom.appendChild(op);
31455         }
31456         if(this.store.getCount() > 0){
31457            
31458             var vf = this.valueField;
31459             var df = this.displayField;
31460             this.store.data.each(function(r) {
31461                 // which colmsn to use... testing - cdoe / title..
31462                 var op = od.createElement('option');
31463                 op.setAttribute('value', r.data[vf]);
31464                 op.innerHTML = String.format('{0}', r.data[df]);
31465                 dom.appendChild(op);
31466             });
31467             if (typeof(this.defaultValue != 'undefined')) {
31468                 this.setValue(this.defaultValue);
31469             }
31470             
31471              
31472         }else{
31473             //this.onEmptyResults();
31474         }
31475         //this.el.focus();
31476     },
31477     // private
31478     onLoadException : function()
31479     {
31480         dom.innerHTML = '';
31481             
31482         Roo.log("Select on load exception");
31483         return;
31484     
31485         this.collapse();
31486         Roo.log(this.store.reader.jsonData);
31487         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
31488             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
31489         }
31490         
31491         
31492     },
31493     // private
31494     onTypeAhead : function(){
31495          
31496     },
31497
31498     // private
31499     onSelect : function(record, index){
31500         Roo.log('on select?');
31501         return;
31502         if(this.fireEvent('beforeselect', this, record, index) !== false){
31503             this.setFromData(index > -1 ? record.data : false);
31504             this.collapse();
31505             this.fireEvent('select', this, record, index);
31506         }
31507     },
31508
31509     /**
31510      * Returns the currently selected field value or empty string if no value is set.
31511      * @return {String} value The selected value
31512      */
31513     getValue : function(){
31514         var dom = this.el.dom;
31515         this.value = dom.options[dom.selectedIndex].value;
31516         return this.value;
31517         
31518     },
31519
31520     /**
31521      * Clears any text/value currently set in the field
31522      */
31523     clearValue : function(){
31524         this.value = '';
31525         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
31526         
31527     },
31528
31529     /**
31530      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
31531      * will be displayed in the field.  If the value does not match the data value of an existing item,
31532      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
31533      * Otherwise the field will be blank (although the value will still be set).
31534      * @param {String} value The value to match
31535      */
31536     setValue : function(v){
31537         var d = this.el.dom;
31538         for (var i =0; i < d.options.length;i++) {
31539             if (v == d.options[i].value) {
31540                 d.selectedIndex = i;
31541                 this.value = v;
31542                 return;
31543             }
31544         }
31545         this.clearValue();
31546     },
31547     /**
31548      * @property {Object} the last set data for the element
31549      */
31550     
31551     lastData : false,
31552     /**
31553      * Sets the value of the field based on a object which is related to the record format for the store.
31554      * @param {Object} value the value to set as. or false on reset?
31555      */
31556     setFromData : function(o){
31557         Roo.log('setfrom data?');
31558          
31559         
31560         
31561     },
31562     // private
31563     reset : function(){
31564         this.clearValue();
31565     },
31566     // private
31567     findRecord : function(prop, value){
31568         
31569         return false;
31570     
31571         var record;
31572         if(this.store.getCount() > 0){
31573             this.store.each(function(r){
31574                 if(r.data[prop] == value){
31575                     record = r;
31576                     return false;
31577                 }
31578                 return true;
31579             });
31580         }
31581         return record;
31582     },
31583     
31584     getName: function()
31585     {
31586         // returns hidden if it's set..
31587         if (!this.rendered) {return ''};
31588         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
31589         
31590     },
31591      
31592
31593     
31594
31595     // private
31596     onEmptyResults : function(){
31597         Roo.log('empty results');
31598         //this.collapse();
31599     },
31600
31601     /**
31602      * Returns true if the dropdown list is expanded, else false.
31603      */
31604     isExpanded : function(){
31605         return false;
31606     },
31607
31608     /**
31609      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
31610      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
31611      * @param {String} value The data value of the item to select
31612      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
31613      * selected item if it is not currently in view (defaults to true)
31614      * @return {Boolean} True if the value matched an item in the list, else false
31615      */
31616     selectByValue : function(v, scrollIntoView){
31617         Roo.log('select By Value');
31618         return false;
31619     
31620         if(v !== undefined && v !== null){
31621             var r = this.findRecord(this.valueField || this.displayField, v);
31622             if(r){
31623                 this.select(this.store.indexOf(r), scrollIntoView);
31624                 return true;
31625             }
31626         }
31627         return false;
31628     },
31629
31630     /**
31631      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
31632      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
31633      * @param {Number} index The zero-based index of the list item to select
31634      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
31635      * selected item if it is not currently in view (defaults to true)
31636      */
31637     select : function(index, scrollIntoView){
31638         Roo.log('select ');
31639         return  ;
31640         
31641         this.selectedIndex = index;
31642         this.view.select(index);
31643         if(scrollIntoView !== false){
31644             var el = this.view.getNode(index);
31645             if(el){
31646                 this.innerList.scrollChildIntoView(el, false);
31647             }
31648         }
31649     },
31650
31651       
31652
31653     // private
31654     validateBlur : function(){
31655         
31656         return;
31657         
31658     },
31659
31660     // private
31661     initQuery : function(){
31662         this.doQuery(this.getRawValue());
31663     },
31664
31665     // private
31666     doForce : function(){
31667         if(this.el.dom.value.length > 0){
31668             this.el.dom.value =
31669                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
31670              
31671         }
31672     },
31673
31674     /**
31675      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
31676      * query allowing the query action to be canceled if needed.
31677      * @param {String} query The SQL query to execute
31678      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
31679      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
31680      * saved in the current store (defaults to false)
31681      */
31682     doQuery : function(q, forceAll){
31683         
31684         Roo.log('doQuery?');
31685         if(q === undefined || q === null){
31686             q = '';
31687         }
31688         var qe = {
31689             query: q,
31690             forceAll: forceAll,
31691             combo: this,
31692             cancel:false
31693         };
31694         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
31695             return false;
31696         }
31697         q = qe.query;
31698         forceAll = qe.forceAll;
31699         if(forceAll === true || (q.length >= this.minChars)){
31700             if(this.lastQuery != q || this.alwaysQuery){
31701                 this.lastQuery = q;
31702                 if(this.mode == 'local'){
31703                     this.selectedIndex = -1;
31704                     if(forceAll){
31705                         this.store.clearFilter();
31706                     }else{
31707                         this.store.filter(this.displayField, q);
31708                     }
31709                     this.onLoad();
31710                 }else{
31711                     this.store.baseParams[this.queryParam] = q;
31712                     this.store.load({
31713                         params: this.getParams(q)
31714                     });
31715                     this.expand();
31716                 }
31717             }else{
31718                 this.selectedIndex = -1;
31719                 this.onLoad();   
31720             }
31721         }
31722     },
31723
31724     // private
31725     getParams : function(q){
31726         var p = {};
31727         //p[this.queryParam] = q;
31728         if(this.pageSize){
31729             p.start = 0;
31730             p.limit = this.pageSize;
31731         }
31732         return p;
31733     },
31734
31735     /**
31736      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
31737      */
31738     collapse : function(){
31739         
31740     },
31741
31742     // private
31743     collapseIf : function(e){
31744         
31745     },
31746
31747     /**
31748      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
31749      */
31750     expand : function(){
31751         
31752     } ,
31753
31754     // private
31755      
31756
31757     /** 
31758     * @cfg {Boolean} grow 
31759     * @hide 
31760     */
31761     /** 
31762     * @cfg {Number} growMin 
31763     * @hide 
31764     */
31765     /** 
31766     * @cfg {Number} growMax 
31767     * @hide 
31768     */
31769     /**
31770      * @hide
31771      * @method autoSize
31772      */
31773     
31774     setWidth : function()
31775     {
31776         
31777     },
31778     getResizeEl : function(){
31779         return this.el;
31780     }
31781 });//<script type="text/javasscript">
31782  
31783
31784 /**
31785  * @class Roo.DDView
31786  * A DnD enabled version of Roo.View.
31787  * @param {Element/String} container The Element in which to create the View.
31788  * @param {String} tpl The template string used to create the markup for each element of the View
31789  * @param {Object} config The configuration properties. These include all the config options of
31790  * {@link Roo.View} plus some specific to this class.<br>
31791  * <p>
31792  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
31793  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
31794  * <p>
31795  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
31796 .x-view-drag-insert-above {
31797         border-top:1px dotted #3366cc;
31798 }
31799 .x-view-drag-insert-below {
31800         border-bottom:1px dotted #3366cc;
31801 }
31802 </code></pre>
31803  * 
31804  */
31805  
31806 Roo.DDView = function(container, tpl, config) {
31807     Roo.DDView.superclass.constructor.apply(this, arguments);
31808     this.getEl().setStyle("outline", "0px none");
31809     this.getEl().unselectable();
31810     if (this.dragGroup) {
31811                 this.setDraggable(this.dragGroup.split(","));
31812     }
31813     if (this.dropGroup) {
31814                 this.setDroppable(this.dropGroup.split(","));
31815     }
31816     if (this.deletable) {
31817         this.setDeletable();
31818     }
31819     this.isDirtyFlag = false;
31820         this.addEvents({
31821                 "drop" : true
31822         });
31823 };
31824
31825 Roo.extend(Roo.DDView, Roo.View, {
31826 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
31827 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
31828 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
31829 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
31830
31831         isFormField: true,
31832
31833         reset: Roo.emptyFn,
31834         
31835         clearInvalid: Roo.form.Field.prototype.clearInvalid,
31836
31837         validate: function() {
31838                 return true;
31839         },
31840         
31841         destroy: function() {
31842                 this.purgeListeners();
31843                 this.getEl.removeAllListeners();
31844                 this.getEl().remove();
31845                 if (this.dragZone) {
31846                         if (this.dragZone.destroy) {
31847                                 this.dragZone.destroy();
31848                         }
31849                 }
31850                 if (this.dropZone) {
31851                         if (this.dropZone.destroy) {
31852                                 this.dropZone.destroy();
31853                         }
31854                 }
31855         },
31856
31857 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
31858         getName: function() {
31859                 return this.name;
31860         },
31861
31862 /**     Loads the View from a JSON string representing the Records to put into the Store. */
31863         setValue: function(v) {
31864                 if (!this.store) {
31865                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
31866                 }
31867                 var data = {};
31868                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
31869                 this.store.proxy = new Roo.data.MemoryProxy(data);
31870                 this.store.load();
31871         },
31872
31873 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
31874         getValue: function() {
31875                 var result = '(';
31876                 this.store.each(function(rec) {
31877                         result += rec.id + ',';
31878                 });
31879                 return result.substr(0, result.length - 1) + ')';
31880         },
31881         
31882         getIds: function() {
31883                 var i = 0, result = new Array(this.store.getCount());
31884                 this.store.each(function(rec) {
31885                         result[i++] = rec.id;
31886                 });
31887                 return result;
31888         },
31889         
31890         isDirty: function() {
31891                 return this.isDirtyFlag;
31892         },
31893
31894 /**
31895  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
31896  *      whole Element becomes the target, and this causes the drop gesture to append.
31897  */
31898     getTargetFromEvent : function(e) {
31899                 var target = e.getTarget();
31900                 while ((target !== null) && (target.parentNode != this.el.dom)) {
31901                 target = target.parentNode;
31902                 }
31903                 if (!target) {
31904                         target = this.el.dom.lastChild || this.el.dom;
31905                 }
31906                 return target;
31907     },
31908
31909 /**
31910  *      Create the drag data which consists of an object which has the property "ddel" as
31911  *      the drag proxy element. 
31912  */
31913     getDragData : function(e) {
31914         var target = this.findItemFromChild(e.getTarget());
31915                 if(target) {
31916                         this.handleSelection(e);
31917                         var selNodes = this.getSelectedNodes();
31918             var dragData = {
31919                 source: this,
31920                 copy: this.copy || (this.allowCopy && e.ctrlKey),
31921                 nodes: selNodes,
31922                 records: []
31923                         };
31924                         var selectedIndices = this.getSelectedIndexes();
31925                         for (var i = 0; i < selectedIndices.length; i++) {
31926                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
31927                         }
31928                         if (selNodes.length == 1) {
31929                                 dragData.ddel = target.cloneNode(true); // the div element
31930                         } else {
31931                                 var div = document.createElement('div'); // create the multi element drag "ghost"
31932                                 div.className = 'multi-proxy';
31933                                 for (var i = 0, len = selNodes.length; i < len; i++) {
31934                                         div.appendChild(selNodes[i].cloneNode(true));
31935                                 }
31936                                 dragData.ddel = div;
31937                         }
31938             //console.log(dragData)
31939             //console.log(dragData.ddel.innerHTML)
31940                         return dragData;
31941                 }
31942         //console.log('nodragData')
31943                 return false;
31944     },
31945     
31946 /**     Specify to which ddGroup items in this DDView may be dragged. */
31947     setDraggable: function(ddGroup) {
31948         if (ddGroup instanceof Array) {
31949                 Roo.each(ddGroup, this.setDraggable, this);
31950                 return;
31951         }
31952         if (this.dragZone) {
31953                 this.dragZone.addToGroup(ddGroup);
31954         } else {
31955                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
31956                                 containerScroll: true,
31957                                 ddGroup: ddGroup 
31958
31959                         });
31960 //                      Draggability implies selection. DragZone's mousedown selects the element.
31961                         if (!this.multiSelect) { this.singleSelect = true; }
31962
31963 //                      Wire the DragZone's handlers up to methods in *this*
31964                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
31965                 }
31966     },
31967
31968 /**     Specify from which ddGroup this DDView accepts drops. */
31969     setDroppable: function(ddGroup) {
31970         if (ddGroup instanceof Array) {
31971                 Roo.each(ddGroup, this.setDroppable, this);
31972                 return;
31973         }
31974         if (this.dropZone) {
31975                 this.dropZone.addToGroup(ddGroup);
31976         } else {
31977                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
31978                                 containerScroll: true,
31979                                 ddGroup: ddGroup
31980                         });
31981
31982 //                      Wire the DropZone's handlers up to methods in *this*
31983                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
31984                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
31985                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
31986                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
31987                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
31988                 }
31989     },
31990
31991 /**     Decide whether to drop above or below a View node. */
31992     getDropPoint : function(e, n, dd){
31993         if (n == this.el.dom) { return "above"; }
31994                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
31995                 var c = t + (b - t) / 2;
31996                 var y = Roo.lib.Event.getPageY(e);
31997                 if(y <= c) {
31998                         return "above";
31999                 }else{
32000                         return "below";
32001                 }
32002     },
32003
32004     onNodeEnter : function(n, dd, e, data){
32005                 return false;
32006     },
32007     
32008     onNodeOver : function(n, dd, e, data){
32009                 var pt = this.getDropPoint(e, n, dd);
32010                 // set the insert point style on the target node
32011                 var dragElClass = this.dropNotAllowed;
32012                 if (pt) {
32013                         var targetElClass;
32014                         if (pt == "above"){
32015                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
32016                                 targetElClass = "x-view-drag-insert-above";
32017                         } else {
32018                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
32019                                 targetElClass = "x-view-drag-insert-below";
32020                         }
32021                         if (this.lastInsertClass != targetElClass){
32022                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
32023                                 this.lastInsertClass = targetElClass;
32024                         }
32025                 }
32026                 return dragElClass;
32027         },
32028
32029     onNodeOut : function(n, dd, e, data){
32030                 this.removeDropIndicators(n);
32031     },
32032
32033     onNodeDrop : function(n, dd, e, data){
32034         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
32035                 return false;
32036         }
32037         var pt = this.getDropPoint(e, n, dd);
32038                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
32039                 if (pt == "below") { insertAt++; }
32040                 for (var i = 0; i < data.records.length; i++) {
32041                         var r = data.records[i];
32042                         var dup = this.store.getById(r.id);
32043                         if (dup && (dd != this.dragZone)) {
32044                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
32045                         } else {
32046                                 if (data.copy) {
32047                                         this.store.insert(insertAt++, r.copy());
32048                                 } else {
32049                                         data.source.isDirtyFlag = true;
32050                                         r.store.remove(r);
32051                                         this.store.insert(insertAt++, r);
32052                                 }
32053                                 this.isDirtyFlag = true;
32054                         }
32055                 }
32056                 this.dragZone.cachedTarget = null;
32057                 return true;
32058     },
32059
32060     removeDropIndicators : function(n){
32061                 if(n){
32062                         Roo.fly(n).removeClass([
32063                                 "x-view-drag-insert-above",
32064                                 "x-view-drag-insert-below"]);
32065                         this.lastInsertClass = "_noclass";
32066                 }
32067     },
32068
32069 /**
32070  *      Utility method. Add a delete option to the DDView's context menu.
32071  *      @param {String} imageUrl The URL of the "delete" icon image.
32072  */
32073         setDeletable: function(imageUrl) {
32074                 if (!this.singleSelect && !this.multiSelect) {
32075                         this.singleSelect = true;
32076                 }
32077                 var c = this.getContextMenu();
32078                 this.contextMenu.on("itemclick", function(item) {
32079                         switch (item.id) {
32080                                 case "delete":
32081                                         this.remove(this.getSelectedIndexes());
32082                                         break;
32083                         }
32084                 }, this);
32085                 this.contextMenu.add({
32086                         icon: imageUrl,
32087                         id: "delete",
32088                         text: 'Delete'
32089                 });
32090         },
32091         
32092 /**     Return the context menu for this DDView. */
32093         getContextMenu: function() {
32094                 if (!this.contextMenu) {
32095 //                      Create the View's context menu
32096                         this.contextMenu = new Roo.menu.Menu({
32097                                 id: this.id + "-contextmenu"
32098                         });
32099                         this.el.on("contextmenu", this.showContextMenu, this);
32100                 }
32101                 return this.contextMenu;
32102         },
32103         
32104         disableContextMenu: function() {
32105                 if (this.contextMenu) {
32106                         this.el.un("contextmenu", this.showContextMenu, this);
32107                 }
32108         },
32109
32110         showContextMenu: function(e, item) {
32111         item = this.findItemFromChild(e.getTarget());
32112                 if (item) {
32113                         e.stopEvent();
32114                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
32115                         this.contextMenu.showAt(e.getXY());
32116             }
32117     },
32118
32119 /**
32120  *      Remove {@link Roo.data.Record}s at the specified indices.
32121  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
32122  */
32123     remove: function(selectedIndices) {
32124                 selectedIndices = [].concat(selectedIndices);
32125                 for (var i = 0; i < selectedIndices.length; i++) {
32126                         var rec = this.store.getAt(selectedIndices[i]);
32127                         this.store.remove(rec);
32128                 }
32129     },
32130
32131 /**
32132  *      Double click fires the event, but also, if this is draggable, and there is only one other
32133  *      related DropZone, it transfers the selected node.
32134  */
32135     onDblClick : function(e){
32136         var item = this.findItemFromChild(e.getTarget());
32137         if(item){
32138             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
32139                 return false;
32140             }
32141             if (this.dragGroup) {
32142                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
32143                     while (targets.indexOf(this.dropZone) > -1) {
32144                             targets.remove(this.dropZone);
32145                                 }
32146                     if (targets.length == 1) {
32147                                         this.dragZone.cachedTarget = null;
32148                         var el = Roo.get(targets[0].getEl());
32149                         var box = el.getBox(true);
32150                         targets[0].onNodeDrop(el.dom, {
32151                                 target: el.dom,
32152                                 xy: [box.x, box.y + box.height - 1]
32153                         }, null, this.getDragData(e));
32154                     }
32155                 }
32156         }
32157     },
32158     
32159     handleSelection: function(e) {
32160                 this.dragZone.cachedTarget = null;
32161         var item = this.findItemFromChild(e.getTarget());
32162         if (!item) {
32163                 this.clearSelections(true);
32164                 return;
32165         }
32166                 if (item && (this.multiSelect || this.singleSelect)){
32167                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
32168                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
32169                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
32170                                 this.unselect(item);
32171                         } else {
32172                                 this.select(item, this.multiSelect && e.ctrlKey);
32173                                 this.lastSelection = item;
32174                         }
32175                 }
32176     },
32177
32178     onItemClick : function(item, index, e){
32179                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
32180                         return false;
32181                 }
32182                 return true;
32183     },
32184
32185     unselect : function(nodeInfo, suppressEvent){
32186                 var node = this.getNode(nodeInfo);
32187                 if(node && this.isSelected(node)){
32188                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
32189                                 Roo.fly(node).removeClass(this.selectedClass);
32190                                 this.selections.remove(node);
32191                                 if(!suppressEvent){
32192                                         this.fireEvent("selectionchange", this, this.selections);
32193                                 }
32194                         }
32195                 }
32196     }
32197 });
32198 /*
32199  * Based on:
32200  * Ext JS Library 1.1.1
32201  * Copyright(c) 2006-2007, Ext JS, LLC.
32202  *
32203  * Originally Released Under LGPL - original licence link has changed is not relivant.
32204  *
32205  * Fork - LGPL
32206  * <script type="text/javascript">
32207  */
32208  
32209 /**
32210  * @class Roo.LayoutManager
32211  * @extends Roo.util.Observable
32212  * Base class for layout managers.
32213  */
32214 Roo.LayoutManager = function(container, config){
32215     Roo.LayoutManager.superclass.constructor.call(this);
32216     this.el = Roo.get(container);
32217     // ie scrollbar fix
32218     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
32219         document.body.scroll = "no";
32220     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
32221         this.el.position('relative');
32222     }
32223     this.id = this.el.id;
32224     this.el.addClass("x-layout-container");
32225     /** false to disable window resize monitoring @type Boolean */
32226     this.monitorWindowResize = true;
32227     this.regions = {};
32228     this.addEvents({
32229         /**
32230          * @event layout
32231          * Fires when a layout is performed. 
32232          * @param {Roo.LayoutManager} this
32233          */
32234         "layout" : true,
32235         /**
32236          * @event regionresized
32237          * Fires when the user resizes a region. 
32238          * @param {Roo.LayoutRegion} region The resized region
32239          * @param {Number} newSize The new size (width for east/west, height for north/south)
32240          */
32241         "regionresized" : true,
32242         /**
32243          * @event regioncollapsed
32244          * Fires when a region is collapsed. 
32245          * @param {Roo.LayoutRegion} region The collapsed region
32246          */
32247         "regioncollapsed" : true,
32248         /**
32249          * @event regionexpanded
32250          * Fires when a region is expanded.  
32251          * @param {Roo.LayoutRegion} region The expanded region
32252          */
32253         "regionexpanded" : true
32254     });
32255     this.updating = false;
32256     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
32257 };
32258
32259 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
32260     /**
32261      * Returns true if this layout is currently being updated
32262      * @return {Boolean}
32263      */
32264     isUpdating : function(){
32265         return this.updating; 
32266     },
32267     
32268     /**
32269      * Suspend the LayoutManager from doing auto-layouts while
32270      * making multiple add or remove calls
32271      */
32272     beginUpdate : function(){
32273         this.updating = true;    
32274     },
32275     
32276     /**
32277      * Restore auto-layouts and optionally disable the manager from performing a layout
32278      * @param {Boolean} noLayout true to disable a layout update 
32279      */
32280     endUpdate : function(noLayout){
32281         this.updating = false;
32282         if(!noLayout){
32283             this.layout();
32284         }    
32285     },
32286     
32287     layout: function(){
32288         
32289     },
32290     
32291     onRegionResized : function(region, newSize){
32292         this.fireEvent("regionresized", region, newSize);
32293         this.layout();
32294     },
32295     
32296     onRegionCollapsed : function(region){
32297         this.fireEvent("regioncollapsed", region);
32298     },
32299     
32300     onRegionExpanded : function(region){
32301         this.fireEvent("regionexpanded", region);
32302     },
32303         
32304     /**
32305      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
32306      * performs box-model adjustments.
32307      * @return {Object} The size as an object {width: (the width), height: (the height)}
32308      */
32309     getViewSize : function(){
32310         var size;
32311         if(this.el.dom != document.body){
32312             size = this.el.getSize();
32313         }else{
32314             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
32315         }
32316         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
32317         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
32318         return size;
32319     },
32320     
32321     /**
32322      * Returns the Element this layout is bound to.
32323      * @return {Roo.Element}
32324      */
32325     getEl : function(){
32326         return this.el;
32327     },
32328     
32329     /**
32330      * Returns the specified region.
32331      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
32332      * @return {Roo.LayoutRegion}
32333      */
32334     getRegion : function(target){
32335         return this.regions[target.toLowerCase()];
32336     },
32337     
32338     onWindowResize : function(){
32339         if(this.monitorWindowResize){
32340             this.layout();
32341         }
32342     }
32343 });/*
32344  * Based on:
32345  * Ext JS Library 1.1.1
32346  * Copyright(c) 2006-2007, Ext JS, LLC.
32347  *
32348  * Originally Released Under LGPL - original licence link has changed is not relivant.
32349  *
32350  * Fork - LGPL
32351  * <script type="text/javascript">
32352  */
32353 /**
32354  * @class Roo.BorderLayout
32355  * @extends Roo.LayoutManager
32356  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
32357  * please see: <br><br>
32358  * <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>
32359  * <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>
32360  * Example:
32361  <pre><code>
32362  var layout = new Roo.BorderLayout(document.body, {
32363     north: {
32364         initialSize: 25,
32365         titlebar: false
32366     },
32367     west: {
32368         split:true,
32369         initialSize: 200,
32370         minSize: 175,
32371         maxSize: 400,
32372         titlebar: true,
32373         collapsible: true
32374     },
32375     east: {
32376         split:true,
32377         initialSize: 202,
32378         minSize: 175,
32379         maxSize: 400,
32380         titlebar: true,
32381         collapsible: true
32382     },
32383     south: {
32384         split:true,
32385         initialSize: 100,
32386         minSize: 100,
32387         maxSize: 200,
32388         titlebar: true,
32389         collapsible: true
32390     },
32391     center: {
32392         titlebar: true,
32393         autoScroll:true,
32394         resizeTabs: true,
32395         minTabWidth: 50,
32396         preferredTabWidth: 150
32397     }
32398 });
32399
32400 // shorthand
32401 var CP = Roo.ContentPanel;
32402
32403 layout.beginUpdate();
32404 layout.add("north", new CP("north", "North"));
32405 layout.add("south", new CP("south", {title: "South", closable: true}));
32406 layout.add("west", new CP("west", {title: "West"}));
32407 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
32408 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
32409 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
32410 layout.getRegion("center").showPanel("center1");
32411 layout.endUpdate();
32412 </code></pre>
32413
32414 <b>The container the layout is rendered into can be either the body element or any other element.
32415 If it is not the body element, the container needs to either be an absolute positioned element,
32416 or you will need to add "position:relative" to the css of the container.  You will also need to specify
32417 the container size if it is not the body element.</b>
32418
32419 * @constructor
32420 * Create a new BorderLayout
32421 * @param {String/HTMLElement/Element} container The container this layout is bound to
32422 * @param {Object} config Configuration options
32423  */
32424 Roo.BorderLayout = function(container, config){
32425     config = config || {};
32426     Roo.BorderLayout.superclass.constructor.call(this, container, config);
32427     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
32428     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
32429         var target = this.factory.validRegions[i];
32430         if(config[target]){
32431             this.addRegion(target, config[target]);
32432         }
32433     }
32434 };
32435
32436 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
32437     /**
32438      * Creates and adds a new region if it doesn't already exist.
32439      * @param {String} target The target region key (north, south, east, west or center).
32440      * @param {Object} config The regions config object
32441      * @return {BorderLayoutRegion} The new region
32442      */
32443     addRegion : function(target, config){
32444         if(!this.regions[target]){
32445             var r = this.factory.create(target, this, config);
32446             this.bindRegion(target, r);
32447         }
32448         return this.regions[target];
32449     },
32450
32451     // private (kinda)
32452     bindRegion : function(name, r){
32453         this.regions[name] = r;
32454         r.on("visibilitychange", this.layout, this);
32455         r.on("paneladded", this.layout, this);
32456         r.on("panelremoved", this.layout, this);
32457         r.on("invalidated", this.layout, this);
32458         r.on("resized", this.onRegionResized, this);
32459         r.on("collapsed", this.onRegionCollapsed, this);
32460         r.on("expanded", this.onRegionExpanded, this);
32461     },
32462
32463     /**
32464      * Performs a layout update.
32465      */
32466     layout : function(){
32467         if(this.updating) return;
32468         var size = this.getViewSize();
32469         var w = size.width;
32470         var h = size.height;
32471         var centerW = w;
32472         var centerH = h;
32473         var centerY = 0;
32474         var centerX = 0;
32475         //var x = 0, y = 0;
32476
32477         var rs = this.regions;
32478         var north = rs["north"];
32479         var south = rs["south"]; 
32480         var west = rs["west"];
32481         var east = rs["east"];
32482         var center = rs["center"];
32483         //if(this.hideOnLayout){ // not supported anymore
32484             //c.el.setStyle("display", "none");
32485         //}
32486         if(north && north.isVisible()){
32487             var b = north.getBox();
32488             var m = north.getMargins();
32489             b.width = w - (m.left+m.right);
32490             b.x = m.left;
32491             b.y = m.top;
32492             centerY = b.height + b.y + m.bottom;
32493             centerH -= centerY;
32494             north.updateBox(this.safeBox(b));
32495         }
32496         if(south && south.isVisible()){
32497             var b = south.getBox();
32498             var m = south.getMargins();
32499             b.width = w - (m.left+m.right);
32500             b.x = m.left;
32501             var totalHeight = (b.height + m.top + m.bottom);
32502             b.y = h - totalHeight + m.top;
32503             centerH -= totalHeight;
32504             south.updateBox(this.safeBox(b));
32505         }
32506         if(west && west.isVisible()){
32507             var b = west.getBox();
32508             var m = west.getMargins();
32509             b.height = centerH - (m.top+m.bottom);
32510             b.x = m.left;
32511             b.y = centerY + m.top;
32512             var totalWidth = (b.width + m.left + m.right);
32513             centerX += totalWidth;
32514             centerW -= totalWidth;
32515             west.updateBox(this.safeBox(b));
32516         }
32517         if(east && east.isVisible()){
32518             var b = east.getBox();
32519             var m = east.getMargins();
32520             b.height = centerH - (m.top+m.bottom);
32521             var totalWidth = (b.width + m.left + m.right);
32522             b.x = w - totalWidth + m.left;
32523             b.y = centerY + m.top;
32524             centerW -= totalWidth;
32525             east.updateBox(this.safeBox(b));
32526         }
32527         if(center){
32528             var m = center.getMargins();
32529             var centerBox = {
32530                 x: centerX + m.left,
32531                 y: centerY + m.top,
32532                 width: centerW - (m.left+m.right),
32533                 height: centerH - (m.top+m.bottom)
32534             };
32535             //if(this.hideOnLayout){
32536                 //center.el.setStyle("display", "block");
32537             //}
32538             center.updateBox(this.safeBox(centerBox));
32539         }
32540         this.el.repaint();
32541         this.fireEvent("layout", this);
32542     },
32543
32544     // private
32545     safeBox : function(box){
32546         box.width = Math.max(0, box.width);
32547         box.height = Math.max(0, box.height);
32548         return box;
32549     },
32550
32551     /**
32552      * Adds a ContentPanel (or subclass) to this layout.
32553      * @param {String} target The target region key (north, south, east, west or center).
32554      * @param {Roo.ContentPanel} panel The panel to add
32555      * @return {Roo.ContentPanel} The added panel
32556      */
32557     add : function(target, panel){
32558          
32559         target = target.toLowerCase();
32560         return this.regions[target].add(panel);
32561     },
32562
32563     /**
32564      * Remove a ContentPanel (or subclass) to this layout.
32565      * @param {String} target The target region key (north, south, east, west or center).
32566      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
32567      * @return {Roo.ContentPanel} The removed panel
32568      */
32569     remove : function(target, panel){
32570         target = target.toLowerCase();
32571         return this.regions[target].remove(panel);
32572     },
32573
32574     /**
32575      * Searches all regions for a panel with the specified id
32576      * @param {String} panelId
32577      * @return {Roo.ContentPanel} The panel or null if it wasn't found
32578      */
32579     findPanel : function(panelId){
32580         var rs = this.regions;
32581         for(var target in rs){
32582             if(typeof rs[target] != "function"){
32583                 var p = rs[target].getPanel(panelId);
32584                 if(p){
32585                     return p;
32586                 }
32587             }
32588         }
32589         return null;
32590     },
32591
32592     /**
32593      * Searches all regions for a panel with the specified id and activates (shows) it.
32594      * @param {String/ContentPanel} panelId The panels id or the panel itself
32595      * @return {Roo.ContentPanel} The shown panel or null
32596      */
32597     showPanel : function(panelId) {
32598       var rs = this.regions;
32599       for(var target in rs){
32600          var r = rs[target];
32601          if(typeof r != "function"){
32602             if(r.hasPanel(panelId)){
32603                return r.showPanel(panelId);
32604             }
32605          }
32606       }
32607       return null;
32608    },
32609
32610    /**
32611      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
32612      * @param {Roo.state.Provider} provider (optional) An alternate state provider
32613      */
32614     restoreState : function(provider){
32615         if(!provider){
32616             provider = Roo.state.Manager;
32617         }
32618         var sm = new Roo.LayoutStateManager();
32619         sm.init(this, provider);
32620     },
32621
32622     /**
32623      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
32624      * object should contain properties for each region to add ContentPanels to, and each property's value should be
32625      * a valid ContentPanel config object.  Example:
32626      * <pre><code>
32627 // Create the main layout
32628 var layout = new Roo.BorderLayout('main-ct', {
32629     west: {
32630         split:true,
32631         minSize: 175,
32632         titlebar: true
32633     },
32634     center: {
32635         title:'Components'
32636     }
32637 }, 'main-ct');
32638
32639 // Create and add multiple ContentPanels at once via configs
32640 layout.batchAdd({
32641    west: {
32642        id: 'source-files',
32643        autoCreate:true,
32644        title:'Ext Source Files',
32645        autoScroll:true,
32646        fitToFrame:true
32647    },
32648    center : {
32649        el: cview,
32650        autoScroll:true,
32651        fitToFrame:true,
32652        toolbar: tb,
32653        resizeEl:'cbody'
32654    }
32655 });
32656 </code></pre>
32657      * @param {Object} regions An object containing ContentPanel configs by region name
32658      */
32659     batchAdd : function(regions){
32660         this.beginUpdate();
32661         for(var rname in regions){
32662             var lr = this.regions[rname];
32663             if(lr){
32664                 this.addTypedPanels(lr, regions[rname]);
32665             }
32666         }
32667         this.endUpdate();
32668     },
32669
32670     // private
32671     addTypedPanels : function(lr, ps){
32672         if(typeof ps == 'string'){
32673             lr.add(new Roo.ContentPanel(ps));
32674         }
32675         else if(ps instanceof Array){
32676             for(var i =0, len = ps.length; i < len; i++){
32677                 this.addTypedPanels(lr, ps[i]);
32678             }
32679         }
32680         else if(!ps.events){ // raw config?
32681             var el = ps.el;
32682             delete ps.el; // prevent conflict
32683             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
32684         }
32685         else {  // panel object assumed!
32686             lr.add(ps);
32687         }
32688     },
32689     /**
32690      * Adds a xtype elements to the layout.
32691      * <pre><code>
32692
32693 layout.addxtype({
32694        xtype : 'ContentPanel',
32695        region: 'west',
32696        items: [ .... ]
32697    }
32698 );
32699
32700 layout.addxtype({
32701         xtype : 'NestedLayoutPanel',
32702         region: 'west',
32703         layout: {
32704            center: { },
32705            west: { }   
32706         },
32707         items : [ ... list of content panels or nested layout panels.. ]
32708    }
32709 );
32710 </code></pre>
32711      * @param {Object} cfg Xtype definition of item to add.
32712      */
32713     addxtype : function(cfg)
32714     {
32715         // basically accepts a pannel...
32716         // can accept a layout region..!?!?
32717         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
32718         
32719         if (!cfg.xtype.match(/Panel$/)) {
32720             return false;
32721         }
32722         var ret = false;
32723         
32724         if (typeof(cfg.region) == 'undefined') {
32725             Roo.log("Failed to add Panel, region was not set");
32726             Roo.log(cfg);
32727             return false;
32728         }
32729         var region = cfg.region;
32730         delete cfg.region;
32731         
32732           
32733         var xitems = [];
32734         if (cfg.items) {
32735             xitems = cfg.items;
32736             delete cfg.items;
32737         }
32738         var nb = false;
32739         
32740         switch(cfg.xtype) 
32741         {
32742             case 'ContentPanel':  // ContentPanel (el, cfg)
32743             case 'ScrollPanel':  // ContentPanel (el, cfg)
32744             case 'ViewPanel': 
32745                 if(cfg.autoCreate) {
32746                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32747                 } else {
32748                     var el = this.el.createChild();
32749                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
32750                 }
32751                 
32752                 this.add(region, ret);
32753                 break;
32754             
32755             
32756             case 'TreePanel': // our new panel!
32757                 cfg.el = this.el.createChild();
32758                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32759                 this.add(region, ret);
32760                 break;
32761             
32762             case 'NestedLayoutPanel': 
32763                 // create a new Layout (which is  a Border Layout...
32764                 var el = this.el.createChild();
32765                 var clayout = cfg.layout;
32766                 delete cfg.layout;
32767                 clayout.items   = clayout.items  || [];
32768                 // replace this exitems with the clayout ones..
32769                 xitems = clayout.items;
32770                  
32771                 
32772                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
32773                     cfg.background = false;
32774                 }
32775                 var layout = new Roo.BorderLayout(el, clayout);
32776                 
32777                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
32778                 //console.log('adding nested layout panel '  + cfg.toSource());
32779                 this.add(region, ret);
32780                 nb = {}; /// find first...
32781                 break;
32782                 
32783             case 'GridPanel': 
32784             
32785                 // needs grid and region
32786                 
32787                 //var el = this.getRegion(region).el.createChild();
32788                 var el = this.el.createChild();
32789                 // create the grid first...
32790                 
32791                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
32792                 delete cfg.grid;
32793                 if (region == 'center' && this.active ) {
32794                     cfg.background = false;
32795                 }
32796                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
32797                 
32798                 this.add(region, ret);
32799                 if (cfg.background) {
32800                     ret.on('activate', function(gp) {
32801                         if (!gp.grid.rendered) {
32802                             gp.grid.render();
32803                         }
32804                     });
32805                 } else {
32806                     grid.render();
32807                 }
32808                 break;
32809            
32810            
32811            
32812                 
32813                 
32814                 
32815             default: 
32816                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
32817                 return null;
32818              // GridPanel (grid, cfg)
32819             
32820         }
32821         this.beginUpdate();
32822         // add children..
32823         var region = '';
32824         var abn = {};
32825         Roo.each(xitems, function(i)  {
32826             region = nb && i.region ? i.region : false;
32827             
32828             var add = ret.addxtype(i);
32829            
32830             if (region) {
32831                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
32832                 if (!i.background) {
32833                     abn[region] = nb[region] ;
32834                 }
32835             }
32836             
32837         });
32838         this.endUpdate();
32839
32840         // make the last non-background panel active..
32841         //if (nb) { Roo.log(abn); }
32842         if (nb) {
32843             
32844             for(var r in abn) {
32845                 region = this.getRegion(r);
32846                 if (region) {
32847                     // tried using nb[r], but it does not work..
32848                      
32849                     region.showPanel(abn[r]);
32850                    
32851                 }
32852             }
32853         }
32854         return ret;
32855         
32856     }
32857 });
32858
32859 /**
32860  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
32861  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
32862  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
32863  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
32864  * <pre><code>
32865 // shorthand
32866 var CP = Roo.ContentPanel;
32867
32868 var layout = Roo.BorderLayout.create({
32869     north: {
32870         initialSize: 25,
32871         titlebar: false,
32872         panels: [new CP("north", "North")]
32873     },
32874     west: {
32875         split:true,
32876         initialSize: 200,
32877         minSize: 175,
32878         maxSize: 400,
32879         titlebar: true,
32880         collapsible: true,
32881         panels: [new CP("west", {title: "West"})]
32882     },
32883     east: {
32884         split:true,
32885         initialSize: 202,
32886         minSize: 175,
32887         maxSize: 400,
32888         titlebar: true,
32889         collapsible: true,
32890         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
32891     },
32892     south: {
32893         split:true,
32894         initialSize: 100,
32895         minSize: 100,
32896         maxSize: 200,
32897         titlebar: true,
32898         collapsible: true,
32899         panels: [new CP("south", {title: "South", closable: true})]
32900     },
32901     center: {
32902         titlebar: true,
32903         autoScroll:true,
32904         resizeTabs: true,
32905         minTabWidth: 50,
32906         preferredTabWidth: 150,
32907         panels: [
32908             new CP("center1", {title: "Close Me", closable: true}),
32909             new CP("center2", {title: "Center Panel", closable: false})
32910         ]
32911     }
32912 }, document.body);
32913
32914 layout.getRegion("center").showPanel("center1");
32915 </code></pre>
32916  * @param config
32917  * @param targetEl
32918  */
32919 Roo.BorderLayout.create = function(config, targetEl){
32920     var layout = new Roo.BorderLayout(targetEl || document.body, config);
32921     layout.beginUpdate();
32922     var regions = Roo.BorderLayout.RegionFactory.validRegions;
32923     for(var j = 0, jlen = regions.length; j < jlen; j++){
32924         var lr = regions[j];
32925         if(layout.regions[lr] && config[lr].panels){
32926             var r = layout.regions[lr];
32927             var ps = config[lr].panels;
32928             layout.addTypedPanels(r, ps);
32929         }
32930     }
32931     layout.endUpdate();
32932     return layout;
32933 };
32934
32935 // private
32936 Roo.BorderLayout.RegionFactory = {
32937     // private
32938     validRegions : ["north","south","east","west","center"],
32939
32940     // private
32941     create : function(target, mgr, config){
32942         target = target.toLowerCase();
32943         if(config.lightweight || config.basic){
32944             return new Roo.BasicLayoutRegion(mgr, config, target);
32945         }
32946         switch(target){
32947             case "north":
32948                 return new Roo.NorthLayoutRegion(mgr, config);
32949             case "south":
32950                 return new Roo.SouthLayoutRegion(mgr, config);
32951             case "east":
32952                 return new Roo.EastLayoutRegion(mgr, config);
32953             case "west":
32954                 return new Roo.WestLayoutRegion(mgr, config);
32955             case "center":
32956                 return new Roo.CenterLayoutRegion(mgr, config);
32957         }
32958         throw 'Layout region "'+target+'" not supported.';
32959     }
32960 };/*
32961  * Based on:
32962  * Ext JS Library 1.1.1
32963  * Copyright(c) 2006-2007, Ext JS, LLC.
32964  *
32965  * Originally Released Under LGPL - original licence link has changed is not relivant.
32966  *
32967  * Fork - LGPL
32968  * <script type="text/javascript">
32969  */
32970  
32971 /**
32972  * @class Roo.BasicLayoutRegion
32973  * @extends Roo.util.Observable
32974  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
32975  * and does not have a titlebar, tabs or any other features. All it does is size and position 
32976  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
32977  */
32978 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
32979     this.mgr = mgr;
32980     this.position  = pos;
32981     this.events = {
32982         /**
32983          * @scope Roo.BasicLayoutRegion
32984          */
32985         
32986         /**
32987          * @event beforeremove
32988          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
32989          * @param {Roo.LayoutRegion} this
32990          * @param {Roo.ContentPanel} panel The panel
32991          * @param {Object} e The cancel event object
32992          */
32993         "beforeremove" : true,
32994         /**
32995          * @event invalidated
32996          * Fires when the layout for this region is changed.
32997          * @param {Roo.LayoutRegion} this
32998          */
32999         "invalidated" : true,
33000         /**
33001          * @event visibilitychange
33002          * Fires when this region is shown or hidden 
33003          * @param {Roo.LayoutRegion} this
33004          * @param {Boolean} visibility true or false
33005          */
33006         "visibilitychange" : true,
33007         /**
33008          * @event paneladded
33009          * Fires when a panel is added. 
33010          * @param {Roo.LayoutRegion} this
33011          * @param {Roo.ContentPanel} panel The panel
33012          */
33013         "paneladded" : true,
33014         /**
33015          * @event panelremoved
33016          * Fires when a panel is removed. 
33017          * @param {Roo.LayoutRegion} this
33018          * @param {Roo.ContentPanel} panel The panel
33019          */
33020         "panelremoved" : true,
33021         /**
33022          * @event collapsed
33023          * Fires when this region is collapsed.
33024          * @param {Roo.LayoutRegion} this
33025          */
33026         "collapsed" : true,
33027         /**
33028          * @event expanded
33029          * Fires when this region is expanded.
33030          * @param {Roo.LayoutRegion} this
33031          */
33032         "expanded" : true,
33033         /**
33034          * @event slideshow
33035          * Fires when this region is slid into view.
33036          * @param {Roo.LayoutRegion} this
33037          */
33038         "slideshow" : true,
33039         /**
33040          * @event slidehide
33041          * Fires when this region slides out of view. 
33042          * @param {Roo.LayoutRegion} this
33043          */
33044         "slidehide" : true,
33045         /**
33046          * @event panelactivated
33047          * Fires when a panel is activated. 
33048          * @param {Roo.LayoutRegion} this
33049          * @param {Roo.ContentPanel} panel The activated panel
33050          */
33051         "panelactivated" : true,
33052         /**
33053          * @event resized
33054          * Fires when the user resizes this region. 
33055          * @param {Roo.LayoutRegion} this
33056          * @param {Number} newSize The new size (width for east/west, height for north/south)
33057          */
33058         "resized" : true
33059     };
33060     /** A collection of panels in this region. @type Roo.util.MixedCollection */
33061     this.panels = new Roo.util.MixedCollection();
33062     this.panels.getKey = this.getPanelId.createDelegate(this);
33063     this.box = null;
33064     this.activePanel = null;
33065     // ensure listeners are added...
33066     
33067     if (config.listeners || config.events) {
33068         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
33069             listeners : config.listeners || {},
33070             events : config.events || {}
33071         });
33072     }
33073     
33074     if(skipConfig !== true){
33075         this.applyConfig(config);
33076     }
33077 };
33078
33079 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
33080     getPanelId : function(p){
33081         return p.getId();
33082     },
33083     
33084     applyConfig : function(config){
33085         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33086         this.config = config;
33087         
33088     },
33089     
33090     /**
33091      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
33092      * the width, for horizontal (north, south) the height.
33093      * @param {Number} newSize The new width or height
33094      */
33095     resizeTo : function(newSize){
33096         var el = this.el ? this.el :
33097                  (this.activePanel ? this.activePanel.getEl() : null);
33098         if(el){
33099             switch(this.position){
33100                 case "east":
33101                 case "west":
33102                     el.setWidth(newSize);
33103                     this.fireEvent("resized", this, newSize);
33104                 break;
33105                 case "north":
33106                 case "south":
33107                     el.setHeight(newSize);
33108                     this.fireEvent("resized", this, newSize);
33109                 break;                
33110             }
33111         }
33112     },
33113     
33114     getBox : function(){
33115         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
33116     },
33117     
33118     getMargins : function(){
33119         return this.margins;
33120     },
33121     
33122     updateBox : function(box){
33123         this.box = box;
33124         var el = this.activePanel.getEl();
33125         el.dom.style.left = box.x + "px";
33126         el.dom.style.top = box.y + "px";
33127         this.activePanel.setSize(box.width, box.height);
33128     },
33129     
33130     /**
33131      * Returns the container element for this region.
33132      * @return {Roo.Element}
33133      */
33134     getEl : function(){
33135         return this.activePanel;
33136     },
33137     
33138     /**
33139      * Returns true if this region is currently visible.
33140      * @return {Boolean}
33141      */
33142     isVisible : function(){
33143         return this.activePanel ? true : false;
33144     },
33145     
33146     setActivePanel : function(panel){
33147         panel = this.getPanel(panel);
33148         if(this.activePanel && this.activePanel != panel){
33149             this.activePanel.setActiveState(false);
33150             this.activePanel.getEl().setLeftTop(-10000,-10000);
33151         }
33152         this.activePanel = panel;
33153         panel.setActiveState(true);
33154         if(this.box){
33155             panel.setSize(this.box.width, this.box.height);
33156         }
33157         this.fireEvent("panelactivated", this, panel);
33158         this.fireEvent("invalidated");
33159     },
33160     
33161     /**
33162      * Show the specified panel.
33163      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
33164      * @return {Roo.ContentPanel} The shown panel or null
33165      */
33166     showPanel : function(panel){
33167         if(panel = this.getPanel(panel)){
33168             this.setActivePanel(panel);
33169         }
33170         return panel;
33171     },
33172     
33173     /**
33174      * Get the active panel for this region.
33175      * @return {Roo.ContentPanel} The active panel or null
33176      */
33177     getActivePanel : function(){
33178         return this.activePanel;
33179     },
33180     
33181     /**
33182      * Add the passed ContentPanel(s)
33183      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33184      * @return {Roo.ContentPanel} The panel added (if only one was added)
33185      */
33186     add : function(panel){
33187         if(arguments.length > 1){
33188             for(var i = 0, len = arguments.length; i < len; i++) {
33189                 this.add(arguments[i]);
33190             }
33191             return null;
33192         }
33193         if(this.hasPanel(panel)){
33194             this.showPanel(panel);
33195             return panel;
33196         }
33197         var el = panel.getEl();
33198         if(el.dom.parentNode != this.mgr.el.dom){
33199             this.mgr.el.dom.appendChild(el.dom);
33200         }
33201         if(panel.setRegion){
33202             panel.setRegion(this);
33203         }
33204         this.panels.add(panel);
33205         el.setStyle("position", "absolute");
33206         if(!panel.background){
33207             this.setActivePanel(panel);
33208             if(this.config.initialSize && this.panels.getCount()==1){
33209                 this.resizeTo(this.config.initialSize);
33210             }
33211         }
33212         this.fireEvent("paneladded", this, panel);
33213         return panel;
33214     },
33215     
33216     /**
33217      * Returns true if the panel is in this region.
33218      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33219      * @return {Boolean}
33220      */
33221     hasPanel : function(panel){
33222         if(typeof panel == "object"){ // must be panel obj
33223             panel = panel.getId();
33224         }
33225         return this.getPanel(panel) ? true : false;
33226     },
33227     
33228     /**
33229      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33230      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33231      * @param {Boolean} preservePanel Overrides the config preservePanel option
33232      * @return {Roo.ContentPanel} The panel that was removed
33233      */
33234     remove : function(panel, preservePanel){
33235         panel = this.getPanel(panel);
33236         if(!panel){
33237             return null;
33238         }
33239         var e = {};
33240         this.fireEvent("beforeremove", this, panel, e);
33241         if(e.cancel === true){
33242             return null;
33243         }
33244         var panelId = panel.getId();
33245         this.panels.removeKey(panelId);
33246         return panel;
33247     },
33248     
33249     /**
33250      * Returns the panel specified or null if it's not in this region.
33251      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33252      * @return {Roo.ContentPanel}
33253      */
33254     getPanel : function(id){
33255         if(typeof id == "object"){ // must be panel obj
33256             return id;
33257         }
33258         return this.panels.get(id);
33259     },
33260     
33261     /**
33262      * Returns this regions position (north/south/east/west/center).
33263      * @return {String} 
33264      */
33265     getPosition: function(){
33266         return this.position;    
33267     }
33268 });/*
33269  * Based on:
33270  * Ext JS Library 1.1.1
33271  * Copyright(c) 2006-2007, Ext JS, LLC.
33272  *
33273  * Originally Released Under LGPL - original licence link has changed is not relivant.
33274  *
33275  * Fork - LGPL
33276  * <script type="text/javascript">
33277  */
33278  
33279 /**
33280  * @class Roo.LayoutRegion
33281  * @extends Roo.BasicLayoutRegion
33282  * This class represents a region in a layout manager.
33283  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
33284  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
33285  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
33286  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
33287  * @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})
33288  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
33289  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
33290  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
33291  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
33292  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
33293  * @cfg {String}    title           The title for the region (overrides panel titles)
33294  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
33295  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
33296  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
33297  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
33298  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
33299  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
33300  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
33301  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
33302  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
33303  * @cfg {Boolean}   showPin         True to show a pin button
33304  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
33305  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
33306  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
33307  * @cfg {Number}    width           For East/West panels
33308  * @cfg {Number}    height          For North/South panels
33309  * @cfg {Boolean}   split           To show the splitter
33310  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
33311  */
33312 Roo.LayoutRegion = function(mgr, config, pos){
33313     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
33314     var dh = Roo.DomHelper;
33315     /** This region's container element 
33316     * @type Roo.Element */
33317     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
33318     /** This region's title element 
33319     * @type Roo.Element */
33320
33321     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
33322         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
33323         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
33324     ]}, true);
33325     this.titleEl.enableDisplayMode();
33326     /** This region's title text element 
33327     * @type HTMLElement */
33328     this.titleTextEl = this.titleEl.dom.firstChild;
33329     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
33330     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
33331     this.closeBtn.enableDisplayMode();
33332     this.closeBtn.on("click", this.closeClicked, this);
33333     this.closeBtn.hide();
33334
33335     this.createBody(config);
33336     this.visible = true;
33337     this.collapsed = false;
33338
33339     if(config.hideWhenEmpty){
33340         this.hide();
33341         this.on("paneladded", this.validateVisibility, this);
33342         this.on("panelremoved", this.validateVisibility, this);
33343     }
33344     this.applyConfig(config);
33345 };
33346
33347 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
33348
33349     createBody : function(){
33350         /** This region's body element 
33351         * @type Roo.Element */
33352         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
33353     },
33354
33355     applyConfig : function(c){
33356         if(c.collapsible && this.position != "center" && !this.collapsedEl){
33357             var dh = Roo.DomHelper;
33358             if(c.titlebar !== false){
33359                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
33360                 this.collapseBtn.on("click", this.collapse, this);
33361                 this.collapseBtn.enableDisplayMode();
33362
33363                 if(c.showPin === true || this.showPin){
33364                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
33365                     this.stickBtn.enableDisplayMode();
33366                     this.stickBtn.on("click", this.expand, this);
33367                     this.stickBtn.hide();
33368                 }
33369             }
33370             /** This region's collapsed element
33371             * @type Roo.Element */
33372             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
33373                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
33374             ]}, true);
33375             if(c.floatable !== false){
33376                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
33377                this.collapsedEl.on("click", this.collapseClick, this);
33378             }
33379
33380             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
33381                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
33382                    id: "message", unselectable: "on", style:{"float":"left"}});
33383                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
33384              }
33385             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
33386             this.expandBtn.on("click", this.expand, this);
33387         }
33388         if(this.collapseBtn){
33389             this.collapseBtn.setVisible(c.collapsible == true);
33390         }
33391         this.cmargins = c.cmargins || this.cmargins ||
33392                          (this.position == "west" || this.position == "east" ?
33393                              {top: 0, left: 2, right:2, bottom: 0} :
33394                              {top: 2, left: 0, right:0, bottom: 2});
33395         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33396         this.bottomTabs = c.tabPosition != "top";
33397         this.autoScroll = c.autoScroll || false;
33398         if(this.autoScroll){
33399             this.bodyEl.setStyle("overflow", "auto");
33400         }else{
33401             this.bodyEl.setStyle("overflow", "hidden");
33402         }
33403         //if(c.titlebar !== false){
33404             if((!c.titlebar && !c.title) || c.titlebar === false){
33405                 this.titleEl.hide();
33406             }else{
33407                 this.titleEl.show();
33408                 if(c.title){
33409                     this.titleTextEl.innerHTML = c.title;
33410                 }
33411             }
33412         //}
33413         this.duration = c.duration || .30;
33414         this.slideDuration = c.slideDuration || .45;
33415         this.config = c;
33416         if(c.collapsed){
33417             this.collapse(true);
33418         }
33419         if(c.hidden){
33420             this.hide();
33421         }
33422     },
33423     /**
33424      * Returns true if this region is currently visible.
33425      * @return {Boolean}
33426      */
33427     isVisible : function(){
33428         return this.visible;
33429     },
33430
33431     /**
33432      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
33433      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
33434      */
33435     setCollapsedTitle : function(title){
33436         title = title || "&#160;";
33437         if(this.collapsedTitleTextEl){
33438             this.collapsedTitleTextEl.innerHTML = title;
33439         }
33440     },
33441
33442     getBox : function(){
33443         var b;
33444         if(!this.collapsed){
33445             b = this.el.getBox(false, true);
33446         }else{
33447             b = this.collapsedEl.getBox(false, true);
33448         }
33449         return b;
33450     },
33451
33452     getMargins : function(){
33453         return this.collapsed ? this.cmargins : this.margins;
33454     },
33455
33456     highlight : function(){
33457         this.el.addClass("x-layout-panel-dragover");
33458     },
33459
33460     unhighlight : function(){
33461         this.el.removeClass("x-layout-panel-dragover");
33462     },
33463
33464     updateBox : function(box){
33465         this.box = box;
33466         if(!this.collapsed){
33467             this.el.dom.style.left = box.x + "px";
33468             this.el.dom.style.top = box.y + "px";
33469             this.updateBody(box.width, box.height);
33470         }else{
33471             this.collapsedEl.dom.style.left = box.x + "px";
33472             this.collapsedEl.dom.style.top = box.y + "px";
33473             this.collapsedEl.setSize(box.width, box.height);
33474         }
33475         if(this.tabs){
33476             this.tabs.autoSizeTabs();
33477         }
33478     },
33479
33480     updateBody : function(w, h){
33481         if(w !== null){
33482             this.el.setWidth(w);
33483             w -= this.el.getBorderWidth("rl");
33484             if(this.config.adjustments){
33485                 w += this.config.adjustments[0];
33486             }
33487         }
33488         if(h !== null){
33489             this.el.setHeight(h);
33490             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
33491             h -= this.el.getBorderWidth("tb");
33492             if(this.config.adjustments){
33493                 h += this.config.adjustments[1];
33494             }
33495             this.bodyEl.setHeight(h);
33496             if(this.tabs){
33497                 h = this.tabs.syncHeight(h);
33498             }
33499         }
33500         if(this.panelSize){
33501             w = w !== null ? w : this.panelSize.width;
33502             h = h !== null ? h : this.panelSize.height;
33503         }
33504         if(this.activePanel){
33505             var el = this.activePanel.getEl();
33506             w = w !== null ? w : el.getWidth();
33507             h = h !== null ? h : el.getHeight();
33508             this.panelSize = {width: w, height: h};
33509             this.activePanel.setSize(w, h);
33510         }
33511         if(Roo.isIE && this.tabs){
33512             this.tabs.el.repaint();
33513         }
33514     },
33515
33516     /**
33517      * Returns the container element for this region.
33518      * @return {Roo.Element}
33519      */
33520     getEl : function(){
33521         return this.el;
33522     },
33523
33524     /**
33525      * Hides this region.
33526      */
33527     hide : function(){
33528         if(!this.collapsed){
33529             this.el.dom.style.left = "-2000px";
33530             this.el.hide();
33531         }else{
33532             this.collapsedEl.dom.style.left = "-2000px";
33533             this.collapsedEl.hide();
33534         }
33535         this.visible = false;
33536         this.fireEvent("visibilitychange", this, false);
33537     },
33538
33539     /**
33540      * Shows this region if it was previously hidden.
33541      */
33542     show : function(){
33543         if(!this.collapsed){
33544             this.el.show();
33545         }else{
33546             this.collapsedEl.show();
33547         }
33548         this.visible = true;
33549         this.fireEvent("visibilitychange", this, true);
33550     },
33551
33552     closeClicked : function(){
33553         if(this.activePanel){
33554             this.remove(this.activePanel);
33555         }
33556     },
33557
33558     collapseClick : function(e){
33559         if(this.isSlid){
33560            e.stopPropagation();
33561            this.slideIn();
33562         }else{
33563            e.stopPropagation();
33564            this.slideOut();
33565         }
33566     },
33567
33568     /**
33569      * Collapses this region.
33570      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
33571      */
33572     collapse : function(skipAnim){
33573         if(this.collapsed) return;
33574         this.collapsed = true;
33575         if(this.split){
33576             this.split.el.hide();
33577         }
33578         if(this.config.animate && skipAnim !== true){
33579             this.fireEvent("invalidated", this);
33580             this.animateCollapse();
33581         }else{
33582             this.el.setLocation(-20000,-20000);
33583             this.el.hide();
33584             this.collapsedEl.show();
33585             this.fireEvent("collapsed", this);
33586             this.fireEvent("invalidated", this);
33587         }
33588     },
33589
33590     animateCollapse : function(){
33591         // overridden
33592     },
33593
33594     /**
33595      * Expands this region if it was previously collapsed.
33596      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
33597      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
33598      */
33599     expand : function(e, skipAnim){
33600         if(e) e.stopPropagation();
33601         if(!this.collapsed || this.el.hasActiveFx()) return;
33602         if(this.isSlid){
33603             this.afterSlideIn();
33604             skipAnim = true;
33605         }
33606         this.collapsed = false;
33607         if(this.config.animate && skipAnim !== true){
33608             this.animateExpand();
33609         }else{
33610             this.el.show();
33611             if(this.split){
33612                 this.split.el.show();
33613             }
33614             this.collapsedEl.setLocation(-2000,-2000);
33615             this.collapsedEl.hide();
33616             this.fireEvent("invalidated", this);
33617             this.fireEvent("expanded", this);
33618         }
33619     },
33620
33621     animateExpand : function(){
33622         // overridden
33623     },
33624
33625     initTabs : function()
33626     {
33627         this.bodyEl.setStyle("overflow", "hidden");
33628         var ts = new Roo.TabPanel(
33629                 this.bodyEl.dom,
33630                 {
33631                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
33632                     disableTooltips: this.config.disableTabTips,
33633                     toolbar : this.config.toolbar
33634                 }
33635         );
33636         if(this.config.hideTabs){
33637             ts.stripWrap.setDisplayed(false);
33638         }
33639         this.tabs = ts;
33640         ts.resizeTabs = this.config.resizeTabs === true;
33641         ts.minTabWidth = this.config.minTabWidth || 40;
33642         ts.maxTabWidth = this.config.maxTabWidth || 250;
33643         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
33644         ts.monitorResize = false;
33645         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
33646         ts.bodyEl.addClass('x-layout-tabs-body');
33647         this.panels.each(this.initPanelAsTab, this);
33648     },
33649
33650     initPanelAsTab : function(panel){
33651         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
33652                     this.config.closeOnTab && panel.isClosable());
33653         if(panel.tabTip !== undefined){
33654             ti.setTooltip(panel.tabTip);
33655         }
33656         ti.on("activate", function(){
33657               this.setActivePanel(panel);
33658         }, this);
33659         if(this.config.closeOnTab){
33660             ti.on("beforeclose", function(t, e){
33661                 e.cancel = true;
33662                 this.remove(panel);
33663             }, this);
33664         }
33665         return ti;
33666     },
33667
33668     updatePanelTitle : function(panel, title){
33669         if(this.activePanel == panel){
33670             this.updateTitle(title);
33671         }
33672         if(this.tabs){
33673             var ti = this.tabs.getTab(panel.getEl().id);
33674             ti.setText(title);
33675             if(panel.tabTip !== undefined){
33676                 ti.setTooltip(panel.tabTip);
33677             }
33678         }
33679     },
33680
33681     updateTitle : function(title){
33682         if(this.titleTextEl && !this.config.title){
33683             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
33684         }
33685     },
33686
33687     setActivePanel : function(panel){
33688         panel = this.getPanel(panel);
33689         if(this.activePanel && this.activePanel != panel){
33690             this.activePanel.setActiveState(false);
33691         }
33692         this.activePanel = panel;
33693         panel.setActiveState(true);
33694         if(this.panelSize){
33695             panel.setSize(this.panelSize.width, this.panelSize.height);
33696         }
33697         if(this.closeBtn){
33698             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
33699         }
33700         this.updateTitle(panel.getTitle());
33701         if(this.tabs){
33702             this.fireEvent("invalidated", this);
33703         }
33704         this.fireEvent("panelactivated", this, panel);
33705     },
33706
33707     /**
33708      * Shows the specified panel.
33709      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
33710      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
33711      */
33712     showPanel : function(panel){
33713         if(panel = this.getPanel(panel)){
33714             if(this.tabs){
33715                 var tab = this.tabs.getTab(panel.getEl().id);
33716                 if(tab.isHidden()){
33717                     this.tabs.unhideTab(tab.id);
33718                 }
33719                 tab.activate();
33720             }else{
33721                 this.setActivePanel(panel);
33722             }
33723         }
33724         return panel;
33725     },
33726
33727     /**
33728      * Get the active panel for this region.
33729      * @return {Roo.ContentPanel} The active panel or null
33730      */
33731     getActivePanel : function(){
33732         return this.activePanel;
33733     },
33734
33735     validateVisibility : function(){
33736         if(this.panels.getCount() < 1){
33737             this.updateTitle("&#160;");
33738             this.closeBtn.hide();
33739             this.hide();
33740         }else{
33741             if(!this.isVisible()){
33742                 this.show();
33743             }
33744         }
33745     },
33746
33747     /**
33748      * Adds the passed ContentPanel(s) to this region.
33749      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33750      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
33751      */
33752     add : function(panel){
33753         if(arguments.length > 1){
33754             for(var i = 0, len = arguments.length; i < len; i++) {
33755                 this.add(arguments[i]);
33756             }
33757             return null;
33758         }
33759         if(this.hasPanel(panel)){
33760             this.showPanel(panel);
33761             return panel;
33762         }
33763         panel.setRegion(this);
33764         this.panels.add(panel);
33765         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
33766             this.bodyEl.dom.appendChild(panel.getEl().dom);
33767             if(panel.background !== true){
33768                 this.setActivePanel(panel);
33769             }
33770             this.fireEvent("paneladded", this, panel);
33771             return panel;
33772         }
33773         if(!this.tabs){
33774             this.initTabs();
33775         }else{
33776             this.initPanelAsTab(panel);
33777         }
33778         if(panel.background !== true){
33779             this.tabs.activate(panel.getEl().id);
33780         }
33781         this.fireEvent("paneladded", this, panel);
33782         return panel;
33783     },
33784
33785     /**
33786      * Hides the tab for the specified panel.
33787      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33788      */
33789     hidePanel : function(panel){
33790         if(this.tabs && (panel = this.getPanel(panel))){
33791             this.tabs.hideTab(panel.getEl().id);
33792         }
33793     },
33794
33795     /**
33796      * Unhides the tab for a previously hidden panel.
33797      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33798      */
33799     unhidePanel : function(panel){
33800         if(this.tabs && (panel = this.getPanel(panel))){
33801             this.tabs.unhideTab(panel.getEl().id);
33802         }
33803     },
33804
33805     clearPanels : function(){
33806         while(this.panels.getCount() > 0){
33807              this.remove(this.panels.first());
33808         }
33809     },
33810
33811     /**
33812      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33813      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33814      * @param {Boolean} preservePanel Overrides the config preservePanel option
33815      * @return {Roo.ContentPanel} The panel that was removed
33816      */
33817     remove : function(panel, preservePanel){
33818         panel = this.getPanel(panel);
33819         if(!panel){
33820             return null;
33821         }
33822         var e = {};
33823         this.fireEvent("beforeremove", this, panel, e);
33824         if(e.cancel === true){
33825             return null;
33826         }
33827         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
33828         var panelId = panel.getId();
33829         this.panels.removeKey(panelId);
33830         if(preservePanel){
33831             document.body.appendChild(panel.getEl().dom);
33832         }
33833         if(this.tabs){
33834             this.tabs.removeTab(panel.getEl().id);
33835         }else if (!preservePanel){
33836             this.bodyEl.dom.removeChild(panel.getEl().dom);
33837         }
33838         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
33839             var p = this.panels.first();
33840             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
33841             tempEl.appendChild(p.getEl().dom);
33842             this.bodyEl.update("");
33843             this.bodyEl.dom.appendChild(p.getEl().dom);
33844             tempEl = null;
33845             this.updateTitle(p.getTitle());
33846             this.tabs = null;
33847             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
33848             this.setActivePanel(p);
33849         }
33850         panel.setRegion(null);
33851         if(this.activePanel == panel){
33852             this.activePanel = null;
33853         }
33854         if(this.config.autoDestroy !== false && preservePanel !== true){
33855             try{panel.destroy();}catch(e){}
33856         }
33857         this.fireEvent("panelremoved", this, panel);
33858         return panel;
33859     },
33860
33861     /**
33862      * Returns the TabPanel component used by this region
33863      * @return {Roo.TabPanel}
33864      */
33865     getTabs : function(){
33866         return this.tabs;
33867     },
33868
33869     createTool : function(parentEl, className){
33870         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
33871             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
33872         btn.addClassOnOver("x-layout-tools-button-over");
33873         return btn;
33874     }
33875 });/*
33876  * Based on:
33877  * Ext JS Library 1.1.1
33878  * Copyright(c) 2006-2007, Ext JS, LLC.
33879  *
33880  * Originally Released Under LGPL - original licence link has changed is not relivant.
33881  *
33882  * Fork - LGPL
33883  * <script type="text/javascript">
33884  */
33885  
33886
33887
33888 /**
33889  * @class Roo.SplitLayoutRegion
33890  * @extends Roo.LayoutRegion
33891  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
33892  */
33893 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
33894     this.cursor = cursor;
33895     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
33896 };
33897
33898 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
33899     splitTip : "Drag to resize.",
33900     collapsibleSplitTip : "Drag to resize. Double click to hide.",
33901     useSplitTips : false,
33902
33903     applyConfig : function(config){
33904         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
33905         if(config.split){
33906             if(!this.split){
33907                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
33908                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
33909                 /** The SplitBar for this region 
33910                 * @type Roo.SplitBar */
33911                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
33912                 this.split.on("moved", this.onSplitMove, this);
33913                 this.split.useShim = config.useShim === true;
33914                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
33915                 if(this.useSplitTips){
33916                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
33917                 }
33918                 if(config.collapsible){
33919                     this.split.el.on("dblclick", this.collapse,  this);
33920                 }
33921             }
33922             if(typeof config.minSize != "undefined"){
33923                 this.split.minSize = config.minSize;
33924             }
33925             if(typeof config.maxSize != "undefined"){
33926                 this.split.maxSize = config.maxSize;
33927             }
33928             if(config.hideWhenEmpty || config.hidden || config.collapsed){
33929                 this.hideSplitter();
33930             }
33931         }
33932     },
33933
33934     getHMaxSize : function(){
33935          var cmax = this.config.maxSize || 10000;
33936          var center = this.mgr.getRegion("center");
33937          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
33938     },
33939
33940     getVMaxSize : function(){
33941          var cmax = this.config.maxSize || 10000;
33942          var center = this.mgr.getRegion("center");
33943          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
33944     },
33945
33946     onSplitMove : function(split, newSize){
33947         this.fireEvent("resized", this, newSize);
33948     },
33949     
33950     /** 
33951      * Returns the {@link Roo.SplitBar} for this region.
33952      * @return {Roo.SplitBar}
33953      */
33954     getSplitBar : function(){
33955         return this.split;
33956     },
33957     
33958     hide : function(){
33959         this.hideSplitter();
33960         Roo.SplitLayoutRegion.superclass.hide.call(this);
33961     },
33962
33963     hideSplitter : function(){
33964         if(this.split){
33965             this.split.el.setLocation(-2000,-2000);
33966             this.split.el.hide();
33967         }
33968     },
33969
33970     show : function(){
33971         if(this.split){
33972             this.split.el.show();
33973         }
33974         Roo.SplitLayoutRegion.superclass.show.call(this);
33975     },
33976     
33977     beforeSlide: function(){
33978         if(Roo.isGecko){// firefox overflow auto bug workaround
33979             this.bodyEl.clip();
33980             if(this.tabs) this.tabs.bodyEl.clip();
33981             if(this.activePanel){
33982                 this.activePanel.getEl().clip();
33983                 
33984                 if(this.activePanel.beforeSlide){
33985                     this.activePanel.beforeSlide();
33986                 }
33987             }
33988         }
33989     },
33990     
33991     afterSlide : function(){
33992         if(Roo.isGecko){// firefox overflow auto bug workaround
33993             this.bodyEl.unclip();
33994             if(this.tabs) this.tabs.bodyEl.unclip();
33995             if(this.activePanel){
33996                 this.activePanel.getEl().unclip();
33997                 if(this.activePanel.afterSlide){
33998                     this.activePanel.afterSlide();
33999                 }
34000             }
34001         }
34002     },
34003
34004     initAutoHide : function(){
34005         if(this.autoHide !== false){
34006             if(!this.autoHideHd){
34007                 var st = new Roo.util.DelayedTask(this.slideIn, this);
34008                 this.autoHideHd = {
34009                     "mouseout": function(e){
34010                         if(!e.within(this.el, true)){
34011                             st.delay(500);
34012                         }
34013                     },
34014                     "mouseover" : function(e){
34015                         st.cancel();
34016                     },
34017                     scope : this
34018                 };
34019             }
34020             this.el.on(this.autoHideHd);
34021         }
34022     },
34023
34024     clearAutoHide : function(){
34025         if(this.autoHide !== false){
34026             this.el.un("mouseout", this.autoHideHd.mouseout);
34027             this.el.un("mouseover", this.autoHideHd.mouseover);
34028         }
34029     },
34030
34031     clearMonitor : function(){
34032         Roo.get(document).un("click", this.slideInIf, this);
34033     },
34034
34035     // these names are backwards but not changed for compat
34036     slideOut : function(){
34037         if(this.isSlid || this.el.hasActiveFx()){
34038             return;
34039         }
34040         this.isSlid = true;
34041         if(this.collapseBtn){
34042             this.collapseBtn.hide();
34043         }
34044         this.closeBtnState = this.closeBtn.getStyle('display');
34045         this.closeBtn.hide();
34046         if(this.stickBtn){
34047             this.stickBtn.show();
34048         }
34049         this.el.show();
34050         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
34051         this.beforeSlide();
34052         this.el.setStyle("z-index", 10001);
34053         this.el.slideIn(this.getSlideAnchor(), {
34054             callback: function(){
34055                 this.afterSlide();
34056                 this.initAutoHide();
34057                 Roo.get(document).on("click", this.slideInIf, this);
34058                 this.fireEvent("slideshow", this);
34059             },
34060             scope: this,
34061             block: true
34062         });
34063     },
34064
34065     afterSlideIn : function(){
34066         this.clearAutoHide();
34067         this.isSlid = false;
34068         this.clearMonitor();
34069         this.el.setStyle("z-index", "");
34070         if(this.collapseBtn){
34071             this.collapseBtn.show();
34072         }
34073         this.closeBtn.setStyle('display', this.closeBtnState);
34074         if(this.stickBtn){
34075             this.stickBtn.hide();
34076         }
34077         this.fireEvent("slidehide", this);
34078     },
34079
34080     slideIn : function(cb){
34081         if(!this.isSlid || this.el.hasActiveFx()){
34082             Roo.callback(cb);
34083             return;
34084         }
34085         this.isSlid = false;
34086         this.beforeSlide();
34087         this.el.slideOut(this.getSlideAnchor(), {
34088             callback: function(){
34089                 this.el.setLeftTop(-10000, -10000);
34090                 this.afterSlide();
34091                 this.afterSlideIn();
34092                 Roo.callback(cb);
34093             },
34094             scope: this,
34095             block: true
34096         });
34097     },
34098     
34099     slideInIf : function(e){
34100         if(!e.within(this.el)){
34101             this.slideIn();
34102         }
34103     },
34104
34105     animateCollapse : function(){
34106         this.beforeSlide();
34107         this.el.setStyle("z-index", 20000);
34108         var anchor = this.getSlideAnchor();
34109         this.el.slideOut(anchor, {
34110             callback : function(){
34111                 this.el.setStyle("z-index", "");
34112                 this.collapsedEl.slideIn(anchor, {duration:.3});
34113                 this.afterSlide();
34114                 this.el.setLocation(-10000,-10000);
34115                 this.el.hide();
34116                 this.fireEvent("collapsed", this);
34117             },
34118             scope: this,
34119             block: true
34120         });
34121     },
34122
34123     animateExpand : function(){
34124         this.beforeSlide();
34125         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
34126         this.el.setStyle("z-index", 20000);
34127         this.collapsedEl.hide({
34128             duration:.1
34129         });
34130         this.el.slideIn(this.getSlideAnchor(), {
34131             callback : function(){
34132                 this.el.setStyle("z-index", "");
34133                 this.afterSlide();
34134                 if(this.split){
34135                     this.split.el.show();
34136                 }
34137                 this.fireEvent("invalidated", this);
34138                 this.fireEvent("expanded", this);
34139             },
34140             scope: this,
34141             block: true
34142         });
34143     },
34144
34145     anchors : {
34146         "west" : "left",
34147         "east" : "right",
34148         "north" : "top",
34149         "south" : "bottom"
34150     },
34151
34152     sanchors : {
34153         "west" : "l",
34154         "east" : "r",
34155         "north" : "t",
34156         "south" : "b"
34157     },
34158
34159     canchors : {
34160         "west" : "tl-tr",
34161         "east" : "tr-tl",
34162         "north" : "tl-bl",
34163         "south" : "bl-tl"
34164     },
34165
34166     getAnchor : function(){
34167         return this.anchors[this.position];
34168     },
34169
34170     getCollapseAnchor : function(){
34171         return this.canchors[this.position];
34172     },
34173
34174     getSlideAnchor : function(){
34175         return this.sanchors[this.position];
34176     },
34177
34178     getAlignAdj : function(){
34179         var cm = this.cmargins;
34180         switch(this.position){
34181             case "west":
34182                 return [0, 0];
34183             break;
34184             case "east":
34185                 return [0, 0];
34186             break;
34187             case "north":
34188                 return [0, 0];
34189             break;
34190             case "south":
34191                 return [0, 0];
34192             break;
34193         }
34194     },
34195
34196     getExpandAdj : function(){
34197         var c = this.collapsedEl, cm = this.cmargins;
34198         switch(this.position){
34199             case "west":
34200                 return [-(cm.right+c.getWidth()+cm.left), 0];
34201             break;
34202             case "east":
34203                 return [cm.right+c.getWidth()+cm.left, 0];
34204             break;
34205             case "north":
34206                 return [0, -(cm.top+cm.bottom+c.getHeight())];
34207             break;
34208             case "south":
34209                 return [0, cm.top+cm.bottom+c.getHeight()];
34210             break;
34211         }
34212     }
34213 });/*
34214  * Based on:
34215  * Ext JS Library 1.1.1
34216  * Copyright(c) 2006-2007, Ext JS, LLC.
34217  *
34218  * Originally Released Under LGPL - original licence link has changed is not relivant.
34219  *
34220  * Fork - LGPL
34221  * <script type="text/javascript">
34222  */
34223 /*
34224  * These classes are private internal classes
34225  */
34226 Roo.CenterLayoutRegion = function(mgr, config){
34227     Roo.LayoutRegion.call(this, mgr, config, "center");
34228     this.visible = true;
34229     this.minWidth = config.minWidth || 20;
34230     this.minHeight = config.minHeight || 20;
34231 };
34232
34233 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
34234     hide : function(){
34235         // center panel can't be hidden
34236     },
34237     
34238     show : function(){
34239         // center panel can't be hidden
34240     },
34241     
34242     getMinWidth: function(){
34243         return this.minWidth;
34244     },
34245     
34246     getMinHeight: function(){
34247         return this.minHeight;
34248     }
34249 });
34250
34251
34252 Roo.NorthLayoutRegion = function(mgr, config){
34253     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
34254     if(this.split){
34255         this.split.placement = Roo.SplitBar.TOP;
34256         this.split.orientation = Roo.SplitBar.VERTICAL;
34257         this.split.el.addClass("x-layout-split-v");
34258     }
34259     var size = config.initialSize || config.height;
34260     if(typeof size != "undefined"){
34261         this.el.setHeight(size);
34262     }
34263 };
34264 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
34265     orientation: Roo.SplitBar.VERTICAL,
34266     getBox : function(){
34267         if(this.collapsed){
34268             return this.collapsedEl.getBox();
34269         }
34270         var box = this.el.getBox();
34271         if(this.split){
34272             box.height += this.split.el.getHeight();
34273         }
34274         return box;
34275     },
34276     
34277     updateBox : function(box){
34278         if(this.split && !this.collapsed){
34279             box.height -= this.split.el.getHeight();
34280             this.split.el.setLeft(box.x);
34281             this.split.el.setTop(box.y+box.height);
34282             this.split.el.setWidth(box.width);
34283         }
34284         if(this.collapsed){
34285             this.updateBody(box.width, null);
34286         }
34287         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34288     }
34289 });
34290
34291 Roo.SouthLayoutRegion = function(mgr, config){
34292     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
34293     if(this.split){
34294         this.split.placement = Roo.SplitBar.BOTTOM;
34295         this.split.orientation = Roo.SplitBar.VERTICAL;
34296         this.split.el.addClass("x-layout-split-v");
34297     }
34298     var size = config.initialSize || config.height;
34299     if(typeof size != "undefined"){
34300         this.el.setHeight(size);
34301     }
34302 };
34303 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
34304     orientation: Roo.SplitBar.VERTICAL,
34305     getBox : function(){
34306         if(this.collapsed){
34307             return this.collapsedEl.getBox();
34308         }
34309         var box = this.el.getBox();
34310         if(this.split){
34311             var sh = this.split.el.getHeight();
34312             box.height += sh;
34313             box.y -= sh;
34314         }
34315         return box;
34316     },
34317     
34318     updateBox : function(box){
34319         if(this.split && !this.collapsed){
34320             var sh = this.split.el.getHeight();
34321             box.height -= sh;
34322             box.y += sh;
34323             this.split.el.setLeft(box.x);
34324             this.split.el.setTop(box.y-sh);
34325             this.split.el.setWidth(box.width);
34326         }
34327         if(this.collapsed){
34328             this.updateBody(box.width, null);
34329         }
34330         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34331     }
34332 });
34333
34334 Roo.EastLayoutRegion = function(mgr, config){
34335     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
34336     if(this.split){
34337         this.split.placement = Roo.SplitBar.RIGHT;
34338         this.split.orientation = Roo.SplitBar.HORIZONTAL;
34339         this.split.el.addClass("x-layout-split-h");
34340     }
34341     var size = config.initialSize || config.width;
34342     if(typeof size != "undefined"){
34343         this.el.setWidth(size);
34344     }
34345 };
34346 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
34347     orientation: Roo.SplitBar.HORIZONTAL,
34348     getBox : function(){
34349         if(this.collapsed){
34350             return this.collapsedEl.getBox();
34351         }
34352         var box = this.el.getBox();
34353         if(this.split){
34354             var sw = this.split.el.getWidth();
34355             box.width += sw;
34356             box.x -= sw;
34357         }
34358         return box;
34359     },
34360
34361     updateBox : function(box){
34362         if(this.split && !this.collapsed){
34363             var sw = this.split.el.getWidth();
34364             box.width -= sw;
34365             this.split.el.setLeft(box.x);
34366             this.split.el.setTop(box.y);
34367             this.split.el.setHeight(box.height);
34368             box.x += sw;
34369         }
34370         if(this.collapsed){
34371             this.updateBody(null, box.height);
34372         }
34373         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34374     }
34375 });
34376
34377 Roo.WestLayoutRegion = function(mgr, config){
34378     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
34379     if(this.split){
34380         this.split.placement = Roo.SplitBar.LEFT;
34381         this.split.orientation = Roo.SplitBar.HORIZONTAL;
34382         this.split.el.addClass("x-layout-split-h");
34383     }
34384     var size = config.initialSize || config.width;
34385     if(typeof size != "undefined"){
34386         this.el.setWidth(size);
34387     }
34388 };
34389 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
34390     orientation: Roo.SplitBar.HORIZONTAL,
34391     getBox : function(){
34392         if(this.collapsed){
34393             return this.collapsedEl.getBox();
34394         }
34395         var box = this.el.getBox();
34396         if(this.split){
34397             box.width += this.split.el.getWidth();
34398         }
34399         return box;
34400     },
34401     
34402     updateBox : function(box){
34403         if(this.split && !this.collapsed){
34404             var sw = this.split.el.getWidth();
34405             box.width -= sw;
34406             this.split.el.setLeft(box.x+box.width);
34407             this.split.el.setTop(box.y);
34408             this.split.el.setHeight(box.height);
34409         }
34410         if(this.collapsed){
34411             this.updateBody(null, box.height);
34412         }
34413         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34414     }
34415 });
34416 /*
34417  * Based on:
34418  * Ext JS Library 1.1.1
34419  * Copyright(c) 2006-2007, Ext JS, LLC.
34420  *
34421  * Originally Released Under LGPL - original licence link has changed is not relivant.
34422  *
34423  * Fork - LGPL
34424  * <script type="text/javascript">
34425  */
34426  
34427  
34428 /*
34429  * Private internal class for reading and applying state
34430  */
34431 Roo.LayoutStateManager = function(layout){
34432      // default empty state
34433      this.state = {
34434         north: {},
34435         south: {},
34436         east: {},
34437         west: {}       
34438     };
34439 };
34440
34441 Roo.LayoutStateManager.prototype = {
34442     init : function(layout, provider){
34443         this.provider = provider;
34444         var state = provider.get(layout.id+"-layout-state");
34445         if(state){
34446             var wasUpdating = layout.isUpdating();
34447             if(!wasUpdating){
34448                 layout.beginUpdate();
34449             }
34450             for(var key in state){
34451                 if(typeof state[key] != "function"){
34452                     var rstate = state[key];
34453                     var r = layout.getRegion(key);
34454                     if(r && rstate){
34455                         if(rstate.size){
34456                             r.resizeTo(rstate.size);
34457                         }
34458                         if(rstate.collapsed == true){
34459                             r.collapse(true);
34460                         }else{
34461                             r.expand(null, true);
34462                         }
34463                     }
34464                 }
34465             }
34466             if(!wasUpdating){
34467                 layout.endUpdate();
34468             }
34469             this.state = state; 
34470         }
34471         this.layout = layout;
34472         layout.on("regionresized", this.onRegionResized, this);
34473         layout.on("regioncollapsed", this.onRegionCollapsed, this);
34474         layout.on("regionexpanded", this.onRegionExpanded, this);
34475     },
34476     
34477     storeState : function(){
34478         this.provider.set(this.layout.id+"-layout-state", this.state);
34479     },
34480     
34481     onRegionResized : function(region, newSize){
34482         this.state[region.getPosition()].size = newSize;
34483         this.storeState();
34484     },
34485     
34486     onRegionCollapsed : function(region){
34487         this.state[region.getPosition()].collapsed = true;
34488         this.storeState();
34489     },
34490     
34491     onRegionExpanded : function(region){
34492         this.state[region.getPosition()].collapsed = false;
34493         this.storeState();
34494     }
34495 };/*
34496  * Based on:
34497  * Ext JS Library 1.1.1
34498  * Copyright(c) 2006-2007, Ext JS, LLC.
34499  *
34500  * Originally Released Under LGPL - original licence link has changed is not relivant.
34501  *
34502  * Fork - LGPL
34503  * <script type="text/javascript">
34504  */
34505 /**
34506  * @class Roo.ContentPanel
34507  * @extends Roo.util.Observable
34508  * A basic ContentPanel element.
34509  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
34510  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
34511  * @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
34512  * @cfg {Boolean}   closable      True if the panel can be closed/removed
34513  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
34514  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
34515  * @cfg {Toolbar}   toolbar       A toolbar for this panel
34516  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
34517  * @cfg {String} title          The title for this panel
34518  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
34519  * @cfg {String} url            Calls {@link #setUrl} with this value
34520  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
34521  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
34522  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
34523  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
34524
34525  * @constructor
34526  * Create a new ContentPanel.
34527  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
34528  * @param {String/Object} config A string to set only the title or a config object
34529  * @param {String} content (optional) Set the HTML content for this panel
34530  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
34531  */
34532 Roo.ContentPanel = function(el, config, content){
34533     
34534      
34535     /*
34536     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
34537         config = el;
34538         el = Roo.id();
34539     }
34540     if (config && config.parentLayout) { 
34541         el = config.parentLayout.el.createChild(); 
34542     }
34543     */
34544     if(el.autoCreate){ // xtype is available if this is called from factory
34545         config = el;
34546         el = Roo.id();
34547     }
34548     this.el = Roo.get(el);
34549     if(!this.el && config && config.autoCreate){
34550         if(typeof config.autoCreate == "object"){
34551             if(!config.autoCreate.id){
34552                 config.autoCreate.id = config.id||el;
34553             }
34554             this.el = Roo.DomHelper.append(document.body,
34555                         config.autoCreate, true);
34556         }else{
34557             this.el = Roo.DomHelper.append(document.body,
34558                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
34559         }
34560     }
34561     this.closable = false;
34562     this.loaded = false;
34563     this.active = false;
34564     if(typeof config == "string"){
34565         this.title = config;
34566     }else{
34567         Roo.apply(this, config);
34568     }
34569     
34570     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
34571         this.wrapEl = this.el.wrap();
34572         this.toolbar.container = this.el.insertSibling(false, 'before');
34573         this.toolbar = new Roo.Toolbar(this.toolbar);
34574     }
34575     
34576     // xtype created footer. - not sure if will work as we normally have to render first..
34577     if (this.footer && !this.footer.el && this.footer.xtype) {
34578         if (!this.wrapEl) {
34579             this.wrapEl = this.el.wrap();
34580         }
34581     
34582         this.footer.container = this.wrapEl.createChild();
34583          
34584         this.footer = Roo.factory(this.footer, Roo);
34585         
34586     }
34587     
34588     if(this.resizeEl){
34589         this.resizeEl = Roo.get(this.resizeEl, true);
34590     }else{
34591         this.resizeEl = this.el;
34592     }
34593     // handle view.xtype
34594     
34595  
34596     
34597     
34598     this.addEvents({
34599         /**
34600          * @event activate
34601          * Fires when this panel is activated. 
34602          * @param {Roo.ContentPanel} this
34603          */
34604         "activate" : true,
34605         /**
34606          * @event deactivate
34607          * Fires when this panel is activated. 
34608          * @param {Roo.ContentPanel} this
34609          */
34610         "deactivate" : true,
34611
34612         /**
34613          * @event resize
34614          * Fires when this panel is resized if fitToFrame is true.
34615          * @param {Roo.ContentPanel} this
34616          * @param {Number} width The width after any component adjustments
34617          * @param {Number} height The height after any component adjustments
34618          */
34619         "resize" : true,
34620         
34621          /**
34622          * @event render
34623          * Fires when this tab is created
34624          * @param {Roo.ContentPanel} this
34625          */
34626         "render" : true
34627         
34628         
34629         
34630     });
34631     
34632
34633     
34634     
34635     if(this.autoScroll){
34636         this.resizeEl.setStyle("overflow", "auto");
34637     } else {
34638         // fix randome scrolling
34639         this.el.on('scroll', function() {
34640             Roo.log('fix random scolling');
34641             this.scrollTo('top',0); 
34642         });
34643     }
34644     content = content || this.content;
34645     if(content){
34646         this.setContent(content);
34647     }
34648     if(config && config.url){
34649         this.setUrl(this.url, this.params, this.loadOnce);
34650     }
34651     
34652     
34653     
34654     Roo.ContentPanel.superclass.constructor.call(this);
34655     
34656     if (this.view && typeof(this.view.xtype) != 'undefined') {
34657         this.view.el = this.el.appendChild(document.createElement("div"));
34658         this.view = Roo.factory(this.view); 
34659         this.view.render  &&  this.view.render(false, '');  
34660     }
34661     
34662     
34663     this.fireEvent('render', this);
34664 };
34665
34666 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
34667     tabTip:'',
34668     setRegion : function(region){
34669         this.region = region;
34670         if(region){
34671            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
34672         }else{
34673            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
34674         } 
34675     },
34676     
34677     /**
34678      * Returns the toolbar for this Panel if one was configured. 
34679      * @return {Roo.Toolbar} 
34680      */
34681     getToolbar : function(){
34682         return this.toolbar;
34683     },
34684     
34685     setActiveState : function(active){
34686         this.active = active;
34687         if(!active){
34688             this.fireEvent("deactivate", this);
34689         }else{
34690             this.fireEvent("activate", this);
34691         }
34692     },
34693     /**
34694      * Updates this panel's element
34695      * @param {String} content The new content
34696      * @param {Boolean} loadScripts (optional) true to look for and process scripts
34697     */
34698     setContent : function(content, loadScripts){
34699         this.el.update(content, loadScripts);
34700     },
34701
34702     ignoreResize : function(w, h){
34703         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
34704             return true;
34705         }else{
34706             this.lastSize = {width: w, height: h};
34707             return false;
34708         }
34709     },
34710     /**
34711      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
34712      * @return {Roo.UpdateManager} The UpdateManager
34713      */
34714     getUpdateManager : function(){
34715         return this.el.getUpdateManager();
34716     },
34717      /**
34718      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
34719      * @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:
34720 <pre><code>
34721 panel.load({
34722     url: "your-url.php",
34723     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
34724     callback: yourFunction,
34725     scope: yourObject, //(optional scope)
34726     discardUrl: false,
34727     nocache: false,
34728     text: "Loading...",
34729     timeout: 30,
34730     scripts: false
34731 });
34732 </code></pre>
34733      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
34734      * 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.
34735      * @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}
34736      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
34737      * @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.
34738      * @return {Roo.ContentPanel} this
34739      */
34740     load : function(){
34741         var um = this.el.getUpdateManager();
34742         um.update.apply(um, arguments);
34743         return this;
34744     },
34745
34746
34747     /**
34748      * 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.
34749      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
34750      * @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)
34751      * @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)
34752      * @return {Roo.UpdateManager} The UpdateManager
34753      */
34754     setUrl : function(url, params, loadOnce){
34755         if(this.refreshDelegate){
34756             this.removeListener("activate", this.refreshDelegate);
34757         }
34758         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
34759         this.on("activate", this.refreshDelegate);
34760         return this.el.getUpdateManager();
34761     },
34762     
34763     _handleRefresh : function(url, params, loadOnce){
34764         if(!loadOnce || !this.loaded){
34765             var updater = this.el.getUpdateManager();
34766             updater.update(url, params, this._setLoaded.createDelegate(this));
34767         }
34768     },
34769     
34770     _setLoaded : function(){
34771         this.loaded = true;
34772     }, 
34773     
34774     /**
34775      * Returns this panel's id
34776      * @return {String} 
34777      */
34778     getId : function(){
34779         return this.el.id;
34780     },
34781     
34782     /** 
34783      * Returns this panel's element - used by regiosn to add.
34784      * @return {Roo.Element} 
34785      */
34786     getEl : function(){
34787         return this.wrapEl || this.el;
34788     },
34789     
34790     adjustForComponents : function(width, height)
34791     {
34792         //Roo.log('adjustForComponents ');
34793         if(this.resizeEl != this.el){
34794             width -= this.el.getFrameWidth('lr');
34795             height -= this.el.getFrameWidth('tb');
34796         }
34797         if(this.toolbar){
34798             var te = this.toolbar.getEl();
34799             height -= te.getHeight();
34800             te.setWidth(width);
34801         }
34802         if(this.footer){
34803             var te = this.footer.getEl();
34804             Roo.log("footer:" + te.getHeight());
34805             
34806             height -= te.getHeight();
34807             te.setWidth(width);
34808         }
34809         
34810         
34811         if(this.adjustments){
34812             width += this.adjustments[0];
34813             height += this.adjustments[1];
34814         }
34815         return {"width": width, "height": height};
34816     },
34817     
34818     setSize : function(width, height){
34819         if(this.fitToFrame && !this.ignoreResize(width, height)){
34820             if(this.fitContainer && this.resizeEl != this.el){
34821                 this.el.setSize(width, height);
34822             }
34823             var size = this.adjustForComponents(width, height);
34824             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
34825             this.fireEvent('resize', this, size.width, size.height);
34826         }
34827     },
34828     
34829     /**
34830      * Returns this panel's title
34831      * @return {String} 
34832      */
34833     getTitle : function(){
34834         return this.title;
34835     },
34836     
34837     /**
34838      * Set this panel's title
34839      * @param {String} title
34840      */
34841     setTitle : function(title){
34842         this.title = title;
34843         if(this.region){
34844             this.region.updatePanelTitle(this, title);
34845         }
34846     },
34847     
34848     /**
34849      * Returns true is this panel was configured to be closable
34850      * @return {Boolean} 
34851      */
34852     isClosable : function(){
34853         return this.closable;
34854     },
34855     
34856     beforeSlide : function(){
34857         this.el.clip();
34858         this.resizeEl.clip();
34859     },
34860     
34861     afterSlide : function(){
34862         this.el.unclip();
34863         this.resizeEl.unclip();
34864     },
34865     
34866     /**
34867      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
34868      *   Will fail silently if the {@link #setUrl} method has not been called.
34869      *   This does not activate the panel, just updates its content.
34870      */
34871     refresh : function(){
34872         if(this.refreshDelegate){
34873            this.loaded = false;
34874            this.refreshDelegate();
34875         }
34876     },
34877     
34878     /**
34879      * Destroys this panel
34880      */
34881     destroy : function(){
34882         this.el.removeAllListeners();
34883         var tempEl = document.createElement("span");
34884         tempEl.appendChild(this.el.dom);
34885         tempEl.innerHTML = "";
34886         this.el.remove();
34887         this.el = null;
34888     },
34889     
34890     /**
34891      * form - if the content panel contains a form - this is a reference to it.
34892      * @type {Roo.form.Form}
34893      */
34894     form : false,
34895     /**
34896      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
34897      *    This contains a reference to it.
34898      * @type {Roo.View}
34899      */
34900     view : false,
34901     
34902       /**
34903      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
34904      * <pre><code>
34905
34906 layout.addxtype({
34907        xtype : 'Form',
34908        items: [ .... ]
34909    }
34910 );
34911
34912 </code></pre>
34913      * @param {Object} cfg Xtype definition of item to add.
34914      */
34915     
34916     addxtype : function(cfg) {
34917         // add form..
34918         if (cfg.xtype.match(/^Form$/)) {
34919             
34920             var el;
34921             //if (this.footer) {
34922             //    el = this.footer.container.insertSibling(false, 'before');
34923             //} else {
34924                 el = this.el.createChild();
34925             //}
34926
34927             this.form = new  Roo.form.Form(cfg);
34928             
34929             
34930             if ( this.form.allItems.length) this.form.render(el.dom);
34931             return this.form;
34932         }
34933         // should only have one of theses..
34934         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
34935             // views.. should not be just added - used named prop 'view''
34936             
34937             cfg.el = this.el.appendChild(document.createElement("div"));
34938             // factory?
34939             
34940             var ret = new Roo.factory(cfg);
34941              
34942              ret.render && ret.render(false, ''); // render blank..
34943             this.view = ret;
34944             return ret;
34945         }
34946         return false;
34947     }
34948 });
34949
34950 /**
34951  * @class Roo.GridPanel
34952  * @extends Roo.ContentPanel
34953  * @constructor
34954  * Create a new GridPanel.
34955  * @param {Roo.grid.Grid} grid The grid for this panel
34956  * @param {String/Object} config A string to set only the panel's title, or a config object
34957  */
34958 Roo.GridPanel = function(grid, config){
34959     
34960   
34961     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
34962         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
34963         
34964     this.wrapper.dom.appendChild(grid.getGridEl().dom);
34965     
34966     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
34967     
34968     if(this.toolbar){
34969         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
34970     }
34971     // xtype created footer. - not sure if will work as we normally have to render first..
34972     if (this.footer && !this.footer.el && this.footer.xtype) {
34973         
34974         this.footer.container = this.grid.getView().getFooterPanel(true);
34975         this.footer.dataSource = this.grid.dataSource;
34976         this.footer = Roo.factory(this.footer, Roo);
34977         
34978     }
34979     
34980     grid.monitorWindowResize = false; // turn off autosizing
34981     grid.autoHeight = false;
34982     grid.autoWidth = false;
34983     this.grid = grid;
34984     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
34985 };
34986
34987 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
34988     getId : function(){
34989         return this.grid.id;
34990     },
34991     
34992     /**
34993      * Returns the grid for this panel
34994      * @return {Roo.grid.Grid} 
34995      */
34996     getGrid : function(){
34997         return this.grid;    
34998     },
34999     
35000     setSize : function(width, height){
35001         if(!this.ignoreResize(width, height)){
35002             var grid = this.grid;
35003             var size = this.adjustForComponents(width, height);
35004             grid.getGridEl().setSize(size.width, size.height);
35005             grid.autoSize();
35006         }
35007     },
35008     
35009     beforeSlide : function(){
35010         this.grid.getView().scroller.clip();
35011     },
35012     
35013     afterSlide : function(){
35014         this.grid.getView().scroller.unclip();
35015     },
35016     
35017     destroy : function(){
35018         this.grid.destroy();
35019         delete this.grid;
35020         Roo.GridPanel.superclass.destroy.call(this); 
35021     }
35022 });
35023
35024
35025 /**
35026  * @class Roo.NestedLayoutPanel
35027  * @extends Roo.ContentPanel
35028  * @constructor
35029  * Create a new NestedLayoutPanel.
35030  * 
35031  * 
35032  * @param {Roo.BorderLayout} layout The layout for this panel
35033  * @param {String/Object} config A string to set only the title or a config object
35034  */
35035 Roo.NestedLayoutPanel = function(layout, config)
35036 {
35037     // construct with only one argument..
35038     /* FIXME - implement nicer consturctors
35039     if (layout.layout) {
35040         config = layout;
35041         layout = config.layout;
35042         delete config.layout;
35043     }
35044     if (layout.xtype && !layout.getEl) {
35045         // then layout needs constructing..
35046         layout = Roo.factory(layout, Roo);
35047     }
35048     */
35049     
35050     
35051     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
35052     
35053     layout.monitorWindowResize = false; // turn off autosizing
35054     this.layout = layout;
35055     this.layout.getEl().addClass("x-layout-nested-layout");
35056     
35057     
35058     
35059     
35060 };
35061
35062 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
35063
35064     setSize : function(width, height){
35065         if(!this.ignoreResize(width, height)){
35066             var size = this.adjustForComponents(width, height);
35067             var el = this.layout.getEl();
35068             el.setSize(size.width, size.height);
35069             var touch = el.dom.offsetWidth;
35070             this.layout.layout();
35071             // ie requires a double layout on the first pass
35072             if(Roo.isIE && !this.initialized){
35073                 this.initialized = true;
35074                 this.layout.layout();
35075             }
35076         }
35077     },
35078     
35079     // activate all subpanels if not currently active..
35080     
35081     setActiveState : function(active){
35082         this.active = active;
35083         if(!active){
35084             this.fireEvent("deactivate", this);
35085             return;
35086         }
35087         
35088         this.fireEvent("activate", this);
35089         // not sure if this should happen before or after..
35090         if (!this.layout) {
35091             return; // should not happen..
35092         }
35093         var reg = false;
35094         for (var r in this.layout.regions) {
35095             reg = this.layout.getRegion(r);
35096             if (reg.getActivePanel()) {
35097                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
35098                 reg.setActivePanel(reg.getActivePanel());
35099                 continue;
35100             }
35101             if (!reg.panels.length) {
35102                 continue;
35103             }
35104             reg.showPanel(reg.getPanel(0));
35105         }
35106         
35107         
35108         
35109         
35110     },
35111     
35112     /**
35113      * Returns the nested BorderLayout for this panel
35114      * @return {Roo.BorderLayout} 
35115      */
35116     getLayout : function(){
35117         return this.layout;
35118     },
35119     
35120      /**
35121      * Adds a xtype elements to the layout of the nested panel
35122      * <pre><code>
35123
35124 panel.addxtype({
35125        xtype : 'ContentPanel',
35126        region: 'west',
35127        items: [ .... ]
35128    }
35129 );
35130
35131 panel.addxtype({
35132         xtype : 'NestedLayoutPanel',
35133         region: 'west',
35134         layout: {
35135            center: { },
35136            west: { }   
35137         },
35138         items : [ ... list of content panels or nested layout panels.. ]
35139    }
35140 );
35141 </code></pre>
35142      * @param {Object} cfg Xtype definition of item to add.
35143      */
35144     addxtype : function(cfg) {
35145         return this.layout.addxtype(cfg);
35146     
35147     }
35148 });
35149
35150 Roo.ScrollPanel = function(el, config, content){
35151     config = config || {};
35152     config.fitToFrame = true;
35153     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
35154     
35155     this.el.dom.style.overflow = "hidden";
35156     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
35157     this.el.removeClass("x-layout-inactive-content");
35158     this.el.on("mousewheel", this.onWheel, this);
35159
35160     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
35161     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
35162     up.unselectable(); down.unselectable();
35163     up.on("click", this.scrollUp, this);
35164     down.on("click", this.scrollDown, this);
35165     up.addClassOnOver("x-scroller-btn-over");
35166     down.addClassOnOver("x-scroller-btn-over");
35167     up.addClassOnClick("x-scroller-btn-click");
35168     down.addClassOnClick("x-scroller-btn-click");
35169     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
35170
35171     this.resizeEl = this.el;
35172     this.el = wrap; this.up = up; this.down = down;
35173 };
35174
35175 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
35176     increment : 100,
35177     wheelIncrement : 5,
35178     scrollUp : function(){
35179         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
35180     },
35181
35182     scrollDown : function(){
35183         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
35184     },
35185
35186     afterScroll : function(){
35187         var el = this.resizeEl;
35188         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
35189         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
35190         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
35191     },
35192
35193     setSize : function(){
35194         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
35195         this.afterScroll();
35196     },
35197
35198     onWheel : function(e){
35199         var d = e.getWheelDelta();
35200         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
35201         this.afterScroll();
35202         e.stopEvent();
35203     },
35204
35205     setContent : function(content, loadScripts){
35206         this.resizeEl.update(content, loadScripts);
35207     }
35208
35209 });
35210
35211
35212
35213
35214
35215
35216
35217
35218
35219 /**
35220  * @class Roo.TreePanel
35221  * @extends Roo.ContentPanel
35222  * @constructor
35223  * Create a new TreePanel. - defaults to fit/scoll contents.
35224  * @param {String/Object} config A string to set only the panel's title, or a config object
35225  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
35226  */
35227 Roo.TreePanel = function(config){
35228     var el = config.el;
35229     var tree = config.tree;
35230     delete config.tree; 
35231     delete config.el; // hopefull!
35232     
35233     // wrapper for IE7 strict & safari scroll issue
35234     
35235     var treeEl = el.createChild();
35236     config.resizeEl = treeEl;
35237     
35238     
35239     
35240     Roo.TreePanel.superclass.constructor.call(this, el, config);
35241  
35242  
35243     this.tree = new Roo.tree.TreePanel(treeEl , tree);
35244     //console.log(tree);
35245     this.on('activate', function()
35246     {
35247         if (this.tree.rendered) {
35248             return;
35249         }
35250         //console.log('render tree');
35251         this.tree.render();
35252     });
35253     // this should not be needed.. - it's actually the 'el' that resizes?
35254     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
35255     
35256     //this.on('resize',  function (cp, w, h) {
35257     //        this.tree.innerCt.setWidth(w);
35258     //        this.tree.innerCt.setHeight(h);
35259     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
35260     //});
35261
35262         
35263     
35264 };
35265
35266 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
35267     fitToFrame : true,
35268     autoScroll : true
35269 });
35270
35271
35272
35273
35274
35275
35276
35277
35278
35279
35280
35281 /*
35282  * Based on:
35283  * Ext JS Library 1.1.1
35284  * Copyright(c) 2006-2007, Ext JS, LLC.
35285  *
35286  * Originally Released Under LGPL - original licence link has changed is not relivant.
35287  *
35288  * Fork - LGPL
35289  * <script type="text/javascript">
35290  */
35291  
35292
35293 /**
35294  * @class Roo.ReaderLayout
35295  * @extends Roo.BorderLayout
35296  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
35297  * center region containing two nested regions (a top one for a list view and one for item preview below),
35298  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
35299  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
35300  * expedites the setup of the overall layout and regions for this common application style.
35301  * Example:
35302  <pre><code>
35303 var reader = new Roo.ReaderLayout();
35304 var CP = Roo.ContentPanel;  // shortcut for adding
35305
35306 reader.beginUpdate();
35307 reader.add("north", new CP("north", "North"));
35308 reader.add("west", new CP("west", {title: "West"}));
35309 reader.add("east", new CP("east", {title: "East"}));
35310
35311 reader.regions.listView.add(new CP("listView", "List"));
35312 reader.regions.preview.add(new CP("preview", "Preview"));
35313 reader.endUpdate();
35314 </code></pre>
35315 * @constructor
35316 * Create a new ReaderLayout
35317 * @param {Object} config Configuration options
35318 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
35319 * document.body if omitted)
35320 */
35321 Roo.ReaderLayout = function(config, renderTo){
35322     var c = config || {size:{}};
35323     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
35324         north: c.north !== false ? Roo.apply({
35325             split:false,
35326             initialSize: 32,
35327             titlebar: false
35328         }, c.north) : false,
35329         west: c.west !== false ? Roo.apply({
35330             split:true,
35331             initialSize: 200,
35332             minSize: 175,
35333             maxSize: 400,
35334             titlebar: true,
35335             collapsible: true,
35336             animate: true,
35337             margins:{left:5,right:0,bottom:5,top:5},
35338             cmargins:{left:5,right:5,bottom:5,top:5}
35339         }, c.west) : false,
35340         east: c.east !== false ? Roo.apply({
35341             split:true,
35342             initialSize: 200,
35343             minSize: 175,
35344             maxSize: 400,
35345             titlebar: true,
35346             collapsible: true,
35347             animate: true,
35348             margins:{left:0,right:5,bottom:5,top:5},
35349             cmargins:{left:5,right:5,bottom:5,top:5}
35350         }, c.east) : false,
35351         center: Roo.apply({
35352             tabPosition: 'top',
35353             autoScroll:false,
35354             closeOnTab: true,
35355             titlebar:false,
35356             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
35357         }, c.center)
35358     });
35359
35360     this.el.addClass('x-reader');
35361
35362     this.beginUpdate();
35363
35364     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
35365         south: c.preview !== false ? Roo.apply({
35366             split:true,
35367             initialSize: 200,
35368             minSize: 100,
35369             autoScroll:true,
35370             collapsible:true,
35371             titlebar: true,
35372             cmargins:{top:5,left:0, right:0, bottom:0}
35373         }, c.preview) : false,
35374         center: Roo.apply({
35375             autoScroll:false,
35376             titlebar:false,
35377             minHeight:200
35378         }, c.listView)
35379     });
35380     this.add('center', new Roo.NestedLayoutPanel(inner,
35381             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
35382
35383     this.endUpdate();
35384
35385     this.regions.preview = inner.getRegion('south');
35386     this.regions.listView = inner.getRegion('center');
35387 };
35388
35389 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
35390  * Based on:
35391  * Ext JS Library 1.1.1
35392  * Copyright(c) 2006-2007, Ext JS, LLC.
35393  *
35394  * Originally Released Under LGPL - original licence link has changed is not relivant.
35395  *
35396  * Fork - LGPL
35397  * <script type="text/javascript">
35398  */
35399  
35400 /**
35401  * @class Roo.grid.Grid
35402  * @extends Roo.util.Observable
35403  * This class represents the primary interface of a component based grid control.
35404  * <br><br>Usage:<pre><code>
35405  var grid = new Roo.grid.Grid("my-container-id", {
35406      ds: myDataStore,
35407      cm: myColModel,
35408      selModel: mySelectionModel,
35409      autoSizeColumns: true,
35410      monitorWindowResize: false,
35411      trackMouseOver: true
35412  });
35413  // set any options
35414  grid.render();
35415  * </code></pre>
35416  * <b>Common Problems:</b><br/>
35417  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
35418  * element will correct this<br/>
35419  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
35420  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
35421  * are unpredictable.<br/>
35422  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
35423  * grid to calculate dimensions/offsets.<br/>
35424   * @constructor
35425  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
35426  * The container MUST have some type of size defined for the grid to fill. The container will be
35427  * automatically set to position relative if it isn't already.
35428  * @param {Object} config A config object that sets properties on this grid.
35429  */
35430 Roo.grid.Grid = function(container, config){
35431         // initialize the container
35432         this.container = Roo.get(container);
35433         this.container.update("");
35434         this.container.setStyle("overflow", "hidden");
35435     this.container.addClass('x-grid-container');
35436
35437     this.id = this.container.id;
35438
35439     Roo.apply(this, config);
35440     // check and correct shorthanded configs
35441     if(this.ds){
35442         this.dataSource = this.ds;
35443         delete this.ds;
35444     }
35445     if(this.cm){
35446         this.colModel = this.cm;
35447         delete this.cm;
35448     }
35449     if(this.sm){
35450         this.selModel = this.sm;
35451         delete this.sm;
35452     }
35453
35454     if (this.selModel) {
35455         this.selModel = Roo.factory(this.selModel, Roo.grid);
35456         this.sm = this.selModel;
35457         this.sm.xmodule = this.xmodule || false;
35458     }
35459     if (typeof(this.colModel.config) == 'undefined') {
35460         this.colModel = new Roo.grid.ColumnModel(this.colModel);
35461         this.cm = this.colModel;
35462         this.cm.xmodule = this.xmodule || false;
35463     }
35464     if (this.dataSource) {
35465         this.dataSource= Roo.factory(this.dataSource, Roo.data);
35466         this.ds = this.dataSource;
35467         this.ds.xmodule = this.xmodule || false;
35468          
35469     }
35470     
35471     
35472     
35473     if(this.width){
35474         this.container.setWidth(this.width);
35475     }
35476
35477     if(this.height){
35478         this.container.setHeight(this.height);
35479     }
35480     /** @private */
35481         this.addEvents({
35482         // raw events
35483         /**
35484          * @event click
35485          * The raw click event for the entire grid.
35486          * @param {Roo.EventObject} e
35487          */
35488         "click" : true,
35489         /**
35490          * @event dblclick
35491          * The raw dblclick event for the entire grid.
35492          * @param {Roo.EventObject} e
35493          */
35494         "dblclick" : true,
35495         /**
35496          * @event contextmenu
35497          * The raw contextmenu event for the entire grid.
35498          * @param {Roo.EventObject} e
35499          */
35500         "contextmenu" : true,
35501         /**
35502          * @event mousedown
35503          * The raw mousedown event for the entire grid.
35504          * @param {Roo.EventObject} e
35505          */
35506         "mousedown" : true,
35507         /**
35508          * @event mouseup
35509          * The raw mouseup event for the entire grid.
35510          * @param {Roo.EventObject} e
35511          */
35512         "mouseup" : true,
35513         /**
35514          * @event mouseover
35515          * The raw mouseover event for the entire grid.
35516          * @param {Roo.EventObject} e
35517          */
35518         "mouseover" : true,
35519         /**
35520          * @event mouseout
35521          * The raw mouseout event for the entire grid.
35522          * @param {Roo.EventObject} e
35523          */
35524         "mouseout" : true,
35525         /**
35526          * @event keypress
35527          * The raw keypress event for the entire grid.
35528          * @param {Roo.EventObject} e
35529          */
35530         "keypress" : true,
35531         /**
35532          * @event keydown
35533          * The raw keydown event for the entire grid.
35534          * @param {Roo.EventObject} e
35535          */
35536         "keydown" : true,
35537
35538         // custom events
35539
35540         /**
35541          * @event cellclick
35542          * Fires when a cell is clicked
35543          * @param {Grid} this
35544          * @param {Number} rowIndex
35545          * @param {Number} columnIndex
35546          * @param {Roo.EventObject} e
35547          */
35548         "cellclick" : true,
35549         /**
35550          * @event celldblclick
35551          * Fires when a cell is double clicked
35552          * @param {Grid} this
35553          * @param {Number} rowIndex
35554          * @param {Number} columnIndex
35555          * @param {Roo.EventObject} e
35556          */
35557         "celldblclick" : true,
35558         /**
35559          * @event rowclick
35560          * Fires when a row is clicked
35561          * @param {Grid} this
35562          * @param {Number} rowIndex
35563          * @param {Roo.EventObject} e
35564          */
35565         "rowclick" : true,
35566         /**
35567          * @event rowdblclick
35568          * Fires when a row is double clicked
35569          * @param {Grid} this
35570          * @param {Number} rowIndex
35571          * @param {Roo.EventObject} e
35572          */
35573         "rowdblclick" : true,
35574         /**
35575          * @event headerclick
35576          * Fires when a header is clicked
35577          * @param {Grid} this
35578          * @param {Number} columnIndex
35579          * @param {Roo.EventObject} e
35580          */
35581         "headerclick" : true,
35582         /**
35583          * @event headerdblclick
35584          * Fires when a header cell is double clicked
35585          * @param {Grid} this
35586          * @param {Number} columnIndex
35587          * @param {Roo.EventObject} e
35588          */
35589         "headerdblclick" : true,
35590         /**
35591          * @event rowcontextmenu
35592          * Fires when a row is right clicked
35593          * @param {Grid} this
35594          * @param {Number} rowIndex
35595          * @param {Roo.EventObject} e
35596          */
35597         "rowcontextmenu" : true,
35598         /**
35599          * @event cellcontextmenu
35600          * Fires when a cell is right clicked
35601          * @param {Grid} this
35602          * @param {Number} rowIndex
35603          * @param {Number} cellIndex
35604          * @param {Roo.EventObject} e
35605          */
35606          "cellcontextmenu" : true,
35607         /**
35608          * @event headercontextmenu
35609          * Fires when a header is right clicked
35610          * @param {Grid} this
35611          * @param {Number} columnIndex
35612          * @param {Roo.EventObject} e
35613          */
35614         "headercontextmenu" : true,
35615         /**
35616          * @event bodyscroll
35617          * Fires when the body element is scrolled
35618          * @param {Number} scrollLeft
35619          * @param {Number} scrollTop
35620          */
35621         "bodyscroll" : true,
35622         /**
35623          * @event columnresize
35624          * Fires when the user resizes a column
35625          * @param {Number} columnIndex
35626          * @param {Number} newSize
35627          */
35628         "columnresize" : true,
35629         /**
35630          * @event columnmove
35631          * Fires when the user moves a column
35632          * @param {Number} oldIndex
35633          * @param {Number} newIndex
35634          */
35635         "columnmove" : true,
35636         /**
35637          * @event startdrag
35638          * Fires when row(s) start being dragged
35639          * @param {Grid} this
35640          * @param {Roo.GridDD} dd The drag drop object
35641          * @param {event} e The raw browser event
35642          */
35643         "startdrag" : true,
35644         /**
35645          * @event enddrag
35646          * Fires when a drag operation is complete
35647          * @param {Grid} this
35648          * @param {Roo.GridDD} dd The drag drop object
35649          * @param {event} e The raw browser event
35650          */
35651         "enddrag" : true,
35652         /**
35653          * @event dragdrop
35654          * Fires when dragged row(s) are dropped on a valid DD target
35655          * @param {Grid} this
35656          * @param {Roo.GridDD} dd The drag drop object
35657          * @param {String} targetId The target drag drop object
35658          * @param {event} e The raw browser event
35659          */
35660         "dragdrop" : true,
35661         /**
35662          * @event dragover
35663          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
35664          * @param {Grid} this
35665          * @param {Roo.GridDD} dd The drag drop object
35666          * @param {String} targetId The target drag drop object
35667          * @param {event} e The raw browser event
35668          */
35669         "dragover" : true,
35670         /**
35671          * @event dragenter
35672          *  Fires when the dragged row(s) first cross another DD target while being dragged
35673          * @param {Grid} this
35674          * @param {Roo.GridDD} dd The drag drop object
35675          * @param {String} targetId The target drag drop object
35676          * @param {event} e The raw browser event
35677          */
35678         "dragenter" : true,
35679         /**
35680          * @event dragout
35681          * Fires when the dragged row(s) leave another DD target while being dragged
35682          * @param {Grid} this
35683          * @param {Roo.GridDD} dd The drag drop object
35684          * @param {String} targetId The target drag drop object
35685          * @param {event} e The raw browser event
35686          */
35687         "dragout" : true,
35688         /**
35689          * @event rowclass
35690          * Fires when a row is rendered, so you can change add a style to it.
35691          * @param {GridView} gridview   The grid view
35692          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
35693          */
35694         'rowclass' : true,
35695
35696         /**
35697          * @event render
35698          * Fires when the grid is rendered
35699          * @param {Grid} grid
35700          */
35701         'render' : true
35702     });
35703
35704     Roo.grid.Grid.superclass.constructor.call(this);
35705 };
35706 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
35707     
35708     /**
35709      * @cfg {String} ddGroup - drag drop group.
35710      */
35711
35712     /**
35713      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
35714      */
35715     minColumnWidth : 25,
35716
35717     /**
35718      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
35719      * <b>on initial render.</b> It is more efficient to explicitly size the columns
35720      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
35721      */
35722     autoSizeColumns : false,
35723
35724     /**
35725      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
35726      */
35727     autoSizeHeaders : true,
35728
35729     /**
35730      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
35731      */
35732     monitorWindowResize : true,
35733
35734     /**
35735      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
35736      * rows measured to get a columns size. Default is 0 (all rows).
35737      */
35738     maxRowsToMeasure : 0,
35739
35740     /**
35741      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
35742      */
35743     trackMouseOver : true,
35744
35745     /**
35746     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
35747     */
35748     
35749     /**
35750     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
35751     */
35752     enableDragDrop : false,
35753     
35754     /**
35755     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
35756     */
35757     enableColumnMove : true,
35758     
35759     /**
35760     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
35761     */
35762     enableColumnHide : true,
35763     
35764     /**
35765     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
35766     */
35767     enableRowHeightSync : false,
35768     
35769     /**
35770     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
35771     */
35772     stripeRows : true,
35773     
35774     /**
35775     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
35776     */
35777     autoHeight : false,
35778
35779     /**
35780      * @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.
35781      */
35782     autoExpandColumn : false,
35783
35784     /**
35785     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
35786     * Default is 50.
35787     */
35788     autoExpandMin : 50,
35789
35790     /**
35791     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
35792     */
35793     autoExpandMax : 1000,
35794
35795     /**
35796     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
35797     */
35798     view : null,
35799
35800     /**
35801     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
35802     */
35803     loadMask : false,
35804     /**
35805     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
35806     */
35807     dropTarget: false,
35808     
35809    
35810     
35811     // private
35812     rendered : false,
35813
35814     /**
35815     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
35816     * of a fixed width. Default is false.
35817     */
35818     /**
35819     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
35820     */
35821     /**
35822      * Called once after all setup has been completed and the grid is ready to be rendered.
35823      * @return {Roo.grid.Grid} this
35824      */
35825     render : function()
35826     {
35827         var c = this.container;
35828         // try to detect autoHeight/width mode
35829         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
35830             this.autoHeight = true;
35831         }
35832         var view = this.getView();
35833         view.init(this);
35834
35835         c.on("click", this.onClick, this);
35836         c.on("dblclick", this.onDblClick, this);
35837         c.on("contextmenu", this.onContextMenu, this);
35838         c.on("keydown", this.onKeyDown, this);
35839         if (Roo.isTouch) {
35840             c.on("touchstart", this.onTouchStart, this);
35841         }
35842
35843         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
35844
35845         this.getSelectionModel().init(this);
35846
35847         view.render();
35848
35849         if(this.loadMask){
35850             this.loadMask = new Roo.LoadMask(this.container,
35851                     Roo.apply({store:this.dataSource}, this.loadMask));
35852         }
35853         
35854         
35855         if (this.toolbar && this.toolbar.xtype) {
35856             this.toolbar.container = this.getView().getHeaderPanel(true);
35857             this.toolbar = new Roo.Toolbar(this.toolbar);
35858         }
35859         if (this.footer && this.footer.xtype) {
35860             this.footer.dataSource = this.getDataSource();
35861             this.footer.container = this.getView().getFooterPanel(true);
35862             this.footer = Roo.factory(this.footer, Roo);
35863         }
35864         if (this.dropTarget && this.dropTarget.xtype) {
35865             delete this.dropTarget.xtype;
35866             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
35867         }
35868         
35869         
35870         this.rendered = true;
35871         this.fireEvent('render', this);
35872         return this;
35873     },
35874
35875         /**
35876          * Reconfigures the grid to use a different Store and Column Model.
35877          * The View will be bound to the new objects and refreshed.
35878          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
35879          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
35880          */
35881     reconfigure : function(dataSource, colModel){
35882         if(this.loadMask){
35883             this.loadMask.destroy();
35884             this.loadMask = new Roo.LoadMask(this.container,
35885                     Roo.apply({store:dataSource}, this.loadMask));
35886         }
35887         this.view.bind(dataSource, colModel);
35888         this.dataSource = dataSource;
35889         this.colModel = colModel;
35890         this.view.refresh(true);
35891     },
35892
35893     // private
35894     onKeyDown : function(e){
35895         this.fireEvent("keydown", e);
35896     },
35897
35898     /**
35899      * Destroy this grid.
35900      * @param {Boolean} removeEl True to remove the element
35901      */
35902     destroy : function(removeEl, keepListeners){
35903         if(this.loadMask){
35904             this.loadMask.destroy();
35905         }
35906         var c = this.container;
35907         c.removeAllListeners();
35908         this.view.destroy();
35909         this.colModel.purgeListeners();
35910         if(!keepListeners){
35911             this.purgeListeners();
35912         }
35913         c.update("");
35914         if(removeEl === true){
35915             c.remove();
35916         }
35917     },
35918
35919     // private
35920     processEvent : function(name, e){
35921         // does this fire select???
35922         Roo.log('grid:processEvent '  + name);
35923         
35924         if (name != 'touchstart' ) {
35925             this.fireEvent(name, e);    
35926         }
35927         
35928         var t = e.getTarget();
35929         var v = this.view;
35930         var header = v.findHeaderIndex(t);
35931         if(header !== false){
35932             this.fireEvent("header" + (name == 'touchstart' ? 'click' : name), this, header, e);
35933         }else{
35934             var row = v.findRowIndex(t);
35935             var cell = v.findCellIndex(t);
35936             if (name == 'touchstart') {
35937                 // first touch is always a click.
35938                 // hopefull this happens after selection is updated.?
35939                 name = false;
35940                 
35941                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
35942                     var cs = this.selModel.getSelectedCell();
35943                     if (row == cs[0] && cell == cs[1]){
35944                         name = 'dblclick';
35945                     }
35946                 }
35947                 if (typeof(this.selModel.getSelections) != 'undefined') {
35948                     var cs = this.selModel.getSelections();
35949                     var ds = this.dataSource;
35950                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
35951                         name = 'dblclick';
35952                     }
35953                 }
35954                 if (!name) {
35955                     return;
35956                 }
35957             }
35958             
35959             
35960             if(row !== false){
35961                 this.fireEvent("row" + name, this, row, e);
35962                 if(cell !== false){
35963                     this.fireEvent("cell" + name, this, row, cell, e);
35964                 }
35965             }
35966         }
35967     },
35968
35969     // private
35970     onClick : function(e){
35971         this.processEvent("click", e);
35972     },
35973    // private
35974     onTouchStart : function(e){
35975         this.processEvent("touchstart", e);
35976     },
35977
35978     // private
35979     onContextMenu : function(e, t){
35980         this.processEvent("contextmenu", e);
35981     },
35982
35983     // private
35984     onDblClick : function(e){
35985         this.processEvent("dblclick", e);
35986     },
35987
35988     // private
35989     walkCells : function(row, col, step, fn, scope){
35990         var cm = this.colModel, clen = cm.getColumnCount();
35991         var ds = this.dataSource, rlen = ds.getCount(), first = true;
35992         if(step < 0){
35993             if(col < 0){
35994                 row--;
35995                 first = false;
35996             }
35997             while(row >= 0){
35998                 if(!first){
35999                     col = clen-1;
36000                 }
36001                 first = false;
36002                 while(col >= 0){
36003                     if(fn.call(scope || this, row, col, cm) === true){
36004                         return [row, col];
36005                     }
36006                     col--;
36007                 }
36008                 row--;
36009             }
36010         } else {
36011             if(col >= clen){
36012                 row++;
36013                 first = false;
36014             }
36015             while(row < rlen){
36016                 if(!first){
36017                     col = 0;
36018                 }
36019                 first = false;
36020                 while(col < clen){
36021                     if(fn.call(scope || this, row, col, cm) === true){
36022                         return [row, col];
36023                     }
36024                     col++;
36025                 }
36026                 row++;
36027             }
36028         }
36029         return null;
36030     },
36031
36032     // private
36033     getSelections : function(){
36034         return this.selModel.getSelections();
36035     },
36036
36037     /**
36038      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
36039      * but if manual update is required this method will initiate it.
36040      */
36041     autoSize : function(){
36042         if(this.rendered){
36043             this.view.layout();
36044             if(this.view.adjustForScroll){
36045                 this.view.adjustForScroll();
36046             }
36047         }
36048     },
36049
36050     /**
36051      * Returns the grid's underlying element.
36052      * @return {Element} The element
36053      */
36054     getGridEl : function(){
36055         return this.container;
36056     },
36057
36058     // private for compatibility, overridden by editor grid
36059     stopEditing : function(){},
36060
36061     /**
36062      * Returns the grid's SelectionModel.
36063      * @return {SelectionModel}
36064      */
36065     getSelectionModel : function(){
36066         if(!this.selModel){
36067             this.selModel = new Roo.grid.RowSelectionModel();
36068         }
36069         return this.selModel;
36070     },
36071
36072     /**
36073      * Returns the grid's DataSource.
36074      * @return {DataSource}
36075      */
36076     getDataSource : function(){
36077         return this.dataSource;
36078     },
36079
36080     /**
36081      * Returns the grid's ColumnModel.
36082      * @return {ColumnModel}
36083      */
36084     getColumnModel : function(){
36085         return this.colModel;
36086     },
36087
36088     /**
36089      * Returns the grid's GridView object.
36090      * @return {GridView}
36091      */
36092     getView : function(){
36093         if(!this.view){
36094             this.view = new Roo.grid.GridView(this.viewConfig);
36095         }
36096         return this.view;
36097     },
36098     /**
36099      * Called to get grid's drag proxy text, by default returns this.ddText.
36100      * @return {String}
36101      */
36102     getDragDropText : function(){
36103         var count = this.selModel.getCount();
36104         return String.format(this.ddText, count, count == 1 ? '' : 's');
36105     }
36106 });
36107 /**
36108  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
36109  * %0 is replaced with the number of selected rows.
36110  * @type String
36111  */
36112 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
36113  * Based on:
36114  * Ext JS Library 1.1.1
36115  * Copyright(c) 2006-2007, Ext JS, LLC.
36116  *
36117  * Originally Released Under LGPL - original licence link has changed is not relivant.
36118  *
36119  * Fork - LGPL
36120  * <script type="text/javascript">
36121  */
36122  
36123 Roo.grid.AbstractGridView = function(){
36124         this.grid = null;
36125         
36126         this.events = {
36127             "beforerowremoved" : true,
36128             "beforerowsinserted" : true,
36129             "beforerefresh" : true,
36130             "rowremoved" : true,
36131             "rowsinserted" : true,
36132             "rowupdated" : true,
36133             "refresh" : true
36134         };
36135     Roo.grid.AbstractGridView.superclass.constructor.call(this);
36136 };
36137
36138 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
36139     rowClass : "x-grid-row",
36140     cellClass : "x-grid-cell",
36141     tdClass : "x-grid-td",
36142     hdClass : "x-grid-hd",
36143     splitClass : "x-grid-hd-split",
36144     
36145         init: function(grid){
36146         this.grid = grid;
36147                 var cid = this.grid.getGridEl().id;
36148         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
36149         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
36150         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
36151         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
36152         },
36153         
36154         getColumnRenderers : function(){
36155         var renderers = [];
36156         var cm = this.grid.colModel;
36157         var colCount = cm.getColumnCount();
36158         for(var i = 0; i < colCount; i++){
36159             renderers[i] = cm.getRenderer(i);
36160         }
36161         return renderers;
36162     },
36163     
36164     getColumnIds : function(){
36165         var ids = [];
36166         var cm = this.grid.colModel;
36167         var colCount = cm.getColumnCount();
36168         for(var i = 0; i < colCount; i++){
36169             ids[i] = cm.getColumnId(i);
36170         }
36171         return ids;
36172     },
36173     
36174     getDataIndexes : function(){
36175         if(!this.indexMap){
36176             this.indexMap = this.buildIndexMap();
36177         }
36178         return this.indexMap.colToData;
36179     },
36180     
36181     getColumnIndexByDataIndex : function(dataIndex){
36182         if(!this.indexMap){
36183             this.indexMap = this.buildIndexMap();
36184         }
36185         return this.indexMap.dataToCol[dataIndex];
36186     },
36187     
36188     /**
36189      * Set a css style for a column dynamically. 
36190      * @param {Number} colIndex The index of the column
36191      * @param {String} name The css property name
36192      * @param {String} value The css value
36193      */
36194     setCSSStyle : function(colIndex, name, value){
36195         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
36196         Roo.util.CSS.updateRule(selector, name, value);
36197     },
36198     
36199     generateRules : function(cm){
36200         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
36201         Roo.util.CSS.removeStyleSheet(rulesId);
36202         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
36203             var cid = cm.getColumnId(i);
36204             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
36205                          this.tdSelector, cid, " {\n}\n",
36206                          this.hdSelector, cid, " {\n}\n",
36207                          this.splitSelector, cid, " {\n}\n");
36208         }
36209         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
36210     }
36211 });/*
36212  * Based on:
36213  * Ext JS Library 1.1.1
36214  * Copyright(c) 2006-2007, Ext JS, LLC.
36215  *
36216  * Originally Released Under LGPL - original licence link has changed is not relivant.
36217  *
36218  * Fork - LGPL
36219  * <script type="text/javascript">
36220  */
36221
36222 // private
36223 // This is a support class used internally by the Grid components
36224 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
36225     this.grid = grid;
36226     this.view = grid.getView();
36227     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
36228     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
36229     if(hd2){
36230         this.setHandleElId(Roo.id(hd));
36231         this.setOuterHandleElId(Roo.id(hd2));
36232     }
36233     this.scroll = false;
36234 };
36235 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
36236     maxDragWidth: 120,
36237     getDragData : function(e){
36238         var t = Roo.lib.Event.getTarget(e);
36239         var h = this.view.findHeaderCell(t);
36240         if(h){
36241             return {ddel: h.firstChild, header:h};
36242         }
36243         return false;
36244     },
36245
36246     onInitDrag : function(e){
36247         this.view.headersDisabled = true;
36248         var clone = this.dragData.ddel.cloneNode(true);
36249         clone.id = Roo.id();
36250         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
36251         this.proxy.update(clone);
36252         return true;
36253     },
36254
36255     afterValidDrop : function(){
36256         var v = this.view;
36257         setTimeout(function(){
36258             v.headersDisabled = false;
36259         }, 50);
36260     },
36261
36262     afterInvalidDrop : function(){
36263         var v = this.view;
36264         setTimeout(function(){
36265             v.headersDisabled = false;
36266         }, 50);
36267     }
36268 });
36269 /*
36270  * Based on:
36271  * Ext JS Library 1.1.1
36272  * Copyright(c) 2006-2007, Ext JS, LLC.
36273  *
36274  * Originally Released Under LGPL - original licence link has changed is not relivant.
36275  *
36276  * Fork - LGPL
36277  * <script type="text/javascript">
36278  */
36279 // private
36280 // This is a support class used internally by the Grid components
36281 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
36282     this.grid = grid;
36283     this.view = grid.getView();
36284     // split the proxies so they don't interfere with mouse events
36285     this.proxyTop = Roo.DomHelper.append(document.body, {
36286         cls:"col-move-top", html:"&#160;"
36287     }, true);
36288     this.proxyBottom = Roo.DomHelper.append(document.body, {
36289         cls:"col-move-bottom", html:"&#160;"
36290     }, true);
36291     this.proxyTop.hide = this.proxyBottom.hide = function(){
36292         this.setLeftTop(-100,-100);
36293         this.setStyle("visibility", "hidden");
36294     };
36295     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
36296     // temporarily disabled
36297     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
36298     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
36299 };
36300 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
36301     proxyOffsets : [-4, -9],
36302     fly: Roo.Element.fly,
36303
36304     getTargetFromEvent : function(e){
36305         var t = Roo.lib.Event.getTarget(e);
36306         var cindex = this.view.findCellIndex(t);
36307         if(cindex !== false){
36308             return this.view.getHeaderCell(cindex);
36309         }
36310         return null;
36311     },
36312
36313     nextVisible : function(h){
36314         var v = this.view, cm = this.grid.colModel;
36315         h = h.nextSibling;
36316         while(h){
36317             if(!cm.isHidden(v.getCellIndex(h))){
36318                 return h;
36319             }
36320             h = h.nextSibling;
36321         }
36322         return null;
36323     },
36324
36325     prevVisible : function(h){
36326         var v = this.view, cm = this.grid.colModel;
36327         h = h.prevSibling;
36328         while(h){
36329             if(!cm.isHidden(v.getCellIndex(h))){
36330                 return h;
36331             }
36332             h = h.prevSibling;
36333         }
36334         return null;
36335     },
36336
36337     positionIndicator : function(h, n, e){
36338         var x = Roo.lib.Event.getPageX(e);
36339         var r = Roo.lib.Dom.getRegion(n.firstChild);
36340         var px, pt, py = r.top + this.proxyOffsets[1];
36341         if((r.right - x) <= (r.right-r.left)/2){
36342             px = r.right+this.view.borderWidth;
36343             pt = "after";
36344         }else{
36345             px = r.left;
36346             pt = "before";
36347         }
36348         var oldIndex = this.view.getCellIndex(h);
36349         var newIndex = this.view.getCellIndex(n);
36350
36351         if(this.grid.colModel.isFixed(newIndex)){
36352             return false;
36353         }
36354
36355         var locked = this.grid.colModel.isLocked(newIndex);
36356
36357         if(pt == "after"){
36358             newIndex++;
36359         }
36360         if(oldIndex < newIndex){
36361             newIndex--;
36362         }
36363         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
36364             return false;
36365         }
36366         px +=  this.proxyOffsets[0];
36367         this.proxyTop.setLeftTop(px, py);
36368         this.proxyTop.show();
36369         if(!this.bottomOffset){
36370             this.bottomOffset = this.view.mainHd.getHeight();
36371         }
36372         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
36373         this.proxyBottom.show();
36374         return pt;
36375     },
36376
36377     onNodeEnter : function(n, dd, e, data){
36378         if(data.header != n){
36379             this.positionIndicator(data.header, n, e);
36380         }
36381     },
36382
36383     onNodeOver : function(n, dd, e, data){
36384         var result = false;
36385         if(data.header != n){
36386             result = this.positionIndicator(data.header, n, e);
36387         }
36388         if(!result){
36389             this.proxyTop.hide();
36390             this.proxyBottom.hide();
36391         }
36392         return result ? this.dropAllowed : this.dropNotAllowed;
36393     },
36394
36395     onNodeOut : function(n, dd, e, data){
36396         this.proxyTop.hide();
36397         this.proxyBottom.hide();
36398     },
36399
36400     onNodeDrop : function(n, dd, e, data){
36401         var h = data.header;
36402         if(h != n){
36403             var cm = this.grid.colModel;
36404             var x = Roo.lib.Event.getPageX(e);
36405             var r = Roo.lib.Dom.getRegion(n.firstChild);
36406             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
36407             var oldIndex = this.view.getCellIndex(h);
36408             var newIndex = this.view.getCellIndex(n);
36409             var locked = cm.isLocked(newIndex);
36410             if(pt == "after"){
36411                 newIndex++;
36412             }
36413             if(oldIndex < newIndex){
36414                 newIndex--;
36415             }
36416             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
36417                 return false;
36418             }
36419             cm.setLocked(oldIndex, locked, true);
36420             cm.moveColumn(oldIndex, newIndex);
36421             this.grid.fireEvent("columnmove", oldIndex, newIndex);
36422             return true;
36423         }
36424         return false;
36425     }
36426 });
36427 /*
36428  * Based on:
36429  * Ext JS Library 1.1.1
36430  * Copyright(c) 2006-2007, Ext JS, LLC.
36431  *
36432  * Originally Released Under LGPL - original licence link has changed is not relivant.
36433  *
36434  * Fork - LGPL
36435  * <script type="text/javascript">
36436  */
36437   
36438 /**
36439  * @class Roo.grid.GridView
36440  * @extends Roo.util.Observable
36441  *
36442  * @constructor
36443  * @param {Object} config
36444  */
36445 Roo.grid.GridView = function(config){
36446     Roo.grid.GridView.superclass.constructor.call(this);
36447     this.el = null;
36448
36449     Roo.apply(this, config);
36450 };
36451
36452 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
36453
36454     unselectable :  'unselectable="on"',
36455     unselectableCls :  'x-unselectable',
36456     
36457     
36458     rowClass : "x-grid-row",
36459
36460     cellClass : "x-grid-col",
36461
36462     tdClass : "x-grid-td",
36463
36464     hdClass : "x-grid-hd",
36465
36466     splitClass : "x-grid-split",
36467
36468     sortClasses : ["sort-asc", "sort-desc"],
36469
36470     enableMoveAnim : false,
36471
36472     hlColor: "C3DAF9",
36473
36474     dh : Roo.DomHelper,
36475
36476     fly : Roo.Element.fly,
36477
36478     css : Roo.util.CSS,
36479
36480     borderWidth: 1,
36481
36482     splitOffset: 3,
36483
36484     scrollIncrement : 22,
36485
36486     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
36487
36488     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
36489
36490     bind : function(ds, cm){
36491         if(this.ds){
36492             this.ds.un("load", this.onLoad, this);
36493             this.ds.un("datachanged", this.onDataChange, this);
36494             this.ds.un("add", this.onAdd, this);
36495             this.ds.un("remove", this.onRemove, this);
36496             this.ds.un("update", this.onUpdate, this);
36497             this.ds.un("clear", this.onClear, this);
36498         }
36499         if(ds){
36500             ds.on("load", this.onLoad, this);
36501             ds.on("datachanged", this.onDataChange, this);
36502             ds.on("add", this.onAdd, this);
36503             ds.on("remove", this.onRemove, this);
36504             ds.on("update", this.onUpdate, this);
36505             ds.on("clear", this.onClear, this);
36506         }
36507         this.ds = ds;
36508
36509         if(this.cm){
36510             this.cm.un("widthchange", this.onColWidthChange, this);
36511             this.cm.un("headerchange", this.onHeaderChange, this);
36512             this.cm.un("hiddenchange", this.onHiddenChange, this);
36513             this.cm.un("columnmoved", this.onColumnMove, this);
36514             this.cm.un("columnlockchange", this.onColumnLock, this);
36515         }
36516         if(cm){
36517             this.generateRules(cm);
36518             cm.on("widthchange", this.onColWidthChange, this);
36519             cm.on("headerchange", this.onHeaderChange, this);
36520             cm.on("hiddenchange", this.onHiddenChange, this);
36521             cm.on("columnmoved", this.onColumnMove, this);
36522             cm.on("columnlockchange", this.onColumnLock, this);
36523         }
36524         this.cm = cm;
36525     },
36526
36527     init: function(grid){
36528         Roo.grid.GridView.superclass.init.call(this, grid);
36529
36530         this.bind(grid.dataSource, grid.colModel);
36531
36532         grid.on("headerclick", this.handleHeaderClick, this);
36533
36534         if(grid.trackMouseOver){
36535             grid.on("mouseover", this.onRowOver, this);
36536             grid.on("mouseout", this.onRowOut, this);
36537         }
36538         grid.cancelTextSelection = function(){};
36539         this.gridId = grid.id;
36540
36541         var tpls = this.templates || {};
36542
36543         if(!tpls.master){
36544             tpls.master = new Roo.Template(
36545                '<div class="x-grid" hidefocus="true">',
36546                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
36547                   '<div class="x-grid-topbar"></div>',
36548                   '<div class="x-grid-scroller"><div></div></div>',
36549                   '<div class="x-grid-locked">',
36550                       '<div class="x-grid-header">{lockedHeader}</div>',
36551                       '<div class="x-grid-body">{lockedBody}</div>',
36552                   "</div>",
36553                   '<div class="x-grid-viewport">',
36554                       '<div class="x-grid-header">{header}</div>',
36555                       '<div class="x-grid-body">{body}</div>',
36556                   "</div>",
36557                   '<div class="x-grid-bottombar"></div>',
36558                  
36559                   '<div class="x-grid-resize-proxy">&#160;</div>',
36560                "</div>"
36561             );
36562             tpls.master.disableformats = true;
36563         }
36564
36565         if(!tpls.header){
36566             tpls.header = new Roo.Template(
36567                '<table border="0" cellspacing="0" cellpadding="0">',
36568                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
36569                "</table>{splits}"
36570             );
36571             tpls.header.disableformats = true;
36572         }
36573         tpls.header.compile();
36574
36575         if(!tpls.hcell){
36576             tpls.hcell = new Roo.Template(
36577                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
36578                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
36579                 "</div></td>"
36580              );
36581              tpls.hcell.disableFormats = true;
36582         }
36583         tpls.hcell.compile();
36584
36585         if(!tpls.hsplit){
36586             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
36587                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
36588             tpls.hsplit.disableFormats = true;
36589         }
36590         tpls.hsplit.compile();
36591
36592         if(!tpls.body){
36593             tpls.body = new Roo.Template(
36594                '<table border="0" cellspacing="0" cellpadding="0">',
36595                "<tbody>{rows}</tbody>",
36596                "</table>"
36597             );
36598             tpls.body.disableFormats = true;
36599         }
36600         tpls.body.compile();
36601
36602         if(!tpls.row){
36603             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
36604             tpls.row.disableFormats = true;
36605         }
36606         tpls.row.compile();
36607
36608         if(!tpls.cell){
36609             tpls.cell = new Roo.Template(
36610                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
36611                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
36612                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
36613                 "</td>"
36614             );
36615             tpls.cell.disableFormats = true;
36616         }
36617         tpls.cell.compile();
36618
36619         this.templates = tpls;
36620     },
36621
36622     // remap these for backwards compat
36623     onColWidthChange : function(){
36624         this.updateColumns.apply(this, arguments);
36625     },
36626     onHeaderChange : function(){
36627         this.updateHeaders.apply(this, arguments);
36628     }, 
36629     onHiddenChange : function(){
36630         this.handleHiddenChange.apply(this, arguments);
36631     },
36632     onColumnMove : function(){
36633         this.handleColumnMove.apply(this, arguments);
36634     },
36635     onColumnLock : function(){
36636         this.handleLockChange.apply(this, arguments);
36637     },
36638
36639     onDataChange : function(){
36640         this.refresh();
36641         this.updateHeaderSortState();
36642     },
36643
36644     onClear : function(){
36645         this.refresh();
36646     },
36647
36648     onUpdate : function(ds, record){
36649         this.refreshRow(record);
36650     },
36651
36652     refreshRow : function(record){
36653         var ds = this.ds, index;
36654         if(typeof record == 'number'){
36655             index = record;
36656             record = ds.getAt(index);
36657         }else{
36658             index = ds.indexOf(record);
36659         }
36660         this.insertRows(ds, index, index, true);
36661         this.onRemove(ds, record, index+1, true);
36662         this.syncRowHeights(index, index);
36663         this.layout();
36664         this.fireEvent("rowupdated", this, index, record);
36665     },
36666
36667     onAdd : function(ds, records, index){
36668         this.insertRows(ds, index, index + (records.length-1));
36669     },
36670
36671     onRemove : function(ds, record, index, isUpdate){
36672         if(isUpdate !== true){
36673             this.fireEvent("beforerowremoved", this, index, record);
36674         }
36675         var bt = this.getBodyTable(), lt = this.getLockedTable();
36676         if(bt.rows[index]){
36677             bt.firstChild.removeChild(bt.rows[index]);
36678         }
36679         if(lt.rows[index]){
36680             lt.firstChild.removeChild(lt.rows[index]);
36681         }
36682         if(isUpdate !== true){
36683             this.stripeRows(index);
36684             this.syncRowHeights(index, index);
36685             this.layout();
36686             this.fireEvent("rowremoved", this, index, record);
36687         }
36688     },
36689
36690     onLoad : function(){
36691         this.scrollToTop();
36692     },
36693
36694     /**
36695      * Scrolls the grid to the top
36696      */
36697     scrollToTop : function(){
36698         if(this.scroller){
36699             this.scroller.dom.scrollTop = 0;
36700             this.syncScroll();
36701         }
36702     },
36703
36704     /**
36705      * Gets a panel in the header of the grid that can be used for toolbars etc.
36706      * After modifying the contents of this panel a call to grid.autoSize() may be
36707      * required to register any changes in size.
36708      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
36709      * @return Roo.Element
36710      */
36711     getHeaderPanel : function(doShow){
36712         if(doShow){
36713             this.headerPanel.show();
36714         }
36715         return this.headerPanel;
36716     },
36717
36718     /**
36719      * Gets a panel in the footer of the grid that can be used for toolbars etc.
36720      * After modifying the contents of this panel a call to grid.autoSize() may be
36721      * required to register any changes in size.
36722      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
36723      * @return Roo.Element
36724      */
36725     getFooterPanel : function(doShow){
36726         if(doShow){
36727             this.footerPanel.show();
36728         }
36729         return this.footerPanel;
36730     },
36731
36732     initElements : function(){
36733         var E = Roo.Element;
36734         var el = this.grid.getGridEl().dom.firstChild;
36735         var cs = el.childNodes;
36736
36737         this.el = new E(el);
36738         
36739          this.focusEl = new E(el.firstChild);
36740         this.focusEl.swallowEvent("click", true);
36741         
36742         this.headerPanel = new E(cs[1]);
36743         this.headerPanel.enableDisplayMode("block");
36744
36745         this.scroller = new E(cs[2]);
36746         this.scrollSizer = new E(this.scroller.dom.firstChild);
36747
36748         this.lockedWrap = new E(cs[3]);
36749         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
36750         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
36751
36752         this.mainWrap = new E(cs[4]);
36753         this.mainHd = new E(this.mainWrap.dom.firstChild);
36754         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
36755
36756         this.footerPanel = new E(cs[5]);
36757         this.footerPanel.enableDisplayMode("block");
36758
36759         this.resizeProxy = new E(cs[6]);
36760
36761         this.headerSelector = String.format(
36762            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
36763            this.lockedHd.id, this.mainHd.id
36764         );
36765
36766         this.splitterSelector = String.format(
36767            '#{0} div.x-grid-split, #{1} div.x-grid-split',
36768            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
36769         );
36770     },
36771     idToCssName : function(s)
36772     {
36773         return s.replace(/[^a-z0-9]+/ig, '-');
36774     },
36775
36776     getHeaderCell : function(index){
36777         return Roo.DomQuery.select(this.headerSelector)[index];
36778     },
36779
36780     getHeaderCellMeasure : function(index){
36781         return this.getHeaderCell(index).firstChild;
36782     },
36783
36784     getHeaderCellText : function(index){
36785         return this.getHeaderCell(index).firstChild.firstChild;
36786     },
36787
36788     getLockedTable : function(){
36789         return this.lockedBody.dom.firstChild;
36790     },
36791
36792     getBodyTable : function(){
36793         return this.mainBody.dom.firstChild;
36794     },
36795
36796     getLockedRow : function(index){
36797         return this.getLockedTable().rows[index];
36798     },
36799
36800     getRow : function(index){
36801         return this.getBodyTable().rows[index];
36802     },
36803
36804     getRowComposite : function(index){
36805         if(!this.rowEl){
36806             this.rowEl = new Roo.CompositeElementLite();
36807         }
36808         var els = [], lrow, mrow;
36809         if(lrow = this.getLockedRow(index)){
36810             els.push(lrow);
36811         }
36812         if(mrow = this.getRow(index)){
36813             els.push(mrow);
36814         }
36815         this.rowEl.elements = els;
36816         return this.rowEl;
36817     },
36818     /**
36819      * Gets the 'td' of the cell
36820      * 
36821      * @param {Integer} rowIndex row to select
36822      * @param {Integer} colIndex column to select
36823      * 
36824      * @return {Object} 
36825      */
36826     getCell : function(rowIndex, colIndex){
36827         var locked = this.cm.getLockedCount();
36828         var source;
36829         if(colIndex < locked){
36830             source = this.lockedBody.dom.firstChild;
36831         }else{
36832             source = this.mainBody.dom.firstChild;
36833             colIndex -= locked;
36834         }
36835         return source.rows[rowIndex].childNodes[colIndex];
36836     },
36837
36838     getCellText : function(rowIndex, colIndex){
36839         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
36840     },
36841
36842     getCellBox : function(cell){
36843         var b = this.fly(cell).getBox();
36844         if(Roo.isOpera){ // opera fails to report the Y
36845             b.y = cell.offsetTop + this.mainBody.getY();
36846         }
36847         return b;
36848     },
36849
36850     getCellIndex : function(cell){
36851         var id = String(cell.className).match(this.cellRE);
36852         if(id){
36853             return parseInt(id[1], 10);
36854         }
36855         return 0;
36856     },
36857
36858     findHeaderIndex : function(n){
36859         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
36860         return r ? this.getCellIndex(r) : false;
36861     },
36862
36863     findHeaderCell : function(n){
36864         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
36865         return r ? r : false;
36866     },
36867
36868     findRowIndex : function(n){
36869         if(!n){
36870             return false;
36871         }
36872         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
36873         return r ? r.rowIndex : false;
36874     },
36875
36876     findCellIndex : function(node){
36877         var stop = this.el.dom;
36878         while(node && node != stop){
36879             if(this.findRE.test(node.className)){
36880                 return this.getCellIndex(node);
36881             }
36882             node = node.parentNode;
36883         }
36884         return false;
36885     },
36886
36887     getColumnId : function(index){
36888         return this.cm.getColumnId(index);
36889     },
36890
36891     getSplitters : function()
36892     {
36893         if(this.splitterSelector){
36894            return Roo.DomQuery.select(this.splitterSelector);
36895         }else{
36896             return null;
36897       }
36898     },
36899
36900     getSplitter : function(index){
36901         return this.getSplitters()[index];
36902     },
36903
36904     onRowOver : function(e, t){
36905         var row;
36906         if((row = this.findRowIndex(t)) !== false){
36907             this.getRowComposite(row).addClass("x-grid-row-over");
36908         }
36909     },
36910
36911     onRowOut : function(e, t){
36912         var row;
36913         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
36914             this.getRowComposite(row).removeClass("x-grid-row-over");
36915         }
36916     },
36917
36918     renderHeaders : function(){
36919         var cm = this.cm;
36920         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
36921         var cb = [], lb = [], sb = [], lsb = [], p = {};
36922         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
36923             p.cellId = "x-grid-hd-0-" + i;
36924             p.splitId = "x-grid-csplit-0-" + i;
36925             p.id = cm.getColumnId(i);
36926             p.title = cm.getColumnTooltip(i) || "";
36927             p.value = cm.getColumnHeader(i) || "";
36928             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
36929             if(!cm.isLocked(i)){
36930                 cb[cb.length] = ct.apply(p);
36931                 sb[sb.length] = st.apply(p);
36932             }else{
36933                 lb[lb.length] = ct.apply(p);
36934                 lsb[lsb.length] = st.apply(p);
36935             }
36936         }
36937         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
36938                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
36939     },
36940
36941     updateHeaders : function(){
36942         var html = this.renderHeaders();
36943         this.lockedHd.update(html[0]);
36944         this.mainHd.update(html[1]);
36945     },
36946
36947     /**
36948      * Focuses the specified row.
36949      * @param {Number} row The row index
36950      */
36951     focusRow : function(row)
36952     {
36953         //Roo.log('GridView.focusRow');
36954         var x = this.scroller.dom.scrollLeft;
36955         this.focusCell(row, 0, false);
36956         this.scroller.dom.scrollLeft = x;
36957     },
36958
36959     /**
36960      * Focuses the specified cell.
36961      * @param {Number} row The row index
36962      * @param {Number} col The column index
36963      * @param {Boolean} hscroll false to disable horizontal scrolling
36964      */
36965     focusCell : function(row, col, hscroll)
36966     {
36967         //Roo.log('GridView.focusCell');
36968         var el = this.ensureVisible(row, col, hscroll);
36969         this.focusEl.alignTo(el, "tl-tl");
36970         if(Roo.isGecko){
36971             this.focusEl.focus();
36972         }else{
36973             this.focusEl.focus.defer(1, this.focusEl);
36974         }
36975     },
36976
36977     /**
36978      * Scrolls the specified cell into view
36979      * @param {Number} row The row index
36980      * @param {Number} col The column index
36981      * @param {Boolean} hscroll false to disable horizontal scrolling
36982      */
36983     ensureVisible : function(row, col, hscroll)
36984     {
36985         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
36986         //return null; //disable for testing.
36987         if(typeof row != "number"){
36988             row = row.rowIndex;
36989         }
36990         if(row < 0 && row >= this.ds.getCount()){
36991             return  null;
36992         }
36993         col = (col !== undefined ? col : 0);
36994         var cm = this.grid.colModel;
36995         while(cm.isHidden(col)){
36996             col++;
36997         }
36998
36999         var el = this.getCell(row, col);
37000         if(!el){
37001             return null;
37002         }
37003         var c = this.scroller.dom;
37004
37005         var ctop = parseInt(el.offsetTop, 10);
37006         var cleft = parseInt(el.offsetLeft, 10);
37007         var cbot = ctop + el.offsetHeight;
37008         var cright = cleft + el.offsetWidth;
37009         
37010         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
37011         var stop = parseInt(c.scrollTop, 10);
37012         var sleft = parseInt(c.scrollLeft, 10);
37013         var sbot = stop + ch;
37014         var sright = sleft + c.clientWidth;
37015         /*
37016         Roo.log('GridView.ensureVisible:' +
37017                 ' ctop:' + ctop +
37018                 ' c.clientHeight:' + c.clientHeight +
37019                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
37020                 ' stop:' + stop +
37021                 ' cbot:' + cbot +
37022                 ' sbot:' + sbot +
37023                 ' ch:' + ch  
37024                 );
37025         */
37026         if(ctop < stop){
37027              c.scrollTop = ctop;
37028             //Roo.log("set scrolltop to ctop DISABLE?");
37029         }else if(cbot > sbot){
37030             //Roo.log("set scrolltop to cbot-ch");
37031             c.scrollTop = cbot-ch;
37032         }
37033         
37034         if(hscroll !== false){
37035             if(cleft < sleft){
37036                 c.scrollLeft = cleft;
37037             }else if(cright > sright){
37038                 c.scrollLeft = cright-c.clientWidth;
37039             }
37040         }
37041          
37042         return el;
37043     },
37044
37045     updateColumns : function(){
37046         this.grid.stopEditing();
37047         var cm = this.grid.colModel, colIds = this.getColumnIds();
37048         //var totalWidth = cm.getTotalWidth();
37049         var pos = 0;
37050         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37051             //if(cm.isHidden(i)) continue;
37052             var w = cm.getColumnWidth(i);
37053             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
37054             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
37055         }
37056         this.updateSplitters();
37057     },
37058
37059     generateRules : function(cm){
37060         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
37061         Roo.util.CSS.removeStyleSheet(rulesId);
37062         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37063             var cid = cm.getColumnId(i);
37064             var align = '';
37065             if(cm.config[i].align){
37066                 align = 'text-align:'+cm.config[i].align+';';
37067             }
37068             var hidden = '';
37069             if(cm.isHidden(i)){
37070                 hidden = 'display:none;';
37071             }
37072             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
37073             ruleBuf.push(
37074                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
37075                     this.hdSelector, cid, " {\n", align, width, "}\n",
37076                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
37077                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
37078         }
37079         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
37080     },
37081
37082     updateSplitters : function(){
37083         var cm = this.cm, s = this.getSplitters();
37084         if(s){ // splitters not created yet
37085             var pos = 0, locked = true;
37086             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37087                 if(cm.isHidden(i)) continue;
37088                 var w = cm.getColumnWidth(i); // make sure it's a number
37089                 if(!cm.isLocked(i) && locked){
37090                     pos = 0;
37091                     locked = false;
37092                 }
37093                 pos += w;
37094                 s[i].style.left = (pos-this.splitOffset) + "px";
37095             }
37096         }
37097     },
37098
37099     handleHiddenChange : function(colModel, colIndex, hidden){
37100         if(hidden){
37101             this.hideColumn(colIndex);
37102         }else{
37103             this.unhideColumn(colIndex);
37104         }
37105     },
37106
37107     hideColumn : function(colIndex){
37108         var cid = this.getColumnId(colIndex);
37109         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
37110         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
37111         if(Roo.isSafari){
37112             this.updateHeaders();
37113         }
37114         this.updateSplitters();
37115         this.layout();
37116     },
37117
37118     unhideColumn : function(colIndex){
37119         var cid = this.getColumnId(colIndex);
37120         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
37121         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
37122
37123         if(Roo.isSafari){
37124             this.updateHeaders();
37125         }
37126         this.updateSplitters();
37127         this.layout();
37128     },
37129
37130     insertRows : function(dm, firstRow, lastRow, isUpdate){
37131         if(firstRow == 0 && lastRow == dm.getCount()-1){
37132             this.refresh();
37133         }else{
37134             if(!isUpdate){
37135                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
37136             }
37137             var s = this.getScrollState();
37138             var markup = this.renderRows(firstRow, lastRow);
37139             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
37140             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
37141             this.restoreScroll(s);
37142             if(!isUpdate){
37143                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
37144                 this.syncRowHeights(firstRow, lastRow);
37145                 this.stripeRows(firstRow);
37146                 this.layout();
37147             }
37148         }
37149     },
37150
37151     bufferRows : function(markup, target, index){
37152         var before = null, trows = target.rows, tbody = target.tBodies[0];
37153         if(index < trows.length){
37154             before = trows[index];
37155         }
37156         var b = document.createElement("div");
37157         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
37158         var rows = b.firstChild.rows;
37159         for(var i = 0, len = rows.length; i < len; i++){
37160             if(before){
37161                 tbody.insertBefore(rows[0], before);
37162             }else{
37163                 tbody.appendChild(rows[0]);
37164             }
37165         }
37166         b.innerHTML = "";
37167         b = null;
37168     },
37169
37170     deleteRows : function(dm, firstRow, lastRow){
37171         if(dm.getRowCount()<1){
37172             this.fireEvent("beforerefresh", this);
37173             this.mainBody.update("");
37174             this.lockedBody.update("");
37175             this.fireEvent("refresh", this);
37176         }else{
37177             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
37178             var bt = this.getBodyTable();
37179             var tbody = bt.firstChild;
37180             var rows = bt.rows;
37181             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
37182                 tbody.removeChild(rows[firstRow]);
37183             }
37184             this.stripeRows(firstRow);
37185             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
37186         }
37187     },
37188
37189     updateRows : function(dataSource, firstRow, lastRow){
37190         var s = this.getScrollState();
37191         this.refresh();
37192         this.restoreScroll(s);
37193     },
37194
37195     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
37196         if(!noRefresh){
37197            this.refresh();
37198         }
37199         this.updateHeaderSortState();
37200     },
37201
37202     getScrollState : function(){
37203         
37204         var sb = this.scroller.dom;
37205         return {left: sb.scrollLeft, top: sb.scrollTop};
37206     },
37207
37208     stripeRows : function(startRow){
37209         if(!this.grid.stripeRows || this.ds.getCount() < 1){
37210             return;
37211         }
37212         startRow = startRow || 0;
37213         var rows = this.getBodyTable().rows;
37214         var lrows = this.getLockedTable().rows;
37215         var cls = ' x-grid-row-alt ';
37216         for(var i = startRow, len = rows.length; i < len; i++){
37217             var row = rows[i], lrow = lrows[i];
37218             var isAlt = ((i+1) % 2 == 0);
37219             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
37220             if(isAlt == hasAlt){
37221                 continue;
37222             }
37223             if(isAlt){
37224                 row.className += " x-grid-row-alt";
37225             }else{
37226                 row.className = row.className.replace("x-grid-row-alt", "");
37227             }
37228             if(lrow){
37229                 lrow.className = row.className;
37230             }
37231         }
37232     },
37233
37234     restoreScroll : function(state){
37235         //Roo.log('GridView.restoreScroll');
37236         var sb = this.scroller.dom;
37237         sb.scrollLeft = state.left;
37238         sb.scrollTop = state.top;
37239         this.syncScroll();
37240     },
37241
37242     syncScroll : function(){
37243         //Roo.log('GridView.syncScroll');
37244         var sb = this.scroller.dom;
37245         var sh = this.mainHd.dom;
37246         var bs = this.mainBody.dom;
37247         var lv = this.lockedBody.dom;
37248         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
37249         lv.scrollTop = bs.scrollTop = sb.scrollTop;
37250     },
37251
37252     handleScroll : function(e){
37253         this.syncScroll();
37254         var sb = this.scroller.dom;
37255         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
37256         e.stopEvent();
37257     },
37258
37259     handleWheel : function(e){
37260         var d = e.getWheelDelta();
37261         this.scroller.dom.scrollTop -= d*22;
37262         // set this here to prevent jumpy scrolling on large tables
37263         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
37264         e.stopEvent();
37265     },
37266
37267     renderRows : function(startRow, endRow){
37268         // pull in all the crap needed to render rows
37269         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
37270         var colCount = cm.getColumnCount();
37271
37272         if(ds.getCount() < 1){
37273             return ["", ""];
37274         }
37275
37276         // build a map for all the columns
37277         var cs = [];
37278         for(var i = 0; i < colCount; i++){
37279             var name = cm.getDataIndex(i);
37280             cs[i] = {
37281                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
37282                 renderer : cm.getRenderer(i),
37283                 id : cm.getColumnId(i),
37284                 locked : cm.isLocked(i)
37285             };
37286         }
37287
37288         startRow = startRow || 0;
37289         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
37290
37291         // records to render
37292         var rs = ds.getRange(startRow, endRow);
37293
37294         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
37295     },
37296
37297     // As much as I hate to duplicate code, this was branched because FireFox really hates
37298     // [].join("") on strings. The performance difference was substantial enough to
37299     // branch this function
37300     doRender : Roo.isGecko ?
37301             function(cs, rs, ds, startRow, colCount, stripe){
37302                 var ts = this.templates, ct = ts.cell, rt = ts.row;
37303                 // buffers
37304                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
37305                 
37306                 var hasListener = this.grid.hasListener('rowclass');
37307                 var rowcfg = {};
37308                 for(var j = 0, len = rs.length; j < len; j++){
37309                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
37310                     for(var i = 0; i < colCount; i++){
37311                         c = cs[i];
37312                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
37313                         p.id = c.id;
37314                         p.css = p.attr = "";
37315                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
37316                         if(p.value == undefined || p.value === "") p.value = "&#160;";
37317                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
37318                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
37319                         }
37320                         var markup = ct.apply(p);
37321                         if(!c.locked){
37322                             cb+= markup;
37323                         }else{
37324                             lcb+= markup;
37325                         }
37326                     }
37327                     var alt = [];
37328                     if(stripe && ((rowIndex+1) % 2 == 0)){
37329                         alt.push("x-grid-row-alt")
37330                     }
37331                     if(r.dirty){
37332                         alt.push(  " x-grid-dirty-row");
37333                     }
37334                     rp.cells = lcb;
37335                     if(this.getRowClass){
37336                         alt.push(this.getRowClass(r, rowIndex));
37337                     }
37338                     if (hasListener) {
37339                         rowcfg = {
37340                              
37341                             record: r,
37342                             rowIndex : rowIndex,
37343                             rowClass : ''
37344                         }
37345                         this.grid.fireEvent('rowclass', this, rowcfg);
37346                         alt.push(rowcfg.rowClass);
37347                     }
37348                     rp.alt = alt.join(" ");
37349                     lbuf+= rt.apply(rp);
37350                     rp.cells = cb;
37351                     buf+=  rt.apply(rp);
37352                 }
37353                 return [lbuf, buf];
37354             } :
37355             function(cs, rs, ds, startRow, colCount, stripe){
37356                 var ts = this.templates, ct = ts.cell, rt = ts.row;
37357                 // buffers
37358                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
37359                 var hasListener = this.grid.hasListener('rowclass');
37360  
37361                 var rowcfg = {};
37362                 for(var j = 0, len = rs.length; j < len; j++){
37363                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
37364                     for(var i = 0; i < colCount; i++){
37365                         c = cs[i];
37366                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
37367                         p.id = c.id;
37368                         p.css = p.attr = "";
37369                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
37370                         if(p.value == undefined || p.value === "") p.value = "&#160;";
37371                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
37372                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
37373                         }
37374                         
37375                         var markup = ct.apply(p);
37376                         if(!c.locked){
37377                             cb[cb.length] = markup;
37378                         }else{
37379                             lcb[lcb.length] = markup;
37380                         }
37381                     }
37382                     var alt = [];
37383                     if(stripe && ((rowIndex+1) % 2 == 0)){
37384                         alt.push( "x-grid-row-alt");
37385                     }
37386                     if(r.dirty){
37387                         alt.push(" x-grid-dirty-row");
37388                     }
37389                     rp.cells = lcb;
37390                     if(this.getRowClass){
37391                         alt.push( this.getRowClass(r, rowIndex));
37392                     }
37393                     if (hasListener) {
37394                         rowcfg = {
37395                              
37396                             record: r,
37397                             rowIndex : rowIndex,
37398                             rowClass : ''
37399                         }
37400                         this.grid.fireEvent('rowclass', this, rowcfg);
37401                         alt.push(rowcfg.rowClass);
37402                     }
37403                     rp.alt = alt.join(" ");
37404                     rp.cells = lcb.join("");
37405                     lbuf[lbuf.length] = rt.apply(rp);
37406                     rp.cells = cb.join("");
37407                     buf[buf.length] =  rt.apply(rp);
37408                 }
37409                 return [lbuf.join(""), buf.join("")];
37410             },
37411
37412     renderBody : function(){
37413         var markup = this.renderRows();
37414         var bt = this.templates.body;
37415         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
37416     },
37417
37418     /**
37419      * Refreshes the grid
37420      * @param {Boolean} headersToo
37421      */
37422     refresh : function(headersToo){
37423         this.fireEvent("beforerefresh", this);
37424         this.grid.stopEditing();
37425         var result = this.renderBody();
37426         this.lockedBody.update(result[0]);
37427         this.mainBody.update(result[1]);
37428         if(headersToo === true){
37429             this.updateHeaders();
37430             this.updateColumns();
37431             this.updateSplitters();
37432             this.updateHeaderSortState();
37433         }
37434         this.syncRowHeights();
37435         this.layout();
37436         this.fireEvent("refresh", this);
37437     },
37438
37439     handleColumnMove : function(cm, oldIndex, newIndex){
37440         this.indexMap = null;
37441         var s = this.getScrollState();
37442         this.refresh(true);
37443         this.restoreScroll(s);
37444         this.afterMove(newIndex);
37445     },
37446
37447     afterMove : function(colIndex){
37448         if(this.enableMoveAnim && Roo.enableFx){
37449             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
37450         }
37451         // if multisort - fix sortOrder, and reload..
37452         if (this.grid.dataSource.multiSort) {
37453             // the we can call sort again..
37454             var dm = this.grid.dataSource;
37455             var cm = this.grid.colModel;
37456             var so = [];
37457             for(var i = 0; i < cm.config.length; i++ ) {
37458                 
37459                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
37460                     continue; // dont' bother, it's not in sort list or being set.
37461                 }
37462                 
37463                 so.push(cm.config[i].dataIndex);
37464             };
37465             dm.sortOrder = so;
37466             dm.load(dm.lastOptions);
37467             
37468             
37469         }
37470         
37471     },
37472
37473     updateCell : function(dm, rowIndex, dataIndex){
37474         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
37475         if(typeof colIndex == "undefined"){ // not present in grid
37476             return;
37477         }
37478         var cm = this.grid.colModel;
37479         var cell = this.getCell(rowIndex, colIndex);
37480         var cellText = this.getCellText(rowIndex, colIndex);
37481
37482         var p = {
37483             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
37484             id : cm.getColumnId(colIndex),
37485             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
37486         };
37487         var renderer = cm.getRenderer(colIndex);
37488         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
37489         if(typeof val == "undefined" || val === "") val = "&#160;";
37490         cellText.innerHTML = val;
37491         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
37492         this.syncRowHeights(rowIndex, rowIndex);
37493     },
37494
37495     calcColumnWidth : function(colIndex, maxRowsToMeasure){
37496         var maxWidth = 0;
37497         if(this.grid.autoSizeHeaders){
37498             var h = this.getHeaderCellMeasure(colIndex);
37499             maxWidth = Math.max(maxWidth, h.scrollWidth);
37500         }
37501         var tb, index;
37502         if(this.cm.isLocked(colIndex)){
37503             tb = this.getLockedTable();
37504             index = colIndex;
37505         }else{
37506             tb = this.getBodyTable();
37507             index = colIndex - this.cm.getLockedCount();
37508         }
37509         if(tb && tb.rows){
37510             var rows = tb.rows;
37511             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
37512             for(var i = 0; i < stopIndex; i++){
37513                 var cell = rows[i].childNodes[index].firstChild;
37514                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
37515             }
37516         }
37517         return maxWidth + /*margin for error in IE*/ 5;
37518     },
37519     /**
37520      * Autofit a column to its content.
37521      * @param {Number} colIndex
37522      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
37523      */
37524      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
37525          if(this.cm.isHidden(colIndex)){
37526              return; // can't calc a hidden column
37527          }
37528         if(forceMinSize){
37529             var cid = this.cm.getColumnId(colIndex);
37530             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
37531            if(this.grid.autoSizeHeaders){
37532                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
37533            }
37534         }
37535         var newWidth = this.calcColumnWidth(colIndex);
37536         this.cm.setColumnWidth(colIndex,
37537             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
37538         if(!suppressEvent){
37539             this.grid.fireEvent("columnresize", colIndex, newWidth);
37540         }
37541     },
37542
37543     /**
37544      * Autofits all columns to their content and then expands to fit any extra space in the grid
37545      */
37546      autoSizeColumns : function(){
37547         var cm = this.grid.colModel;
37548         var colCount = cm.getColumnCount();
37549         for(var i = 0; i < colCount; i++){
37550             this.autoSizeColumn(i, true, true);
37551         }
37552         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
37553             this.fitColumns();
37554         }else{
37555             this.updateColumns();
37556             this.layout();
37557         }
37558     },
37559
37560     /**
37561      * Autofits all columns to the grid's width proportionate with their current size
37562      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
37563      */
37564     fitColumns : function(reserveScrollSpace){
37565         var cm = this.grid.colModel;
37566         var colCount = cm.getColumnCount();
37567         var cols = [];
37568         var width = 0;
37569         var i, w;
37570         for (i = 0; i < colCount; i++){
37571             if(!cm.isHidden(i) && !cm.isFixed(i)){
37572                 w = cm.getColumnWidth(i);
37573                 cols.push(i);
37574                 cols.push(w);
37575                 width += w;
37576             }
37577         }
37578         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
37579         if(reserveScrollSpace){
37580             avail -= 17;
37581         }
37582         var frac = (avail - cm.getTotalWidth())/width;
37583         while (cols.length){
37584             w = cols.pop();
37585             i = cols.pop();
37586             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
37587         }
37588         this.updateColumns();
37589         this.layout();
37590     },
37591
37592     onRowSelect : function(rowIndex){
37593         var row = this.getRowComposite(rowIndex);
37594         row.addClass("x-grid-row-selected");
37595     },
37596
37597     onRowDeselect : function(rowIndex){
37598         var row = this.getRowComposite(rowIndex);
37599         row.removeClass("x-grid-row-selected");
37600     },
37601
37602     onCellSelect : function(row, col){
37603         var cell = this.getCell(row, col);
37604         if(cell){
37605             Roo.fly(cell).addClass("x-grid-cell-selected");
37606         }
37607     },
37608
37609     onCellDeselect : function(row, col){
37610         var cell = this.getCell(row, col);
37611         if(cell){
37612             Roo.fly(cell).removeClass("x-grid-cell-selected");
37613         }
37614     },
37615
37616     updateHeaderSortState : function(){
37617         
37618         // sort state can be single { field: xxx, direction : yyy}
37619         // or   { xxx=>ASC , yyy : DESC ..... }
37620         
37621         var mstate = {};
37622         if (!this.ds.multiSort) { 
37623             var state = this.ds.getSortState();
37624             if(!state){
37625                 return;
37626             }
37627             mstate[state.field] = state.direction;
37628             // FIXME... - this is not used here.. but might be elsewhere..
37629             this.sortState = state;
37630             
37631         } else {
37632             mstate = this.ds.sortToggle;
37633         }
37634         //remove existing sort classes..
37635         
37636         var sc = this.sortClasses;
37637         var hds = this.el.select(this.headerSelector).removeClass(sc);
37638         
37639         for(var f in mstate) {
37640         
37641             var sortColumn = this.cm.findColumnIndex(f);
37642             
37643             if(sortColumn != -1){
37644                 var sortDir = mstate[f];        
37645                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
37646             }
37647         }
37648         
37649          
37650         
37651     },
37652
37653
37654     handleHeaderClick : function(g, index){
37655         if(this.headersDisabled){
37656             return;
37657         }
37658         var dm = g.dataSource, cm = g.colModel;
37659         if(!cm.isSortable(index)){
37660             return;
37661         }
37662         g.stopEditing();
37663         
37664         if (dm.multiSort) {
37665             // update the sortOrder
37666             var so = [];
37667             for(var i = 0; i < cm.config.length; i++ ) {
37668                 
37669                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
37670                     continue; // dont' bother, it's not in sort list or being set.
37671                 }
37672                 
37673                 so.push(cm.config[i].dataIndex);
37674             };
37675             dm.sortOrder = so;
37676         }
37677         
37678         
37679         dm.sort(cm.getDataIndex(index));
37680     },
37681
37682
37683     destroy : function(){
37684         if(this.colMenu){
37685             this.colMenu.removeAll();
37686             Roo.menu.MenuMgr.unregister(this.colMenu);
37687             this.colMenu.getEl().remove();
37688             delete this.colMenu;
37689         }
37690         if(this.hmenu){
37691             this.hmenu.removeAll();
37692             Roo.menu.MenuMgr.unregister(this.hmenu);
37693             this.hmenu.getEl().remove();
37694             delete this.hmenu;
37695         }
37696         if(this.grid.enableColumnMove){
37697             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
37698             if(dds){
37699                 for(var dd in dds){
37700                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
37701                         var elid = dds[dd].dragElId;
37702                         dds[dd].unreg();
37703                         Roo.get(elid).remove();
37704                     } else if(dds[dd].config.isTarget){
37705                         dds[dd].proxyTop.remove();
37706                         dds[dd].proxyBottom.remove();
37707                         dds[dd].unreg();
37708                     }
37709                     if(Roo.dd.DDM.locationCache[dd]){
37710                         delete Roo.dd.DDM.locationCache[dd];
37711                     }
37712                 }
37713                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
37714             }
37715         }
37716         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
37717         this.bind(null, null);
37718         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
37719     },
37720
37721     handleLockChange : function(){
37722         this.refresh(true);
37723     },
37724
37725     onDenyColumnLock : function(){
37726
37727     },
37728
37729     onDenyColumnHide : function(){
37730
37731     },
37732
37733     handleHdMenuClick : function(item){
37734         var index = this.hdCtxIndex;
37735         var cm = this.cm, ds = this.ds;
37736         switch(item.id){
37737             case "asc":
37738                 ds.sort(cm.getDataIndex(index), "ASC");
37739                 break;
37740             case "desc":
37741                 ds.sort(cm.getDataIndex(index), "DESC");
37742                 break;
37743             case "lock":
37744                 var lc = cm.getLockedCount();
37745                 if(cm.getColumnCount(true) <= lc+1){
37746                     this.onDenyColumnLock();
37747                     return;
37748                 }
37749                 if(lc != index){
37750                     cm.setLocked(index, true, true);
37751                     cm.moveColumn(index, lc);
37752                     this.grid.fireEvent("columnmove", index, lc);
37753                 }else{
37754                     cm.setLocked(index, true);
37755                 }
37756             break;
37757             case "unlock":
37758                 var lc = cm.getLockedCount();
37759                 if((lc-1) != index){
37760                     cm.setLocked(index, false, true);
37761                     cm.moveColumn(index, lc-1);
37762                     this.grid.fireEvent("columnmove", index, lc-1);
37763                 }else{
37764                     cm.setLocked(index, false);
37765                 }
37766             break;
37767             default:
37768                 index = cm.getIndexById(item.id.substr(4));
37769                 if(index != -1){
37770                     if(item.checked && cm.getColumnCount(true) <= 1){
37771                         this.onDenyColumnHide();
37772                         return false;
37773                     }
37774                     cm.setHidden(index, item.checked);
37775                 }
37776         }
37777         return true;
37778     },
37779
37780     beforeColMenuShow : function(){
37781         var cm = this.cm,  colCount = cm.getColumnCount();
37782         this.colMenu.removeAll();
37783         for(var i = 0; i < colCount; i++){
37784             this.colMenu.add(new Roo.menu.CheckItem({
37785                 id: "col-"+cm.getColumnId(i),
37786                 text: cm.getColumnHeader(i),
37787                 checked: !cm.isHidden(i),
37788                 hideOnClick:false
37789             }));
37790         }
37791     },
37792
37793     handleHdCtx : function(g, index, e){
37794         e.stopEvent();
37795         var hd = this.getHeaderCell(index);
37796         this.hdCtxIndex = index;
37797         var ms = this.hmenu.items, cm = this.cm;
37798         ms.get("asc").setDisabled(!cm.isSortable(index));
37799         ms.get("desc").setDisabled(!cm.isSortable(index));
37800         if(this.grid.enableColLock !== false){
37801             ms.get("lock").setDisabled(cm.isLocked(index));
37802             ms.get("unlock").setDisabled(!cm.isLocked(index));
37803         }
37804         this.hmenu.show(hd, "tl-bl");
37805     },
37806
37807     handleHdOver : function(e){
37808         var hd = this.findHeaderCell(e.getTarget());
37809         if(hd && !this.headersDisabled){
37810             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
37811                this.fly(hd).addClass("x-grid-hd-over");
37812             }
37813         }
37814     },
37815
37816     handleHdOut : function(e){
37817         var hd = this.findHeaderCell(e.getTarget());
37818         if(hd){
37819             this.fly(hd).removeClass("x-grid-hd-over");
37820         }
37821     },
37822
37823     handleSplitDblClick : function(e, t){
37824         var i = this.getCellIndex(t);
37825         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
37826             this.autoSizeColumn(i, true);
37827             this.layout();
37828         }
37829     },
37830
37831     render : function(){
37832
37833         var cm = this.cm;
37834         var colCount = cm.getColumnCount();
37835
37836         if(this.grid.monitorWindowResize === true){
37837             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37838         }
37839         var header = this.renderHeaders();
37840         var body = this.templates.body.apply({rows:""});
37841         var html = this.templates.master.apply({
37842             lockedBody: body,
37843             body: body,
37844             lockedHeader: header[0],
37845             header: header[1]
37846         });
37847
37848         //this.updateColumns();
37849
37850         this.grid.getGridEl().dom.innerHTML = html;
37851
37852         this.initElements();
37853         
37854         // a kludge to fix the random scolling effect in webkit
37855         this.el.on("scroll", function() {
37856             this.el.dom.scrollTop=0; // hopefully not recursive..
37857         },this);
37858
37859         this.scroller.on("scroll", this.handleScroll, this);
37860         this.lockedBody.on("mousewheel", this.handleWheel, this);
37861         this.mainBody.on("mousewheel", this.handleWheel, this);
37862
37863         this.mainHd.on("mouseover", this.handleHdOver, this);
37864         this.mainHd.on("mouseout", this.handleHdOut, this);
37865         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
37866                 {delegate: "."+this.splitClass});
37867
37868         this.lockedHd.on("mouseover", this.handleHdOver, this);
37869         this.lockedHd.on("mouseout", this.handleHdOut, this);
37870         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
37871                 {delegate: "."+this.splitClass});
37872
37873         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
37874             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
37875         }
37876
37877         this.updateSplitters();
37878
37879         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
37880             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
37881             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
37882         }
37883
37884         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
37885             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
37886             this.hmenu.add(
37887                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
37888                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
37889             );
37890             if(this.grid.enableColLock !== false){
37891                 this.hmenu.add('-',
37892                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
37893                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
37894                 );
37895             }
37896             if(this.grid.enableColumnHide !== false){
37897
37898                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
37899                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
37900                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
37901
37902                 this.hmenu.add('-',
37903                     {id:"columns", text: this.columnsText, menu: this.colMenu}
37904                 );
37905             }
37906             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
37907
37908             this.grid.on("headercontextmenu", this.handleHdCtx, this);
37909         }
37910
37911         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
37912             this.dd = new Roo.grid.GridDragZone(this.grid, {
37913                 ddGroup : this.grid.ddGroup || 'GridDD'
37914             });
37915             
37916         }
37917
37918         /*
37919         for(var i = 0; i < colCount; i++){
37920             if(cm.isHidden(i)){
37921                 this.hideColumn(i);
37922             }
37923             if(cm.config[i].align){
37924                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
37925                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
37926             }
37927         }*/
37928         
37929         this.updateHeaderSortState();
37930
37931         this.beforeInitialResize();
37932         this.layout(true);
37933
37934         // two part rendering gives faster view to the user
37935         this.renderPhase2.defer(1, this);
37936     },
37937
37938     renderPhase2 : function(){
37939         // render the rows now
37940         this.refresh();
37941         if(this.grid.autoSizeColumns){
37942             this.autoSizeColumns();
37943         }
37944     },
37945
37946     beforeInitialResize : function(){
37947
37948     },
37949
37950     onColumnSplitterMoved : function(i, w){
37951         this.userResized = true;
37952         var cm = this.grid.colModel;
37953         cm.setColumnWidth(i, w, true);
37954         var cid = cm.getColumnId(i);
37955         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
37956         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
37957         this.updateSplitters();
37958         this.layout();
37959         this.grid.fireEvent("columnresize", i, w);
37960     },
37961
37962     syncRowHeights : function(startIndex, endIndex){
37963         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
37964             startIndex = startIndex || 0;
37965             var mrows = this.getBodyTable().rows;
37966             var lrows = this.getLockedTable().rows;
37967             var len = mrows.length-1;
37968             endIndex = Math.min(endIndex || len, len);
37969             for(var i = startIndex; i <= endIndex; i++){
37970                 var m = mrows[i], l = lrows[i];
37971                 var h = Math.max(m.offsetHeight, l.offsetHeight);
37972                 m.style.height = l.style.height = h + "px";
37973             }
37974         }
37975     },
37976
37977     layout : function(initialRender, is2ndPass){
37978         var g = this.grid;
37979         var auto = g.autoHeight;
37980         var scrollOffset = 16;
37981         var c = g.getGridEl(), cm = this.cm,
37982                 expandCol = g.autoExpandColumn,
37983                 gv = this;
37984         //c.beginMeasure();
37985
37986         if(!c.dom.offsetWidth){ // display:none?
37987             if(initialRender){
37988                 this.lockedWrap.show();
37989                 this.mainWrap.show();
37990             }
37991             return;
37992         }
37993
37994         var hasLock = this.cm.isLocked(0);
37995
37996         var tbh = this.headerPanel.getHeight();
37997         var bbh = this.footerPanel.getHeight();
37998
37999         if(auto){
38000             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
38001             var newHeight = ch + c.getBorderWidth("tb");
38002             if(g.maxHeight){
38003                 newHeight = Math.min(g.maxHeight, newHeight);
38004             }
38005             c.setHeight(newHeight);
38006         }
38007
38008         if(g.autoWidth){
38009             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
38010         }
38011
38012         var s = this.scroller;
38013
38014         var csize = c.getSize(true);
38015
38016         this.el.setSize(csize.width, csize.height);
38017
38018         this.headerPanel.setWidth(csize.width);
38019         this.footerPanel.setWidth(csize.width);
38020
38021         var hdHeight = this.mainHd.getHeight();
38022         var vw = csize.width;
38023         var vh = csize.height - (tbh + bbh);
38024
38025         s.setSize(vw, vh);
38026
38027         var bt = this.getBodyTable();
38028         var ltWidth = hasLock ?
38029                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
38030
38031         var scrollHeight = bt.offsetHeight;
38032         var scrollWidth = ltWidth + bt.offsetWidth;
38033         var vscroll = false, hscroll = false;
38034
38035         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
38036
38037         var lw = this.lockedWrap, mw = this.mainWrap;
38038         var lb = this.lockedBody, mb = this.mainBody;
38039
38040         setTimeout(function(){
38041             var t = s.dom.offsetTop;
38042             var w = s.dom.clientWidth,
38043                 h = s.dom.clientHeight;
38044
38045             lw.setTop(t);
38046             lw.setSize(ltWidth, h);
38047
38048             mw.setLeftTop(ltWidth, t);
38049             mw.setSize(w-ltWidth, h);
38050
38051             lb.setHeight(h-hdHeight);
38052             mb.setHeight(h-hdHeight);
38053
38054             if(is2ndPass !== true && !gv.userResized && expandCol){
38055                 // high speed resize without full column calculation
38056                 
38057                 var ci = cm.getIndexById(expandCol);
38058                 if (ci < 0) {
38059                     ci = cm.findColumnIndex(expandCol);
38060                 }
38061                 ci = Math.max(0, ci); // make sure it's got at least the first col.
38062                 var expandId = cm.getColumnId(ci);
38063                 var  tw = cm.getTotalWidth(false);
38064                 var currentWidth = cm.getColumnWidth(ci);
38065                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
38066                 if(currentWidth != cw){
38067                     cm.setColumnWidth(ci, cw, true);
38068                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
38069                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
38070                     gv.updateSplitters();
38071                     gv.layout(false, true);
38072                 }
38073             }
38074
38075             if(initialRender){
38076                 lw.show();
38077                 mw.show();
38078             }
38079             //c.endMeasure();
38080         }, 10);
38081     },
38082
38083     onWindowResize : function(){
38084         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
38085             return;
38086         }
38087         this.layout();
38088     },
38089
38090     appendFooter : function(parentEl){
38091         return null;
38092     },
38093
38094     sortAscText : "Sort Ascending",
38095     sortDescText : "Sort Descending",
38096     lockText : "Lock Column",
38097     unlockText : "Unlock Column",
38098     columnsText : "Columns"
38099 });
38100
38101
38102 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
38103     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
38104     this.proxy.el.addClass('x-grid3-col-dd');
38105 };
38106
38107 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
38108     handleMouseDown : function(e){
38109
38110     },
38111
38112     callHandleMouseDown : function(e){
38113         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
38114     }
38115 });
38116 /*
38117  * Based on:
38118  * Ext JS Library 1.1.1
38119  * Copyright(c) 2006-2007, Ext JS, LLC.
38120  *
38121  * Originally Released Under LGPL - original licence link has changed is not relivant.
38122  *
38123  * Fork - LGPL
38124  * <script type="text/javascript">
38125  */
38126  
38127 // private
38128 // This is a support class used internally by the Grid components
38129 Roo.grid.SplitDragZone = function(grid, hd, hd2){
38130     this.grid = grid;
38131     this.view = grid.getView();
38132     this.proxy = this.view.resizeProxy;
38133     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
38134         "gridSplitters" + this.grid.getGridEl().id, {
38135         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
38136     });
38137     this.setHandleElId(Roo.id(hd));
38138     this.setOuterHandleElId(Roo.id(hd2));
38139     this.scroll = false;
38140 };
38141 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
38142     fly: Roo.Element.fly,
38143
38144     b4StartDrag : function(x, y){
38145         this.view.headersDisabled = true;
38146         this.proxy.setHeight(this.view.mainWrap.getHeight());
38147         var w = this.cm.getColumnWidth(this.cellIndex);
38148         var minw = Math.max(w-this.grid.minColumnWidth, 0);
38149         this.resetConstraints();
38150         this.setXConstraint(minw, 1000);
38151         this.setYConstraint(0, 0);
38152         this.minX = x - minw;
38153         this.maxX = x + 1000;
38154         this.startPos = x;
38155         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
38156     },
38157
38158
38159     handleMouseDown : function(e){
38160         ev = Roo.EventObject.setEvent(e);
38161         var t = this.fly(ev.getTarget());
38162         if(t.hasClass("x-grid-split")){
38163             this.cellIndex = this.view.getCellIndex(t.dom);
38164             this.split = t.dom;
38165             this.cm = this.grid.colModel;
38166             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
38167                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
38168             }
38169         }
38170     },
38171
38172     endDrag : function(e){
38173         this.view.headersDisabled = false;
38174         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
38175         var diff = endX - this.startPos;
38176         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
38177     },
38178
38179     autoOffset : function(){
38180         this.setDelta(0,0);
38181     }
38182 });/*
38183  * Based on:
38184  * Ext JS Library 1.1.1
38185  * Copyright(c) 2006-2007, Ext JS, LLC.
38186  *
38187  * Originally Released Under LGPL - original licence link has changed is not relivant.
38188  *
38189  * Fork - LGPL
38190  * <script type="text/javascript">
38191  */
38192  
38193 // private
38194 // This is a support class used internally by the Grid components
38195 Roo.grid.GridDragZone = function(grid, config){
38196     this.view = grid.getView();
38197     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
38198     if(this.view.lockedBody){
38199         this.setHandleElId(Roo.id(this.view.mainBody.dom));
38200         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
38201     }
38202     this.scroll = false;
38203     this.grid = grid;
38204     this.ddel = document.createElement('div');
38205     this.ddel.className = 'x-grid-dd-wrap';
38206 };
38207
38208 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
38209     ddGroup : "GridDD",
38210
38211     getDragData : function(e){
38212         var t = Roo.lib.Event.getTarget(e);
38213         var rowIndex = this.view.findRowIndex(t);
38214         var sm = this.grid.selModel;
38215             
38216         //Roo.log(rowIndex);
38217         
38218         if (sm.getSelectedCell) {
38219             // cell selection..
38220             if (!sm.getSelectedCell()) {
38221                 return false;
38222             }
38223             if (rowIndex != sm.getSelectedCell()[0]) {
38224                 return false;
38225             }
38226         
38227         }
38228         
38229         if(rowIndex !== false){
38230             
38231             // if editorgrid.. 
38232             
38233             
38234             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
38235                
38236             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
38237               //  
38238             //}
38239             if (e.hasModifier()){
38240                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
38241             }
38242             
38243             Roo.log("getDragData");
38244             
38245             return {
38246                 grid: this.grid,
38247                 ddel: this.ddel,
38248                 rowIndex: rowIndex,
38249                 selections:sm.getSelections ? sm.getSelections() : (
38250                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
38251                 )
38252             };
38253         }
38254         return false;
38255     },
38256
38257     onInitDrag : function(e){
38258         var data = this.dragData;
38259         this.ddel.innerHTML = this.grid.getDragDropText();
38260         this.proxy.update(this.ddel);
38261         // fire start drag?
38262     },
38263
38264     afterRepair : function(){
38265         this.dragging = false;
38266     },
38267
38268     getRepairXY : function(e, data){
38269         return false;
38270     },
38271
38272     onEndDrag : function(data, e){
38273         // fire end drag?
38274     },
38275
38276     onValidDrop : function(dd, e, id){
38277         // fire drag drop?
38278         this.hideProxy();
38279     },
38280
38281     beforeInvalidDrop : function(e, id){
38282
38283     }
38284 });/*
38285  * Based on:
38286  * Ext JS Library 1.1.1
38287  * Copyright(c) 2006-2007, Ext JS, LLC.
38288  *
38289  * Originally Released Under LGPL - original licence link has changed is not relivant.
38290  *
38291  * Fork - LGPL
38292  * <script type="text/javascript">
38293  */
38294  
38295
38296 /**
38297  * @class Roo.grid.ColumnModel
38298  * @extends Roo.util.Observable
38299  * This is the default implementation of a ColumnModel used by the Grid. It defines
38300  * the columns in the grid.
38301  * <br>Usage:<br>
38302  <pre><code>
38303  var colModel = new Roo.grid.ColumnModel([
38304         {header: "Ticker", width: 60, sortable: true, locked: true},
38305         {header: "Company Name", width: 150, sortable: true},
38306         {header: "Market Cap.", width: 100, sortable: true},
38307         {header: "$ Sales", width: 100, sortable: true, renderer: money},
38308         {header: "Employees", width: 100, sortable: true, resizable: false}
38309  ]);
38310  </code></pre>
38311  * <p>
38312  
38313  * The config options listed for this class are options which may appear in each
38314  * individual column definition.
38315  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
38316  * @constructor
38317  * @param {Object} config An Array of column config objects. See this class's
38318  * config objects for details.
38319 */
38320 Roo.grid.ColumnModel = function(config){
38321         /**
38322      * The config passed into the constructor
38323      */
38324     this.config = config;
38325     this.lookup = {};
38326
38327     // if no id, create one
38328     // if the column does not have a dataIndex mapping,
38329     // map it to the order it is in the config
38330     for(var i = 0, len = config.length; i < len; i++){
38331         var c = config[i];
38332         if(typeof c.dataIndex == "undefined"){
38333             c.dataIndex = i;
38334         }
38335         if(typeof c.renderer == "string"){
38336             c.renderer = Roo.util.Format[c.renderer];
38337         }
38338         if(typeof c.id == "undefined"){
38339             c.id = Roo.id();
38340         }
38341         if(c.editor && c.editor.xtype){
38342             c.editor  = Roo.factory(c.editor, Roo.grid);
38343         }
38344         if(c.editor && c.editor.isFormField){
38345             c.editor = new Roo.grid.GridEditor(c.editor);
38346         }
38347         this.lookup[c.id] = c;
38348     }
38349
38350     /**
38351      * The width of columns which have no width specified (defaults to 100)
38352      * @type Number
38353      */
38354     this.defaultWidth = 100;
38355
38356     /**
38357      * Default sortable of columns which have no sortable specified (defaults to false)
38358      * @type Boolean
38359      */
38360     this.defaultSortable = false;
38361
38362     this.addEvents({
38363         /**
38364              * @event widthchange
38365              * Fires when the width of a column changes.
38366              * @param {ColumnModel} this
38367              * @param {Number} columnIndex The column index
38368              * @param {Number} newWidth The new width
38369              */
38370             "widthchange": true,
38371         /**
38372              * @event headerchange
38373              * Fires when the text of a header changes.
38374              * @param {ColumnModel} this
38375              * @param {Number} columnIndex The column index
38376              * @param {Number} newText The new header text
38377              */
38378             "headerchange": true,
38379         /**
38380              * @event hiddenchange
38381              * Fires when a column is hidden or "unhidden".
38382              * @param {ColumnModel} this
38383              * @param {Number} columnIndex The column index
38384              * @param {Boolean} hidden true if hidden, false otherwise
38385              */
38386             "hiddenchange": true,
38387             /**
38388          * @event columnmoved
38389          * Fires when a column is moved.
38390          * @param {ColumnModel} this
38391          * @param {Number} oldIndex
38392          * @param {Number} newIndex
38393          */
38394         "columnmoved" : true,
38395         /**
38396          * @event columlockchange
38397          * Fires when a column's locked state is changed
38398          * @param {ColumnModel} this
38399          * @param {Number} colIndex
38400          * @param {Boolean} locked true if locked
38401          */
38402         "columnlockchange" : true
38403     });
38404     Roo.grid.ColumnModel.superclass.constructor.call(this);
38405 };
38406 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
38407     /**
38408      * @cfg {String} header The header text to display in the Grid view.
38409      */
38410     /**
38411      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
38412      * {@link Roo.data.Record} definition from which to draw the column's value. If not
38413      * specified, the column's index is used as an index into the Record's data Array.
38414      */
38415     /**
38416      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
38417      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
38418      */
38419     /**
38420      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
38421      * Defaults to the value of the {@link #defaultSortable} property.
38422      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
38423      */
38424     /**
38425      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
38426      */
38427     /**
38428      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
38429      */
38430     /**
38431      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
38432      */
38433     /**
38434      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
38435      */
38436     /**
38437      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
38438      * given the cell's data value. See {@link #setRenderer}. If not specified, the
38439      * default renderer uses the raw data value.
38440      */
38441        /**
38442      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
38443      */
38444     /**
38445      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
38446      */
38447
38448     /**
38449      * Returns the id of the column at the specified index.
38450      * @param {Number} index The column index
38451      * @return {String} the id
38452      */
38453     getColumnId : function(index){
38454         return this.config[index].id;
38455     },
38456
38457     /**
38458      * Returns the column for a specified id.
38459      * @param {String} id The column id
38460      * @return {Object} the column
38461      */
38462     getColumnById : function(id){
38463         return this.lookup[id];
38464     },
38465
38466     
38467     /**
38468      * Returns the column for a specified dataIndex.
38469      * @param {String} dataIndex The column dataIndex
38470      * @return {Object|Boolean} the column or false if not found
38471      */
38472     getColumnByDataIndex: function(dataIndex){
38473         var index = this.findColumnIndex(dataIndex);
38474         return index > -1 ? this.config[index] : false;
38475     },
38476     
38477     /**
38478      * Returns the index for a specified column id.
38479      * @param {String} id The column id
38480      * @return {Number} the index, or -1 if not found
38481      */
38482     getIndexById : function(id){
38483         for(var i = 0, len = this.config.length; i < len; i++){
38484             if(this.config[i].id == id){
38485                 return i;
38486             }
38487         }
38488         return -1;
38489     },
38490     
38491     /**
38492      * Returns the index for a specified column dataIndex.
38493      * @param {String} dataIndex The column dataIndex
38494      * @return {Number} the index, or -1 if not found
38495      */
38496     
38497     findColumnIndex : function(dataIndex){
38498         for(var i = 0, len = this.config.length; i < len; i++){
38499             if(this.config[i].dataIndex == dataIndex){
38500                 return i;
38501             }
38502         }
38503         return -1;
38504     },
38505     
38506     
38507     moveColumn : function(oldIndex, newIndex){
38508         var c = this.config[oldIndex];
38509         this.config.splice(oldIndex, 1);
38510         this.config.splice(newIndex, 0, c);
38511         this.dataMap = null;
38512         this.fireEvent("columnmoved", this, oldIndex, newIndex);
38513     },
38514
38515     isLocked : function(colIndex){
38516         return this.config[colIndex].locked === true;
38517     },
38518
38519     setLocked : function(colIndex, value, suppressEvent){
38520         if(this.isLocked(colIndex) == value){
38521             return;
38522         }
38523         this.config[colIndex].locked = value;
38524         if(!suppressEvent){
38525             this.fireEvent("columnlockchange", this, colIndex, value);
38526         }
38527     },
38528
38529     getTotalLockedWidth : function(){
38530         var totalWidth = 0;
38531         for(var i = 0; i < this.config.length; i++){
38532             if(this.isLocked(i) && !this.isHidden(i)){
38533                 this.totalWidth += this.getColumnWidth(i);
38534             }
38535         }
38536         return totalWidth;
38537     },
38538
38539     getLockedCount : function(){
38540         for(var i = 0, len = this.config.length; i < len; i++){
38541             if(!this.isLocked(i)){
38542                 return i;
38543             }
38544         }
38545     },
38546
38547     /**
38548      * Returns the number of columns.
38549      * @return {Number}
38550      */
38551     getColumnCount : function(visibleOnly){
38552         if(visibleOnly === true){
38553             var c = 0;
38554             for(var i = 0, len = this.config.length; i < len; i++){
38555                 if(!this.isHidden(i)){
38556                     c++;
38557                 }
38558             }
38559             return c;
38560         }
38561         return this.config.length;
38562     },
38563
38564     /**
38565      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
38566      * @param {Function} fn
38567      * @param {Object} scope (optional)
38568      * @return {Array} result
38569      */
38570     getColumnsBy : function(fn, scope){
38571         var r = [];
38572         for(var i = 0, len = this.config.length; i < len; i++){
38573             var c = this.config[i];
38574             if(fn.call(scope||this, c, i) === true){
38575                 r[r.length] = c;
38576             }
38577         }
38578         return r;
38579     },
38580
38581     /**
38582      * Returns true if the specified column is sortable.
38583      * @param {Number} col The column index
38584      * @return {Boolean}
38585      */
38586     isSortable : function(col){
38587         if(typeof this.config[col].sortable == "undefined"){
38588             return this.defaultSortable;
38589         }
38590         return this.config[col].sortable;
38591     },
38592
38593     /**
38594      * Returns the rendering (formatting) function defined for the column.
38595      * @param {Number} col The column index.
38596      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
38597      */
38598     getRenderer : function(col){
38599         if(!this.config[col].renderer){
38600             return Roo.grid.ColumnModel.defaultRenderer;
38601         }
38602         return this.config[col].renderer;
38603     },
38604
38605     /**
38606      * Sets the rendering (formatting) function for a column.
38607      * @param {Number} col The column index
38608      * @param {Function} fn The function to use to process the cell's raw data
38609      * to return HTML markup for the grid view. The render function is called with
38610      * the following parameters:<ul>
38611      * <li>Data value.</li>
38612      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
38613      * <li>css A CSS style string to apply to the table cell.</li>
38614      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
38615      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
38616      * <li>Row index</li>
38617      * <li>Column index</li>
38618      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
38619      */
38620     setRenderer : function(col, fn){
38621         this.config[col].renderer = fn;
38622     },
38623
38624     /**
38625      * Returns the width for the specified column.
38626      * @param {Number} col The column index
38627      * @return {Number}
38628      */
38629     getColumnWidth : function(col){
38630         return this.config[col].width * 1 || this.defaultWidth;
38631     },
38632
38633     /**
38634      * Sets the width for a column.
38635      * @param {Number} col The column index
38636      * @param {Number} width The new width
38637      */
38638     setColumnWidth : function(col, width, suppressEvent){
38639         this.config[col].width = width;
38640         this.totalWidth = null;
38641         if(!suppressEvent){
38642              this.fireEvent("widthchange", this, col, width);
38643         }
38644     },
38645
38646     /**
38647      * Returns the total width of all columns.
38648      * @param {Boolean} includeHidden True to include hidden column widths
38649      * @return {Number}
38650      */
38651     getTotalWidth : function(includeHidden){
38652         if(!this.totalWidth){
38653             this.totalWidth = 0;
38654             for(var i = 0, len = this.config.length; i < len; i++){
38655                 if(includeHidden || !this.isHidden(i)){
38656                     this.totalWidth += this.getColumnWidth(i);
38657                 }
38658             }
38659         }
38660         return this.totalWidth;
38661     },
38662
38663     /**
38664      * Returns the header for the specified column.
38665      * @param {Number} col The column index
38666      * @return {String}
38667      */
38668     getColumnHeader : function(col){
38669         return this.config[col].header;
38670     },
38671
38672     /**
38673      * Sets the header for a column.
38674      * @param {Number} col The column index
38675      * @param {String} header The new header
38676      */
38677     setColumnHeader : function(col, header){
38678         this.config[col].header = header;
38679         this.fireEvent("headerchange", this, col, header);
38680     },
38681
38682     /**
38683      * Returns the tooltip for the specified column.
38684      * @param {Number} col The column index
38685      * @return {String}
38686      */
38687     getColumnTooltip : function(col){
38688             return this.config[col].tooltip;
38689     },
38690     /**
38691      * Sets the tooltip for a column.
38692      * @param {Number} col The column index
38693      * @param {String} tooltip The new tooltip
38694      */
38695     setColumnTooltip : function(col, tooltip){
38696             this.config[col].tooltip = tooltip;
38697     },
38698
38699     /**
38700      * Returns the dataIndex for the specified column.
38701      * @param {Number} col The column index
38702      * @return {Number}
38703      */
38704     getDataIndex : function(col){
38705         return this.config[col].dataIndex;
38706     },
38707
38708     /**
38709      * Sets the dataIndex for a column.
38710      * @param {Number} col The column index
38711      * @param {Number} dataIndex The new dataIndex
38712      */
38713     setDataIndex : function(col, dataIndex){
38714         this.config[col].dataIndex = dataIndex;
38715     },
38716
38717     
38718     
38719     /**
38720      * Returns true if the cell is editable.
38721      * @param {Number} colIndex The column index
38722      * @param {Number} rowIndex The row index
38723      * @return {Boolean}
38724      */
38725     isCellEditable : function(colIndex, rowIndex){
38726         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
38727     },
38728
38729     /**
38730      * Returns the editor defined for the cell/column.
38731      * return false or null to disable editing.
38732      * @param {Number} colIndex The column index
38733      * @param {Number} rowIndex The row index
38734      * @return {Object}
38735      */
38736     getCellEditor : function(colIndex, rowIndex){
38737         return this.config[colIndex].editor;
38738     },
38739
38740     /**
38741      * Sets if a column is editable.
38742      * @param {Number} col The column index
38743      * @param {Boolean} editable True if the column is editable
38744      */
38745     setEditable : function(col, editable){
38746         this.config[col].editable = editable;
38747     },
38748
38749
38750     /**
38751      * Returns true if the column is hidden.
38752      * @param {Number} colIndex The column index
38753      * @return {Boolean}
38754      */
38755     isHidden : function(colIndex){
38756         return this.config[colIndex].hidden;
38757     },
38758
38759
38760     /**
38761      * Returns true if the column width cannot be changed
38762      */
38763     isFixed : function(colIndex){
38764         return this.config[colIndex].fixed;
38765     },
38766
38767     /**
38768      * Returns true if the column can be resized
38769      * @return {Boolean}
38770      */
38771     isResizable : function(colIndex){
38772         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
38773     },
38774     /**
38775      * Sets if a column is hidden.
38776      * @param {Number} colIndex The column index
38777      * @param {Boolean} hidden True if the column is hidden
38778      */
38779     setHidden : function(colIndex, hidden){
38780         this.config[colIndex].hidden = hidden;
38781         this.totalWidth = null;
38782         this.fireEvent("hiddenchange", this, colIndex, hidden);
38783     },
38784
38785     /**
38786      * Sets the editor for a column.
38787      * @param {Number} col The column index
38788      * @param {Object} editor The editor object
38789      */
38790     setEditor : function(col, editor){
38791         this.config[col].editor = editor;
38792     }
38793 });
38794
38795 Roo.grid.ColumnModel.defaultRenderer = function(value){
38796         if(typeof value == "string" && value.length < 1){
38797             return "&#160;";
38798         }
38799         return value;
38800 };
38801
38802 // Alias for backwards compatibility
38803 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
38804 /*
38805  * Based on:
38806  * Ext JS Library 1.1.1
38807  * Copyright(c) 2006-2007, Ext JS, LLC.
38808  *
38809  * Originally Released Under LGPL - original licence link has changed is not relivant.
38810  *
38811  * Fork - LGPL
38812  * <script type="text/javascript">
38813  */
38814
38815 /**
38816  * @class Roo.grid.AbstractSelectionModel
38817  * @extends Roo.util.Observable
38818  * Abstract base class for grid SelectionModels.  It provides the interface that should be
38819  * implemented by descendant classes.  This class should not be directly instantiated.
38820  * @constructor
38821  */
38822 Roo.grid.AbstractSelectionModel = function(){
38823     this.locked = false;
38824     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
38825 };
38826
38827 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
38828     /** @ignore Called by the grid automatically. Do not call directly. */
38829     init : function(grid){
38830         this.grid = grid;
38831         this.initEvents();
38832     },
38833
38834     /**
38835      * Locks the selections.
38836      */
38837     lock : function(){
38838         this.locked = true;
38839     },
38840
38841     /**
38842      * Unlocks the selections.
38843      */
38844     unlock : function(){
38845         this.locked = false;
38846     },
38847
38848     /**
38849      * Returns true if the selections are locked.
38850      * @return {Boolean}
38851      */
38852     isLocked : function(){
38853         return this.locked;
38854     }
38855 });/*
38856  * Based on:
38857  * Ext JS Library 1.1.1
38858  * Copyright(c) 2006-2007, Ext JS, LLC.
38859  *
38860  * Originally Released Under LGPL - original licence link has changed is not relivant.
38861  *
38862  * Fork - LGPL
38863  * <script type="text/javascript">
38864  */
38865 /**
38866  * @extends Roo.grid.AbstractSelectionModel
38867  * @class Roo.grid.RowSelectionModel
38868  * The default SelectionModel used by {@link Roo.grid.Grid}.
38869  * It supports multiple selections and keyboard selection/navigation. 
38870  * @constructor
38871  * @param {Object} config
38872  */
38873 Roo.grid.RowSelectionModel = function(config){
38874     Roo.apply(this, config);
38875     this.selections = new Roo.util.MixedCollection(false, function(o){
38876         return o.id;
38877     });
38878
38879     this.last = false;
38880     this.lastActive = false;
38881
38882     this.addEvents({
38883         /**
38884              * @event selectionchange
38885              * Fires when the selection changes
38886              * @param {SelectionModel} this
38887              */
38888             "selectionchange" : true,
38889         /**
38890              * @event afterselectionchange
38891              * Fires after the selection changes (eg. by key press or clicking)
38892              * @param {SelectionModel} this
38893              */
38894             "afterselectionchange" : true,
38895         /**
38896              * @event beforerowselect
38897              * Fires when a row is selected being selected, return false to cancel.
38898              * @param {SelectionModel} this
38899              * @param {Number} rowIndex The selected index
38900              * @param {Boolean} keepExisting False if other selections will be cleared
38901              */
38902             "beforerowselect" : true,
38903         /**
38904              * @event rowselect
38905              * Fires when a row is selected.
38906              * @param {SelectionModel} this
38907              * @param {Number} rowIndex The selected index
38908              * @param {Roo.data.Record} r The record
38909              */
38910             "rowselect" : true,
38911         /**
38912              * @event rowdeselect
38913              * Fires when a row is deselected.
38914              * @param {SelectionModel} this
38915              * @param {Number} rowIndex The selected index
38916              */
38917         "rowdeselect" : true
38918     });
38919     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
38920     this.locked = false;
38921 };
38922
38923 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
38924     /**
38925      * @cfg {Boolean} singleSelect
38926      * True to allow selection of only one row at a time (defaults to false)
38927      */
38928     singleSelect : false,
38929
38930     // private
38931     initEvents : function(){
38932
38933         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
38934             this.grid.on("mousedown", this.handleMouseDown, this);
38935         }else{ // allow click to work like normal
38936             this.grid.on("rowclick", this.handleDragableRowClick, this);
38937         }
38938
38939         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
38940             "up" : function(e){
38941                 if(!e.shiftKey){
38942                     this.selectPrevious(e.shiftKey);
38943                 }else if(this.last !== false && this.lastActive !== false){
38944                     var last = this.last;
38945                     this.selectRange(this.last,  this.lastActive-1);
38946                     this.grid.getView().focusRow(this.lastActive);
38947                     if(last !== false){
38948                         this.last = last;
38949                     }
38950                 }else{
38951                     this.selectFirstRow();
38952                 }
38953                 this.fireEvent("afterselectionchange", this);
38954             },
38955             "down" : function(e){
38956                 if(!e.shiftKey){
38957                     this.selectNext(e.shiftKey);
38958                 }else if(this.last !== false && this.lastActive !== false){
38959                     var last = this.last;
38960                     this.selectRange(this.last,  this.lastActive+1);
38961                     this.grid.getView().focusRow(this.lastActive);
38962                     if(last !== false){
38963                         this.last = last;
38964                     }
38965                 }else{
38966                     this.selectFirstRow();
38967                 }
38968                 this.fireEvent("afterselectionchange", this);
38969             },
38970             scope: this
38971         });
38972
38973         var view = this.grid.view;
38974         view.on("refresh", this.onRefresh, this);
38975         view.on("rowupdated", this.onRowUpdated, this);
38976         view.on("rowremoved", this.onRemove, this);
38977     },
38978
38979     // private
38980     onRefresh : function(){
38981         var ds = this.grid.dataSource, i, v = this.grid.view;
38982         var s = this.selections;
38983         s.each(function(r){
38984             if((i = ds.indexOfId(r.id)) != -1){
38985                 v.onRowSelect(i);
38986             }else{
38987                 s.remove(r);
38988             }
38989         });
38990     },
38991
38992     // private
38993     onRemove : function(v, index, r){
38994         this.selections.remove(r);
38995     },
38996
38997     // private
38998     onRowUpdated : function(v, index, r){
38999         if(this.isSelected(r)){
39000             v.onRowSelect(index);
39001         }
39002     },
39003
39004     /**
39005      * Select records.
39006      * @param {Array} records The records to select
39007      * @param {Boolean} keepExisting (optional) True to keep existing selections
39008      */
39009     selectRecords : function(records, keepExisting){
39010         if(!keepExisting){
39011             this.clearSelections();
39012         }
39013         var ds = this.grid.dataSource;
39014         for(var i = 0, len = records.length; i < len; i++){
39015             this.selectRow(ds.indexOf(records[i]), true);
39016         }
39017     },
39018
39019     /**
39020      * Gets the number of selected rows.
39021      * @return {Number}
39022      */
39023     getCount : function(){
39024         return this.selections.length;
39025     },
39026
39027     /**
39028      * Selects the first row in the grid.
39029      */
39030     selectFirstRow : function(){
39031         this.selectRow(0);
39032     },
39033
39034     /**
39035      * Select the last row.
39036      * @param {Boolean} keepExisting (optional) True to keep existing selections
39037      */
39038     selectLastRow : function(keepExisting){
39039         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
39040     },
39041
39042     /**
39043      * Selects the row immediately following the last selected row.
39044      * @param {Boolean} keepExisting (optional) True to keep existing selections
39045      */
39046     selectNext : function(keepExisting){
39047         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
39048             this.selectRow(this.last+1, keepExisting);
39049             this.grid.getView().focusRow(this.last);
39050         }
39051     },
39052
39053     /**
39054      * Selects the row that precedes the last selected row.
39055      * @param {Boolean} keepExisting (optional) True to keep existing selections
39056      */
39057     selectPrevious : function(keepExisting){
39058         if(this.last){
39059             this.selectRow(this.last-1, keepExisting);
39060             this.grid.getView().focusRow(this.last);
39061         }
39062     },
39063
39064     /**
39065      * Returns the selected records
39066      * @return {Array} Array of selected records
39067      */
39068     getSelections : function(){
39069         return [].concat(this.selections.items);
39070     },
39071
39072     /**
39073      * Returns the first selected record.
39074      * @return {Record}
39075      */
39076     getSelected : function(){
39077         return this.selections.itemAt(0);
39078     },
39079
39080
39081     /**
39082      * Clears all selections.
39083      */
39084     clearSelections : function(fast){
39085         if(this.locked) return;
39086         if(fast !== true){
39087             var ds = this.grid.dataSource;
39088             var s = this.selections;
39089             s.each(function(r){
39090                 this.deselectRow(ds.indexOfId(r.id));
39091             }, this);
39092             s.clear();
39093         }else{
39094             this.selections.clear();
39095         }
39096         this.last = false;
39097     },
39098
39099
39100     /**
39101      * Selects all rows.
39102      */
39103     selectAll : function(){
39104         if(this.locked) return;
39105         this.selections.clear();
39106         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
39107             this.selectRow(i, true);
39108         }
39109     },
39110
39111     /**
39112      * Returns True if there is a selection.
39113      * @return {Boolean}
39114      */
39115     hasSelection : function(){
39116         return this.selections.length > 0;
39117     },
39118
39119     /**
39120      * Returns True if the specified row is selected.
39121      * @param {Number/Record} record The record or index of the record to check
39122      * @return {Boolean}
39123      */
39124     isSelected : function(index){
39125         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
39126         return (r && this.selections.key(r.id) ? true : false);
39127     },
39128
39129     /**
39130      * Returns True if the specified record id is selected.
39131      * @param {String} id The id of record to check
39132      * @return {Boolean}
39133      */
39134     isIdSelected : function(id){
39135         return (this.selections.key(id) ? true : false);
39136     },
39137
39138     // private
39139     handleMouseDown : function(e, t){
39140         var view = this.grid.getView(), rowIndex;
39141         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
39142             return;
39143         };
39144         if(e.shiftKey && this.last !== false){
39145             var last = this.last;
39146             this.selectRange(last, rowIndex, e.ctrlKey);
39147             this.last = last; // reset the last
39148             view.focusRow(rowIndex);
39149         }else{
39150             var isSelected = this.isSelected(rowIndex);
39151             if(e.button !== 0 && isSelected){
39152                 view.focusRow(rowIndex);
39153             }else if(e.ctrlKey && isSelected){
39154                 this.deselectRow(rowIndex);
39155             }else if(!isSelected){
39156                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
39157                 view.focusRow(rowIndex);
39158             }
39159         }
39160         this.fireEvent("afterselectionchange", this);
39161     },
39162     // private
39163     handleDragableRowClick :  function(grid, rowIndex, e) 
39164     {
39165         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
39166             this.selectRow(rowIndex, false);
39167             grid.view.focusRow(rowIndex);
39168              this.fireEvent("afterselectionchange", this);
39169         }
39170     },
39171     
39172     /**
39173      * Selects multiple rows.
39174      * @param {Array} rows Array of the indexes of the row to select
39175      * @param {Boolean} keepExisting (optional) True to keep existing selections
39176      */
39177     selectRows : function(rows, keepExisting){
39178         if(!keepExisting){
39179             this.clearSelections();
39180         }
39181         for(var i = 0, len = rows.length; i < len; i++){
39182             this.selectRow(rows[i], true);
39183         }
39184     },
39185
39186     /**
39187      * Selects a range of rows. All rows in between startRow and endRow are also selected.
39188      * @param {Number} startRow The index of the first row in the range
39189      * @param {Number} endRow The index of the last row in the range
39190      * @param {Boolean} keepExisting (optional) True to retain existing selections
39191      */
39192     selectRange : function(startRow, endRow, keepExisting){
39193         if(this.locked) return;
39194         if(!keepExisting){
39195             this.clearSelections();
39196         }
39197         if(startRow <= endRow){
39198             for(var i = startRow; i <= endRow; i++){
39199                 this.selectRow(i, true);
39200             }
39201         }else{
39202             for(var i = startRow; i >= endRow; i--){
39203                 this.selectRow(i, true);
39204             }
39205         }
39206     },
39207
39208     /**
39209      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
39210      * @param {Number} startRow The index of the first row in the range
39211      * @param {Number} endRow The index of the last row in the range
39212      */
39213     deselectRange : function(startRow, endRow, preventViewNotify){
39214         if(this.locked) return;
39215         for(var i = startRow; i <= endRow; i++){
39216             this.deselectRow(i, preventViewNotify);
39217         }
39218     },
39219
39220     /**
39221      * Selects a row.
39222      * @param {Number} row The index of the row to select
39223      * @param {Boolean} keepExisting (optional) True to keep existing selections
39224      */
39225     selectRow : function(index, keepExisting, preventViewNotify){
39226         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
39227         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
39228             if(!keepExisting || this.singleSelect){
39229                 this.clearSelections();
39230             }
39231             var r = this.grid.dataSource.getAt(index);
39232             this.selections.add(r);
39233             this.last = this.lastActive = index;
39234             if(!preventViewNotify){
39235                 this.grid.getView().onRowSelect(index);
39236             }
39237             this.fireEvent("rowselect", this, index, r);
39238             this.fireEvent("selectionchange", this);
39239         }
39240     },
39241
39242     /**
39243      * Deselects a row.
39244      * @param {Number} row The index of the row to deselect
39245      */
39246     deselectRow : function(index, preventViewNotify){
39247         if(this.locked) return;
39248         if(this.last == index){
39249             this.last = false;
39250         }
39251         if(this.lastActive == index){
39252             this.lastActive = false;
39253         }
39254         var r = this.grid.dataSource.getAt(index);
39255         this.selections.remove(r);
39256         if(!preventViewNotify){
39257             this.grid.getView().onRowDeselect(index);
39258         }
39259         this.fireEvent("rowdeselect", this, index);
39260         this.fireEvent("selectionchange", this);
39261     },
39262
39263     // private
39264     restoreLast : function(){
39265         if(this._last){
39266             this.last = this._last;
39267         }
39268     },
39269
39270     // private
39271     acceptsNav : function(row, col, cm){
39272         return !cm.isHidden(col) && cm.isCellEditable(col, row);
39273     },
39274
39275     // private
39276     onEditorKey : function(field, e){
39277         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
39278         if(k == e.TAB){
39279             e.stopEvent();
39280             ed.completeEdit();
39281             if(e.shiftKey){
39282                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
39283             }else{
39284                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
39285             }
39286         }else if(k == e.ENTER && !e.ctrlKey){
39287             e.stopEvent();
39288             ed.completeEdit();
39289             if(e.shiftKey){
39290                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
39291             }else{
39292                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
39293             }
39294         }else if(k == e.ESC){
39295             ed.cancelEdit();
39296         }
39297         if(newCell){
39298             g.startEditing(newCell[0], newCell[1]);
39299         }
39300     }
39301 });/*
39302  * Based on:
39303  * Ext JS Library 1.1.1
39304  * Copyright(c) 2006-2007, Ext JS, LLC.
39305  *
39306  * Originally Released Under LGPL - original licence link has changed is not relivant.
39307  *
39308  * Fork - LGPL
39309  * <script type="text/javascript">
39310  */
39311 /**
39312  * @class Roo.grid.CellSelectionModel
39313  * @extends Roo.grid.AbstractSelectionModel
39314  * This class provides the basic implementation for cell selection in a grid.
39315  * @constructor
39316  * @param {Object} config The object containing the configuration of this model.
39317  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
39318  */
39319 Roo.grid.CellSelectionModel = function(config){
39320     Roo.apply(this, config);
39321
39322     this.selection = null;
39323
39324     this.addEvents({
39325         /**
39326              * @event beforerowselect
39327              * Fires before a cell is selected.
39328              * @param {SelectionModel} this
39329              * @param {Number} rowIndex The selected row index
39330              * @param {Number} colIndex The selected cell index
39331              */
39332             "beforecellselect" : true,
39333         /**
39334              * @event cellselect
39335              * Fires when a cell is selected.
39336              * @param {SelectionModel} this
39337              * @param {Number} rowIndex The selected row index
39338              * @param {Number} colIndex The selected cell index
39339              */
39340             "cellselect" : true,
39341         /**
39342              * @event selectionchange
39343              * Fires when the active selection changes.
39344              * @param {SelectionModel} this
39345              * @param {Object} selection null for no selection or an object (o) with two properties
39346                 <ul>
39347                 <li>o.record: the record object for the row the selection is in</li>
39348                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
39349                 </ul>
39350              */
39351             "selectionchange" : true,
39352         /**
39353              * @event tabend
39354              * Fires when the tab (or enter) was pressed on the last editable cell
39355              * You can use this to trigger add new row.
39356              * @param {SelectionModel} this
39357              */
39358             "tabend" : true,
39359          /**
39360              * @event beforeeditnext
39361              * Fires before the next editable sell is made active
39362              * You can use this to skip to another cell or fire the tabend
39363              *    if you set cell to false
39364              * @param {Object} eventdata object : { cell : [ row, col ] } 
39365              */
39366             "beforeeditnext" : true
39367     });
39368     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
39369 };
39370
39371 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
39372     
39373     enter_is_tab: false,
39374
39375     /** @ignore */
39376     initEvents : function(){
39377         this.grid.on("mousedown", this.handleMouseDown, this);
39378         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
39379         var view = this.grid.view;
39380         view.on("refresh", this.onViewChange, this);
39381         view.on("rowupdated", this.onRowUpdated, this);
39382         view.on("beforerowremoved", this.clearSelections, this);
39383         view.on("beforerowsinserted", this.clearSelections, this);
39384         if(this.grid.isEditor){
39385             this.grid.on("beforeedit", this.beforeEdit,  this);
39386         }
39387     },
39388
39389         //private
39390     beforeEdit : function(e){
39391         this.select(e.row, e.column, false, true, e.record);
39392     },
39393
39394         //private
39395     onRowUpdated : function(v, index, r){
39396         if(this.selection && this.selection.record == r){
39397             v.onCellSelect(index, this.selection.cell[1]);
39398         }
39399     },
39400
39401         //private
39402     onViewChange : function(){
39403         this.clearSelections(true);
39404     },
39405
39406         /**
39407          * Returns the currently selected cell,.
39408          * @return {Array} The selected cell (row, column) or null if none selected.
39409          */
39410     getSelectedCell : function(){
39411         return this.selection ? this.selection.cell : null;
39412     },
39413
39414     /**
39415      * Clears all selections.
39416      * @param {Boolean} true to prevent the gridview from being notified about the change.
39417      */
39418     clearSelections : function(preventNotify){
39419         var s = this.selection;
39420         if(s){
39421             if(preventNotify !== true){
39422                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
39423             }
39424             this.selection = null;
39425             this.fireEvent("selectionchange", this, null);
39426         }
39427     },
39428
39429     /**
39430      * Returns true if there is a selection.
39431      * @return {Boolean}
39432      */
39433     hasSelection : function(){
39434         return this.selection ? true : false;
39435     },
39436
39437     /** @ignore */
39438     handleMouseDown : function(e, t){
39439         var v = this.grid.getView();
39440         if(this.isLocked()){
39441             return;
39442         };
39443         var row = v.findRowIndex(t);
39444         var cell = v.findCellIndex(t);
39445         if(row !== false && cell !== false){
39446             this.select(row, cell);
39447         }
39448     },
39449
39450     /**
39451      * Selects a cell.
39452      * @param {Number} rowIndex
39453      * @param {Number} collIndex
39454      */
39455     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
39456         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
39457             this.clearSelections();
39458             r = r || this.grid.dataSource.getAt(rowIndex);
39459             this.selection = {
39460                 record : r,
39461                 cell : [rowIndex, colIndex]
39462             };
39463             if(!preventViewNotify){
39464                 var v = this.grid.getView();
39465                 v.onCellSelect(rowIndex, colIndex);
39466                 if(preventFocus !== true){
39467                     v.focusCell(rowIndex, colIndex);
39468                 }
39469             }
39470             this.fireEvent("cellselect", this, rowIndex, colIndex);
39471             this.fireEvent("selectionchange", this, this.selection);
39472         }
39473     },
39474
39475         //private
39476     isSelectable : function(rowIndex, colIndex, cm){
39477         return !cm.isHidden(colIndex);
39478     },
39479
39480     /** @ignore */
39481     handleKeyDown : function(e){
39482         //Roo.log('Cell Sel Model handleKeyDown');
39483         if(!e.isNavKeyPress()){
39484             return;
39485         }
39486         var g = this.grid, s = this.selection;
39487         if(!s){
39488             e.stopEvent();
39489             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
39490             if(cell){
39491                 this.select(cell[0], cell[1]);
39492             }
39493             return;
39494         }
39495         var sm = this;
39496         var walk = function(row, col, step){
39497             return g.walkCells(row, col, step, sm.isSelectable,  sm);
39498         };
39499         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
39500         var newCell;
39501
39502       
39503
39504         switch(k){
39505             case e.TAB:
39506                 // handled by onEditorKey
39507                 if (g.isEditor && g.editing) {
39508                     return;
39509                 }
39510                 if(e.shiftKey) {
39511                     newCell = walk(r, c-1, -1);
39512                 } else {
39513                     newCell = walk(r, c+1, 1);
39514                 }
39515                 break;
39516             
39517             case e.DOWN:
39518                newCell = walk(r+1, c, 1);
39519                 break;
39520             
39521             case e.UP:
39522                 newCell = walk(r-1, c, -1);
39523                 break;
39524             
39525             case e.RIGHT:
39526                 newCell = walk(r, c+1, 1);
39527                 break;
39528             
39529             case e.LEFT:
39530                 newCell = walk(r, c-1, -1);
39531                 break;
39532             
39533             case e.ENTER:
39534                 
39535                 if(g.isEditor && !g.editing){
39536                    g.startEditing(r, c);
39537                    e.stopEvent();
39538                    return;
39539                 }
39540                 
39541                 
39542              break;
39543         };
39544         if(newCell){
39545             this.select(newCell[0], newCell[1]);
39546             e.stopEvent();
39547             
39548         }
39549     },
39550
39551     acceptsNav : function(row, col, cm){
39552         return !cm.isHidden(col) && cm.isCellEditable(col, row);
39553     },
39554     /**
39555      * Selects a cell.
39556      * @param {Number} field (not used) - as it's normally used as a listener
39557      * @param {Number} e - event - fake it by using
39558      *
39559      * var e = Roo.EventObjectImpl.prototype;
39560      * e.keyCode = e.TAB
39561      *
39562      * 
39563      */
39564     onEditorKey : function(field, e){
39565         
39566         var k = e.getKey(),
39567             newCell,
39568             g = this.grid,
39569             ed = g.activeEditor,
39570             forward = false;
39571         ///Roo.log('onEditorKey' + k);
39572         
39573         
39574         if (this.enter_is_tab && k == e.ENTER) {
39575             k = e.TAB;
39576         }
39577         
39578         if(k == e.TAB){
39579             if(e.shiftKey){
39580                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
39581             }else{
39582                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
39583                 forward = true;
39584             }
39585             
39586             e.stopEvent();
39587             
39588         } else if(k == e.ENTER &&  !e.ctrlKey){
39589             ed.completeEdit();
39590             e.stopEvent();
39591             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
39592         
39593                 } else if(k == e.ESC){
39594             ed.cancelEdit();
39595         }
39596                 
39597         if (newCell) {
39598             var ecall = { cell : newCell, forward : forward };
39599             this.fireEvent('beforeeditnext', ecall );
39600             newCell = ecall.cell;
39601                         forward = ecall.forward;
39602         }
39603                 
39604         if(newCell){
39605             //Roo.log('next cell after edit');
39606             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
39607         } else if (forward) {
39608             // tabbed past last
39609             this.fireEvent.defer(100, this, ['tabend',this]);
39610         }
39611     }
39612 });/*
39613  * Based on:
39614  * Ext JS Library 1.1.1
39615  * Copyright(c) 2006-2007, Ext JS, LLC.
39616  *
39617  * Originally Released Under LGPL - original licence link has changed is not relivant.
39618  *
39619  * Fork - LGPL
39620  * <script type="text/javascript">
39621  */
39622  
39623 /**
39624  * @class Roo.grid.EditorGrid
39625  * @extends Roo.grid.Grid
39626  * Class for creating and editable grid.
39627  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
39628  * The container MUST have some type of size defined for the grid to fill. The container will be 
39629  * automatically set to position relative if it isn't already.
39630  * @param {Object} dataSource The data model to bind to
39631  * @param {Object} colModel The column model with info about this grid's columns
39632  */
39633 Roo.grid.EditorGrid = function(container, config){
39634     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
39635     this.getGridEl().addClass("xedit-grid");
39636
39637     if(!this.selModel){
39638         this.selModel = new Roo.grid.CellSelectionModel();
39639     }
39640
39641     this.activeEditor = null;
39642
39643         this.addEvents({
39644             /**
39645              * @event beforeedit
39646              * Fires before cell editing is triggered. The edit event object has the following properties <br />
39647              * <ul style="padding:5px;padding-left:16px;">
39648              * <li>grid - This grid</li>
39649              * <li>record - The record being edited</li>
39650              * <li>field - The field name being edited</li>
39651              * <li>value - The value for the field being edited.</li>
39652              * <li>row - The grid row index</li>
39653              * <li>column - The grid column index</li>
39654              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
39655              * </ul>
39656              * @param {Object} e An edit event (see above for description)
39657              */
39658             "beforeedit" : true,
39659             /**
39660              * @event afteredit
39661              * Fires after a cell is edited. <br />
39662              * <ul style="padding:5px;padding-left:16px;">
39663              * <li>grid - This grid</li>
39664              * <li>record - The record being edited</li>
39665              * <li>field - The field name being edited</li>
39666              * <li>value - The value being set</li>
39667              * <li>originalValue - The original value for the field, before the edit.</li>
39668              * <li>row - The grid row index</li>
39669              * <li>column - The grid column index</li>
39670              * </ul>
39671              * @param {Object} e An edit event (see above for description)
39672              */
39673             "afteredit" : true,
39674             /**
39675              * @event validateedit
39676              * Fires after a cell is edited, but before the value is set in the record. 
39677          * You can use this to modify the value being set in the field, Return false
39678              * to cancel the change. The edit event object has the following properties <br />
39679              * <ul style="padding:5px;padding-left:16px;">
39680          * <li>editor - This editor</li>
39681              * <li>grid - This grid</li>
39682              * <li>record - The record being edited</li>
39683              * <li>field - The field name being edited</li>
39684              * <li>value - The value being set</li>
39685              * <li>originalValue - The original value for the field, before the edit.</li>
39686              * <li>row - The grid row index</li>
39687              * <li>column - The grid column index</li>
39688              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
39689              * </ul>
39690              * @param {Object} e An edit event (see above for description)
39691              */
39692             "validateedit" : true
39693         });
39694     this.on("bodyscroll", this.stopEditing,  this);
39695     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
39696 };
39697
39698 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
39699     /**
39700      * @cfg {Number} clicksToEdit
39701      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
39702      */
39703     clicksToEdit: 2,
39704
39705     // private
39706     isEditor : true,
39707     // private
39708     trackMouseOver: false, // causes very odd FF errors
39709
39710     onCellDblClick : function(g, row, col){
39711         this.startEditing(row, col);
39712     },
39713
39714     onEditComplete : function(ed, value, startValue){
39715         this.editing = false;
39716         this.activeEditor = null;
39717         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
39718         var r = ed.record;
39719         var field = this.colModel.getDataIndex(ed.col);
39720         var e = {
39721             grid: this,
39722             record: r,
39723             field: field,
39724             originalValue: startValue,
39725             value: value,
39726             row: ed.row,
39727             column: ed.col,
39728             cancel:false,
39729             editor: ed
39730         };
39731         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
39732         cell.show();
39733           
39734         if(String(value) !== String(startValue)){
39735             
39736             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
39737                 r.set(field, e.value);
39738                 // if we are dealing with a combo box..
39739                 // then we also set the 'name' colum to be the displayField
39740                 if (ed.field.displayField && ed.field.name) {
39741                     r.set(ed.field.name, ed.field.el.dom.value);
39742                 }
39743                 
39744                 delete e.cancel; //?? why!!!
39745                 this.fireEvent("afteredit", e);
39746             }
39747         } else {
39748             this.fireEvent("afteredit", e); // always fire it!
39749         }
39750         this.view.focusCell(ed.row, ed.col);
39751     },
39752
39753     /**
39754      * Starts editing the specified for the specified row/column
39755      * @param {Number} rowIndex
39756      * @param {Number} colIndex
39757      */
39758     startEditing : function(row, col){
39759         this.stopEditing();
39760         if(this.colModel.isCellEditable(col, row)){
39761             this.view.ensureVisible(row, col, true);
39762           
39763             var r = this.dataSource.getAt(row);
39764             var field = this.colModel.getDataIndex(col);
39765             var cell = Roo.get(this.view.getCell(row,col));
39766             var e = {
39767                 grid: this,
39768                 record: r,
39769                 field: field,
39770                 value: r.data[field],
39771                 row: row,
39772                 column: col,
39773                 cancel:false 
39774             };
39775             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
39776                 this.editing = true;
39777                 var ed = this.colModel.getCellEditor(col, row);
39778                 
39779                 if (!ed) {
39780                     return;
39781                 }
39782                 if(!ed.rendered){
39783                     ed.render(ed.parentEl || document.body);
39784                 }
39785                 ed.field.reset();
39786                
39787                 cell.hide();
39788                 
39789                 (function(){ // complex but required for focus issues in safari, ie and opera
39790                     ed.row = row;
39791                     ed.col = col;
39792                     ed.record = r;
39793                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
39794                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
39795                     this.activeEditor = ed;
39796                     var v = r.data[field];
39797                     ed.startEdit(this.view.getCell(row, col), v);
39798                     // combo's with 'displayField and name set
39799                     if (ed.field.displayField && ed.field.name) {
39800                         ed.field.el.dom.value = r.data[ed.field.name];
39801                     }
39802                     
39803                     
39804                 }).defer(50, this);
39805             }
39806         }
39807     },
39808         
39809     /**
39810      * Stops any active editing
39811      */
39812     stopEditing : function(){
39813         if(this.activeEditor){
39814             this.activeEditor.completeEdit();
39815         }
39816         this.activeEditor = null;
39817     },
39818         
39819          /**
39820      * Called to get grid's drag proxy text, by default returns this.ddText.
39821      * @return {String}
39822      */
39823     getDragDropText : function(){
39824         var count = this.selModel.getSelectedCell() ? 1 : 0;
39825         return String.format(this.ddText, count, count == 1 ? '' : 's');
39826     }
39827         
39828 });/*
39829  * Based on:
39830  * Ext JS Library 1.1.1
39831  * Copyright(c) 2006-2007, Ext JS, LLC.
39832  *
39833  * Originally Released Under LGPL - original licence link has changed is not relivant.
39834  *
39835  * Fork - LGPL
39836  * <script type="text/javascript">
39837  */
39838
39839 // private - not really -- you end up using it !
39840 // This is a support class used internally by the Grid components
39841
39842 /**
39843  * @class Roo.grid.GridEditor
39844  * @extends Roo.Editor
39845  * Class for creating and editable grid elements.
39846  * @param {Object} config any settings (must include field)
39847  */
39848 Roo.grid.GridEditor = function(field, config){
39849     if (!config && field.field) {
39850         config = field;
39851         field = Roo.factory(config.field, Roo.form);
39852     }
39853     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
39854     field.monitorTab = false;
39855 };
39856
39857 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
39858     
39859     /**
39860      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
39861      */
39862     
39863     alignment: "tl-tl",
39864     autoSize: "width",
39865     hideEl : false,
39866     cls: "x-small-editor x-grid-editor",
39867     shim:false,
39868     shadow:"frame"
39869 });/*
39870  * Based on:
39871  * Ext JS Library 1.1.1
39872  * Copyright(c) 2006-2007, Ext JS, LLC.
39873  *
39874  * Originally Released Under LGPL - original licence link has changed is not relivant.
39875  *
39876  * Fork - LGPL
39877  * <script type="text/javascript">
39878  */
39879   
39880
39881   
39882 Roo.grid.PropertyRecord = Roo.data.Record.create([
39883     {name:'name',type:'string'},  'value'
39884 ]);
39885
39886
39887 Roo.grid.PropertyStore = function(grid, source){
39888     this.grid = grid;
39889     this.store = new Roo.data.Store({
39890         recordType : Roo.grid.PropertyRecord
39891     });
39892     this.store.on('update', this.onUpdate,  this);
39893     if(source){
39894         this.setSource(source);
39895     }
39896     Roo.grid.PropertyStore.superclass.constructor.call(this);
39897 };
39898
39899
39900
39901 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
39902     setSource : function(o){
39903         this.source = o;
39904         this.store.removeAll();
39905         var data = [];
39906         for(var k in o){
39907             if(this.isEditableValue(o[k])){
39908                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
39909             }
39910         }
39911         this.store.loadRecords({records: data}, {}, true);
39912     },
39913
39914     onUpdate : function(ds, record, type){
39915         if(type == Roo.data.Record.EDIT){
39916             var v = record.data['value'];
39917             var oldValue = record.modified['value'];
39918             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
39919                 this.source[record.id] = v;
39920                 record.commit();
39921                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
39922             }else{
39923                 record.reject();
39924             }
39925         }
39926     },
39927
39928     getProperty : function(row){
39929        return this.store.getAt(row);
39930     },
39931
39932     isEditableValue: function(val){
39933         if(val && val instanceof Date){
39934             return true;
39935         }else if(typeof val == 'object' || typeof val == 'function'){
39936             return false;
39937         }
39938         return true;
39939     },
39940
39941     setValue : function(prop, value){
39942         this.source[prop] = value;
39943         this.store.getById(prop).set('value', value);
39944     },
39945
39946     getSource : function(){
39947         return this.source;
39948     }
39949 });
39950
39951 Roo.grid.PropertyColumnModel = function(grid, store){
39952     this.grid = grid;
39953     var g = Roo.grid;
39954     g.PropertyColumnModel.superclass.constructor.call(this, [
39955         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
39956         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
39957     ]);
39958     this.store = store;
39959     this.bselect = Roo.DomHelper.append(document.body, {
39960         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
39961             {tag: 'option', value: 'true', html: 'true'},
39962             {tag: 'option', value: 'false', html: 'false'}
39963         ]
39964     });
39965     Roo.id(this.bselect);
39966     var f = Roo.form;
39967     this.editors = {
39968         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
39969         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
39970         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
39971         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
39972         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
39973     };
39974     this.renderCellDelegate = this.renderCell.createDelegate(this);
39975     this.renderPropDelegate = this.renderProp.createDelegate(this);
39976 };
39977
39978 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
39979     
39980     
39981     nameText : 'Name',
39982     valueText : 'Value',
39983     
39984     dateFormat : 'm/j/Y',
39985     
39986     
39987     renderDate : function(dateVal){
39988         return dateVal.dateFormat(this.dateFormat);
39989     },
39990
39991     renderBool : function(bVal){
39992         return bVal ? 'true' : 'false';
39993     },
39994
39995     isCellEditable : function(colIndex, rowIndex){
39996         return colIndex == 1;
39997     },
39998
39999     getRenderer : function(col){
40000         return col == 1 ?
40001             this.renderCellDelegate : this.renderPropDelegate;
40002     },
40003
40004     renderProp : function(v){
40005         return this.getPropertyName(v);
40006     },
40007
40008     renderCell : function(val){
40009         var rv = val;
40010         if(val instanceof Date){
40011             rv = this.renderDate(val);
40012         }else if(typeof val == 'boolean'){
40013             rv = this.renderBool(val);
40014         }
40015         return Roo.util.Format.htmlEncode(rv);
40016     },
40017
40018     getPropertyName : function(name){
40019         var pn = this.grid.propertyNames;
40020         return pn && pn[name] ? pn[name] : name;
40021     },
40022
40023     getCellEditor : function(colIndex, rowIndex){
40024         var p = this.store.getProperty(rowIndex);
40025         var n = p.data['name'], val = p.data['value'];
40026         
40027         if(typeof(this.grid.customEditors[n]) == 'string'){
40028             return this.editors[this.grid.customEditors[n]];
40029         }
40030         if(typeof(this.grid.customEditors[n]) != 'undefined'){
40031             return this.grid.customEditors[n];
40032         }
40033         if(val instanceof Date){
40034             return this.editors['date'];
40035         }else if(typeof val == 'number'){
40036             return this.editors['number'];
40037         }else if(typeof val == 'boolean'){
40038             return this.editors['boolean'];
40039         }else{
40040             return this.editors['string'];
40041         }
40042     }
40043 });
40044
40045 /**
40046  * @class Roo.grid.PropertyGrid
40047  * @extends Roo.grid.EditorGrid
40048  * This class represents the  interface of a component based property grid control.
40049  * <br><br>Usage:<pre><code>
40050  var grid = new Roo.grid.PropertyGrid("my-container-id", {
40051       
40052  });
40053  // set any options
40054  grid.render();
40055  * </code></pre>
40056   
40057  * @constructor
40058  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
40059  * The container MUST have some type of size defined for the grid to fill. The container will be
40060  * automatically set to position relative if it isn't already.
40061  * @param {Object} config A config object that sets properties on this grid.
40062  */
40063 Roo.grid.PropertyGrid = function(container, config){
40064     config = config || {};
40065     var store = new Roo.grid.PropertyStore(this);
40066     this.store = store;
40067     var cm = new Roo.grid.PropertyColumnModel(this, store);
40068     store.store.sort('name', 'ASC');
40069     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
40070         ds: store.store,
40071         cm: cm,
40072         enableColLock:false,
40073         enableColumnMove:false,
40074         stripeRows:false,
40075         trackMouseOver: false,
40076         clicksToEdit:1
40077     }, config));
40078     this.getGridEl().addClass('x-props-grid');
40079     this.lastEditRow = null;
40080     this.on('columnresize', this.onColumnResize, this);
40081     this.addEvents({
40082          /**
40083              * @event beforepropertychange
40084              * Fires before a property changes (return false to stop?)
40085              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
40086              * @param {String} id Record Id
40087              * @param {String} newval New Value
40088          * @param {String} oldval Old Value
40089              */
40090         "beforepropertychange": true,
40091         /**
40092              * @event propertychange
40093              * Fires after a property changes
40094              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
40095              * @param {String} id Record Id
40096              * @param {String} newval New Value
40097          * @param {String} oldval Old Value
40098              */
40099         "propertychange": true
40100     });
40101     this.customEditors = this.customEditors || {};
40102 };
40103 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
40104     
40105      /**
40106      * @cfg {Object} customEditors map of colnames=> custom editors.
40107      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
40108      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
40109      * false disables editing of the field.
40110          */
40111     
40112       /**
40113      * @cfg {Object} propertyNames map of property Names to their displayed value
40114          */
40115     
40116     render : function(){
40117         Roo.grid.PropertyGrid.superclass.render.call(this);
40118         this.autoSize.defer(100, this);
40119     },
40120
40121     autoSize : function(){
40122         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
40123         if(this.view){
40124             this.view.fitColumns();
40125         }
40126     },
40127
40128     onColumnResize : function(){
40129         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
40130         this.autoSize();
40131     },
40132     /**
40133      * Sets the data for the Grid
40134      * accepts a Key => Value object of all the elements avaiable.
40135      * @param {Object} data  to appear in grid.
40136      */
40137     setSource : function(source){
40138         this.store.setSource(source);
40139         //this.autoSize();
40140     },
40141     /**
40142      * Gets all the data from the grid.
40143      * @return {Object} data  data stored in grid
40144      */
40145     getSource : function(){
40146         return this.store.getSource();
40147     }
40148 });/*
40149  * Based on:
40150  * Ext JS Library 1.1.1
40151  * Copyright(c) 2006-2007, Ext JS, LLC.
40152  *
40153  * Originally Released Under LGPL - original licence link has changed is not relivant.
40154  *
40155  * Fork - LGPL
40156  * <script type="text/javascript">
40157  */
40158  
40159 /**
40160  * @class Roo.LoadMask
40161  * A simple utility class for generically masking elements while loading data.  If the element being masked has
40162  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
40163  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
40164  * element's UpdateManager load indicator and will be destroyed after the initial load.
40165  * @constructor
40166  * Create a new LoadMask
40167  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
40168  * @param {Object} config The config object
40169  */
40170 Roo.LoadMask = function(el, config){
40171     this.el = Roo.get(el);
40172     Roo.apply(this, config);
40173     if(this.store){
40174         this.store.on('beforeload', this.onBeforeLoad, this);
40175         this.store.on('load', this.onLoad, this);
40176         this.store.on('loadexception', this.onLoadException, this);
40177         this.removeMask = false;
40178     }else{
40179         var um = this.el.getUpdateManager();
40180         um.showLoadIndicator = false; // disable the default indicator
40181         um.on('beforeupdate', this.onBeforeLoad, this);
40182         um.on('update', this.onLoad, this);
40183         um.on('failure', this.onLoad, this);
40184         this.removeMask = true;
40185     }
40186 };
40187
40188 Roo.LoadMask.prototype = {
40189     /**
40190      * @cfg {Boolean} removeMask
40191      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
40192      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
40193      */
40194     /**
40195      * @cfg {String} msg
40196      * The text to display in a centered loading message box (defaults to 'Loading...')
40197      */
40198     msg : 'Loading...',
40199     /**
40200      * @cfg {String} msgCls
40201      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
40202      */
40203     msgCls : 'x-mask-loading',
40204
40205     /**
40206      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
40207      * @type Boolean
40208      */
40209     disabled: false,
40210
40211     /**
40212      * Disables the mask to prevent it from being displayed
40213      */
40214     disable : function(){
40215        this.disabled = true;
40216     },
40217
40218     /**
40219      * Enables the mask so that it can be displayed
40220      */
40221     enable : function(){
40222         this.disabled = false;
40223     },
40224     
40225     onLoadException : function()
40226     {
40227         Roo.log(arguments);
40228         
40229         if (typeof(arguments[3]) != 'undefined') {
40230             Roo.MessageBox.alert("Error loading",arguments[3]);
40231         } 
40232         /*
40233         try {
40234             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
40235                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
40236             }   
40237         } catch(e) {
40238             
40239         }
40240         */
40241     
40242         
40243         
40244         this.el.unmask(this.removeMask);
40245     },
40246     // private
40247     onLoad : function()
40248     {
40249         this.el.unmask(this.removeMask);
40250     },
40251
40252     // private
40253     onBeforeLoad : function(){
40254         if(!this.disabled){
40255             this.el.mask(this.msg, this.msgCls);
40256         }
40257     },
40258
40259     // private
40260     destroy : function(){
40261         if(this.store){
40262             this.store.un('beforeload', this.onBeforeLoad, this);
40263             this.store.un('load', this.onLoad, this);
40264             this.store.un('loadexception', this.onLoadException, this);
40265         }else{
40266             var um = this.el.getUpdateManager();
40267             um.un('beforeupdate', this.onBeforeLoad, this);
40268             um.un('update', this.onLoad, this);
40269             um.un('failure', this.onLoad, this);
40270         }
40271     }
40272 };/*
40273  * Based on:
40274  * Ext JS Library 1.1.1
40275  * Copyright(c) 2006-2007, Ext JS, LLC.
40276  *
40277  * Originally Released Under LGPL - original licence link has changed is not relivant.
40278  *
40279  * Fork - LGPL
40280  * <script type="text/javascript">
40281  */
40282
40283
40284 /**
40285  * @class Roo.XTemplate
40286  * @extends Roo.Template
40287  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
40288 <pre><code>
40289 var t = new Roo.XTemplate(
40290         '&lt;select name="{name}"&gt;',
40291                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
40292         '&lt;/select&gt;'
40293 );
40294  
40295 // then append, applying the master template values
40296  </code></pre>
40297  *
40298  * Supported features:
40299  *
40300  *  Tags:
40301
40302 <pre><code>
40303       {a_variable} - output encoded.
40304       {a_variable.format:("Y-m-d")} - call a method on the variable
40305       {a_variable:raw} - unencoded output
40306       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
40307       {a_variable:this.method_on_template(...)} - call a method on the template object.
40308  
40309 </code></pre>
40310  *  The tpl tag:
40311 <pre><code>
40312         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
40313         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
40314         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
40315         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
40316   
40317         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
40318         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
40319 </code></pre>
40320  *      
40321  */
40322 Roo.XTemplate = function()
40323 {
40324     Roo.XTemplate.superclass.constructor.apply(this, arguments);
40325     if (this.html) {
40326         this.compile();
40327     }
40328 };
40329
40330
40331 Roo.extend(Roo.XTemplate, Roo.Template, {
40332
40333     /**
40334      * The various sub templates
40335      */
40336     tpls : false,
40337     /**
40338      *
40339      * basic tag replacing syntax
40340      * WORD:WORD()
40341      *
40342      * // you can fake an object call by doing this
40343      *  x.t:(test,tesT) 
40344      * 
40345      */
40346     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
40347
40348     /**
40349      * compile the template
40350      *
40351      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
40352      *
40353      */
40354     compile: function()
40355     {
40356         var s = this.html;
40357      
40358         s = ['<tpl>', s, '</tpl>'].join('');
40359     
40360         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
40361             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
40362             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
40363             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
40364             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
40365             m,
40366             id     = 0,
40367             tpls   = [];
40368     
40369         while(true == !!(m = s.match(re))){
40370             var forMatch   = m[0].match(nameRe),
40371                 ifMatch   = m[0].match(ifRe),
40372                 execMatch   = m[0].match(execRe),
40373                 namedMatch   = m[0].match(namedRe),
40374                 
40375                 exp  = null, 
40376                 fn   = null,
40377                 exec = null,
40378                 name = forMatch && forMatch[1] ? forMatch[1] : '';
40379                 
40380             if (ifMatch) {
40381                 // if - puts fn into test..
40382                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
40383                 if(exp){
40384                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
40385                 }
40386             }
40387             
40388             if (execMatch) {
40389                 // exec - calls a function... returns empty if true is  returned.
40390                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
40391                 if(exp){
40392                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
40393                 }
40394             }
40395             
40396             
40397             if (name) {
40398                 // for = 
40399                 switch(name){
40400                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
40401                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
40402                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
40403                 }
40404             }
40405             var uid = namedMatch ? namedMatch[1] : id;
40406             
40407             
40408             tpls.push({
40409                 id:     namedMatch ? namedMatch[1] : id,
40410                 target: name,
40411                 exec:   exec,
40412                 test:   fn,
40413                 body:   m[1] || ''
40414             });
40415             if (namedMatch) {
40416                 s = s.replace(m[0], '');
40417             } else { 
40418                 s = s.replace(m[0], '{xtpl'+ id + '}');
40419             }
40420             ++id;
40421         }
40422         this.tpls = [];
40423         for(var i = tpls.length-1; i >= 0; --i){
40424             this.compileTpl(tpls[i]);
40425             this.tpls[tpls[i].id] = tpls[i];
40426         }
40427         this.master = tpls[tpls.length-1];
40428         return this;
40429     },
40430     /**
40431      * same as applyTemplate, except it's done to one of the subTemplates
40432      * when using named templates, you can do:
40433      *
40434      * var str = pl.applySubTemplate('your-name', values);
40435      *
40436      * 
40437      * @param {Number} id of the template
40438      * @param {Object} values to apply to template
40439      * @param {Object} parent (normaly the instance of this object)
40440      */
40441     applySubTemplate : function(id, values, parent)
40442     {
40443         
40444         
40445         var t = this.tpls[id];
40446         
40447         
40448         try { 
40449             if(t.test && !t.test.call(this, values, parent)){
40450                 return '';
40451             }
40452         } catch(e) {
40453             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
40454             Roo.log(e.toString());
40455             Roo.log(t.test);
40456             return ''
40457         }
40458         try { 
40459             
40460             if(t.exec && t.exec.call(this, values, parent)){
40461                 return '';
40462             }
40463         } catch(e) {
40464             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
40465             Roo.log(e.toString());
40466             Roo.log(t.exec);
40467             return ''
40468         }
40469         try {
40470             var vs = t.target ? t.target.call(this, values, parent) : values;
40471             parent = t.target ? values : parent;
40472             if(t.target && vs instanceof Array){
40473                 var buf = [];
40474                 for(var i = 0, len = vs.length; i < len; i++){
40475                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
40476                 }
40477                 return buf.join('');
40478             }
40479             return t.compiled.call(this, vs, parent);
40480         } catch (e) {
40481             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
40482             Roo.log(e.toString());
40483             Roo.log(t.compiled);
40484             return '';
40485         }
40486     },
40487
40488     compileTpl : function(tpl)
40489     {
40490         var fm = Roo.util.Format;
40491         var useF = this.disableFormats !== true;
40492         var sep = Roo.isGecko ? "+" : ",";
40493         var undef = function(str) {
40494             Roo.log("Property not found :"  + str);
40495             return '';
40496         };
40497         
40498         var fn = function(m, name, format, args)
40499         {
40500             //Roo.log(arguments);
40501             args = args ? args.replace(/\\'/g,"'") : args;
40502             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
40503             if (typeof(format) == 'undefined') {
40504                 format= 'htmlEncode';
40505             }
40506             if (format == 'raw' ) {
40507                 format = false;
40508             }
40509             
40510             if(name.substr(0, 4) == 'xtpl'){
40511                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
40512             }
40513             
40514             // build an array of options to determine if value is undefined..
40515             
40516             // basically get 'xxxx.yyyy' then do
40517             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
40518             //    (function () { Roo.log("Property not found"); return ''; })() :
40519             //    ......
40520             
40521             var udef_ar = [];
40522             var lookfor = '';
40523             Roo.each(name.split('.'), function(st) {
40524                 lookfor += (lookfor.length ? '.': '') + st;
40525                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
40526             });
40527             
40528             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
40529             
40530             
40531             if(format && useF){
40532                 
40533                 args = args ? ',' + args : "";
40534                  
40535                 if(format.substr(0, 5) != "this."){
40536                     format = "fm." + format + '(';
40537                 }else{
40538                     format = 'this.call("'+ format.substr(5) + '", ';
40539                     args = ", values";
40540                 }
40541                 
40542                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
40543             }
40544              
40545             if (args.length) {
40546                 // called with xxyx.yuu:(test,test)
40547                 // change to ()
40548                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
40549             }
40550             // raw.. - :raw modifier..
40551             return "'"+ sep + udef_st  + name + ")"+sep+"'";
40552             
40553         };
40554         var body;
40555         // branched to use + in gecko and [].join() in others
40556         if(Roo.isGecko){
40557             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
40558                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
40559                     "';};};";
40560         }else{
40561             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
40562             body.push(tpl.body.replace(/(\r\n|\n)/g,
40563                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
40564             body.push("'].join('');};};");
40565             body = body.join('');
40566         }
40567         
40568         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
40569        
40570         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
40571         eval(body);
40572         
40573         return this;
40574     },
40575
40576     applyTemplate : function(values){
40577         return this.master.compiled.call(this, values, {});
40578         //var s = this.subs;
40579     },
40580
40581     apply : function(){
40582         return this.applyTemplate.apply(this, arguments);
40583     }
40584
40585  });
40586
40587 Roo.XTemplate.from = function(el){
40588     el = Roo.getDom(el);
40589     return new Roo.XTemplate(el.value || el.innerHTML);
40590 };/*
40591  * Original code for Roojs - LGPL
40592  * <script type="text/javascript">
40593  */
40594  
40595 /**
40596  * @class Roo.XComponent
40597  * A delayed Element creator...
40598  * Or a way to group chunks of interface together.
40599  * 
40600  * Mypart.xyx = new Roo.XComponent({
40601
40602     parent : 'Mypart.xyz', // empty == document.element.!!
40603     order : '001',
40604     name : 'xxxx'
40605     region : 'xxxx'
40606     disabled : function() {} 
40607      
40608     tree : function() { // return an tree of xtype declared components
40609         var MODULE = this;
40610         return 
40611         {
40612             xtype : 'NestedLayoutPanel',
40613             // technicall
40614         }
40615      ]
40616  *})
40617  *
40618  *
40619  * It can be used to build a big heiracy, with parent etc.
40620  * or you can just use this to render a single compoent to a dom element
40621  * MYPART.render(Roo.Element | String(id) | dom_element )
40622  * 
40623  * @extends Roo.util.Observable
40624  * @constructor
40625  * @param cfg {Object} configuration of component
40626  * 
40627  */
40628 Roo.XComponent = function(cfg) {
40629     Roo.apply(this, cfg);
40630     this.addEvents({ 
40631         /**
40632              * @event built
40633              * Fires when this the componnt is built
40634              * @param {Roo.XComponent} c the component
40635              */
40636         'built' : true
40637         
40638     });
40639     this.region = this.region || 'center'; // default..
40640     Roo.XComponent.register(this);
40641     this.modules = false;
40642     this.el = false; // where the layout goes..
40643     
40644     
40645 }
40646 Roo.extend(Roo.XComponent, Roo.util.Observable, {
40647     /**
40648      * @property el
40649      * The created element (with Roo.factory())
40650      * @type {Roo.Layout}
40651      */
40652     el  : false,
40653     
40654     /**
40655      * @property el
40656      * for BC  - use el in new code
40657      * @type {Roo.Layout}
40658      */
40659     panel : false,
40660     
40661     /**
40662      * @property layout
40663      * for BC  - use el in new code
40664      * @type {Roo.Layout}
40665      */
40666     layout : false,
40667     
40668      /**
40669      * @cfg {Function|boolean} disabled
40670      * If this module is disabled by some rule, return true from the funtion
40671      */
40672     disabled : false,
40673     
40674     /**
40675      * @cfg {String} parent 
40676      * Name of parent element which it get xtype added to..
40677      */
40678     parent: false,
40679     
40680     /**
40681      * @cfg {String} order
40682      * Used to set the order in which elements are created (usefull for multiple tabs)
40683      */
40684     
40685     order : false,
40686     /**
40687      * @cfg {String} name
40688      * String to display while loading.
40689      */
40690     name : false,
40691     /**
40692      * @cfg {String} region
40693      * Region to render component to (defaults to center)
40694      */
40695     region : 'center',
40696     
40697     /**
40698      * @cfg {Array} items
40699      * A single item array - the first element is the root of the tree..
40700      * It's done this way to stay compatible with the Xtype system...
40701      */
40702     items : false,
40703     
40704     /**
40705      * @property _tree
40706      * The method that retuns the tree of parts that make up this compoennt 
40707      * @type {function}
40708      */
40709     _tree  : false,
40710     
40711      /**
40712      * render
40713      * render element to dom or tree
40714      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
40715      */
40716     
40717     render : function(el)
40718     {
40719         
40720         el = el || false;
40721         var hp = this.parent ? 1 : 0;
40722         
40723         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
40724             // if parent is a '#.....' string, then let's use that..
40725             var ename = this.parent.substr(1)
40726             this.parent = (this.parent == '#bootstrap') ? { el : true}  : false; // flags it as a top module...
40727             el = Roo.get(ename);
40728             if (!el && !this.parent) {
40729                 Roo.log("Warning - element can not be found :#" + ename );
40730                 return;
40731             }
40732         }
40733         
40734         
40735         if (!this.parent) {
40736             
40737             el = el ? Roo.get(el) : false;      
40738             
40739             // it's a top level one..
40740             this.parent =  {
40741                 el : new Roo.BorderLayout(el || document.body, {
40742                 
40743                      center: {
40744                          titlebar: false,
40745                          autoScroll:false,
40746                          closeOnTab: true,
40747                          tabPosition: 'top',
40748                           //resizeTabs: true,
40749                          alwaysShowTabs: el && hp? false :  true,
40750                          hideTabs: el || !hp ? true :  false,
40751                          minTabWidth: 140
40752                      }
40753                  })
40754             }
40755         }
40756         
40757                 if (!this.parent.el) {
40758                         // probably an old style ctor, which has been disabled.
40759                         return;
40760                         
40761                 }
40762                 // The 'tree' method is  '_tree now' 
40763             
40764         var tree = this._tree ? this._tree() : this.tree();
40765         tree.region = tree.region || this.region;
40766         this.el = this.parent.el.addxtype(tree);
40767         this.fireEvent('built', this);
40768         
40769         this.panel = this.el;
40770         this.layout = this.panel.layout;
40771                 this.parentLayout = this.parent.layout  || false;  
40772          
40773     }
40774     
40775 });
40776
40777 Roo.apply(Roo.XComponent, {
40778     /**
40779      * @property  hideProgress
40780      * true to disable the building progress bar.. usefull on single page renders.
40781      * @type Boolean
40782      */
40783     hideProgress : false,
40784     /**
40785      * @property  buildCompleted
40786      * True when the builder has completed building the interface.
40787      * @type Boolean
40788      */
40789     buildCompleted : false,
40790      
40791     /**
40792      * @property  topModule
40793      * the upper most module - uses document.element as it's constructor.
40794      * @type Object
40795      */
40796      
40797     topModule  : false,
40798       
40799     /**
40800      * @property  modules
40801      * array of modules to be created by registration system.
40802      * @type {Array} of Roo.XComponent
40803      */
40804     
40805     modules : [],
40806     /**
40807      * @property  elmodules
40808      * array of modules to be created by which use #ID 
40809      * @type {Array} of Roo.XComponent
40810      */
40811      
40812     elmodules : [],
40813
40814     
40815     /**
40816      * Register components to be built later.
40817      *
40818      * This solves the following issues
40819      * - Building is not done on page load, but after an authentication process has occured.
40820      * - Interface elements are registered on page load
40821      * - Parent Interface elements may not be loaded before child, so this handles that..
40822      * 
40823      *
40824      * example:
40825      * 
40826      * MyApp.register({
40827           order : '000001',
40828           module : 'Pman.Tab.projectMgr',
40829           region : 'center',
40830           parent : 'Pman.layout',
40831           disabled : false,  // or use a function..
40832         })
40833      
40834      * * @param {Object} details about module
40835      */
40836     register : function(obj) {
40837                 
40838         Roo.XComponent.event.fireEvent('register', obj);
40839         switch(typeof(obj.disabled) ) {
40840                 
40841             case 'undefined':
40842                 break;
40843             
40844             case 'function':
40845                 if ( obj.disabled() ) {
40846                         return;
40847                 }
40848                 break;
40849             
40850             default:
40851                 if (obj.disabled) {
40852                         return;
40853                 }
40854                 break;
40855         }
40856                 
40857         this.modules.push(obj);
40858          
40859     },
40860     /**
40861      * convert a string to an object..
40862      * eg. 'AAA.BBB' -> finds AAA.BBB
40863
40864      */
40865     
40866     toObject : function(str)
40867     {
40868         if (!str || typeof(str) == 'object') {
40869             return str;
40870         }
40871         if (str.substring(0,1) == '#') {
40872             return str;
40873         }
40874
40875         var ar = str.split('.');
40876         var rt, o;
40877         rt = ar.shift();
40878             /** eval:var:o */
40879         try {
40880             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
40881         } catch (e) {
40882             throw "Module not found : " + str;
40883         }
40884         
40885         if (o === false) {
40886             throw "Module not found : " + str;
40887         }
40888         Roo.each(ar, function(e) {
40889             if (typeof(o[e]) == 'undefined') {
40890                 throw "Module not found : " + str;
40891             }
40892             o = o[e];
40893         });
40894         
40895         return o;
40896         
40897     },
40898     
40899     
40900     /**
40901      * move modules into their correct place in the tree..
40902      * 
40903      */
40904     preBuild : function ()
40905     {
40906         var _t = this;
40907         Roo.each(this.modules , function (obj)
40908         {
40909             Roo.XComponent.event.fireEvent('beforebuild', obj);
40910             
40911             var opar = obj.parent;
40912             try { 
40913                 obj.parent = this.toObject(opar);
40914             } catch(e) {
40915                 Roo.log("parent:toObject failed: " + e.toString());
40916                 return;
40917             }
40918             
40919             if (!obj.parent) {
40920                 Roo.debug && Roo.log("GOT top level module");
40921                 Roo.debug && Roo.log(obj);
40922                 obj.modules = new Roo.util.MixedCollection(false, 
40923                     function(o) { return o.order + '' }
40924                 );
40925                 this.topModule = obj;
40926                 return;
40927             }
40928                         // parent is a string (usually a dom element name..)
40929             if (typeof(obj.parent) == 'string') {
40930                 this.elmodules.push(obj);
40931                 return;
40932             }
40933             if (obj.parent.constructor != Roo.XComponent) {
40934                 Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
40935             }
40936             if (!obj.parent.modules) {
40937                 obj.parent.modules = new Roo.util.MixedCollection(false, 
40938                     function(o) { return o.order + '' }
40939                 );
40940             }
40941             if (obj.parent.disabled) {
40942                 obj.disabled = true;
40943             }
40944             obj.parent.modules.add(obj);
40945         }, this);
40946     },
40947     
40948      /**
40949      * make a list of modules to build.
40950      * @return {Array} list of modules. 
40951      */ 
40952     
40953     buildOrder : function()
40954     {
40955         var _this = this;
40956         var cmp = function(a,b) {   
40957             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
40958         };
40959         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
40960             throw "No top level modules to build";
40961         }
40962         
40963         // make a flat list in order of modules to build.
40964         var mods = this.topModule ? [ this.topModule ] : [];
40965                 
40966         
40967         // elmodules (is a list of DOM based modules )
40968         Roo.each(this.elmodules, function(e) {
40969             mods.push(e);
40970             if (!this.topModule &&
40971                 typeof(e.parent) == 'string' &&
40972                 e.parent.substring(0,1) == '#' &&
40973                 Roo.get(e.parent.substr(1))
40974                ) {
40975                 
40976                 _this.topModule = e;
40977             }
40978             
40979         });
40980
40981         
40982         // add modules to their parents..
40983         var addMod = function(m) {
40984             Roo.debug && Roo.log("build Order: add: " + m.name);
40985                 
40986             mods.push(m);
40987             if (m.modules && !m.disabled) {
40988                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
40989                 m.modules.keySort('ASC',  cmp );
40990                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
40991     
40992                 m.modules.each(addMod);
40993             } else {
40994                 Roo.debug && Roo.log("build Order: no child modules");
40995             }
40996             // not sure if this is used any more..
40997             if (m.finalize) {
40998                 m.finalize.name = m.name + " (clean up) ";
40999                 mods.push(m.finalize);
41000             }
41001             
41002         }
41003         if (this.topModule && this.topModule.modules) { 
41004             this.topModule.modules.keySort('ASC',  cmp );
41005             this.topModule.modules.each(addMod);
41006         } 
41007         return mods;
41008     },
41009     
41010      /**
41011      * Build the registered modules.
41012      * @param {Object} parent element.
41013      * @param {Function} optional method to call after module has been added.
41014      * 
41015      */ 
41016    
41017     build : function() 
41018     {
41019         
41020         this.preBuild();
41021         var mods = this.buildOrder();
41022       
41023         //this.allmods = mods;
41024         //Roo.debug && Roo.log(mods);
41025         //return;
41026         if (!mods.length) { // should not happen
41027             throw "NO modules!!!";
41028         }
41029         
41030         
41031         var msg = "Building Interface...";
41032         // flash it up as modal - so we store the mask!?
41033         if (!this.hideProgress) {
41034             Roo.MessageBox.show({ title: 'loading' });
41035             Roo.MessageBox.show({
41036                title: "Please wait...",
41037                msg: msg,
41038                width:450,
41039                progress:true,
41040                closable:false,
41041                modal: false
41042               
41043             });
41044         }
41045         var total = mods.length;
41046         
41047         var _this = this;
41048         var progressRun = function() {
41049             if (!mods.length) {
41050                 Roo.debug && Roo.log('hide?');
41051                 if (!this.hideProgress) {
41052                     Roo.MessageBox.hide();
41053                 }
41054                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
41055                 
41056                 // THE END...
41057                 return false;   
41058             }
41059             
41060             var m = mods.shift();
41061             
41062             
41063             Roo.debug && Roo.log(m);
41064             // not sure if this is supported any more.. - modules that are are just function
41065             if (typeof(m) == 'function') { 
41066                 m.call(this);
41067                 return progressRun.defer(10, _this);
41068             } 
41069             
41070             
41071             msg = "Building Interface " + (total  - mods.length) + 
41072                     " of " + total + 
41073                     (m.name ? (' - ' + m.name) : '');
41074                         Roo.debug && Roo.log(msg);
41075             if (!this.hideProgress) { 
41076                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
41077             }
41078             
41079          
41080             // is the module disabled?
41081             var disabled = (typeof(m.disabled) == 'function') ?
41082                 m.disabled.call(m.module.disabled) : m.disabled;    
41083             
41084             
41085             if (disabled) {
41086                 return progressRun(); // we do not update the display!
41087             }
41088             
41089             // now build 
41090             
41091                         
41092                         
41093             m.render();
41094             // it's 10 on top level, and 1 on others??? why...
41095             return progressRun.defer(10, _this);
41096              
41097         }
41098         progressRun.defer(1, _this);
41099      
41100         
41101         
41102     },
41103         
41104         
41105         /**
41106          * Event Object.
41107          *
41108          *
41109          */
41110         event: false, 
41111     /**
41112          * wrapper for event.on - aliased later..  
41113          * Typically use to register a event handler for register:
41114          *
41115          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
41116          *
41117          */
41118     on : false
41119    
41120     
41121     
41122 });
41123
41124 Roo.XComponent.event = new Roo.util.Observable({
41125                 events : { 
41126                         /**
41127                          * @event register
41128                          * Fires when an Component is registered,
41129                          * set the disable property on the Component to stop registration.
41130                          * @param {Roo.XComponent} c the component being registerd.
41131                          * 
41132                          */
41133                         'register' : true,
41134             /**
41135                          * @event beforebuild
41136                          * Fires before each Component is built
41137                          * can be used to apply permissions.
41138                          * @param {Roo.XComponent} c the component being registerd.
41139                          * 
41140                          */
41141                         'beforebuild' : true,
41142                         /**
41143                          * @event buildcomplete
41144                          * Fires on the top level element when all elements have been built
41145                          * @param {Roo.XComponent} the top level component.
41146                          */
41147                         'buildcomplete' : true
41148                         
41149                 }
41150 });
41151
41152 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
41153