roojs-ui.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
832         if (this.isLocked()) {
833             Roo.log('locked');
834             return;
835         }
836
837         this.DDM.refreshCache(this.groups);
838         Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
839         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
840         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
841             Roo.log('no outer handes or not over target');
842                 // do nothing.
843         } else {
844             Roo.log('check validator');
845             if (this.clickValidator(e)) {
846                 Roo.log('validate success');
847                 // set the initial element position
848                 this.setStartPosition();
849
850
851                 this.b4MouseDown(e);
852                 this.onMouseDown(e);
853
854                 this.DDM.handleMouseDown(e, this);
855
856                 this.DDM.stopEvent(e);
857             } else {
858
859
860             }
861         }
862     },
863
864     clickValidator: function(e) {
865         var target = e.getTarget();
866         return ( this.isValidHandleChild(target) &&
867                     (this.id == this.handleElId ||
868                         this.DDM.handleWasClicked(target, this.id)) );
869     },
870
871     /**
872      * Allows you to specify a tag name that should not start a drag operation
873      * when clicked.  This is designed to facilitate embedding links within a
874      * drag handle that do something other than start the drag.
875      * @method addInvalidHandleType
876      * @param {string} tagName the type of element to exclude
877      */
878     addInvalidHandleType: function(tagName) {
879         var type = tagName.toUpperCase();
880         this.invalidHandleTypes[type] = type;
881     },
882
883     /**
884      * Lets you to specify an element id for a child of a drag handle
885      * that should not initiate a drag
886      * @method addInvalidHandleId
887      * @param {string} id the element id of the element you wish to ignore
888      */
889     addInvalidHandleId: function(id) {
890         if (typeof id !== "string") {
891             id = Roo.id(id);
892         }
893         this.invalidHandleIds[id] = id;
894     },
895
896     /**
897      * Lets you specify a css class of elements that will not initiate a drag
898      * @method addInvalidHandleClass
899      * @param {string} cssClass the class of the elements you wish to ignore
900      */
901     addInvalidHandleClass: function(cssClass) {
902         this.invalidHandleClasses.push(cssClass);
903     },
904
905     /**
906      * Unsets an excluded tag name set by addInvalidHandleType
907      * @method removeInvalidHandleType
908      * @param {string} tagName the type of element to unexclude
909      */
910     removeInvalidHandleType: function(tagName) {
911         var type = tagName.toUpperCase();
912         // this.invalidHandleTypes[type] = null;
913         delete this.invalidHandleTypes[type];
914     },
915
916     /**
917      * Unsets an invalid handle id
918      * @method removeInvalidHandleId
919      * @param {string} id the id of the element to re-enable
920      */
921     removeInvalidHandleId: function(id) {
922         if (typeof id !== "string") {
923             id = Roo.id(id);
924         }
925         delete this.invalidHandleIds[id];
926     },
927
928     /**
929      * Unsets an invalid css class
930      * @method removeInvalidHandleClass
931      * @param {string} cssClass the class of the element(s) you wish to
932      * re-enable
933      */
934     removeInvalidHandleClass: function(cssClass) {
935         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
936             if (this.invalidHandleClasses[i] == cssClass) {
937                 delete this.invalidHandleClasses[i];
938             }
939         }
940     },
941
942     /**
943      * Checks the tag exclusion list to see if this click should be ignored
944      * @method isValidHandleChild
945      * @param {HTMLElement} node the HTMLElement to evaluate
946      * @return {boolean} true if this is a valid tag type, false if not
947      */
948     isValidHandleChild: function(node) {
949
950         var valid = true;
951         // var n = (node.nodeName == "#text") ? node.parentNode : node;
952         var nodeName;
953         try {
954             nodeName = node.nodeName.toUpperCase();
955         } catch(e) {
956             nodeName = node.nodeName;
957         }
958         valid = valid && !this.invalidHandleTypes[nodeName];
959         valid = valid && !this.invalidHandleIds[node.id];
960
961         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
962             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
963         }
964
965
966         return valid;
967
968     },
969
970     /**
971      * Create the array of horizontal tick marks if an interval was specified
972      * in setXConstraint().
973      * @method setXTicks
974      * @private
975      */
976     setXTicks: function(iStartX, iTickSize) {
977         this.xTicks = [];
978         this.xTickSize = iTickSize;
979
980         var tickMap = {};
981
982         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
983             if (!tickMap[i]) {
984                 this.xTicks[this.xTicks.length] = i;
985                 tickMap[i] = true;
986             }
987         }
988
989         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
990             if (!tickMap[i]) {
991                 this.xTicks[this.xTicks.length] = i;
992                 tickMap[i] = true;
993             }
994         }
995
996         this.xTicks.sort(this.DDM.numericSort) ;
997     },
998
999     /**
1000      * Create the array of vertical tick marks if an interval was specified in
1001      * setYConstraint().
1002      * @method setYTicks
1003      * @private
1004      */
1005     setYTicks: function(iStartY, iTickSize) {
1006         this.yTicks = [];
1007         this.yTickSize = iTickSize;
1008
1009         var tickMap = {};
1010
1011         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
1012             if (!tickMap[i]) {
1013                 this.yTicks[this.yTicks.length] = i;
1014                 tickMap[i] = true;
1015             }
1016         }
1017
1018         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
1019             if (!tickMap[i]) {
1020                 this.yTicks[this.yTicks.length] = i;
1021                 tickMap[i] = true;
1022             }
1023         }
1024
1025         this.yTicks.sort(this.DDM.numericSort) ;
1026     },
1027
1028     /**
1029      * By default, the element can be dragged any place on the screen.  Use
1030      * this method to limit the horizontal travel of the element.  Pass in
1031      * 0,0 for the parameters if you want to lock the drag to the y axis.
1032      * @method setXConstraint
1033      * @param {int} iLeft the number of pixels the element can move to the left
1034      * @param {int} iRight the number of pixels the element can move to the
1035      * right
1036      * @param {int} iTickSize optional parameter for specifying that the
1037      * element
1038      * should move iTickSize pixels at a time.
1039      */
1040     setXConstraint: function(iLeft, iRight, iTickSize) {
1041         this.leftConstraint = iLeft;
1042         this.rightConstraint = iRight;
1043
1044         this.minX = this.initPageX - iLeft;
1045         this.maxX = this.initPageX + iRight;
1046         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
1047
1048         this.constrainX = true;
1049     },
1050
1051     /**
1052      * Clears any constraints applied to this instance.  Also clears ticks
1053      * since they can't exist independent of a constraint at this time.
1054      * @method clearConstraints
1055      */
1056     clearConstraints: function() {
1057         this.constrainX = false;
1058         this.constrainY = false;
1059         this.clearTicks();
1060     },
1061
1062     /**
1063      * Clears any tick interval defined for this instance
1064      * @method clearTicks
1065      */
1066     clearTicks: function() {
1067         this.xTicks = null;
1068         this.yTicks = null;
1069         this.xTickSize = 0;
1070         this.yTickSize = 0;
1071     },
1072
1073     /**
1074      * By default, the element can be dragged any place on the screen.  Set
1075      * this to limit the vertical travel of the element.  Pass in 0,0 for the
1076      * parameters if you want to lock the drag to the x axis.
1077      * @method setYConstraint
1078      * @param {int} iUp the number of pixels the element can move up
1079      * @param {int} iDown the number of pixels the element can move down
1080      * @param {int} iTickSize optional parameter for specifying that the
1081      * element should move iTickSize pixels at a time.
1082      */
1083     setYConstraint: function(iUp, iDown, iTickSize) {
1084         this.topConstraint = iUp;
1085         this.bottomConstraint = iDown;
1086
1087         this.minY = this.initPageY - iUp;
1088         this.maxY = this.initPageY + iDown;
1089         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
1090
1091         this.constrainY = true;
1092
1093     },
1094
1095     /**
1096      * resetConstraints must be called if you manually reposition a dd element.
1097      * @method resetConstraints
1098      * @param {boolean} maintainOffset
1099      */
1100     resetConstraints: function() {
1101
1102
1103         // Maintain offsets if necessary
1104         if (this.initPageX || this.initPageX === 0) {
1105             // figure out how much this thing has moved
1106             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
1107             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
1108
1109             this.setInitPosition(dx, dy);
1110
1111         // This is the first time we have detected the element's position
1112         } else {
1113             this.setInitPosition();
1114         }
1115
1116         if (this.constrainX) {
1117             this.setXConstraint( this.leftConstraint,
1118                                  this.rightConstraint,
1119                                  this.xTickSize        );
1120         }
1121
1122         if (this.constrainY) {
1123             this.setYConstraint( this.topConstraint,
1124                                  this.bottomConstraint,
1125                                  this.yTickSize         );
1126         }
1127     },
1128
1129     /**
1130      * Normally the drag element is moved pixel by pixel, but we can specify
1131      * that it move a number of pixels at a time.  This method resolves the
1132      * location when we have it set up like this.
1133      * @method getTick
1134      * @param {int} val where we want to place the object
1135      * @param {int[]} tickArray sorted array of valid points
1136      * @return {int} the closest tick
1137      * @private
1138      */
1139     getTick: function(val, tickArray) {
1140
1141         if (!tickArray) {
1142             // If tick interval is not defined, it is effectively 1 pixel,
1143             // so we return the value passed to us.
1144             return val;
1145         } else if (tickArray[0] >= val) {
1146             // The value is lower than the first tick, so we return the first
1147             // tick.
1148             return tickArray[0];
1149         } else {
1150             for (var i=0, len=tickArray.length; i<len; ++i) {
1151                 var next = i + 1;
1152                 if (tickArray[next] && tickArray[next] >= val) {
1153                     var diff1 = val - tickArray[i];
1154                     var diff2 = tickArray[next] - val;
1155                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
1156                 }
1157             }
1158
1159             // The value is larger than the last tick, so we return the last
1160             // tick.
1161             return tickArray[tickArray.length - 1];
1162         }
1163     },
1164
1165     /**
1166      * toString method
1167      * @method toString
1168      * @return {string} string representation of the dd obj
1169      */
1170     toString: function() {
1171         return ("DragDrop " + this.id);
1172     }
1173
1174 });
1175
1176 })();
1177 /*
1178  * Based on:
1179  * Ext JS Library 1.1.1
1180  * Copyright(c) 2006-2007, Ext JS, LLC.
1181  *
1182  * Originally Released Under LGPL - original licence link has changed is not relivant.
1183  *
1184  * Fork - LGPL
1185  * <script type="text/javascript">
1186  */
1187
1188
1189 /**
1190  * The drag and drop utility provides a framework for building drag and drop
1191  * applications.  In addition to enabling drag and drop for specific elements,
1192  * the drag and drop elements are tracked by the manager class, and the
1193  * interactions between the various elements are tracked during the drag and
1194  * the implementing code is notified about these important moments.
1195  */
1196
1197 // Only load the library once.  Rewriting the manager class would orphan
1198 // existing drag and drop instances.
1199 if (!Roo.dd.DragDropMgr) {
1200
1201 /**
1202  * @class Roo.dd.DragDropMgr
1203  * DragDropMgr is a singleton that tracks the element interaction for
1204  * all DragDrop items in the window.  Generally, you will not call
1205  * this class directly, but it does have helper methods that could
1206  * be useful in your DragDrop implementations.
1207  * @singleton
1208  */
1209 Roo.dd.DragDropMgr = function() {
1210
1211     var Event = Roo.EventManager;
1212
1213     return {
1214
1215         /**
1216          * Two dimensional Array of registered DragDrop objects.  The first
1217          * dimension is the DragDrop item group, the second the DragDrop
1218          * object.
1219          * @property ids
1220          * @type {string: string}
1221          * @private
1222          * @static
1223          */
1224         ids: {},
1225
1226         /**
1227          * Array of element ids defined as drag handles.  Used to determine
1228          * if the element that generated the mousedown event is actually the
1229          * handle and not the html element itself.
1230          * @property handleIds
1231          * @type {string: string}
1232          * @private
1233          * @static
1234          */
1235         handleIds: {},
1236
1237         /**
1238          * the DragDrop object that is currently being dragged
1239          * @property dragCurrent
1240          * @type DragDrop
1241          * @private
1242          * @static
1243          **/
1244         dragCurrent: null,
1245
1246         /**
1247          * the DragDrop object(s) that are being hovered over
1248          * @property dragOvers
1249          * @type Array
1250          * @private
1251          * @static
1252          */
1253         dragOvers: {},
1254
1255         /**
1256          * the X distance between the cursor and the object being dragged
1257          * @property deltaX
1258          * @type int
1259          * @private
1260          * @static
1261          */
1262         deltaX: 0,
1263
1264         /**
1265          * the Y distance between the cursor and the object being dragged
1266          * @property deltaY
1267          * @type int
1268          * @private
1269          * @static
1270          */
1271         deltaY: 0,
1272
1273         /**
1274          * Flag to determine if we should prevent the default behavior of the
1275          * events we define. By default this is true, but this can be set to
1276          * false if you need the default behavior (not recommended)
1277          * @property preventDefault
1278          * @type boolean
1279          * @static
1280          */
1281         preventDefault: true,
1282
1283         /**
1284          * Flag to determine if we should stop the propagation of the events
1285          * we generate. This is true by default but you may want to set it to
1286          * false if the html element contains other features that require the
1287          * mouse click.
1288          * @property stopPropagation
1289          * @type boolean
1290          * @static
1291          */
1292         stopPropagation: true,
1293
1294         /**
1295          * Internal flag that is set to true when drag and drop has been
1296          * intialized
1297          * @property initialized
1298          * @private
1299          * @static
1300          */
1301         initalized: false,
1302
1303         /**
1304          * All drag and drop can be disabled.
1305          * @property locked
1306          * @private
1307          * @static
1308          */
1309         locked: false,
1310
1311         /**
1312          * Called the first time an element is registered.
1313          * @method init
1314          * @private
1315          * @static
1316          */
1317         init: function() {
1318             this.initialized = true;
1319         },
1320
1321         /**
1322          * In point mode, drag and drop interaction is defined by the
1323          * location of the cursor during the drag/drop
1324          * @property POINT
1325          * @type int
1326          * @static
1327          */
1328         POINT: 0,
1329
1330         /**
1331          * In intersect mode, drag and drop interactio nis defined by the
1332          * overlap of two or more drag and drop objects.
1333          * @property INTERSECT
1334          * @type int
1335          * @static
1336          */
1337         INTERSECT: 1,
1338
1339         /**
1340          * The current drag and drop mode.  Default: POINT
1341          * @property mode
1342          * @type int
1343          * @static
1344          */
1345         mode: 0,
1346
1347         /**
1348          * Runs method on all drag and drop objects
1349          * @method _execOnAll
1350          * @private
1351          * @static
1352          */
1353         _execOnAll: function(sMethod, args) {
1354             for (var i in this.ids) {
1355                 for (var j in this.ids[i]) {
1356                     var oDD = this.ids[i][j];
1357                     if (! this.isTypeOfDD(oDD)) {
1358                         continue;
1359                     }
1360                     oDD[sMethod].apply(oDD, args);
1361                 }
1362             }
1363         },
1364
1365         /**
1366          * Drag and drop initialization.  Sets up the global event handlers
1367          * @method _onLoad
1368          * @private
1369          * @static
1370          */
1371         _onLoad: function() {
1372
1373             this.init();
1374
1375             if (!Roo.isTouch) {
1376                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
1377                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
1378             }
1379             Event.on(document, "touchend",   this.handleMouseUp, this, true);
1380             Event.on(document, "touchmove", this.handleMouseMove, this, true);
1381             
1382             Event.on(window,   "unload",    this._onUnload, this, true);
1383             Event.on(window,   "resize",    this._onResize, this, true);
1384             // Event.on(window,   "mouseout",    this._test);
1385
1386         },
1387
1388         /**
1389          * Reset constraints on all drag and drop objs
1390          * @method _onResize
1391          * @private
1392          * @static
1393          */
1394         _onResize: function(e) {
1395             this._execOnAll("resetConstraints", []);
1396         },
1397
1398         /**
1399          * Lock all drag and drop functionality
1400          * @method lock
1401          * @static
1402          */
1403         lock: function() { this.locked = true; },
1404
1405         /**
1406          * Unlock all drag and drop functionality
1407          * @method unlock
1408          * @static
1409          */
1410         unlock: function() { this.locked = false; },
1411
1412         /**
1413          * Is drag and drop locked?
1414          * @method isLocked
1415          * @return {boolean} True if drag and drop is locked, false otherwise.
1416          * @static
1417          */
1418         isLocked: function() { return this.locked; },
1419
1420         /**
1421          * Location cache that is set for all drag drop objects when a drag is
1422          * initiated, cleared when the drag is finished.
1423          * @property locationCache
1424          * @private
1425          * @static
1426          */
1427         locationCache: {},
1428
1429         /**
1430          * Set useCache to false if you want to force object the lookup of each
1431          * drag and drop linked element constantly during a drag.
1432          * @property useCache
1433          * @type boolean
1434          * @static
1435          */
1436         useCache: true,
1437
1438         /**
1439          * The number of pixels that the mouse needs to move after the
1440          * mousedown before the drag is initiated.  Default=3;
1441          * @property clickPixelThresh
1442          * @type int
1443          * @static
1444          */
1445         clickPixelThresh: 3,
1446
1447         /**
1448          * The number of milliseconds after the mousedown event to initiate the
1449          * drag if we don't get a mouseup event. Default=1000
1450          * @property clickTimeThresh
1451          * @type int
1452          * @static
1453          */
1454         clickTimeThresh: 350,
1455
1456         /**
1457          * Flag that indicates that either the drag pixel threshold or the
1458          * mousdown time threshold has been met
1459          * @property dragThreshMet
1460          * @type boolean
1461          * @private
1462          * @static
1463          */
1464         dragThreshMet: false,
1465
1466         /**
1467          * Timeout used for the click time threshold
1468          * @property clickTimeout
1469          * @type Object
1470          * @private
1471          * @static
1472          */
1473         clickTimeout: null,
1474
1475         /**
1476          * The X position of the mousedown event stored for later use when a
1477          * drag threshold is met.
1478          * @property startX
1479          * @type int
1480          * @private
1481          * @static
1482          */
1483         startX: 0,
1484
1485         /**
1486          * The Y position of the mousedown event stored for later use when a
1487          * drag threshold is met.
1488          * @property startY
1489          * @type int
1490          * @private
1491          * @static
1492          */
1493         startY: 0,
1494
1495         /**
1496          * Each DragDrop instance must be registered with the DragDropMgr.
1497          * This is executed in DragDrop.init()
1498          * @method regDragDrop
1499          * @param {DragDrop} oDD the DragDrop object to register
1500          * @param {String} sGroup the name of the group this element belongs to
1501          * @static
1502          */
1503         regDragDrop: function(oDD, sGroup) {
1504             if (!this.initialized) { this.init(); }
1505
1506             if (!this.ids[sGroup]) {
1507                 this.ids[sGroup] = {};
1508             }
1509             this.ids[sGroup][oDD.id] = oDD;
1510         },
1511
1512         /**
1513          * Removes the supplied dd instance from the supplied group. Executed
1514          * by DragDrop.removeFromGroup, so don't call this function directly.
1515          * @method removeDDFromGroup
1516          * @private
1517          * @static
1518          */
1519         removeDDFromGroup: function(oDD, sGroup) {
1520             if (!this.ids[sGroup]) {
1521                 this.ids[sGroup] = {};
1522             }
1523
1524             var obj = this.ids[sGroup];
1525             if (obj && obj[oDD.id]) {
1526                 delete obj[oDD.id];
1527             }
1528         },
1529
1530         /**
1531          * Unregisters a drag and drop item.  This is executed in
1532          * DragDrop.unreg, use that method instead of calling this directly.
1533          * @method _remove
1534          * @private
1535          * @static
1536          */
1537         _remove: function(oDD) {
1538             for (var g in oDD.groups) {
1539                 if (g && this.ids[g][oDD.id]) {
1540                     delete this.ids[g][oDD.id];
1541                 }
1542             }
1543             delete this.handleIds[oDD.id];
1544         },
1545
1546         /**
1547          * Each DragDrop handle element must be registered.  This is done
1548          * automatically when executing DragDrop.setHandleElId()
1549          * @method regHandle
1550          * @param {String} sDDId the DragDrop id this element is a handle for
1551          * @param {String} sHandleId the id of the element that is the drag
1552          * handle
1553          * @static
1554          */
1555         regHandle: function(sDDId, sHandleId) {
1556             if (!this.handleIds[sDDId]) {
1557                 this.handleIds[sDDId] = {};
1558             }
1559             this.handleIds[sDDId][sHandleId] = sHandleId;
1560         },
1561
1562         /**
1563          * Utility function to determine if a given element has been
1564          * registered as a drag drop item.
1565          * @method isDragDrop
1566          * @param {String} id the element id to check
1567          * @return {boolean} true if this element is a DragDrop item,
1568          * false otherwise
1569          * @static
1570          */
1571         isDragDrop: function(id) {
1572             return ( this.getDDById(id) ) ? true : false;
1573         },
1574
1575         /**
1576          * Returns the drag and drop instances that are in all groups the
1577          * passed in instance belongs to.
1578          * @method getRelated
1579          * @param {DragDrop} p_oDD the obj to get related data for
1580          * @param {boolean} bTargetsOnly if true, only return targetable objs
1581          * @return {DragDrop[]} the related instances
1582          * @static
1583          */
1584         getRelated: function(p_oDD, bTargetsOnly) {
1585             var oDDs = [];
1586             for (var i in p_oDD.groups) {
1587                 for (j in this.ids[i]) {
1588                     var dd = this.ids[i][j];
1589                     if (! this.isTypeOfDD(dd)) {
1590                         continue;
1591                     }
1592                     if (!bTargetsOnly || dd.isTarget) {
1593                         oDDs[oDDs.length] = dd;
1594                     }
1595                 }
1596             }
1597
1598             return oDDs;
1599         },
1600
1601         /**
1602          * Returns true if the specified dd target is a legal target for
1603          * the specifice drag obj
1604          * @method isLegalTarget
1605          * @param {DragDrop} the drag obj
1606          * @param {DragDrop} the target
1607          * @return {boolean} true if the target is a legal target for the
1608          * dd obj
1609          * @static
1610          */
1611         isLegalTarget: function (oDD, oTargetDD) {
1612             var targets = this.getRelated(oDD, true);
1613             for (var i=0, len=targets.length;i<len;++i) {
1614                 if (targets[i].id == oTargetDD.id) {
1615                     return true;
1616                 }
1617             }
1618
1619             return false;
1620         },
1621
1622         /**
1623          * My goal is to be able to transparently determine if an object is
1624          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
1625          * returns "object", oDD.constructor.toString() always returns
1626          * "DragDrop" and not the name of the subclass.  So for now it just
1627          * evaluates a well-known variable in DragDrop.
1628          * @method isTypeOfDD
1629          * @param {Object} the object to evaluate
1630          * @return {boolean} true if typeof oDD = DragDrop
1631          * @static
1632          */
1633         isTypeOfDD: function (oDD) {
1634             return (oDD && oDD.__ygDragDrop);
1635         },
1636
1637         /**
1638          * Utility function to determine if a given element has been
1639          * registered as a drag drop handle for the given Drag Drop object.
1640          * @method isHandle
1641          * @param {String} id the element id to check
1642          * @return {boolean} true if this element is a DragDrop handle, false
1643          * otherwise
1644          * @static
1645          */
1646         isHandle: function(sDDId, sHandleId) {
1647             return ( this.handleIds[sDDId] &&
1648                             this.handleIds[sDDId][sHandleId] );
1649         },
1650
1651         /**
1652          * Returns the DragDrop instance for a given id
1653          * @method getDDById
1654          * @param {String} id the id of the DragDrop object
1655          * @return {DragDrop} the drag drop object, null if it is not found
1656          * @static
1657          */
1658         getDDById: function(id) {
1659             for (var i in this.ids) {
1660                 if (this.ids[i][id]) {
1661                     return this.ids[i][id];
1662                 }
1663             }
1664             return null;
1665         },
1666
1667         /**
1668          * Fired after a registered DragDrop object gets the mousedown event.
1669          * Sets up the events required to track the object being dragged
1670          * @method handleMouseDown
1671          * @param {Event} e the event
1672          * @param oDD the DragDrop object being dragged
1673          * @private
1674          * @static
1675          */
1676         handleMouseDown: function(e, oDD) {
1677             if(Roo.QuickTips){
1678                 Roo.QuickTips.disable();
1679             }
1680             this.currentTarget = e.getTarget();
1681
1682             this.dragCurrent = oDD;
1683
1684             var el = oDD.getEl();
1685
1686             // track start position
1687             this.startX = e.getPageX();
1688             this.startY = e.getPageY();
1689
1690             this.deltaX = this.startX - el.offsetLeft;
1691             this.deltaY = this.startY - el.offsetTop;
1692
1693             this.dragThreshMet = false;
1694
1695             this.clickTimeout = setTimeout(
1696                     function() {
1697                         var DDM = Roo.dd.DDM;
1698                         DDM.startDrag(DDM.startX, DDM.startY);
1699                     },
1700                     this.clickTimeThresh );
1701         },
1702
1703         /**
1704          * Fired when either the drag pixel threshol or the mousedown hold
1705          * time threshold has been met.
1706          * @method startDrag
1707          * @param x {int} the X position of the original mousedown
1708          * @param y {int} the Y position of the original mousedown
1709          * @static
1710          */
1711         startDrag: function(x, y) {
1712             clearTimeout(this.clickTimeout);
1713             if (this.dragCurrent) {
1714                 this.dragCurrent.b4StartDrag(x, y);
1715                 this.dragCurrent.startDrag(x, y);
1716             }
1717             this.dragThreshMet = true;
1718         },
1719
1720         /**
1721          * Internal function to handle the mouseup event.  Will be invoked
1722          * from the context of the document.
1723          * @method handleMouseUp
1724          * @param {Event} e the event
1725          * @private
1726          * @static
1727          */
1728         handleMouseUp: function(e) {
1729
1730             if(Roo.QuickTips){
1731                 Roo.QuickTips.enable();
1732             }
1733             if (! this.dragCurrent) {
1734                 return;
1735             }
1736
1737             clearTimeout(this.clickTimeout);
1738
1739             if (this.dragThreshMet) {
1740                 this.fireEvents(e, true);
1741             } else {
1742             }
1743
1744             this.stopDrag(e);
1745
1746             this.stopEvent(e);
1747         },
1748
1749         /**
1750          * Utility to stop event propagation and event default, if these
1751          * features are turned on.
1752          * @method stopEvent
1753          * @param {Event} e the event as returned by this.getEvent()
1754          * @static
1755          */
1756         stopEvent: function(e){
1757             if(this.stopPropagation) {
1758                 e.stopPropagation();
1759             }
1760
1761             if (this.preventDefault) {
1762                 e.preventDefault();
1763             }
1764         },
1765
1766         /**
1767          * Internal function to clean up event handlers after the drag
1768          * operation is complete
1769          * @method stopDrag
1770          * @param {Event} e the event
1771          * @private
1772          * @static
1773          */
1774         stopDrag: function(e) {
1775             // Fire the drag end event for the item that was dragged
1776             if (this.dragCurrent) {
1777                 if (this.dragThreshMet) {
1778                     this.dragCurrent.b4EndDrag(e);
1779                     this.dragCurrent.endDrag(e);
1780                 }
1781
1782                 this.dragCurrent.onMouseUp(e);
1783             }
1784
1785             this.dragCurrent = null;
1786             this.dragOvers = {};
1787         },
1788
1789         /**
1790          * Internal function to handle the mousemove event.  Will be invoked
1791          * from the context of the html element.
1792          *
1793          * @TODO figure out what we can do about mouse events lost when the
1794          * user drags objects beyond the window boundary.  Currently we can
1795          * detect this in internet explorer by verifying that the mouse is
1796          * down during the mousemove event.  Firefox doesn't give us the
1797          * button state on the mousemove event.
1798          * @method handleMouseMove
1799          * @param {Event} e the event
1800          * @private
1801          * @static
1802          */
1803         handleMouseMove: function(e) {
1804             if (! this.dragCurrent) {
1805                 return true;
1806             }
1807
1808             // var button = e.which || e.button;
1809
1810             // check for IE mouseup outside of page boundary
1811             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
1812                 this.stopEvent(e);
1813                 return this.handleMouseUp(e);
1814             }
1815
1816             if (!this.dragThreshMet) {
1817                 var diffX = Math.abs(this.startX - e.getPageX());
1818                 var diffY = Math.abs(this.startY - e.getPageY());
1819                 if (diffX > this.clickPixelThresh ||
1820                             diffY > this.clickPixelThresh) {
1821                     this.startDrag(this.startX, this.startY);
1822                 }
1823             }
1824
1825             if (this.dragThreshMet) {
1826                 this.dragCurrent.b4Drag(e);
1827                 this.dragCurrent.onDrag(e);
1828                 if(!this.dragCurrent.moveOnly){
1829                     this.fireEvents(e, false);
1830                 }
1831             }
1832
1833             this.stopEvent(e);
1834
1835             return true;
1836         },
1837
1838         /**
1839          * Iterates over all of the DragDrop elements to find ones we are
1840          * hovering over or dropping on
1841          * @method fireEvents
1842          * @param {Event} e the event
1843          * @param {boolean} isDrop is this a drop op or a mouseover op?
1844          * @private
1845          * @static
1846          */
1847         fireEvents: function(e, isDrop) {
1848             var dc = this.dragCurrent;
1849
1850             // If the user did the mouse up outside of the window, we could
1851             // get here even though we have ended the drag.
1852             if (!dc || dc.isLocked()) {
1853                 return;
1854             }
1855
1856             var pt = e.getPoint();
1857
1858             // cache the previous dragOver array
1859             var oldOvers = [];
1860
1861             var outEvts   = [];
1862             var overEvts  = [];
1863             var dropEvts  = [];
1864             var enterEvts = [];
1865
1866             // Check to see if the object(s) we were hovering over is no longer
1867             // being hovered over so we can fire the onDragOut event
1868             for (var i in this.dragOvers) {
1869
1870                 var ddo = this.dragOvers[i];
1871
1872                 if (! this.isTypeOfDD(ddo)) {
1873                     continue;
1874                 }
1875
1876                 if (! this.isOverTarget(pt, ddo, this.mode)) {
1877                     outEvts.push( ddo );
1878                 }
1879
1880                 oldOvers[i] = true;
1881                 delete this.dragOvers[i];
1882             }
1883
1884             for (var sGroup in dc.groups) {
1885
1886                 if ("string" != typeof sGroup) {
1887                     continue;
1888                 }
1889
1890                 for (i in this.ids[sGroup]) {
1891                     var oDD = this.ids[sGroup][i];
1892                     if (! this.isTypeOfDD(oDD)) {
1893                         continue;
1894                     }
1895
1896                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
1897                         if (this.isOverTarget(pt, oDD, this.mode)) {
1898                             // look for drop interactions
1899                             if (isDrop) {
1900                                 dropEvts.push( oDD );
1901                             // look for drag enter and drag over interactions
1902                             } else {
1903
1904                                 // initial drag over: dragEnter fires
1905                                 if (!oldOvers[oDD.id]) {
1906                                     enterEvts.push( oDD );
1907                                 // subsequent drag overs: dragOver fires
1908                                 } else {
1909                                     overEvts.push( oDD );
1910                                 }
1911
1912                                 this.dragOvers[oDD.id] = oDD;
1913                             }
1914                         }
1915                     }
1916                 }
1917             }
1918
1919             if (this.mode) {
1920                 if (outEvts.length) {
1921                     dc.b4DragOut(e, outEvts);
1922                     dc.onDragOut(e, outEvts);
1923                 }
1924
1925                 if (enterEvts.length) {
1926                     dc.onDragEnter(e, enterEvts);
1927                 }
1928
1929                 if (overEvts.length) {
1930                     dc.b4DragOver(e, overEvts);
1931                     dc.onDragOver(e, overEvts);
1932                 }
1933
1934                 if (dropEvts.length) {
1935                     dc.b4DragDrop(e, dropEvts);
1936                     dc.onDragDrop(e, dropEvts);
1937                 }
1938
1939             } else {
1940                 // fire dragout events
1941                 var len = 0;
1942                 for (i=0, len=outEvts.length; i<len; ++i) {
1943                     dc.b4DragOut(e, outEvts[i].id);
1944                     dc.onDragOut(e, outEvts[i].id);
1945                 }
1946
1947                 // fire enter events
1948                 for (i=0,len=enterEvts.length; i<len; ++i) {
1949                     // dc.b4DragEnter(e, oDD.id);
1950                     dc.onDragEnter(e, enterEvts[i].id);
1951                 }
1952
1953                 // fire over events
1954                 for (i=0,len=overEvts.length; i<len; ++i) {
1955                     dc.b4DragOver(e, overEvts[i].id);
1956                     dc.onDragOver(e, overEvts[i].id);
1957                 }
1958
1959                 // fire drop events
1960                 for (i=0, len=dropEvts.length; i<len; ++i) {
1961                     dc.b4DragDrop(e, dropEvts[i].id);
1962                     dc.onDragDrop(e, dropEvts[i].id);
1963                 }
1964
1965             }
1966
1967             // notify about a drop that did not find a target
1968             if (isDrop && !dropEvts.length) {
1969                 dc.onInvalidDrop(e);
1970             }
1971
1972         },
1973
1974         /**
1975          * Helper function for getting the best match from the list of drag
1976          * and drop objects returned by the drag and drop events when we are
1977          * in INTERSECT mode.  It returns either the first object that the
1978          * cursor is over, or the object that has the greatest overlap with
1979          * the dragged element.
1980          * @method getBestMatch
1981          * @param  {DragDrop[]} dds The array of drag and drop objects
1982          * targeted
1983          * @return {DragDrop}       The best single match
1984          * @static
1985          */
1986         getBestMatch: function(dds) {
1987             var winner = null;
1988             // Return null if the input is not what we expect
1989             //if (!dds || !dds.length || dds.length == 0) {
1990                // winner = null;
1991             // If there is only one item, it wins
1992             //} else if (dds.length == 1) {
1993
1994             var len = dds.length;
1995
1996             if (len == 1) {
1997                 winner = dds[0];
1998             } else {
1999                 // Loop through the targeted items
2000                 for (var i=0; i<len; ++i) {
2001                     var dd = dds[i];
2002                     // If the cursor is over the object, it wins.  If the
2003                     // cursor is over multiple matches, the first one we come
2004                     // to wins.
2005                     if (dd.cursorIsOver) {
2006                         winner = dd;
2007                         break;
2008                     // Otherwise the object with the most overlap wins
2009                     } else {
2010                         if (!winner ||
2011                             winner.overlap.getArea() < dd.overlap.getArea()) {
2012                             winner = dd;
2013                         }
2014                     }
2015                 }
2016             }
2017
2018             return winner;
2019         },
2020
2021         /**
2022          * Refreshes the cache of the top-left and bottom-right points of the
2023          * drag and drop objects in the specified group(s).  This is in the
2024          * format that is stored in the drag and drop instance, so typical
2025          * usage is:
2026          * <code>
2027          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
2028          * </code>
2029          * Alternatively:
2030          * <code>
2031          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
2032          * </code>
2033          * @TODO this really should be an indexed array.  Alternatively this
2034          * method could accept both.
2035          * @method refreshCache
2036          * @param {Object} groups an associative array of groups to refresh
2037          * @static
2038          */
2039         refreshCache: function(groups) {
2040             for (var sGroup in groups) {
2041                 if ("string" != typeof sGroup) {
2042                     continue;
2043                 }
2044                 for (var i in this.ids[sGroup]) {
2045                     var oDD = this.ids[sGroup][i];
2046
2047                     if (this.isTypeOfDD(oDD)) {
2048                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
2049                         var loc = this.getLocation(oDD);
2050                         if (loc) {
2051                             this.locationCache[oDD.id] = loc;
2052                         } else {
2053                             delete this.locationCache[oDD.id];
2054                             // this will unregister the drag and drop object if
2055                             // the element is not in a usable state
2056                             // oDD.unreg();
2057                         }
2058                     }
2059                 }
2060             }
2061         },
2062
2063         /**
2064          * This checks to make sure an element exists and is in the DOM.  The
2065          * main purpose is to handle cases where innerHTML is used to remove
2066          * drag and drop objects from the DOM.  IE provides an 'unspecified
2067          * error' when trying to access the offsetParent of such an element
2068          * @method verifyEl
2069          * @param {HTMLElement} el the element to check
2070          * @return {boolean} true if the element looks usable
2071          * @static
2072          */
2073         verifyEl: function(el) {
2074             if (el) {
2075                 var parent;
2076                 if(Roo.isIE){
2077                     try{
2078                         parent = el.offsetParent;
2079                     }catch(e){}
2080                 }else{
2081                     parent = el.offsetParent;
2082                 }
2083                 if (parent) {
2084                     return true;
2085                 }
2086             }
2087
2088             return false;
2089         },
2090
2091         /**
2092          * Returns a Region object containing the drag and drop element's position
2093          * and size, including the padding configured for it
2094          * @method getLocation
2095          * @param {DragDrop} oDD the drag and drop object to get the
2096          *                       location for
2097          * @return {Roo.lib.Region} a Region object representing the total area
2098          *                             the element occupies, including any padding
2099          *                             the instance is configured for.
2100          * @static
2101          */
2102         getLocation: function(oDD) {
2103             if (! this.isTypeOfDD(oDD)) {
2104                 return null;
2105             }
2106
2107             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
2108
2109             try {
2110                 pos= Roo.lib.Dom.getXY(el);
2111             } catch (e) { }
2112
2113             if (!pos) {
2114                 return null;
2115             }
2116
2117             x1 = pos[0];
2118             x2 = x1 + el.offsetWidth;
2119             y1 = pos[1];
2120             y2 = y1 + el.offsetHeight;
2121
2122             t = y1 - oDD.padding[0];
2123             r = x2 + oDD.padding[1];
2124             b = y2 + oDD.padding[2];
2125             l = x1 - oDD.padding[3];
2126
2127             return new Roo.lib.Region( t, r, b, l );
2128         },
2129
2130         /**
2131          * Checks the cursor location to see if it over the target
2132          * @method isOverTarget
2133          * @param {Roo.lib.Point} pt The point to evaluate
2134          * @param {DragDrop} oTarget the DragDrop object we are inspecting
2135          * @return {boolean} true if the mouse is over the target
2136          * @private
2137          * @static
2138          */
2139         isOverTarget: function(pt, oTarget, intersect) {
2140             // use cache if available
2141             var loc = this.locationCache[oTarget.id];
2142             if (!loc || !this.useCache) {
2143                 loc = this.getLocation(oTarget);
2144                 this.locationCache[oTarget.id] = loc;
2145
2146             }
2147
2148             if (!loc) {
2149                 return false;
2150             }
2151
2152             oTarget.cursorIsOver = loc.contains( pt );
2153
2154             // DragDrop is using this as a sanity check for the initial mousedown
2155             // in this case we are done.  In POINT mode, if the drag obj has no
2156             // contraints, we are also done. Otherwise we need to evaluate the
2157             // location of the target as related to the actual location of the
2158             // dragged element.
2159             var dc = this.dragCurrent;
2160             if (!dc || !dc.getTargetCoord ||
2161                     (!intersect && !dc.constrainX && !dc.constrainY)) {
2162                 return oTarget.cursorIsOver;
2163             }
2164
2165             oTarget.overlap = null;
2166
2167             // Get the current location of the drag element, this is the
2168             // location of the mouse event less the delta that represents
2169             // where the original mousedown happened on the element.  We
2170             // need to consider constraints and ticks as well.
2171             var pos = dc.getTargetCoord(pt.x, pt.y);
2172
2173             var el = dc.getDragEl();
2174             var curRegion = new Roo.lib.Region( pos.y,
2175                                                    pos.x + el.offsetWidth,
2176                                                    pos.y + el.offsetHeight,
2177                                                    pos.x );
2178
2179             var overlap = curRegion.intersect(loc);
2180
2181             if (overlap) {
2182                 oTarget.overlap = overlap;
2183                 return (intersect) ? true : oTarget.cursorIsOver;
2184             } else {
2185                 return false;
2186             }
2187         },
2188
2189         /**
2190          * unload event handler
2191          * @method _onUnload
2192          * @private
2193          * @static
2194          */
2195         _onUnload: function(e, me) {
2196             Roo.dd.DragDropMgr.unregAll();
2197         },
2198
2199         /**
2200          * Cleans up the drag and drop events and objects.
2201          * @method unregAll
2202          * @private
2203          * @static
2204          */
2205         unregAll: function() {
2206
2207             if (this.dragCurrent) {
2208                 this.stopDrag();
2209                 this.dragCurrent = null;
2210             }
2211
2212             this._execOnAll("unreg", []);
2213
2214             for (i in this.elementCache) {
2215                 delete this.elementCache[i];
2216             }
2217
2218             this.elementCache = {};
2219             this.ids = {};
2220         },
2221
2222         /**
2223          * A cache of DOM elements
2224          * @property elementCache
2225          * @private
2226          * @static
2227          */
2228         elementCache: {},
2229
2230         /**
2231          * Get the wrapper for the DOM element specified
2232          * @method getElWrapper
2233          * @param {String} id the id of the element to get
2234          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
2235          * @private
2236          * @deprecated This wrapper isn't that useful
2237          * @static
2238          */
2239         getElWrapper: function(id) {
2240             var oWrapper = this.elementCache[id];
2241             if (!oWrapper || !oWrapper.el) {
2242                 oWrapper = this.elementCache[id] =
2243                     new this.ElementWrapper(Roo.getDom(id));
2244             }
2245             return oWrapper;
2246         },
2247
2248         /**
2249          * Returns the actual DOM element
2250          * @method getElement
2251          * @param {String} id the id of the elment to get
2252          * @return {Object} The element
2253          * @deprecated use Roo.getDom instead
2254          * @static
2255          */
2256         getElement: function(id) {
2257             return Roo.getDom(id);
2258         },
2259
2260         /**
2261          * Returns the style property for the DOM element (i.e.,
2262          * document.getElById(id).style)
2263          * @method getCss
2264          * @param {String} id the id of the elment to get
2265          * @return {Object} The style property of the element
2266          * @deprecated use Roo.getDom instead
2267          * @static
2268          */
2269         getCss: function(id) {
2270             var el = Roo.getDom(id);
2271             return (el) ? el.style : null;
2272         },
2273
2274         /**
2275          * Inner class for cached elements
2276          * @class DragDropMgr.ElementWrapper
2277          * @for DragDropMgr
2278          * @private
2279          * @deprecated
2280          */
2281         ElementWrapper: function(el) {
2282                 /**
2283                  * The element
2284                  * @property el
2285                  */
2286                 this.el = el || null;
2287                 /**
2288                  * The element id
2289                  * @property id
2290                  */
2291                 this.id = this.el && el.id;
2292                 /**
2293                  * A reference to the style property
2294                  * @property css
2295                  */
2296                 this.css = this.el && el.style;
2297             },
2298
2299         /**
2300          * Returns the X position of an html element
2301          * @method getPosX
2302          * @param el the element for which to get the position
2303          * @return {int} the X coordinate
2304          * @for DragDropMgr
2305          * @deprecated use Roo.lib.Dom.getX instead
2306          * @static
2307          */
2308         getPosX: function(el) {
2309             return Roo.lib.Dom.getX(el);
2310         },
2311
2312         /**
2313          * Returns the Y position of an html element
2314          * @method getPosY
2315          * @param el the element for which to get the position
2316          * @return {int} the Y coordinate
2317          * @deprecated use Roo.lib.Dom.getY instead
2318          * @static
2319          */
2320         getPosY: function(el) {
2321             return Roo.lib.Dom.getY(el);
2322         },
2323
2324         /**
2325          * Swap two nodes.  In IE, we use the native method, for others we
2326          * emulate the IE behavior
2327          * @method swapNode
2328          * @param n1 the first node to swap
2329          * @param n2 the other node to swap
2330          * @static
2331          */
2332         swapNode: function(n1, n2) {
2333             if (n1.swapNode) {
2334                 n1.swapNode(n2);
2335             } else {
2336                 var p = n2.parentNode;
2337                 var s = n2.nextSibling;
2338
2339                 if (s == n1) {
2340                     p.insertBefore(n1, n2);
2341                 } else if (n2 == n1.nextSibling) {
2342                     p.insertBefore(n2, n1);
2343                 } else {
2344                     n1.parentNode.replaceChild(n2, n1);
2345                     p.insertBefore(n1, s);
2346                 }
2347             }
2348         },
2349
2350         /**
2351          * Returns the current scroll position
2352          * @method getScroll
2353          * @private
2354          * @static
2355          */
2356         getScroll: function () {
2357             var t, l, dde=document.documentElement, db=document.body;
2358             if (dde && (dde.scrollTop || dde.scrollLeft)) {
2359                 t = dde.scrollTop;
2360                 l = dde.scrollLeft;
2361             } else if (db) {
2362                 t = db.scrollTop;
2363                 l = db.scrollLeft;
2364             } else {
2365
2366             }
2367             return { top: t, left: l };
2368         },
2369
2370         /**
2371          * Returns the specified element style property
2372          * @method getStyle
2373          * @param {HTMLElement} el          the element
2374          * @param {string}      styleProp   the style property
2375          * @return {string} The value of the style property
2376          * @deprecated use Roo.lib.Dom.getStyle
2377          * @static
2378          */
2379         getStyle: function(el, styleProp) {
2380             return Roo.fly(el).getStyle(styleProp);
2381         },
2382
2383         /**
2384          * Gets the scrollTop
2385          * @method getScrollTop
2386          * @return {int} the document's scrollTop
2387          * @static
2388          */
2389         getScrollTop: function () { return this.getScroll().top; },
2390
2391         /**
2392          * Gets the scrollLeft
2393          * @method getScrollLeft
2394          * @return {int} the document's scrollTop
2395          * @static
2396          */
2397         getScrollLeft: function () { return this.getScroll().left; },
2398
2399         /**
2400          * Sets the x/y position of an element to the location of the
2401          * target element.
2402          * @method moveToEl
2403          * @param {HTMLElement} moveEl      The element to move
2404          * @param {HTMLElement} targetEl    The position reference element
2405          * @static
2406          */
2407         moveToEl: function (moveEl, targetEl) {
2408             var aCoord = Roo.lib.Dom.getXY(targetEl);
2409             Roo.lib.Dom.setXY(moveEl, aCoord);
2410         },
2411
2412         /**
2413          * Numeric array sort function
2414          * @method numericSort
2415          * @static
2416          */
2417         numericSort: function(a, b) { return (a - b); },
2418
2419         /**
2420          * Internal counter
2421          * @property _timeoutCount
2422          * @private
2423          * @static
2424          */
2425         _timeoutCount: 0,
2426
2427         /**
2428          * Trying to make the load order less important.  Without this we get
2429          * an error if this file is loaded before the Event Utility.
2430          * @method _addListeners
2431          * @private
2432          * @static
2433          */
2434         _addListeners: function() {
2435             var DDM = Roo.dd.DDM;
2436             if ( Roo.lib.Event && document ) {
2437                 DDM._onLoad();
2438             } else {
2439                 if (DDM._timeoutCount > 2000) {
2440                 } else {
2441                     setTimeout(DDM._addListeners, 10);
2442                     if (document && document.body) {
2443                         DDM._timeoutCount += 1;
2444                     }
2445                 }
2446             }
2447         },
2448
2449         /**
2450          * Recursively searches the immediate parent and all child nodes for
2451          * the handle element in order to determine wheter or not it was
2452          * clicked.
2453          * @method handleWasClicked
2454          * @param node the html element to inspect
2455          * @static
2456          */
2457         handleWasClicked: function(node, id) {
2458             if (this.isHandle(id, node.id)) {
2459                 return true;
2460             } else {
2461                 // check to see if this is a text node child of the one we want
2462                 var p = node.parentNode;
2463
2464                 while (p) {
2465                     if (this.isHandle(id, p.id)) {
2466                         return true;
2467                     } else {
2468                         p = p.parentNode;
2469                     }
2470                 }
2471             }
2472
2473             return false;
2474         }
2475
2476     };
2477
2478 }();
2479
2480 // shorter alias, save a few bytes
2481 Roo.dd.DDM = Roo.dd.DragDropMgr;
2482 Roo.dd.DDM._addListeners();
2483
2484 }/*
2485  * Based on:
2486  * Ext JS Library 1.1.1
2487  * Copyright(c) 2006-2007, Ext JS, LLC.
2488  *
2489  * Originally Released Under LGPL - original licence link has changed is not relivant.
2490  *
2491  * Fork - LGPL
2492  * <script type="text/javascript">
2493  */
2494
2495 /**
2496  * @class Roo.dd.DD
2497  * A DragDrop implementation where the linked element follows the
2498  * mouse cursor during a drag.
2499  * @extends Roo.dd.DragDrop
2500  * @constructor
2501  * @param {String} id the id of the linked element
2502  * @param {String} sGroup the group of related DragDrop items
2503  * @param {object} config an object containing configurable attributes
2504  *                Valid properties for DD:
2505  *                    scroll
2506  */
2507 Roo.dd.DD = function(id, sGroup, config) {
2508     if (id) {
2509         this.init(id, sGroup, config);
2510     }
2511 };
2512
2513 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
2514
2515     /**
2516      * When set to true, the utility automatically tries to scroll the browser
2517      * window wehn a drag and drop element is dragged near the viewport boundary.
2518      * Defaults to true.
2519      * @property scroll
2520      * @type boolean
2521      */
2522     scroll: true,
2523
2524     /**
2525      * Sets the pointer offset to the distance between the linked element's top
2526      * left corner and the location the element was clicked
2527      * @method autoOffset
2528      * @param {int} iPageX the X coordinate of the click
2529      * @param {int} iPageY the Y coordinate of the click
2530      */
2531     autoOffset: function(iPageX, iPageY) {
2532         var x = iPageX - this.startPageX;
2533         var y = iPageY - this.startPageY;
2534         this.setDelta(x, y);
2535     },
2536
2537     /**
2538      * Sets the pointer offset.  You can call this directly to force the
2539      * offset to be in a particular location (e.g., pass in 0,0 to set it
2540      * to the center of the object)
2541      * @method setDelta
2542      * @param {int} iDeltaX the distance from the left
2543      * @param {int} iDeltaY the distance from the top
2544      */
2545     setDelta: function(iDeltaX, iDeltaY) {
2546         this.deltaX = iDeltaX;
2547         this.deltaY = iDeltaY;
2548     },
2549
2550     /**
2551      * Sets the drag element to the location of the mousedown or click event,
2552      * maintaining the cursor location relative to the location on the element
2553      * that was clicked.  Override this if you want to place the element in a
2554      * location other than where the cursor is.
2555      * @method setDragElPos
2556      * @param {int} iPageX the X coordinate of the mousedown or drag event
2557      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2558      */
2559     setDragElPos: function(iPageX, iPageY) {
2560         // the first time we do this, we are going to check to make sure
2561         // the element has css positioning
2562
2563         var el = this.getDragEl();
2564         this.alignElWithMouse(el, iPageX, iPageY);
2565     },
2566
2567     /**
2568      * Sets the element to the location of the mousedown or click event,
2569      * maintaining the cursor location relative to the location on the element
2570      * that was clicked.  Override this if you want to place the element in a
2571      * location other than where the cursor is.
2572      * @method alignElWithMouse
2573      * @param {HTMLElement} el the element to move
2574      * @param {int} iPageX the X coordinate of the mousedown or drag event
2575      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2576      */
2577     alignElWithMouse: function(el, iPageX, iPageY) {
2578         var oCoord = this.getTargetCoord(iPageX, iPageY);
2579         var fly = el.dom ? el : Roo.fly(el);
2580         if (!this.deltaSetXY) {
2581             var aCoord = [oCoord.x, oCoord.y];
2582             fly.setXY(aCoord);
2583             var newLeft = fly.getLeft(true);
2584             var newTop  = fly.getTop(true);
2585             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
2586         } else {
2587             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
2588         }
2589
2590         this.cachePosition(oCoord.x, oCoord.y);
2591         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
2592         return oCoord;
2593     },
2594
2595     /**
2596      * Saves the most recent position so that we can reset the constraints and
2597      * tick marks on-demand.  We need to know this so that we can calculate the
2598      * number of pixels the element is offset from its original position.
2599      * @method cachePosition
2600      * @param iPageX the current x position (optional, this just makes it so we
2601      * don't have to look it up again)
2602      * @param iPageY the current y position (optional, this just makes it so we
2603      * don't have to look it up again)
2604      */
2605     cachePosition: function(iPageX, iPageY) {
2606         if (iPageX) {
2607             this.lastPageX = iPageX;
2608             this.lastPageY = iPageY;
2609         } else {
2610             var aCoord = Roo.lib.Dom.getXY(this.getEl());
2611             this.lastPageX = aCoord[0];
2612             this.lastPageY = aCoord[1];
2613         }
2614     },
2615
2616     /**
2617      * Auto-scroll the window if the dragged object has been moved beyond the
2618      * visible window boundary.
2619      * @method autoScroll
2620      * @param {int} x the drag element's x position
2621      * @param {int} y the drag element's y position
2622      * @param {int} h the height of the drag element
2623      * @param {int} w the width of the drag element
2624      * @private
2625      */
2626     autoScroll: function(x, y, h, w) {
2627
2628         if (this.scroll) {
2629             // The client height
2630             var clientH = Roo.lib.Dom.getViewWidth();
2631
2632             // The client width
2633             var clientW = Roo.lib.Dom.getViewHeight();
2634
2635             // The amt scrolled down
2636             var st = this.DDM.getScrollTop();
2637
2638             // The amt scrolled right
2639             var sl = this.DDM.getScrollLeft();
2640
2641             // Location of the bottom of the element
2642             var bot = h + y;
2643
2644             // Location of the right of the element
2645             var right = w + x;
2646
2647             // The distance from the cursor to the bottom of the visible area,
2648             // adjusted so that we don't scroll if the cursor is beyond the
2649             // element drag constraints
2650             var toBot = (clientH + st - y - this.deltaY);
2651
2652             // The distance from the cursor to the right of the visible area
2653             var toRight = (clientW + sl - x - this.deltaX);
2654
2655
2656             // How close to the edge the cursor must be before we scroll
2657             // var thresh = (document.all) ? 100 : 40;
2658             var thresh = 40;
2659
2660             // How many pixels to scroll per autoscroll op.  This helps to reduce
2661             // clunky scrolling. IE is more sensitive about this ... it needs this
2662             // value to be higher.
2663             var scrAmt = (document.all) ? 80 : 30;
2664
2665             // Scroll down if we are near the bottom of the visible page and the
2666             // obj extends below the crease
2667             if ( bot > clientH && toBot < thresh ) {
2668                 window.scrollTo(sl, st + scrAmt);
2669             }
2670
2671             // Scroll up if the window is scrolled down and the top of the object
2672             // goes above the top border
2673             if ( y < st && st > 0 && y - st < thresh ) {
2674                 window.scrollTo(sl, st - scrAmt);
2675             }
2676
2677             // Scroll right if the obj is beyond the right border and the cursor is
2678             // near the border.
2679             if ( right > clientW && toRight < thresh ) {
2680                 window.scrollTo(sl + scrAmt, st);
2681             }
2682
2683             // Scroll left if the window has been scrolled to the right and the obj
2684             // extends past the left border
2685             if ( x < sl && sl > 0 && x - sl < thresh ) {
2686                 window.scrollTo(sl - scrAmt, st);
2687             }
2688         }
2689     },
2690
2691     /**
2692      * Finds the location the element should be placed if we want to move
2693      * it to where the mouse location less the click offset would place us.
2694      * @method getTargetCoord
2695      * @param {int} iPageX the X coordinate of the click
2696      * @param {int} iPageY the Y coordinate of the click
2697      * @return an object that contains the coordinates (Object.x and Object.y)
2698      * @private
2699      */
2700     getTargetCoord: function(iPageX, iPageY) {
2701
2702
2703         var x = iPageX - this.deltaX;
2704         var y = iPageY - this.deltaY;
2705
2706         if (this.constrainX) {
2707             if (x < this.minX) { x = this.minX; }
2708             if (x > this.maxX) { x = this.maxX; }
2709         }
2710
2711         if (this.constrainY) {
2712             if (y < this.minY) { y = this.minY; }
2713             if (y > this.maxY) { y = this.maxY; }
2714         }
2715
2716         x = this.getTick(x, this.xTicks);
2717         y = this.getTick(y, this.yTicks);
2718
2719
2720         return {x:x, y:y};
2721     },
2722
2723     /*
2724      * Sets up config options specific to this class. Overrides
2725      * Roo.dd.DragDrop, but all versions of this method through the
2726      * inheritance chain are called
2727      */
2728     applyConfig: function() {
2729         Roo.dd.DD.superclass.applyConfig.call(this);
2730         this.scroll = (this.config.scroll !== false);
2731     },
2732
2733     /*
2734      * Event that fires prior to the onMouseDown event.  Overrides
2735      * Roo.dd.DragDrop.
2736      */
2737     b4MouseDown: function(e) {
2738         // this.resetConstraints();
2739         this.autoOffset(e.getPageX(),
2740                             e.getPageY());
2741     },
2742
2743     /*
2744      * Event that fires prior to the onDrag event.  Overrides
2745      * Roo.dd.DragDrop.
2746      */
2747     b4Drag: function(e) {
2748         this.setDragElPos(e.getPageX(),
2749                             e.getPageY());
2750     },
2751
2752     toString: function() {
2753         return ("DD " + this.id);
2754     }
2755
2756     //////////////////////////////////////////////////////////////////////////
2757     // Debugging ygDragDrop events that can be overridden
2758     //////////////////////////////////////////////////////////////////////////
2759     /*
2760     startDrag: function(x, y) {
2761     },
2762
2763     onDrag: function(e) {
2764     },
2765
2766     onDragEnter: function(e, id) {
2767     },
2768
2769     onDragOver: function(e, id) {
2770     },
2771
2772     onDragOut: function(e, id) {
2773     },
2774
2775     onDragDrop: function(e, id) {
2776     },
2777
2778     endDrag: function(e) {
2779     }
2780
2781     */
2782
2783 });/*
2784  * Based on:
2785  * Ext JS Library 1.1.1
2786  * Copyright(c) 2006-2007, Ext JS, LLC.
2787  *
2788  * Originally Released Under LGPL - original licence link has changed is not relivant.
2789  *
2790  * Fork - LGPL
2791  * <script type="text/javascript">
2792  */
2793
2794 /**
2795  * @class Roo.dd.DDProxy
2796  * A DragDrop implementation that inserts an empty, bordered div into
2797  * the document that follows the cursor during drag operations.  At the time of
2798  * the click, the frame div is resized to the dimensions of the linked html
2799  * element, and moved to the exact location of the linked element.
2800  *
2801  * References to the "frame" element refer to the single proxy element that
2802  * was created to be dragged in place of all DDProxy elements on the
2803  * page.
2804  *
2805  * @extends Roo.dd.DD
2806  * @constructor
2807  * @param {String} id the id of the linked html element
2808  * @param {String} sGroup the group of related DragDrop objects
2809  * @param {object} config an object containing configurable attributes
2810  *                Valid properties for DDProxy in addition to those in DragDrop:
2811  *                   resizeFrame, centerFrame, dragElId
2812  */
2813 Roo.dd.DDProxy = function(id, sGroup, config) {
2814     if (id) {
2815         this.init(id, sGroup, config);
2816         this.initFrame();
2817     }
2818 };
2819
2820 /**
2821  * The default drag frame div id
2822  * @property Roo.dd.DDProxy.dragElId
2823  * @type String
2824  * @static
2825  */
2826 Roo.dd.DDProxy.dragElId = "ygddfdiv";
2827
2828 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
2829
2830     /**
2831      * By default we resize the drag frame to be the same size as the element
2832      * we want to drag (this is to get the frame effect).  We can turn it off
2833      * if we want a different behavior.
2834      * @property resizeFrame
2835      * @type boolean
2836      */
2837     resizeFrame: true,
2838
2839     /**
2840      * By default the frame is positioned exactly where the drag element is, so
2841      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
2842      * you do not have constraints on the obj is to have the drag frame centered
2843      * around the cursor.  Set centerFrame to true for this effect.
2844      * @property centerFrame
2845      * @type boolean
2846      */
2847     centerFrame: false,
2848
2849     /**
2850      * Creates the proxy element if it does not yet exist
2851      * @method createFrame
2852      */
2853     createFrame: function() {
2854         var self = this;
2855         var body = document.body;
2856
2857         if (!body || !body.firstChild) {
2858             setTimeout( function() { self.createFrame(); }, 50 );
2859             return;
2860         }
2861
2862         var div = this.getDragEl();
2863
2864         if (!div) {
2865             div    = document.createElement("div");
2866             div.id = this.dragElId;
2867             var s  = div.style;
2868
2869             s.position   = "absolute";
2870             s.visibility = "hidden";
2871             s.cursor     = "move";
2872             s.border     = "2px solid #aaa";
2873             s.zIndex     = 999;
2874
2875             // appendChild can blow up IE if invoked prior to the window load event
2876             // while rendering a table.  It is possible there are other scenarios
2877             // that would cause this to happen as well.
2878             body.insertBefore(div, body.firstChild);
2879         }
2880     },
2881
2882     /**
2883      * Initialization for the drag frame element.  Must be called in the
2884      * constructor of all subclasses
2885      * @method initFrame
2886      */
2887     initFrame: function() {
2888         this.createFrame();
2889     },
2890
2891     applyConfig: function() {
2892         Roo.dd.DDProxy.superclass.applyConfig.call(this);
2893
2894         this.resizeFrame = (this.config.resizeFrame !== false);
2895         this.centerFrame = (this.config.centerFrame);
2896         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
2897     },
2898
2899     /**
2900      * Resizes the drag frame to the dimensions of the clicked object, positions
2901      * it over the object, and finally displays it
2902      * @method showFrame
2903      * @param {int} iPageX X click position
2904      * @param {int} iPageY Y click position
2905      * @private
2906      */
2907     showFrame: function(iPageX, iPageY) {
2908         var el = this.getEl();
2909         var dragEl = this.getDragEl();
2910         var s = dragEl.style;
2911
2912         this._resizeProxy();
2913
2914         if (this.centerFrame) {
2915             this.setDelta( Math.round(parseInt(s.width,  10)/2),
2916                            Math.round(parseInt(s.height, 10)/2) );
2917         }
2918
2919         this.setDragElPos(iPageX, iPageY);
2920
2921         Roo.fly(dragEl).show();
2922     },
2923
2924     /**
2925      * The proxy is automatically resized to the dimensions of the linked
2926      * element when a drag is initiated, unless resizeFrame is set to false
2927      * @method _resizeProxy
2928      * @private
2929      */
2930     _resizeProxy: function() {
2931         if (this.resizeFrame) {
2932             var el = this.getEl();
2933             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
2934         }
2935     },
2936
2937     // overrides Roo.dd.DragDrop
2938     b4MouseDown: function(e) {
2939         var x = e.getPageX();
2940         var y = e.getPageY();
2941         this.autoOffset(x, y);
2942         this.setDragElPos(x, y);
2943     },
2944
2945     // overrides Roo.dd.DragDrop
2946     b4StartDrag: function(x, y) {
2947         // show the drag frame
2948         this.showFrame(x, y);
2949     },
2950
2951     // overrides Roo.dd.DragDrop
2952     b4EndDrag: function(e) {
2953         Roo.fly(this.getDragEl()).hide();
2954     },
2955
2956     // overrides Roo.dd.DragDrop
2957     // By default we try to move the element to the last location of the frame.
2958     // This is so that the default behavior mirrors that of Roo.dd.DD.
2959     endDrag: function(e) {
2960
2961         var lel = this.getEl();
2962         var del = this.getDragEl();
2963
2964         // Show the drag frame briefly so we can get its position
2965         del.style.visibility = "";
2966
2967         this.beforeMove();
2968         // Hide the linked element before the move to get around a Safari
2969         // rendering bug.
2970         lel.style.visibility = "hidden";
2971         Roo.dd.DDM.moveToEl(lel, del);
2972         del.style.visibility = "hidden";
2973         lel.style.visibility = "";
2974
2975         this.afterDrag();
2976     },
2977
2978     beforeMove : function(){
2979
2980     },
2981
2982     afterDrag : function(){
2983
2984     },
2985
2986     toString: function() {
2987         return ("DDProxy " + this.id);
2988     }
2989
2990 });
2991 /*
2992  * Based on:
2993  * Ext JS Library 1.1.1
2994  * Copyright(c) 2006-2007, Ext JS, LLC.
2995  *
2996  * Originally Released Under LGPL - original licence link has changed is not relivant.
2997  *
2998  * Fork - LGPL
2999  * <script type="text/javascript">
3000  */
3001
3002  /**
3003  * @class Roo.dd.DDTarget
3004  * A DragDrop implementation that does not move, but can be a drop
3005  * target.  You would get the same result by simply omitting implementation
3006  * for the event callbacks, but this way we reduce the processing cost of the
3007  * event listener and the callbacks.
3008  * @extends Roo.dd.DragDrop
3009  * @constructor
3010  * @param {String} id the id of the element that is a drop target
3011  * @param {String} sGroup the group of related DragDrop objects
3012  * @param {object} config an object containing configurable attributes
3013  *                 Valid properties for DDTarget in addition to those in
3014  *                 DragDrop:
3015  *                    none
3016  */
3017 Roo.dd.DDTarget = function(id, sGroup, config) {
3018     if (id) {
3019         this.initTarget(id, sGroup, config);
3020     }
3021     if (config.listeners || config.events) { 
3022        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
3023             listeners : config.listeners || {}, 
3024             events : config.events || {} 
3025         });    
3026     }
3027 };
3028
3029 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
3030 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
3031     toString: function() {
3032         return ("DDTarget " + this.id);
3033     }
3034 });
3035 /*
3036  * Based on:
3037  * Ext JS Library 1.1.1
3038  * Copyright(c) 2006-2007, Ext JS, LLC.
3039  *
3040  * Originally Released Under LGPL - original licence link has changed is not relivant.
3041  *
3042  * Fork - LGPL
3043  * <script type="text/javascript">
3044  */
3045  
3046
3047 /**
3048  * @class Roo.dd.ScrollManager
3049  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
3050  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
3051  * @singleton
3052  */
3053 Roo.dd.ScrollManager = function(){
3054     var ddm = Roo.dd.DragDropMgr;
3055     var els = {};
3056     var dragEl = null;
3057     var proc = {};
3058     
3059     
3060     
3061     var onStop = function(e){
3062         dragEl = null;
3063         clearProc();
3064     };
3065     
3066     var triggerRefresh = function(){
3067         if(ddm.dragCurrent){
3068              ddm.refreshCache(ddm.dragCurrent.groups);
3069         }
3070     };
3071     
3072     var doScroll = function(){
3073         if(ddm.dragCurrent){
3074             var dds = Roo.dd.ScrollManager;
3075             if(!dds.animate){
3076                 if(proc.el.scroll(proc.dir, dds.increment)){
3077                     triggerRefresh();
3078                 }
3079             }else{
3080                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
3081             }
3082         }
3083     };
3084     
3085     var clearProc = function(){
3086         if(proc.id){
3087             clearInterval(proc.id);
3088         }
3089         proc.id = 0;
3090         proc.el = null;
3091         proc.dir = "";
3092     };
3093     
3094     var startProc = function(el, dir){
3095          Roo.log('scroll startproc');
3096         clearProc();
3097         proc.el = el;
3098         proc.dir = dir;
3099         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
3100     };
3101     
3102     var onFire = function(e, isDrop){
3103        
3104         if(isDrop || !ddm.dragCurrent){ return; }
3105         var dds = Roo.dd.ScrollManager;
3106         if(!dragEl || dragEl != ddm.dragCurrent){
3107             dragEl = ddm.dragCurrent;
3108             // refresh regions on drag start
3109             dds.refreshCache();
3110         }
3111         
3112         var xy = Roo.lib.Event.getXY(e);
3113         var pt = new Roo.lib.Point(xy[0], xy[1]);
3114         for(var id in els){
3115             var el = els[id], r = el._region;
3116             if(r && r.contains(pt) && el.isScrollable()){
3117                 if(r.bottom - pt.y <= dds.thresh){
3118                     if(proc.el != el){
3119                         startProc(el, "down");
3120                     }
3121                     return;
3122                 }else if(r.right - pt.x <= dds.thresh){
3123                     if(proc.el != el){
3124                         startProc(el, "left");
3125                     }
3126                     return;
3127                 }else if(pt.y - r.top <= dds.thresh){
3128                     if(proc.el != el){
3129                         startProc(el, "up");
3130                     }
3131                     return;
3132                 }else if(pt.x - r.left <= dds.thresh){
3133                     if(proc.el != el){
3134                         startProc(el, "right");
3135                     }
3136                     return;
3137                 }
3138             }
3139         }
3140         clearProc();
3141     };
3142     
3143     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
3144     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
3145     
3146     return {
3147         /**
3148          * Registers new overflow element(s) to auto scroll
3149          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
3150          */
3151         register : function(el){
3152             if(el instanceof Array){
3153                 for(var i = 0, len = el.length; i < len; i++) {
3154                         this.register(el[i]);
3155                 }
3156             }else{
3157                 el = Roo.get(el);
3158                 els[el.id] = el;
3159             }
3160             Roo.dd.ScrollManager.els = els;
3161         },
3162         
3163         /**
3164          * Unregisters overflow element(s) so they are no longer scrolled
3165          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
3166          */
3167         unregister : function(el){
3168             if(el instanceof Array){
3169                 for(var i = 0, len = el.length; i < len; i++) {
3170                         this.unregister(el[i]);
3171                 }
3172             }else{
3173                 el = Roo.get(el);
3174                 delete els[el.id];
3175             }
3176         },
3177         
3178         /**
3179          * The number of pixels from the edge of a container the pointer needs to be to 
3180          * trigger scrolling (defaults to 25)
3181          * @type Number
3182          */
3183         thresh : 25,
3184         
3185         /**
3186          * The number of pixels to scroll in each scroll increment (defaults to 50)
3187          * @type Number
3188          */
3189         increment : 100,
3190         
3191         /**
3192          * The frequency of scrolls in milliseconds (defaults to 500)
3193          * @type Number
3194          */
3195         frequency : 500,
3196         
3197         /**
3198          * True to animate the scroll (defaults to true)
3199          * @type Boolean
3200          */
3201         animate: true,
3202         
3203         /**
3204          * The animation duration in seconds - 
3205          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
3206          * @type Number
3207          */
3208         animDuration: .4,
3209         
3210         /**
3211          * Manually trigger a cache refresh.
3212          */
3213         refreshCache : function(){
3214             for(var id in els){
3215                 if(typeof els[id] == 'object'){ // for people extending the object prototype
3216                     els[id]._region = els[id].getRegion();
3217                 }
3218             }
3219         }
3220     };
3221 }();/*
3222  * Based on:
3223  * Ext JS Library 1.1.1
3224  * Copyright(c) 2006-2007, Ext JS, LLC.
3225  *
3226  * Originally Released Under LGPL - original licence link has changed is not relivant.
3227  *
3228  * Fork - LGPL
3229  * <script type="text/javascript">
3230  */
3231  
3232
3233 /**
3234  * @class Roo.dd.Registry
3235  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
3236  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
3237  * @singleton
3238  */
3239 Roo.dd.Registry = function(){
3240     var elements = {}; 
3241     var handles = {}; 
3242     var autoIdSeed = 0;
3243
3244     var getId = function(el, autogen){
3245         if(typeof el == "string"){
3246             return el;
3247         }
3248         var id = el.id;
3249         if(!id && autogen !== false){
3250             id = "roodd-" + (++autoIdSeed);
3251             el.id = id;
3252         }
3253         return id;
3254     };
3255     
3256     return {
3257     /**
3258      * Register a drag drop element
3259      * @param {String|HTMLElement} element The id or DOM node to register
3260      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
3261      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
3262      * knows how to interpret, plus there are some specific properties known to the Registry that should be
3263      * populated in the data object (if applicable):
3264      * <pre>
3265 Value      Description<br />
3266 ---------  ------------------------------------------<br />
3267 handles    Array of DOM nodes that trigger dragging<br />
3268            for the element being registered<br />
3269 isHandle   True if the element passed in triggers<br />
3270            dragging itself, else false
3271 </pre>
3272      */
3273         register : function(el, data){
3274             data = data || {};
3275             if(typeof el == "string"){
3276                 el = document.getElementById(el);
3277             }
3278             data.ddel = el;
3279             elements[getId(el)] = data;
3280             if(data.isHandle !== false){
3281                 handles[data.ddel.id] = data;
3282             }
3283             if(data.handles){
3284                 var hs = data.handles;
3285                 for(var i = 0, len = hs.length; i < len; i++){
3286                         handles[getId(hs[i])] = data;
3287                 }
3288             }
3289         },
3290
3291     /**
3292      * Unregister a drag drop element
3293      * @param {String|HTMLElement}  element The id or DOM node to unregister
3294      */
3295         unregister : function(el){
3296             var id = getId(el, false);
3297             var data = elements[id];
3298             if(data){
3299                 delete elements[id];
3300                 if(data.handles){
3301                     var hs = data.handles;
3302                     for(var i = 0, len = hs.length; i < len; i++){
3303                         delete handles[getId(hs[i], false)];
3304                     }
3305                 }
3306             }
3307         },
3308
3309     /**
3310      * Returns the handle registered for a DOM Node by id
3311      * @param {String|HTMLElement} id The DOM node or id to look up
3312      * @return {Object} handle The custom handle data
3313      */
3314         getHandle : function(id){
3315             if(typeof id != "string"){ // must be element?
3316                 id = id.id;
3317             }
3318             return handles[id];
3319         },
3320
3321     /**
3322      * Returns the handle that is registered for the DOM node that is the target of the event
3323      * @param {Event} e The event
3324      * @return {Object} handle The custom handle data
3325      */
3326         getHandleFromEvent : function(e){
3327             var t = Roo.lib.Event.getTarget(e);
3328             return t ? handles[t.id] : null;
3329         },
3330
3331     /**
3332      * Returns a custom data object that is registered for a DOM node by id
3333      * @param {String|HTMLElement} id The DOM node or id to look up
3334      * @return {Object} data The custom data
3335      */
3336         getTarget : function(id){
3337             if(typeof id != "string"){ // must be element?
3338                 id = id.id;
3339             }
3340             return elements[id];
3341         },
3342
3343     /**
3344      * Returns a custom data object that is registered for the DOM node that is the target of the event
3345      * @param {Event} e The event
3346      * @return {Object} data The custom data
3347      */
3348         getTargetFromEvent : function(e){
3349             var t = Roo.lib.Event.getTarget(e);
3350             return t ? elements[t.id] || handles[t.id] : null;
3351         }
3352     };
3353 }();/*
3354  * Based on:
3355  * Ext JS Library 1.1.1
3356  * Copyright(c) 2006-2007, Ext JS, LLC.
3357  *
3358  * Originally Released Under LGPL - original licence link has changed is not relivant.
3359  *
3360  * Fork - LGPL
3361  * <script type="text/javascript">
3362  */
3363  
3364
3365 /**
3366  * @class Roo.dd.StatusProxy
3367  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
3368  * default drag proxy used by all Roo.dd components.
3369  * @constructor
3370  * @param {Object} config
3371  */
3372 Roo.dd.StatusProxy = function(config){
3373     Roo.apply(this, config);
3374     this.id = this.id || Roo.id();
3375     this.el = new Roo.Layer({
3376         dh: {
3377             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
3378                 {tag: "div", cls: "x-dd-drop-icon"},
3379                 {tag: "div", cls: "x-dd-drag-ghost"}
3380             ]
3381         }, 
3382         shadow: !config || config.shadow !== false
3383     });
3384     this.ghost = Roo.get(this.el.dom.childNodes[1]);
3385     this.dropStatus = this.dropNotAllowed;
3386 };
3387
3388 Roo.dd.StatusProxy.prototype = {
3389     /**
3390      * @cfg {String} dropAllowed
3391      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
3392      */
3393     dropAllowed : "x-dd-drop-ok",
3394     /**
3395      * @cfg {String} dropNotAllowed
3396      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
3397      */
3398     dropNotAllowed : "x-dd-drop-nodrop",
3399
3400     /**
3401      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
3402      * over the current target element.
3403      * @param {String} cssClass The css class for the new drop status indicator image
3404      */
3405     setStatus : function(cssClass){
3406         cssClass = cssClass || this.dropNotAllowed;
3407         if(this.dropStatus != cssClass){
3408             this.el.replaceClass(this.dropStatus, cssClass);
3409             this.dropStatus = cssClass;
3410         }
3411     },
3412
3413     /**
3414      * Resets the status indicator to the default dropNotAllowed value
3415      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
3416      */
3417     reset : function(clearGhost){
3418         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
3419         this.dropStatus = this.dropNotAllowed;
3420         if(clearGhost){
3421             this.ghost.update("");
3422         }
3423     },
3424
3425     /**
3426      * Updates the contents of the ghost element
3427      * @param {String} html The html that will replace the current innerHTML of the ghost element
3428      */
3429     update : function(html){
3430         if(typeof html == "string"){
3431             this.ghost.update(html);
3432         }else{
3433             this.ghost.update("");
3434             html.style.margin = "0";
3435             this.ghost.dom.appendChild(html);
3436         }
3437         // ensure float = none set?? cant remember why though.
3438         var el = this.ghost.dom.firstChild;
3439                 if(el){
3440                         Roo.fly(el).setStyle('float', 'none');
3441                 }
3442     },
3443     
3444     /**
3445      * Returns the underlying proxy {@link Roo.Layer}
3446      * @return {Roo.Layer} el
3447     */
3448     getEl : function(){
3449         return this.el;
3450     },
3451
3452     /**
3453      * Returns the ghost element
3454      * @return {Roo.Element} el
3455      */
3456     getGhost : function(){
3457         return this.ghost;
3458     },
3459
3460     /**
3461      * Hides the proxy
3462      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
3463      */
3464     hide : function(clear){
3465         this.el.hide();
3466         if(clear){
3467             this.reset(true);
3468         }
3469     },
3470
3471     /**
3472      * Stops the repair animation if it's currently running
3473      */
3474     stop : function(){
3475         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
3476             this.anim.stop();
3477         }
3478     },
3479
3480     /**
3481      * Displays this proxy
3482      */
3483     show : function(){
3484         this.el.show();
3485     },
3486
3487     /**
3488      * Force the Layer to sync its shadow and shim positions to the element
3489      */
3490     sync : function(){
3491         this.el.sync();
3492     },
3493
3494     /**
3495      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
3496      * invalid drop operation by the item being dragged.
3497      * @param {Array} xy The XY position of the element ([x, y])
3498      * @param {Function} callback The function to call after the repair is complete
3499      * @param {Object} scope The scope in which to execute the callback
3500      */
3501     repair : function(xy, callback, scope){
3502         this.callback = callback;
3503         this.scope = scope;
3504         if(xy && this.animRepair !== false){
3505             this.el.addClass("x-dd-drag-repair");
3506             this.el.hideUnders(true);
3507             this.anim = this.el.shift({
3508                 duration: this.repairDuration || .5,
3509                 easing: 'easeOut',
3510                 xy: xy,
3511                 stopFx: true,
3512                 callback: this.afterRepair,
3513                 scope: this
3514             });
3515         }else{
3516             this.afterRepair();
3517         }
3518     },
3519
3520     // private
3521     afterRepair : function(){
3522         this.hide(true);
3523         if(typeof this.callback == "function"){
3524             this.callback.call(this.scope || this);
3525         }
3526         this.callback = null;
3527         this.scope = null;
3528     }
3529 };/*
3530  * Based on:
3531  * Ext JS Library 1.1.1
3532  * Copyright(c) 2006-2007, Ext JS, LLC.
3533  *
3534  * Originally Released Under LGPL - original licence link has changed is not relivant.
3535  *
3536  * Fork - LGPL
3537  * <script type="text/javascript">
3538  */
3539
3540 /**
3541  * @class Roo.dd.DragSource
3542  * @extends Roo.dd.DDProxy
3543  * A simple class that provides the basic implementation needed to make any element draggable.
3544  * @constructor
3545  * @param {String/HTMLElement/Element} el The container element
3546  * @param {Object} config
3547  */
3548 Roo.dd.DragSource = function(el, config){
3549     this.el = Roo.get(el);
3550     this.dragData = {};
3551     
3552     Roo.apply(this, config);
3553     
3554     if(!this.proxy){
3555         this.proxy = new Roo.dd.StatusProxy();
3556     }
3557
3558     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
3559           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
3560     
3561     this.dragging = false;
3562 };
3563
3564 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
3565     /**
3566      * @cfg {String} dropAllowed
3567      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3568      */
3569     dropAllowed : "x-dd-drop-ok",
3570     /**
3571      * @cfg {String} dropNotAllowed
3572      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3573      */
3574     dropNotAllowed : "x-dd-drop-nodrop",
3575
3576     /**
3577      * Returns the data object associated with this drag source
3578      * @return {Object} data An object containing arbitrary data
3579      */
3580     getDragData : function(e){
3581         return this.dragData;
3582     },
3583
3584     // private
3585     onDragEnter : function(e, id){
3586         var target = Roo.dd.DragDropMgr.getDDById(id);
3587         this.cachedTarget = target;
3588         if(this.beforeDragEnter(target, e, id) !== false){
3589             if(target.isNotifyTarget){
3590                 var status = target.notifyEnter(this, e, this.dragData);
3591                 this.proxy.setStatus(status);
3592             }else{
3593                 this.proxy.setStatus(this.dropAllowed);
3594             }
3595             
3596             if(this.afterDragEnter){
3597                 /**
3598                  * An empty function by default, but provided so that you can perform a custom action
3599                  * when the dragged item enters the drop target by providing an implementation.
3600                  * @param {Roo.dd.DragDrop} target The drop target
3601                  * @param {Event} e The event object
3602                  * @param {String} id The id of the dragged element
3603                  * @method afterDragEnter
3604                  */
3605                 this.afterDragEnter(target, e, id);
3606             }
3607         }
3608     },
3609
3610     /**
3611      * An empty function by default, but provided so that you can perform a custom action
3612      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
3613      * @param {Roo.dd.DragDrop} target The drop target
3614      * @param {Event} e The event object
3615      * @param {String} id The id of the dragged element
3616      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3617      */
3618     beforeDragEnter : function(target, e, id){
3619         return true;
3620     },
3621
3622     // private
3623     alignElWithMouse: function() {
3624         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
3625         this.proxy.sync();
3626     },
3627
3628     // private
3629     onDragOver : function(e, id){
3630         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3631         if(this.beforeDragOver(target, e, id) !== false){
3632             if(target.isNotifyTarget){
3633                 var status = target.notifyOver(this, e, this.dragData);
3634                 this.proxy.setStatus(status);
3635             }
3636
3637             if(this.afterDragOver){
3638                 /**
3639                  * An empty function by default, but provided so that you can perform a custom action
3640                  * while the dragged item is over the drop target by providing an implementation.
3641                  * @param {Roo.dd.DragDrop} target The drop target
3642                  * @param {Event} e The event object
3643                  * @param {String} id The id of the dragged element
3644                  * @method afterDragOver
3645                  */
3646                 this.afterDragOver(target, e, id);
3647             }
3648         }
3649     },
3650
3651     /**
3652      * An empty function by default, but provided so that you can perform a custom action
3653      * while the dragged item is over the drop target and optionally cancel the onDragOver.
3654      * @param {Roo.dd.DragDrop} target The drop target
3655      * @param {Event} e The event object
3656      * @param {String} id The id of the dragged element
3657      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3658      */
3659     beforeDragOver : function(target, e, id){
3660         return true;
3661     },
3662
3663     // private
3664     onDragOut : function(e, id){
3665         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3666         if(this.beforeDragOut(target, e, id) !== false){
3667             if(target.isNotifyTarget){
3668                 target.notifyOut(this, e, this.dragData);
3669             }
3670             this.proxy.reset();
3671             if(this.afterDragOut){
3672                 /**
3673                  * An empty function by default, but provided so that you can perform a custom action
3674                  * after the dragged item is dragged out of the target without dropping.
3675                  * @param {Roo.dd.DragDrop} target The drop target
3676                  * @param {Event} e The event object
3677                  * @param {String} id The id of the dragged element
3678                  * @method afterDragOut
3679                  */
3680                 this.afterDragOut(target, e, id);
3681             }
3682         }
3683         this.cachedTarget = null;
3684     },
3685
3686     /**
3687      * An empty function by default, but provided so that you can perform a custom action before the dragged
3688      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
3689      * @param {Roo.dd.DragDrop} target The drop target
3690      * @param {Event} e The event object
3691      * @param {String} id The id of the dragged element
3692      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3693      */
3694     beforeDragOut : function(target, e, id){
3695         return true;
3696     },
3697     
3698     // private
3699     onDragDrop : function(e, id){
3700         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3701         if(this.beforeDragDrop(target, e, id) !== false){
3702             if(target.isNotifyTarget){
3703                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
3704                     this.onValidDrop(target, e, id);
3705                 }else{
3706                     this.onInvalidDrop(target, e, id);
3707                 }
3708             }else{
3709                 this.onValidDrop(target, e, id);
3710             }
3711             
3712             if(this.afterDragDrop){
3713                 /**
3714                  * An empty function by default, but provided so that you can perform a custom action
3715                  * after a valid drag drop has occurred by providing an implementation.
3716                  * @param {Roo.dd.DragDrop} target The drop target
3717                  * @param {Event} e The event object
3718                  * @param {String} id The id of the dropped element
3719                  * @method afterDragDrop
3720                  */
3721                 this.afterDragDrop(target, e, id);
3722             }
3723         }
3724         delete this.cachedTarget;
3725     },
3726
3727     /**
3728      * An empty function by default, but provided so that you can perform a custom action before the dragged
3729      * item is dropped onto the target and optionally cancel the onDragDrop.
3730      * @param {Roo.dd.DragDrop} target The drop target
3731      * @param {Event} e The event object
3732      * @param {String} id The id of the dragged element
3733      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
3734      */
3735     beforeDragDrop : function(target, e, id){
3736         return true;
3737     },
3738
3739     // private
3740     onValidDrop : function(target, e, id){
3741         this.hideProxy();
3742         if(this.afterValidDrop){
3743             /**
3744              * An empty function by default, but provided so that you can perform a custom action
3745              * after a valid drop has occurred by providing an implementation.
3746              * @param {Object} target The target DD 
3747              * @param {Event} e The event object
3748              * @param {String} id The id of the dropped element
3749              * @method afterInvalidDrop
3750              */
3751             this.afterValidDrop(target, e, id);
3752         }
3753     },
3754
3755     // private
3756     getRepairXY : function(e, data){
3757         return this.el.getXY();  
3758     },
3759
3760     // private
3761     onInvalidDrop : function(target, e, id){
3762         this.beforeInvalidDrop(target, e, id);
3763         if(this.cachedTarget){
3764             if(this.cachedTarget.isNotifyTarget){
3765                 this.cachedTarget.notifyOut(this, e, this.dragData);
3766             }
3767             this.cacheTarget = null;
3768         }
3769         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
3770
3771         if(this.afterInvalidDrop){
3772             /**
3773              * An empty function by default, but provided so that you can perform a custom action
3774              * after an invalid drop has occurred by providing an implementation.
3775              * @param {Event} e The event object
3776              * @param {String} id The id of the dropped element
3777              * @method afterInvalidDrop
3778              */
3779             this.afterInvalidDrop(e, id);
3780         }
3781     },
3782
3783     // private
3784     afterRepair : function(){
3785         if(Roo.enableFx){
3786             this.el.highlight(this.hlColor || "c3daf9");
3787         }
3788         this.dragging = false;
3789     },
3790
3791     /**
3792      * An empty function by default, but provided so that you can perform a custom action after an invalid
3793      * drop has occurred.
3794      * @param {Roo.dd.DragDrop} target The drop target
3795      * @param {Event} e The event object
3796      * @param {String} id The id of the dragged element
3797      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
3798      */
3799     beforeInvalidDrop : function(target, e, id){
3800         return true;
3801     },
3802
3803     // private
3804     handleMouseDown : function(e){
3805         if(this.dragging) {
3806             return;
3807         }
3808         var data = this.getDragData(e);
3809         if(data && this.onBeforeDrag(data, e) !== false){
3810             this.dragData = data;
3811             this.proxy.stop();
3812             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
3813         } 
3814     },
3815
3816     /**
3817      * An empty function by default, but provided so that you can perform a custom action before the initial
3818      * drag event begins and optionally cancel it.
3819      * @param {Object} data An object containing arbitrary data to be shared with drop targets
3820      * @param {Event} e The event object
3821      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3822      */
3823     onBeforeDrag : function(data, e){
3824         return true;
3825     },
3826
3827     /**
3828      * An empty function by default, but provided so that you can perform a custom action once the initial
3829      * drag event has begun.  The drag cannot be canceled from this function.
3830      * @param {Number} x The x position of the click on the dragged object
3831      * @param {Number} y The y position of the click on the dragged object
3832      */
3833     onStartDrag : Roo.emptyFn,
3834
3835     // private - YUI override
3836     startDrag : function(x, y){
3837         this.proxy.reset();
3838         this.dragging = true;
3839         this.proxy.update("");
3840         this.onInitDrag(x, y);
3841         this.proxy.show();
3842     },
3843
3844     // private
3845     onInitDrag : function(x, y){
3846         var clone = this.el.dom.cloneNode(true);
3847         clone.id = Roo.id(); // prevent duplicate ids
3848         this.proxy.update(clone);
3849         this.onStartDrag(x, y);
3850         return true;
3851     },
3852
3853     /**
3854      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
3855      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
3856      */
3857     getProxy : function(){
3858         return this.proxy;  
3859     },
3860
3861     /**
3862      * Hides the drag source's {@link Roo.dd.StatusProxy}
3863      */
3864     hideProxy : function(){
3865         this.proxy.hide();  
3866         this.proxy.reset(true);
3867         this.dragging = false;
3868     },
3869
3870     // private
3871     triggerCacheRefresh : function(){
3872         Roo.dd.DDM.refreshCache(this.groups);
3873     },
3874
3875     // private - override to prevent hiding
3876     b4EndDrag: function(e) {
3877     },
3878
3879     // private - override to prevent moving
3880     endDrag : function(e){
3881         this.onEndDrag(this.dragData, e);
3882     },
3883
3884     // private
3885     onEndDrag : function(data, e){
3886     },
3887     
3888     // private - pin to cursor
3889     autoOffset : function(x, y) {
3890         this.setDelta(-12, -20);
3891     }    
3892 });/*
3893  * Based on:
3894  * Ext JS Library 1.1.1
3895  * Copyright(c) 2006-2007, Ext JS, LLC.
3896  *
3897  * Originally Released Under LGPL - original licence link has changed is not relivant.
3898  *
3899  * Fork - LGPL
3900  * <script type="text/javascript">
3901  */
3902
3903
3904 /**
3905  * @class Roo.dd.DropTarget
3906  * @extends Roo.dd.DDTarget
3907  * A simple class that provides the basic implementation needed to make any element a drop target that can have
3908  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
3909  * @constructor
3910  * @param {String/HTMLElement/Element} el The container element
3911  * @param {Object} config
3912  */
3913 Roo.dd.DropTarget = function(el, config){
3914     this.el = Roo.get(el);
3915     
3916     var listeners = false; ;
3917     if (config && config.listeners) {
3918         listeners= config.listeners;
3919         delete config.listeners;
3920     }
3921     Roo.apply(this, config);
3922     
3923     if(this.containerScroll){
3924         Roo.dd.ScrollManager.register(this.el);
3925     }
3926     this.addEvents( {
3927          /**
3928          * @scope Roo.dd.DropTarget
3929          */
3930          
3931          /**
3932          * @event enter
3933          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
3934          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
3935          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
3936          * 
3937          * IMPORTANT : it should set this.overClass and this.dropAllowed
3938          * 
3939          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3940          * @param {Event} e The event
3941          * @param {Object} data An object containing arbitrary data supplied by the drag source
3942          */
3943         "enter" : true,
3944         
3945          /**
3946          * @event over
3947          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
3948          * This method will be called on every mouse movement while the drag source is over the drop target.
3949          * This default implementation simply returns the dropAllowed config value.
3950          * 
3951          * IMPORTANT : it should set this.dropAllowed
3952          * 
3953          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3954          * @param {Event} e The event
3955          * @param {Object} data An object containing arbitrary data supplied by the drag source
3956          
3957          */
3958         "over" : true,
3959         /**
3960          * @event out
3961          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
3962          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
3963          * overClass (if any) from the drop element.
3964          * 
3965          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3966          * @param {Event} e The event
3967          * @param {Object} data An object containing arbitrary data supplied by the drag source
3968          */
3969          "out" : true,
3970          
3971         /**
3972          * @event drop
3973          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
3974          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
3975          * implementation that does something to process the drop event and returns true so that the drag source's
3976          * repair action does not run.
3977          * 
3978          * IMPORTANT : it should set this.success
3979          * 
3980          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3981          * @param {Event} e The event
3982          * @param {Object} data An object containing arbitrary data supplied by the drag source
3983         */
3984          "drop" : true
3985     });
3986             
3987      
3988     Roo.dd.DropTarget.superclass.constructor.call(  this, 
3989         this.el.dom, 
3990         this.ddGroup || this.group,
3991         {
3992             isTarget: true,
3993             listeners : listeners || {} 
3994            
3995         
3996         }
3997     );
3998
3999 };
4000
4001 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
4002     /**
4003      * @cfg {String} overClass
4004      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
4005      */
4006      /**
4007      * @cfg {String} ddGroup
4008      * The drag drop group to handle drop events for
4009      */
4010      
4011     /**
4012      * @cfg {String} dropAllowed
4013      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
4014      */
4015     dropAllowed : "x-dd-drop-ok",
4016     /**
4017      * @cfg {String} dropNotAllowed
4018      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
4019      */
4020     dropNotAllowed : "x-dd-drop-nodrop",
4021     /**
4022      * @cfg {boolean} success
4023      * set this after drop listener.. 
4024      */
4025     success : false,
4026     /**
4027      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
4028      * if the drop point is valid for over/enter..
4029      */
4030     valid : false,
4031     // private
4032     isTarget : true,
4033
4034     // private
4035     isNotifyTarget : true,
4036     
4037     /**
4038      * @hide
4039      */
4040     notifyEnter : function(dd, e, data)
4041     {
4042         this.valid = true;
4043         this.fireEvent('enter', dd, e, data);
4044         if(this.overClass){
4045             this.el.addClass(this.overClass);
4046         }
4047         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4048             this.valid ? this.dropAllowed : this.dropNotAllowed
4049         );
4050     },
4051
4052     /**
4053      * @hide
4054      */
4055     notifyOver : function(dd, e, data)
4056     {
4057         this.valid = true;
4058         this.fireEvent('over', dd, e, data);
4059         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4060             this.valid ? this.dropAllowed : this.dropNotAllowed
4061         );
4062     },
4063
4064     /**
4065      * @hide
4066      */
4067     notifyOut : function(dd, e, data)
4068     {
4069         this.fireEvent('out', dd, e, data);
4070         if(this.overClass){
4071             this.el.removeClass(this.overClass);
4072         }
4073     },
4074
4075     /**
4076      * @hide
4077      */
4078     notifyDrop : function(dd, e, data)
4079     {
4080         this.success = false;
4081         this.fireEvent('drop', dd, e, data);
4082         return this.success;
4083     }
4084 });/*
4085  * Based on:
4086  * Ext JS Library 1.1.1
4087  * Copyright(c) 2006-2007, Ext JS, LLC.
4088  *
4089  * Originally Released Under LGPL - original licence link has changed is not relivant.
4090  *
4091  * Fork - LGPL
4092  * <script type="text/javascript">
4093  */
4094
4095
4096 /**
4097  * @class Roo.dd.DragZone
4098  * @extends Roo.dd.DragSource
4099  * This class provides a container DD instance that proxies for multiple child node sources.<br />
4100  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
4101  * @constructor
4102  * @param {String/HTMLElement/Element} el The container element
4103  * @param {Object} config
4104  */
4105 Roo.dd.DragZone = function(el, config){
4106     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
4107     if(this.containerScroll){
4108         Roo.dd.ScrollManager.register(this.el);
4109     }
4110 };
4111
4112 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
4113     /**
4114      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
4115      * for auto scrolling during drag operations.
4116      */
4117     /**
4118      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
4119      * method after a failed drop (defaults to "c3daf9" - light blue)
4120      */
4121
4122     /**
4123      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
4124      * for a valid target to drag based on the mouse down. Override this method
4125      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
4126      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
4127      * @param {EventObject} e The mouse down event
4128      * @return {Object} The dragData
4129      */
4130     getDragData : function(e){
4131         return Roo.dd.Registry.getHandleFromEvent(e);
4132     },
4133     
4134     /**
4135      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
4136      * this.dragData.ddel
4137      * @param {Number} x The x position of the click on the dragged object
4138      * @param {Number} y The y position of the click on the dragged object
4139      * @return {Boolean} true to continue the drag, false to cancel
4140      */
4141     onInitDrag : function(x, y){
4142         this.proxy.update(this.dragData.ddel.cloneNode(true));
4143         this.onStartDrag(x, y);
4144         return true;
4145     },
4146     
4147     /**
4148      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
4149      */
4150     afterRepair : function(){
4151         if(Roo.enableFx){
4152             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
4153         }
4154         this.dragging = false;
4155     },
4156
4157     /**
4158      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
4159      * the XY of this.dragData.ddel
4160      * @param {EventObject} e The mouse up event
4161      * @return {Array} The xy location (e.g. [100, 200])
4162      */
4163     getRepairXY : function(e){
4164         return Roo.Element.fly(this.dragData.ddel).getXY();  
4165     }
4166 });/*
4167  * Based on:
4168  * Ext JS Library 1.1.1
4169  * Copyright(c) 2006-2007, Ext JS, LLC.
4170  *
4171  * Originally Released Under LGPL - original licence link has changed is not relivant.
4172  *
4173  * Fork - LGPL
4174  * <script type="text/javascript">
4175  */
4176 /**
4177  * @class Roo.dd.DropZone
4178  * @extends Roo.dd.DropTarget
4179  * This class provides a container DD instance that proxies for multiple child node targets.<br />
4180  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
4181  * @constructor
4182  * @param {String/HTMLElement/Element} el The container element
4183  * @param {Object} config
4184  */
4185 Roo.dd.DropZone = function(el, config){
4186     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
4187 };
4188
4189 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
4190     /**
4191      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
4192      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
4193      * provide your own custom lookup.
4194      * @param {Event} e The event
4195      * @return {Object} data The custom data
4196      */
4197     getTargetFromEvent : function(e){
4198         return Roo.dd.Registry.getTargetFromEvent(e);
4199     },
4200
4201     /**
4202      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
4203      * that it has registered.  This method has no default implementation and should be overridden to provide
4204      * node-specific processing if necessary.
4205      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
4206      * {@link #getTargetFromEvent} for this node)
4207      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4208      * @param {Event} e The event
4209      * @param {Object} data An object containing arbitrary data supplied by the drag source
4210      */
4211     onNodeEnter : function(n, dd, e, data){
4212         
4213     },
4214
4215     /**
4216      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
4217      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
4218      * overridden to provide the proper feedback.
4219      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4220      * {@link #getTargetFromEvent} for this node)
4221      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4222      * @param {Event} e The event
4223      * @param {Object} data An object containing arbitrary data supplied by the drag source
4224      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4225      * underlying {@link Roo.dd.StatusProxy} can be updated
4226      */
4227     onNodeOver : function(n, dd, e, data){
4228         return this.dropAllowed;
4229     },
4230
4231     /**
4232      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
4233      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
4234      * node-specific processing if necessary.
4235      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4236      * {@link #getTargetFromEvent} for this node)
4237      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4238      * @param {Event} e The event
4239      * @param {Object} data An object containing arbitrary data supplied by the drag source
4240      */
4241     onNodeOut : function(n, dd, e, data){
4242         
4243     },
4244
4245     /**
4246      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
4247      * the drop node.  The default implementation returns false, so it should be overridden to provide the
4248      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
4249      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4250      * {@link #getTargetFromEvent} for this node)
4251      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4252      * @param {Event} e The event
4253      * @param {Object} data An object containing arbitrary data supplied by the drag source
4254      * @return {Boolean} True if the drop was valid, else false
4255      */
4256     onNodeDrop : function(n, dd, e, data){
4257         return false;
4258     },
4259
4260     /**
4261      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
4262      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
4263      * it should be overridden to provide the proper feedback if necessary.
4264      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4265      * @param {Event} e The event
4266      * @param {Object} data An object containing arbitrary data supplied by the drag source
4267      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4268      * underlying {@link Roo.dd.StatusProxy} can be updated
4269      */
4270     onContainerOver : function(dd, e, data){
4271         return this.dropNotAllowed;
4272     },
4273
4274     /**
4275      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
4276      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
4277      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
4278      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
4279      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4280      * @param {Event} e The event
4281      * @param {Object} data An object containing arbitrary data supplied by the drag source
4282      * @return {Boolean} True if the drop was valid, else false
4283      */
4284     onContainerDrop : function(dd, e, data){
4285         return false;
4286     },
4287
4288     /**
4289      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
4290      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
4291      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
4292      * you should override this method and provide a custom implementation.
4293      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4294      * @param {Event} e The event
4295      * @param {Object} data An object containing arbitrary data supplied by the drag source
4296      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4297      * underlying {@link Roo.dd.StatusProxy} can be updated
4298      */
4299     notifyEnter : function(dd, e, data){
4300         return this.dropNotAllowed;
4301     },
4302
4303     /**
4304      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
4305      * This method will be called on every mouse movement while the drag source is over the drop zone.
4306      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
4307      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
4308      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
4309      * registered node, it will call {@link #onContainerOver}.
4310      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4311      * @param {Event} e The event
4312      * @param {Object} data An object containing arbitrary data supplied by the drag source
4313      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4314      * underlying {@link Roo.dd.StatusProxy} can be updated
4315      */
4316     notifyOver : function(dd, e, data){
4317         var n = this.getTargetFromEvent(e);
4318         if(!n){ // not over valid drop target
4319             if(this.lastOverNode){
4320                 this.onNodeOut(this.lastOverNode, dd, e, data);
4321                 this.lastOverNode = null;
4322             }
4323             return this.onContainerOver(dd, e, data);
4324         }
4325         if(this.lastOverNode != n){
4326             if(this.lastOverNode){
4327                 this.onNodeOut(this.lastOverNode, dd, e, data);
4328             }
4329             this.onNodeEnter(n, dd, e, data);
4330             this.lastOverNode = n;
4331         }
4332         return this.onNodeOver(n, dd, e, data);
4333     },
4334
4335     /**
4336      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
4337      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
4338      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
4339      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
4340      * @param {Event} e The event
4341      * @param {Object} data An object containing arbitrary data supplied by the drag zone
4342      */
4343     notifyOut : function(dd, e, data){
4344         if(this.lastOverNode){
4345             this.onNodeOut(this.lastOverNode, dd, e, data);
4346             this.lastOverNode = null;
4347         }
4348     },
4349
4350     /**
4351      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
4352      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
4353      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
4354      * otherwise it will call {@link #onContainerDrop}.
4355      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4356      * @param {Event} e The event
4357      * @param {Object} data An object containing arbitrary data supplied by the drag source
4358      * @return {Boolean} True if the drop was valid, else false
4359      */
4360     notifyDrop : function(dd, e, data){
4361         if(this.lastOverNode){
4362             this.onNodeOut(this.lastOverNode, dd, e, data);
4363             this.lastOverNode = null;
4364         }
4365         var n = this.getTargetFromEvent(e);
4366         return n ?
4367             this.onNodeDrop(n, dd, e, data) :
4368             this.onContainerDrop(dd, e, data);
4369     },
4370
4371     // private
4372     triggerCacheRefresh : function(){
4373         Roo.dd.DDM.refreshCache(this.groups);
4374     }  
4375 });/*
4376  * Based on:
4377  * Ext JS Library 1.1.1
4378  * Copyright(c) 2006-2007, Ext JS, LLC.
4379  *
4380  * Originally Released Under LGPL - original licence link has changed is not relivant.
4381  *
4382  * Fork - LGPL
4383  * <script type="text/javascript">
4384  */
4385
4386
4387 /**
4388  * @class Roo.data.SortTypes
4389  * @singleton
4390  * Defines the default sorting (casting?) comparison functions used when sorting data.
4391  */
4392 Roo.data.SortTypes = {
4393     /**
4394      * Default sort that does nothing
4395      * @param {Mixed} s The value being converted
4396      * @return {Mixed} The comparison value
4397      */
4398     none : function(s){
4399         return s;
4400     },
4401     
4402     /**
4403      * The regular expression used to strip tags
4404      * @type {RegExp}
4405      * @property
4406      */
4407     stripTagsRE : /<\/?[^>]+>/gi,
4408     
4409     /**
4410      * Strips all HTML tags to sort on text only
4411      * @param {Mixed} s The value being converted
4412      * @return {String} The comparison value
4413      */
4414     asText : function(s){
4415         return String(s).replace(this.stripTagsRE, "");
4416     },
4417     
4418     /**
4419      * Strips all HTML tags to sort on text only - Case insensitive
4420      * @param {Mixed} s The value being converted
4421      * @return {String} The comparison value
4422      */
4423     asUCText : function(s){
4424         return String(s).toUpperCase().replace(this.stripTagsRE, "");
4425     },
4426     
4427     /**
4428      * Case insensitive string
4429      * @param {Mixed} s The value being converted
4430      * @return {String} The comparison value
4431      */
4432     asUCString : function(s) {
4433         return String(s).toUpperCase();
4434     },
4435     
4436     /**
4437      * Date sorting
4438      * @param {Mixed} s The value being converted
4439      * @return {Number} The comparison value
4440      */
4441     asDate : function(s) {
4442         if(!s){
4443             return 0;
4444         }
4445         if(s instanceof Date){
4446             return s.getTime();
4447         }
4448         return Date.parse(String(s));
4449     },
4450     
4451     /**
4452      * Float sorting
4453      * @param {Mixed} s The value being converted
4454      * @return {Float} The comparison value
4455      */
4456     asFloat : function(s) {
4457         var val = parseFloat(String(s).replace(/,/g, ""));
4458         if(isNaN(val)) val = 0;
4459         return val;
4460     },
4461     
4462     /**
4463      * Integer sorting
4464      * @param {Mixed} s The value being converted
4465      * @return {Number} The comparison value
4466      */
4467     asInt : function(s) {
4468         var val = parseInt(String(s).replace(/,/g, ""));
4469         if(isNaN(val)) val = 0;
4470         return val;
4471     }
4472 };/*
4473  * Based on:
4474  * Ext JS Library 1.1.1
4475  * Copyright(c) 2006-2007, Ext JS, LLC.
4476  *
4477  * Originally Released Under LGPL - original licence link has changed is not relivant.
4478  *
4479  * Fork - LGPL
4480  * <script type="text/javascript">
4481  */
4482
4483 /**
4484 * @class Roo.data.Record
4485  * Instances of this class encapsulate both record <em>definition</em> information, and record
4486  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
4487  * to access Records cached in an {@link Roo.data.Store} object.<br>
4488  * <p>
4489  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
4490  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
4491  * objects.<br>
4492  * <p>
4493  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
4494  * @constructor
4495  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
4496  * {@link #create}. The parameters are the same.
4497  * @param {Array} data An associative Array of data values keyed by the field name.
4498  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
4499  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
4500  * not specified an integer id is generated.
4501  */
4502 Roo.data.Record = function(data, id){
4503     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
4504     this.data = data;
4505 };
4506
4507 /**
4508  * Generate a constructor for a specific record layout.
4509  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
4510  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
4511  * Each field definition object may contain the following properties: <ul>
4512  * <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,
4513  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
4514  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
4515  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
4516  * is being used, then this is a string containing the javascript expression to reference the data relative to 
4517  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
4518  * to the data item relative to the record element. If the mapping expression is the same as the field name,
4519  * this may be omitted.</p></li>
4520  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
4521  * <ul><li>auto (Default, implies no conversion)</li>
4522  * <li>string</li>
4523  * <li>int</li>
4524  * <li>float</li>
4525  * <li>boolean</li>
4526  * <li>date</li></ul></p></li>
4527  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
4528  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
4529  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
4530  * by the Reader into an object that will be stored in the Record. It is passed the
4531  * following parameters:<ul>
4532  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
4533  * </ul></p></li>
4534  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
4535  * </ul>
4536  * <br>usage:<br><pre><code>
4537 var TopicRecord = Roo.data.Record.create(
4538     {name: 'title', mapping: 'topic_title'},
4539     {name: 'author', mapping: 'username'},
4540     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
4541     {name: 'lastPost', mapping: 'post_time', type: 'date'},
4542     {name: 'lastPoster', mapping: 'user2'},
4543     {name: 'excerpt', mapping: 'post_text'}
4544 );
4545
4546 var myNewRecord = new TopicRecord({
4547     title: 'Do my job please',
4548     author: 'noobie',
4549     totalPosts: 1,
4550     lastPost: new Date(),
4551     lastPoster: 'Animal',
4552     excerpt: 'No way dude!'
4553 });
4554 myStore.add(myNewRecord);
4555 </code></pre>
4556  * @method create
4557  * @static
4558  */
4559 Roo.data.Record.create = function(o){
4560     var f = function(){
4561         f.superclass.constructor.apply(this, arguments);
4562     };
4563     Roo.extend(f, Roo.data.Record);
4564     var p = f.prototype;
4565     p.fields = new Roo.util.MixedCollection(false, function(field){
4566         return field.name;
4567     });
4568     for(var i = 0, len = o.length; i < len; i++){
4569         p.fields.add(new Roo.data.Field(o[i]));
4570     }
4571     f.getField = function(name){
4572         return p.fields.get(name);  
4573     };
4574     return f;
4575 };
4576
4577 Roo.data.Record.AUTO_ID = 1000;
4578 Roo.data.Record.EDIT = 'edit';
4579 Roo.data.Record.REJECT = 'reject';
4580 Roo.data.Record.COMMIT = 'commit';
4581
4582 Roo.data.Record.prototype = {
4583     /**
4584      * Readonly flag - true if this record has been modified.
4585      * @type Boolean
4586      */
4587     dirty : false,
4588     editing : false,
4589     error: null,
4590     modified: null,
4591
4592     // private
4593     join : function(store){
4594         this.store = store;
4595     },
4596
4597     /**
4598      * Set the named field to the specified value.
4599      * @param {String} name The name of the field to set.
4600      * @param {Object} value The value to set the field to.
4601      */
4602     set : function(name, value){
4603         if(this.data[name] == value){
4604             return;
4605         }
4606         this.dirty = true;
4607         if(!this.modified){
4608             this.modified = {};
4609         }
4610         if(typeof this.modified[name] == 'undefined'){
4611             this.modified[name] = this.data[name];
4612         }
4613         this.data[name] = value;
4614         if(!this.editing && this.store){
4615             this.store.afterEdit(this);
4616         }       
4617     },
4618
4619     /**
4620      * Get the value of the named field.
4621      * @param {String} name The name of the field to get the value of.
4622      * @return {Object} The value of the field.
4623      */
4624     get : function(name){
4625         return this.data[name]; 
4626     },
4627
4628     // private
4629     beginEdit : function(){
4630         this.editing = true;
4631         this.modified = {}; 
4632     },
4633
4634     // private
4635     cancelEdit : function(){
4636         this.editing = false;
4637         delete this.modified;
4638     },
4639
4640     // private
4641     endEdit : function(){
4642         this.editing = false;
4643         if(this.dirty && this.store){
4644             this.store.afterEdit(this);
4645         }
4646     },
4647
4648     /**
4649      * Usually called by the {@link Roo.data.Store} which owns the Record.
4650      * Rejects all changes made to the Record since either creation, or the last commit operation.
4651      * Modified fields are reverted to their original values.
4652      * <p>
4653      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4654      * of reject operations.
4655      */
4656     reject : function(){
4657         var m = this.modified;
4658         for(var n in m){
4659             if(typeof m[n] != "function"){
4660                 this.data[n] = m[n];
4661             }
4662         }
4663         this.dirty = false;
4664         delete this.modified;
4665         this.editing = false;
4666         if(this.store){
4667             this.store.afterReject(this);
4668         }
4669     },
4670
4671     /**
4672      * Usually called by the {@link Roo.data.Store} which owns the Record.
4673      * Commits all changes made to the Record since either creation, or the last commit operation.
4674      * <p>
4675      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4676      * of commit operations.
4677      */
4678     commit : function(){
4679         this.dirty = false;
4680         delete this.modified;
4681         this.editing = false;
4682         if(this.store){
4683             this.store.afterCommit(this);
4684         }
4685     },
4686
4687     // private
4688     hasError : function(){
4689         return this.error != null;
4690     },
4691
4692     // private
4693     clearError : function(){
4694         this.error = null;
4695     },
4696
4697     /**
4698      * Creates a copy of this record.
4699      * @param {String} id (optional) A new record id if you don't want to use this record's id
4700      * @return {Record}
4701      */
4702     copy : function(newId) {
4703         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
4704     }
4705 };/*
4706  * Based on:
4707  * Ext JS Library 1.1.1
4708  * Copyright(c) 2006-2007, Ext JS, LLC.
4709  *
4710  * Originally Released Under LGPL - original licence link has changed is not relivant.
4711  *
4712  * Fork - LGPL
4713  * <script type="text/javascript">
4714  */
4715
4716
4717
4718 /**
4719  * @class Roo.data.Store
4720  * @extends Roo.util.Observable
4721  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
4722  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
4723  * <p>
4724  * 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
4725  * has no knowledge of the format of the data returned by the Proxy.<br>
4726  * <p>
4727  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
4728  * instances from the data object. These records are cached and made available through accessor functions.
4729  * @constructor
4730  * Creates a new Store.
4731  * @param {Object} config A config object containing the objects needed for the Store to access data,
4732  * and read the data into Records.
4733  */
4734 Roo.data.Store = function(config){
4735     this.data = new Roo.util.MixedCollection(false);
4736     this.data.getKey = function(o){
4737         return o.id;
4738     };
4739     this.baseParams = {};
4740     // private
4741     this.paramNames = {
4742         "start" : "start",
4743         "limit" : "limit",
4744         "sort" : "sort",
4745         "dir" : "dir",
4746         "multisort" : "_multisort"
4747     };
4748
4749     if(config && config.data){
4750         this.inlineData = config.data;
4751         delete config.data;
4752     }
4753
4754     Roo.apply(this, config);
4755     
4756     if(this.reader){ // reader passed
4757         this.reader = Roo.factory(this.reader, Roo.data);
4758         this.reader.xmodule = this.xmodule || false;
4759         if(!this.recordType){
4760             this.recordType = this.reader.recordType;
4761         }
4762         if(this.reader.onMetaChange){
4763             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
4764         }
4765     }
4766
4767     if(this.recordType){
4768         this.fields = this.recordType.prototype.fields;
4769     }
4770     this.modified = [];
4771
4772     this.addEvents({
4773         /**
4774          * @event datachanged
4775          * Fires when the data cache has changed, and a widget which is using this Store
4776          * as a Record cache should refresh its view.
4777          * @param {Store} this
4778          */
4779         datachanged : true,
4780         /**
4781          * @event metachange
4782          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
4783          * @param {Store} this
4784          * @param {Object} meta The JSON metadata
4785          */
4786         metachange : true,
4787         /**
4788          * @event add
4789          * Fires when Records have been added to the Store
4790          * @param {Store} this
4791          * @param {Roo.data.Record[]} records The array of Records added
4792          * @param {Number} index The index at which the record(s) were added
4793          */
4794         add : true,
4795         /**
4796          * @event remove
4797          * Fires when a Record has been removed from the Store
4798          * @param {Store} this
4799          * @param {Roo.data.Record} record The Record that was removed
4800          * @param {Number} index The index at which the record was removed
4801          */
4802         remove : true,
4803         /**
4804          * @event update
4805          * Fires when a Record has been updated
4806          * @param {Store} this
4807          * @param {Roo.data.Record} record The Record that was updated
4808          * @param {String} operation The update operation being performed.  Value may be one of:
4809          * <pre><code>
4810  Roo.data.Record.EDIT
4811  Roo.data.Record.REJECT
4812  Roo.data.Record.COMMIT
4813          * </code></pre>
4814          */
4815         update : true,
4816         /**
4817          * @event clear
4818          * Fires when the data cache has been cleared.
4819          * @param {Store} this
4820          */
4821         clear : true,
4822         /**
4823          * @event beforeload
4824          * Fires before a request is made for a new data object.  If the beforeload handler returns false
4825          * the load action will be canceled.
4826          * @param {Store} this
4827          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4828          */
4829         beforeload : true,
4830         /**
4831          * @event beforeloadadd
4832          * Fires after a new set of Records has been loaded.
4833          * @param {Store} this
4834          * @param {Roo.data.Record[]} records The Records that were loaded
4835          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4836          */
4837         beforeloadadd : true,
4838         /**
4839          * @event load
4840          * Fires after a new set of Records has been loaded, before they are added to the store.
4841          * @param {Store} this
4842          * @param {Roo.data.Record[]} records The Records that were loaded
4843          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4844          * @params {Object} return from reader
4845          */
4846         load : true,
4847         /**
4848          * @event loadexception
4849          * Fires if an exception occurs in the Proxy during loading.
4850          * Called with the signature of the Proxy's "loadexception" event.
4851          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
4852          * 
4853          * @param {Proxy} 
4854          * @param {Object} return from JsonData.reader() - success, totalRecords, records
4855          * @param {Object} load options 
4856          * @param {Object} jsonData from your request (normally this contains the Exception)
4857          */
4858         loadexception : true
4859     });
4860     
4861     if(this.proxy){
4862         this.proxy = Roo.factory(this.proxy, Roo.data);
4863         this.proxy.xmodule = this.xmodule || false;
4864         this.relayEvents(this.proxy,  ["loadexception"]);
4865     }
4866     this.sortToggle = {};
4867     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
4868
4869     Roo.data.Store.superclass.constructor.call(this);
4870
4871     if(this.inlineData){
4872         this.loadData(this.inlineData);
4873         delete this.inlineData;
4874     }
4875 };
4876
4877 Roo.extend(Roo.data.Store, Roo.util.Observable, {
4878      /**
4879     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
4880     * without a remote query - used by combo/forms at present.
4881     */
4882     
4883     /**
4884     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
4885     */
4886     /**
4887     * @cfg {Array} data Inline data to be loaded when the store is initialized.
4888     */
4889     /**
4890     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
4891     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
4892     */
4893     /**
4894     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
4895     * on any HTTP request
4896     */
4897     /**
4898     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
4899     */
4900     /**
4901     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
4902     */
4903     multiSort: false,
4904     /**
4905     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
4906     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
4907     */
4908     remoteSort : false,
4909
4910     /**
4911     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
4912      * loaded or when a record is removed. (defaults to false).
4913     */
4914     pruneModifiedRecords : false,
4915
4916     // private
4917     lastOptions : null,
4918
4919     /**
4920      * Add Records to the Store and fires the add event.
4921      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4922      */
4923     add : function(records){
4924         records = [].concat(records);
4925         for(var i = 0, len = records.length; i < len; i++){
4926             records[i].join(this);
4927         }
4928         var index = this.data.length;
4929         this.data.addAll(records);
4930         this.fireEvent("add", this, records, index);
4931     },
4932
4933     /**
4934      * Remove a Record from the Store and fires the remove event.
4935      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
4936      */
4937     remove : function(record){
4938         var index = this.data.indexOf(record);
4939         this.data.removeAt(index);
4940         if(this.pruneModifiedRecords){
4941             this.modified.remove(record);
4942         }
4943         this.fireEvent("remove", this, record, index);
4944     },
4945
4946     /**
4947      * Remove all Records from the Store and fires the clear event.
4948      */
4949     removeAll : function(){
4950         this.data.clear();
4951         if(this.pruneModifiedRecords){
4952             this.modified = [];
4953         }
4954         this.fireEvent("clear", this);
4955     },
4956
4957     /**
4958      * Inserts Records to the Store at the given index and fires the add event.
4959      * @param {Number} index The start index at which to insert the passed Records.
4960      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4961      */
4962     insert : function(index, records){
4963         records = [].concat(records);
4964         for(var i = 0, len = records.length; i < len; i++){
4965             this.data.insert(index, records[i]);
4966             records[i].join(this);
4967         }
4968         this.fireEvent("add", this, records, index);
4969     },
4970
4971     /**
4972      * Get the index within the cache of the passed Record.
4973      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
4974      * @return {Number} The index of the passed Record. Returns -1 if not found.
4975      */
4976     indexOf : function(record){
4977         return this.data.indexOf(record);
4978     },
4979
4980     /**
4981      * Get the index within the cache of the Record with the passed id.
4982      * @param {String} id The id of the Record to find.
4983      * @return {Number} The index of the Record. Returns -1 if not found.
4984      */
4985     indexOfId : function(id){
4986         return this.data.indexOfKey(id);
4987     },
4988
4989     /**
4990      * Get the Record with the specified id.
4991      * @param {String} id The id of the Record to find.
4992      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
4993      */
4994     getById : function(id){
4995         return this.data.key(id);
4996     },
4997
4998     /**
4999      * Get the Record at the specified index.
5000      * @param {Number} index The index of the Record to find.
5001      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
5002      */
5003     getAt : function(index){
5004         return this.data.itemAt(index);
5005     },
5006
5007     /**
5008      * Returns a range of Records between specified indices.
5009      * @param {Number} startIndex (optional) The starting index (defaults to 0)
5010      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
5011      * @return {Roo.data.Record[]} An array of Records
5012      */
5013     getRange : function(start, end){
5014         return this.data.getRange(start, end);
5015     },
5016
5017     // private
5018     storeOptions : function(o){
5019         o = Roo.apply({}, o);
5020         delete o.callback;
5021         delete o.scope;
5022         this.lastOptions = o;
5023     },
5024
5025     /**
5026      * Loads the Record cache from the configured Proxy using the configured Reader.
5027      * <p>
5028      * If using remote paging, then the first load call must specify the <em>start</em>
5029      * and <em>limit</em> properties in the options.params property to establish the initial
5030      * position within the dataset, and the number of Records to cache on each read from the Proxy.
5031      * <p>
5032      * <strong>It is important to note that for remote data sources, loading is asynchronous,
5033      * and this call will return before the new data has been loaded. Perform any post-processing
5034      * in a callback function, or in a "load" event handler.</strong>
5035      * <p>
5036      * @param {Object} options An object containing properties which control loading options:<ul>
5037      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
5038      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
5039      * passed the following arguments:<ul>
5040      * <li>r : Roo.data.Record[]</li>
5041      * <li>options: Options object from the load call</li>
5042      * <li>success: Boolean success indicator</li></ul></li>
5043      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
5044      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
5045      * </ul>
5046      */
5047     load : function(options){
5048         options = options || {};
5049         if(this.fireEvent("beforeload", this, options) !== false){
5050             this.storeOptions(options);
5051             var p = Roo.apply(options.params || {}, this.baseParams);
5052             // if meta was not loaded from remote source.. try requesting it.
5053             if (!this.reader.metaFromRemote) {
5054                 p._requestMeta = 1;
5055             }
5056             if(this.sortInfo && this.remoteSort){
5057                 var pn = this.paramNames;
5058                 p[pn["sort"]] = this.sortInfo.field;
5059                 p[pn["dir"]] = this.sortInfo.direction;
5060             }
5061             if (this.multiSort) {
5062                 var pn = this.paramNames;
5063                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
5064             }
5065             
5066             this.proxy.load(p, this.reader, this.loadRecords, this, options);
5067         }
5068     },
5069
5070     /**
5071      * Reloads the Record cache from the configured Proxy using the configured Reader and
5072      * the options from the last load operation performed.
5073      * @param {Object} options (optional) An object containing properties which may override the options
5074      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
5075      * the most recently used options are reused).
5076      */
5077     reload : function(options){
5078         this.load(Roo.applyIf(options||{}, this.lastOptions));
5079     },
5080
5081     // private
5082     // Called as a callback by the Reader during a load operation.
5083     loadRecords : function(o, options, success){
5084         if(!o || success === false){
5085             if(success !== false){
5086                 this.fireEvent("load", this, [], options, o);
5087             }
5088             if(options.callback){
5089                 options.callback.call(options.scope || this, [], options, false);
5090             }
5091             return;
5092         }
5093         // if data returned failure - throw an exception.
5094         if (o.success === false) {
5095             // show a message if no listener is registered.
5096             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
5097                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
5098             }
5099             // loadmask wil be hooked into this..
5100             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
5101             return;
5102         }
5103         var r = o.records, t = o.totalRecords || r.length;
5104         
5105         this.fireEvent("beforeloadadd", this, r, options, o);
5106         
5107         if(!options || options.add !== true){
5108             if(this.pruneModifiedRecords){
5109                 this.modified = [];
5110             }
5111             for(var i = 0, len = r.length; i < len; i++){
5112                 r[i].join(this);
5113             }
5114             if(this.snapshot){
5115                 this.data = this.snapshot;
5116                 delete this.snapshot;
5117             }
5118             this.data.clear();
5119             this.data.addAll(r);
5120             this.totalLength = t;
5121             this.applySort();
5122             this.fireEvent("datachanged", this);
5123         }else{
5124             this.totalLength = Math.max(t, this.data.length+r.length);
5125             this.add(r);
5126         }
5127         this.fireEvent("load", this, r, options, o);
5128         if(options.callback){
5129             options.callback.call(options.scope || this, r, options, true);
5130         }
5131     },
5132
5133
5134     /**
5135      * Loads data from a passed data block. A Reader which understands the format of the data
5136      * must have been configured in the constructor.
5137      * @param {Object} data The data block from which to read the Records.  The format of the data expected
5138      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
5139      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
5140      */
5141     loadData : function(o, append){
5142         var r = this.reader.readRecords(o);
5143         this.loadRecords(r, {add: append}, true);
5144     },
5145
5146     /**
5147      * Gets the number of cached records.
5148      * <p>
5149      * <em>If using paging, this may not be the total size of the dataset. If the data object
5150      * used by the Reader contains the dataset size, then the getTotalCount() function returns
5151      * the data set size</em>
5152      */
5153     getCount : function(){
5154         return this.data.length || 0;
5155     },
5156
5157     /**
5158      * Gets the total number of records in the dataset as returned by the server.
5159      * <p>
5160      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
5161      * the dataset size</em>
5162      */
5163     getTotalCount : function(){
5164         return this.totalLength || 0;
5165     },
5166
5167     /**
5168      * Returns the sort state of the Store as an object with two properties:
5169      * <pre><code>
5170  field {String} The name of the field by which the Records are sorted
5171  direction {String} The sort order, "ASC" or "DESC"
5172      * </code></pre>
5173      */
5174     getSortState : function(){
5175         return this.sortInfo;
5176     },
5177
5178     // private
5179     applySort : function(){
5180         if(this.sortInfo && !this.remoteSort){
5181             var s = this.sortInfo, f = s.field;
5182             var st = this.fields.get(f).sortType;
5183             var fn = function(r1, r2){
5184                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
5185                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
5186             };
5187             this.data.sort(s.direction, fn);
5188             if(this.snapshot && this.snapshot != this.data){
5189                 this.snapshot.sort(s.direction, fn);
5190             }
5191         }
5192     },
5193
5194     /**
5195      * Sets the default sort column and order to be used by the next load operation.
5196      * @param {String} fieldName The name of the field to sort by.
5197      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5198      */
5199     setDefaultSort : function(field, dir){
5200         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
5201     },
5202
5203     /**
5204      * Sort the Records.
5205      * If remote sorting is used, the sort is performed on the server, and the cache is
5206      * reloaded. If local sorting is used, the cache is sorted internally.
5207      * @param {String} fieldName The name of the field to sort by.
5208      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5209      */
5210     sort : function(fieldName, dir){
5211         var f = this.fields.get(fieldName);
5212         if(!dir){
5213             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
5214             
5215             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
5216                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
5217             }else{
5218                 dir = f.sortDir;
5219             }
5220         }
5221         this.sortToggle[f.name] = dir;
5222         this.sortInfo = {field: f.name, direction: dir};
5223         if(!this.remoteSort){
5224             this.applySort();
5225             this.fireEvent("datachanged", this);
5226         }else{
5227             this.load(this.lastOptions);
5228         }
5229     },
5230
5231     /**
5232      * Calls the specified function for each of the Records in the cache.
5233      * @param {Function} fn The function to call. The Record is passed as the first parameter.
5234      * Returning <em>false</em> aborts and exits the iteration.
5235      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
5236      */
5237     each : function(fn, scope){
5238         this.data.each(fn, scope);
5239     },
5240
5241     /**
5242      * Gets all records modified since the last commit.  Modified records are persisted across load operations
5243      * (e.g., during paging).
5244      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
5245      */
5246     getModifiedRecords : function(){
5247         return this.modified;
5248     },
5249
5250     // private
5251     createFilterFn : function(property, value, anyMatch){
5252         if(!value.exec){ // not a regex
5253             value = String(value);
5254             if(value.length == 0){
5255                 return false;
5256             }
5257             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
5258         }
5259         return function(r){
5260             return value.test(r.data[property]);
5261         };
5262     },
5263
5264     /**
5265      * Sums the value of <i>property</i> for each record between start and end and returns the result.
5266      * @param {String} property A field on your records
5267      * @param {Number} start The record index to start at (defaults to 0)
5268      * @param {Number} end The last record index to include (defaults to length - 1)
5269      * @return {Number} The sum
5270      */
5271     sum : function(property, start, end){
5272         var rs = this.data.items, v = 0;
5273         start = start || 0;
5274         end = (end || end === 0) ? end : rs.length-1;
5275
5276         for(var i = start; i <= end; i++){
5277             v += (rs[i].data[property] || 0);
5278         }
5279         return v;
5280     },
5281
5282     /**
5283      * Filter the records by a specified property.
5284      * @param {String} field A field on your records
5285      * @param {String/RegExp} value Either a string that the field
5286      * should start with or a RegExp to test against the field
5287      * @param {Boolean} anyMatch True to match any part not just the beginning
5288      */
5289     filter : function(property, value, anyMatch){
5290         var fn = this.createFilterFn(property, value, anyMatch);
5291         return fn ? this.filterBy(fn) : this.clearFilter();
5292     },
5293
5294     /**
5295      * Filter by a function. The specified function will be called with each
5296      * record in this data source. If the function returns true the record is included,
5297      * otherwise it is filtered.
5298      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5299      * @param {Object} scope (optional) The scope of the function (defaults to this)
5300      */
5301     filterBy : function(fn, scope){
5302         this.snapshot = this.snapshot || this.data;
5303         this.data = this.queryBy(fn, scope||this);
5304         this.fireEvent("datachanged", this);
5305     },
5306
5307     /**
5308      * Query the records by a specified property.
5309      * @param {String} field A field on your records
5310      * @param {String/RegExp} value Either a string that the field
5311      * should start with or a RegExp to test against the field
5312      * @param {Boolean} anyMatch True to match any part not just the beginning
5313      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5314      */
5315     query : function(property, value, anyMatch){
5316         var fn = this.createFilterFn(property, value, anyMatch);
5317         return fn ? this.queryBy(fn) : this.data.clone();
5318     },
5319
5320     /**
5321      * Query by a function. The specified function will be called with each
5322      * record in this data source. If the function returns true the record is included
5323      * in the results.
5324      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5325      * @param {Object} scope (optional) The scope of the function (defaults to this)
5326       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5327      **/
5328     queryBy : function(fn, scope){
5329         var data = this.snapshot || this.data;
5330         return data.filterBy(fn, scope||this);
5331     },
5332
5333     /**
5334      * Collects unique values for a particular dataIndex from this store.
5335      * @param {String} dataIndex The property to collect
5336      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
5337      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
5338      * @return {Array} An array of the unique values
5339      **/
5340     collect : function(dataIndex, allowNull, bypassFilter){
5341         var d = (bypassFilter === true && this.snapshot) ?
5342                 this.snapshot.items : this.data.items;
5343         var v, sv, r = [], l = {};
5344         for(var i = 0, len = d.length; i < len; i++){
5345             v = d[i].data[dataIndex];
5346             sv = String(v);
5347             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
5348                 l[sv] = true;
5349                 r[r.length] = v;
5350             }
5351         }
5352         return r;
5353     },
5354
5355     /**
5356      * Revert to a view of the Record cache with no filtering applied.
5357      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
5358      */
5359     clearFilter : function(suppressEvent){
5360         if(this.snapshot && this.snapshot != this.data){
5361             this.data = this.snapshot;
5362             delete this.snapshot;
5363             if(suppressEvent !== true){
5364                 this.fireEvent("datachanged", this);
5365             }
5366         }
5367     },
5368
5369     // private
5370     afterEdit : function(record){
5371         if(this.modified.indexOf(record) == -1){
5372             this.modified.push(record);
5373         }
5374         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
5375     },
5376     
5377     // private
5378     afterReject : function(record){
5379         this.modified.remove(record);
5380         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
5381     },
5382
5383     // private
5384     afterCommit : function(record){
5385         this.modified.remove(record);
5386         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
5387     },
5388
5389     /**
5390      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
5391      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
5392      */
5393     commitChanges : function(){
5394         var m = this.modified.slice(0);
5395         this.modified = [];
5396         for(var i = 0, len = m.length; i < len; i++){
5397             m[i].commit();
5398         }
5399     },
5400
5401     /**
5402      * Cancel outstanding changes on all changed records.
5403      */
5404     rejectChanges : function(){
5405         var m = this.modified.slice(0);
5406         this.modified = [];
5407         for(var i = 0, len = m.length; i < len; i++){
5408             m[i].reject();
5409         }
5410     },
5411
5412     onMetaChange : function(meta, rtype, o){
5413         this.recordType = rtype;
5414         this.fields = rtype.prototype.fields;
5415         delete this.snapshot;
5416         this.sortInfo = meta.sortInfo || this.sortInfo;
5417         this.modified = [];
5418         this.fireEvent('metachange', this, this.reader.meta);
5419     }
5420 });/*
5421  * Based on:
5422  * Ext JS Library 1.1.1
5423  * Copyright(c) 2006-2007, Ext JS, LLC.
5424  *
5425  * Originally Released Under LGPL - original licence link has changed is not relivant.
5426  *
5427  * Fork - LGPL
5428  * <script type="text/javascript">
5429  */
5430
5431 /**
5432  * @class Roo.data.SimpleStore
5433  * @extends Roo.data.Store
5434  * Small helper class to make creating Stores from Array data easier.
5435  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
5436  * @cfg {Array} fields An array of field definition objects, or field name strings.
5437  * @cfg {Array} data The multi-dimensional array of data
5438  * @constructor
5439  * @param {Object} config
5440  */
5441 Roo.data.SimpleStore = function(config){
5442     Roo.data.SimpleStore.superclass.constructor.call(this, {
5443         isLocal : true,
5444         reader: new Roo.data.ArrayReader({
5445                 id: config.id
5446             },
5447             Roo.data.Record.create(config.fields)
5448         ),
5449         proxy : new Roo.data.MemoryProxy(config.data)
5450     });
5451     this.load();
5452 };
5453 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
5454  * Based on:
5455  * Ext JS Library 1.1.1
5456  * Copyright(c) 2006-2007, Ext JS, LLC.
5457  *
5458  * Originally Released Under LGPL - original licence link has changed is not relivant.
5459  *
5460  * Fork - LGPL
5461  * <script type="text/javascript">
5462  */
5463
5464 /**
5465 /**
5466  * @extends Roo.data.Store
5467  * @class Roo.data.JsonStore
5468  * Small helper class to make creating Stores for JSON data easier. <br/>
5469 <pre><code>
5470 var store = new Roo.data.JsonStore({
5471     url: 'get-images.php',
5472     root: 'images',
5473     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
5474 });
5475 </code></pre>
5476  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
5477  * JsonReader and HttpProxy (unless inline data is provided).</b>
5478  * @cfg {Array} fields An array of field definition objects, or field name strings.
5479  * @constructor
5480  * @param {Object} config
5481  */
5482 Roo.data.JsonStore = function(c){
5483     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
5484         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
5485         reader: new Roo.data.JsonReader(c, c.fields)
5486     }));
5487 };
5488 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
5489  * Based on:
5490  * Ext JS Library 1.1.1
5491  * Copyright(c) 2006-2007, Ext JS, LLC.
5492  *
5493  * Originally Released Under LGPL - original licence link has changed is not relivant.
5494  *
5495  * Fork - LGPL
5496  * <script type="text/javascript">
5497  */
5498
5499  
5500 Roo.data.Field = function(config){
5501     if(typeof config == "string"){
5502         config = {name: config};
5503     }
5504     Roo.apply(this, config);
5505     
5506     if(!this.type){
5507         this.type = "auto";
5508     }
5509     
5510     var st = Roo.data.SortTypes;
5511     // named sortTypes are supported, here we look them up
5512     if(typeof this.sortType == "string"){
5513         this.sortType = st[this.sortType];
5514     }
5515     
5516     // set default sortType for strings and dates
5517     if(!this.sortType){
5518         switch(this.type){
5519             case "string":
5520                 this.sortType = st.asUCString;
5521                 break;
5522             case "date":
5523                 this.sortType = st.asDate;
5524                 break;
5525             default:
5526                 this.sortType = st.none;
5527         }
5528     }
5529
5530     // define once
5531     var stripRe = /[\$,%]/g;
5532
5533     // prebuilt conversion function for this field, instead of
5534     // switching every time we're reading a value
5535     if(!this.convert){
5536         var cv, dateFormat = this.dateFormat;
5537         switch(this.type){
5538             case "":
5539             case "auto":
5540             case undefined:
5541                 cv = function(v){ return v; };
5542                 break;
5543             case "string":
5544                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
5545                 break;
5546             case "int":
5547                 cv = function(v){
5548                     return v !== undefined && v !== null && v !== '' ?
5549                            parseInt(String(v).replace(stripRe, ""), 10) : '';
5550                     };
5551                 break;
5552             case "float":
5553                 cv = function(v){
5554                     return v !== undefined && v !== null && v !== '' ?
5555                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
5556                     };
5557                 break;
5558             case "bool":
5559             case "boolean":
5560                 cv = function(v){ return v === true || v === "true" || v == 1; };
5561                 break;
5562             case "date":
5563                 cv = function(v){
5564                     if(!v){
5565                         return '';
5566                     }
5567                     if(v instanceof Date){
5568                         return v;
5569                     }
5570                     if(dateFormat){
5571                         if(dateFormat == "timestamp"){
5572                             return new Date(v*1000);
5573                         }
5574                         return Date.parseDate(v, dateFormat);
5575                     }
5576                     var parsed = Date.parse(v);
5577                     return parsed ? new Date(parsed) : null;
5578                 };
5579              break;
5580             
5581         }
5582         this.convert = cv;
5583     }
5584 };
5585
5586 Roo.data.Field.prototype = {
5587     dateFormat: null,
5588     defaultValue: "",
5589     mapping: null,
5590     sortType : null,
5591     sortDir : "ASC"
5592 };/*
5593  * Based on:
5594  * Ext JS Library 1.1.1
5595  * Copyright(c) 2006-2007, Ext JS, LLC.
5596  *
5597  * Originally Released Under LGPL - original licence link has changed is not relivant.
5598  *
5599  * Fork - LGPL
5600  * <script type="text/javascript">
5601  */
5602  
5603 // Base class for reading structured data from a data source.  This class is intended to be
5604 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
5605
5606 /**
5607  * @class Roo.data.DataReader
5608  * Base class for reading structured data from a data source.  This class is intended to be
5609  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
5610  */
5611
5612 Roo.data.DataReader = function(meta, recordType){
5613     
5614     this.meta = meta;
5615     
5616     this.recordType = recordType instanceof Array ? 
5617         Roo.data.Record.create(recordType) : recordType;
5618 };
5619
5620 Roo.data.DataReader.prototype = {
5621      /**
5622      * Create an empty record
5623      * @param {Object} data (optional) - overlay some values
5624      * @return {Roo.data.Record} record created.
5625      */
5626     newRow :  function(d) {
5627         var da =  {};
5628         this.recordType.prototype.fields.each(function(c) {
5629             switch( c.type) {
5630                 case 'int' : da[c.name] = 0; break;
5631                 case 'date' : da[c.name] = new Date(); break;
5632                 case 'float' : da[c.name] = 0.0; break;
5633                 case 'boolean' : da[c.name] = false; break;
5634                 default : da[c.name] = ""; break;
5635             }
5636             
5637         });
5638         return new this.recordType(Roo.apply(da, d));
5639     }
5640     
5641 };/*
5642  * Based on:
5643  * Ext JS Library 1.1.1
5644  * Copyright(c) 2006-2007, Ext JS, LLC.
5645  *
5646  * Originally Released Under LGPL - original licence link has changed is not relivant.
5647  *
5648  * Fork - LGPL
5649  * <script type="text/javascript">
5650  */
5651
5652 /**
5653  * @class Roo.data.DataProxy
5654  * @extends Roo.data.Observable
5655  * This class is an abstract base class for implementations which provide retrieval of
5656  * unformatted data objects.<br>
5657  * <p>
5658  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
5659  * (of the appropriate type which knows how to parse the data object) to provide a block of
5660  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
5661  * <p>
5662  * Custom implementations must implement the load method as described in
5663  * {@link Roo.data.HttpProxy#load}.
5664  */
5665 Roo.data.DataProxy = function(){
5666     this.addEvents({
5667         /**
5668          * @event beforeload
5669          * Fires before a network request is made to retrieve a data object.
5670          * @param {Object} This DataProxy object.
5671          * @param {Object} params The params parameter to the load function.
5672          */
5673         beforeload : true,
5674         /**
5675          * @event load
5676          * Fires before the load method's callback is called.
5677          * @param {Object} This DataProxy object.
5678          * @param {Object} o The data object.
5679          * @param {Object} arg The callback argument object passed to the load function.
5680          */
5681         load : true,
5682         /**
5683          * @event loadexception
5684          * Fires if an Exception occurs during data retrieval.
5685          * @param {Object} This DataProxy object.
5686          * @param {Object} o The data object.
5687          * @param {Object} arg The callback argument object passed to the load function.
5688          * @param {Object} e The Exception.
5689          */
5690         loadexception : true
5691     });
5692     Roo.data.DataProxy.superclass.constructor.call(this);
5693 };
5694
5695 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
5696
5697     /**
5698      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
5699      */
5700 /*
5701  * Based on:
5702  * Ext JS Library 1.1.1
5703  * Copyright(c) 2006-2007, Ext JS, LLC.
5704  *
5705  * Originally Released Under LGPL - original licence link has changed is not relivant.
5706  *
5707  * Fork - LGPL
5708  * <script type="text/javascript">
5709  */
5710 /**
5711  * @class Roo.data.MemoryProxy
5712  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
5713  * to the Reader when its load method is called.
5714  * @constructor
5715  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
5716  */
5717 Roo.data.MemoryProxy = function(data){
5718     if (data.data) {
5719         data = data.data;
5720     }
5721     Roo.data.MemoryProxy.superclass.constructor.call(this);
5722     this.data = data;
5723 };
5724
5725 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
5726     /**
5727      * Load data from the requested source (in this case an in-memory
5728      * data object passed to the constructor), read the data object into
5729      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5730      * process that block using the passed callback.
5731      * @param {Object} params This parameter is not used by the MemoryProxy class.
5732      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5733      * object into a block of Roo.data.Records.
5734      * @param {Function} callback The function into which to pass the block of Roo.data.records.
5735      * The function must be passed <ul>
5736      * <li>The Record block object</li>
5737      * <li>The "arg" argument from the load function</li>
5738      * <li>A boolean success indicator</li>
5739      * </ul>
5740      * @param {Object} scope The scope in which to call the callback
5741      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5742      */
5743     load : function(params, reader, callback, scope, arg){
5744         params = params || {};
5745         var result;
5746         try {
5747             result = reader.readRecords(this.data);
5748         }catch(e){
5749             this.fireEvent("loadexception", this, arg, null, e);
5750             callback.call(scope, null, arg, false);
5751             return;
5752         }
5753         callback.call(scope, result, arg, true);
5754     },
5755     
5756     // private
5757     update : function(params, records){
5758         
5759     }
5760 });/*
5761  * Based on:
5762  * Ext JS Library 1.1.1
5763  * Copyright(c) 2006-2007, Ext JS, LLC.
5764  *
5765  * Originally Released Under LGPL - original licence link has changed is not relivant.
5766  *
5767  * Fork - LGPL
5768  * <script type="text/javascript">
5769  */
5770 /**
5771  * @class Roo.data.HttpProxy
5772  * @extends Roo.data.DataProxy
5773  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
5774  * configured to reference a certain URL.<br><br>
5775  * <p>
5776  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
5777  * from which the running page was served.<br><br>
5778  * <p>
5779  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
5780  * <p>
5781  * Be aware that to enable the browser to parse an XML document, the server must set
5782  * the Content-Type header in the HTTP response to "text/xml".
5783  * @constructor
5784  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
5785  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
5786  * will be used to make the request.
5787  */
5788 Roo.data.HttpProxy = function(conn){
5789     Roo.data.HttpProxy.superclass.constructor.call(this);
5790     // is conn a conn config or a real conn?
5791     this.conn = conn;
5792     this.useAjax = !conn || !conn.events;
5793   
5794 };
5795
5796 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
5797     // thse are take from connection...
5798     
5799     /**
5800      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
5801      */
5802     /**
5803      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
5804      * extra parameters to each request made by this object. (defaults to undefined)
5805      */
5806     /**
5807      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
5808      *  to each request made by this object. (defaults to undefined)
5809      */
5810     /**
5811      * @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)
5812      */
5813     /**
5814      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
5815      */
5816      /**
5817      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
5818      * @type Boolean
5819      */
5820   
5821
5822     /**
5823      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
5824      * @type Boolean
5825      */
5826     /**
5827      * Return the {@link Roo.data.Connection} object being used by this Proxy.
5828      * @return {Connection} The Connection object. This object may be used to subscribe to events on
5829      * a finer-grained basis than the DataProxy events.
5830      */
5831     getConnection : function(){
5832         return this.useAjax ? Roo.Ajax : this.conn;
5833     },
5834
5835     /**
5836      * Load data from the configured {@link Roo.data.Connection}, read the data object into
5837      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
5838      * process that block using the passed callback.
5839      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5840      * for the request to the remote server.
5841      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5842      * object into a block of Roo.data.Records.
5843      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5844      * The function must be passed <ul>
5845      * <li>The Record block object</li>
5846      * <li>The "arg" argument from the load function</li>
5847      * <li>A boolean success indicator</li>
5848      * </ul>
5849      * @param {Object} scope The scope in which to call the callback
5850      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5851      */
5852     load : function(params, reader, callback, scope, arg){
5853         if(this.fireEvent("beforeload", this, params) !== false){
5854             var  o = {
5855                 params : params || {},
5856                 request: {
5857                     callback : callback,
5858                     scope : scope,
5859                     arg : arg
5860                 },
5861                 reader: reader,
5862                 callback : this.loadResponse,
5863                 scope: this
5864             };
5865             if(this.useAjax){
5866                 Roo.applyIf(o, this.conn);
5867                 if(this.activeRequest){
5868                     Roo.Ajax.abort(this.activeRequest);
5869                 }
5870                 this.activeRequest = Roo.Ajax.request(o);
5871             }else{
5872                 this.conn.request(o);
5873             }
5874         }else{
5875             callback.call(scope||this, null, arg, false);
5876         }
5877     },
5878
5879     // private
5880     loadResponse : function(o, success, response){
5881         delete this.activeRequest;
5882         if(!success){
5883             this.fireEvent("loadexception", this, o, response);
5884             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5885             return;
5886         }
5887         var result;
5888         try {
5889             result = o.reader.read(response);
5890         }catch(e){
5891             this.fireEvent("loadexception", this, o, response, e);
5892             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5893             return;
5894         }
5895         
5896         this.fireEvent("load", this, o, o.request.arg);
5897         o.request.callback.call(o.request.scope, result, o.request.arg, true);
5898     },
5899
5900     // private
5901     update : function(dataSet){
5902
5903     },
5904
5905     // private
5906     updateResponse : function(dataSet){
5907
5908     }
5909 });/*
5910  * Based on:
5911  * Ext JS Library 1.1.1
5912  * Copyright(c) 2006-2007, Ext JS, LLC.
5913  *
5914  * Originally Released Under LGPL - original licence link has changed is not relivant.
5915  *
5916  * Fork - LGPL
5917  * <script type="text/javascript">
5918  */
5919
5920 /**
5921  * @class Roo.data.ScriptTagProxy
5922  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
5923  * other than the originating domain of the running page.<br><br>
5924  * <p>
5925  * <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
5926  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
5927  * <p>
5928  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
5929  * source code that is used as the source inside a &lt;script> tag.<br><br>
5930  * <p>
5931  * In order for the browser to process the returned data, the server must wrap the data object
5932  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
5933  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
5934  * depending on whether the callback name was passed:
5935  * <p>
5936  * <pre><code>
5937 boolean scriptTag = false;
5938 String cb = request.getParameter("callback");
5939 if (cb != null) {
5940     scriptTag = true;
5941     response.setContentType("text/javascript");
5942 } else {
5943     response.setContentType("application/x-json");
5944 }
5945 Writer out = response.getWriter();
5946 if (scriptTag) {
5947     out.write(cb + "(");
5948 }
5949 out.print(dataBlock.toJsonString());
5950 if (scriptTag) {
5951     out.write(");");
5952 }
5953 </pre></code>
5954  *
5955  * @constructor
5956  * @param {Object} config A configuration object.
5957  */
5958 Roo.data.ScriptTagProxy = function(config){
5959     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
5960     Roo.apply(this, config);
5961     this.head = document.getElementsByTagName("head")[0];
5962 };
5963
5964 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
5965
5966 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
5967     /**
5968      * @cfg {String} url The URL from which to request the data object.
5969      */
5970     /**
5971      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
5972      */
5973     timeout : 30000,
5974     /**
5975      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
5976      * the server the name of the callback function set up by the load call to process the returned data object.
5977      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
5978      * javascript output which calls this named function passing the data object as its only parameter.
5979      */
5980     callbackParam : "callback",
5981     /**
5982      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
5983      * name to the request.
5984      */
5985     nocache : true,
5986
5987     /**
5988      * Load data from the configured URL, read the data object into
5989      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5990      * process that block using the passed callback.
5991      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5992      * for the request to the remote server.
5993      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5994      * object into a block of Roo.data.Records.
5995      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5996      * The function must be passed <ul>
5997      * <li>The Record block object</li>
5998      * <li>The "arg" argument from the load function</li>
5999      * <li>A boolean success indicator</li>
6000      * </ul>
6001      * @param {Object} scope The scope in which to call the callback
6002      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
6003      */
6004     load : function(params, reader, callback, scope, arg){
6005         if(this.fireEvent("beforeload", this, params) !== false){
6006
6007             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
6008
6009             var url = this.url;
6010             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
6011             if(this.nocache){
6012                 url += "&_dc=" + (new Date().getTime());
6013             }
6014             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
6015             var trans = {
6016                 id : transId,
6017                 cb : "stcCallback"+transId,
6018                 scriptId : "stcScript"+transId,
6019                 params : params,
6020                 arg : arg,
6021                 url : url,
6022                 callback : callback,
6023                 scope : scope,
6024                 reader : reader
6025             };
6026             var conn = this;
6027
6028             window[trans.cb] = function(o){
6029                 conn.handleResponse(o, trans);
6030             };
6031
6032             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
6033
6034             if(this.autoAbort !== false){
6035                 this.abort();
6036             }
6037
6038             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
6039
6040             var script = document.createElement("script");
6041             script.setAttribute("src", url);
6042             script.setAttribute("type", "text/javascript");
6043             script.setAttribute("id", trans.scriptId);
6044             this.head.appendChild(script);
6045
6046             this.trans = trans;
6047         }else{
6048             callback.call(scope||this, null, arg, false);
6049         }
6050     },
6051
6052     // private
6053     isLoading : function(){
6054         return this.trans ? true : false;
6055     },
6056
6057     /**
6058      * Abort the current server request.
6059      */
6060     abort : function(){
6061         if(this.isLoading()){
6062             this.destroyTrans(this.trans);
6063         }
6064     },
6065
6066     // private
6067     destroyTrans : function(trans, isLoaded){
6068         this.head.removeChild(document.getElementById(trans.scriptId));
6069         clearTimeout(trans.timeoutId);
6070         if(isLoaded){
6071             window[trans.cb] = undefined;
6072             try{
6073                 delete window[trans.cb];
6074             }catch(e){}
6075         }else{
6076             // if hasn't been loaded, wait for load to remove it to prevent script error
6077             window[trans.cb] = function(){
6078                 window[trans.cb] = undefined;
6079                 try{
6080                     delete window[trans.cb];
6081                 }catch(e){}
6082             };
6083         }
6084     },
6085
6086     // private
6087     handleResponse : function(o, trans){
6088         this.trans = false;
6089         this.destroyTrans(trans, true);
6090         var result;
6091         try {
6092             result = trans.reader.readRecords(o);
6093         }catch(e){
6094             this.fireEvent("loadexception", this, o, trans.arg, e);
6095             trans.callback.call(trans.scope||window, null, trans.arg, false);
6096             return;
6097         }
6098         this.fireEvent("load", this, o, trans.arg);
6099         trans.callback.call(trans.scope||window, result, trans.arg, true);
6100     },
6101
6102     // private
6103     handleFailure : function(trans){
6104         this.trans = false;
6105         this.destroyTrans(trans, false);
6106         this.fireEvent("loadexception", this, null, trans.arg);
6107         trans.callback.call(trans.scope||window, null, trans.arg, false);
6108     }
6109 });/*
6110  * Based on:
6111  * Ext JS Library 1.1.1
6112  * Copyright(c) 2006-2007, Ext JS, LLC.
6113  *
6114  * Originally Released Under LGPL - original licence link has changed is not relivant.
6115  *
6116  * Fork - LGPL
6117  * <script type="text/javascript">
6118  */
6119
6120 /**
6121  * @class Roo.data.JsonReader
6122  * @extends Roo.data.DataReader
6123  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
6124  * based on mappings in a provided Roo.data.Record constructor.
6125  * 
6126  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
6127  * in the reply previously. 
6128  * 
6129  * <p>
6130  * Example code:
6131  * <pre><code>
6132 var RecordDef = Roo.data.Record.create([
6133     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6134     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6135 ]);
6136 var myReader = new Roo.data.JsonReader({
6137     totalProperty: "results",    // The property which contains the total dataset size (optional)
6138     root: "rows",                // The property which contains an Array of row objects
6139     id: "id"                     // The property within each row object that provides an ID for the record (optional)
6140 }, RecordDef);
6141 </code></pre>
6142  * <p>
6143  * This would consume a JSON file like this:
6144  * <pre><code>
6145 { 'results': 2, 'rows': [
6146     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
6147     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
6148 }
6149 </code></pre>
6150  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
6151  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6152  * paged from the remote server.
6153  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
6154  * @cfg {String} root name of the property which contains the Array of row objects.
6155  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
6156  * @constructor
6157  * Create a new JsonReader
6158  * @param {Object} meta Metadata configuration options
6159  * @param {Object} recordType Either an Array of field definition objects,
6160  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
6161  */
6162 Roo.data.JsonReader = function(meta, recordType){
6163     
6164     meta = meta || {};
6165     // set some defaults:
6166     Roo.applyIf(meta, {
6167         totalProperty: 'total',
6168         successProperty : 'success',
6169         root : 'data',
6170         id : 'id'
6171     });
6172     
6173     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6174 };
6175 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
6176     
6177     /**
6178      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
6179      * Used by Store query builder to append _requestMeta to params.
6180      * 
6181      */
6182     metaFromRemote : false,
6183     /**
6184      * This method is only used by a DataProxy which has retrieved data from a remote server.
6185      * @param {Object} response The XHR object which contains the JSON data in its responseText.
6186      * @return {Object} data A data block which is used by an Roo.data.Store object as
6187      * a cache of Roo.data.Records.
6188      */
6189     read : function(response){
6190         var json = response.responseText;
6191        
6192         var o = /* eval:var:o */ eval("("+json+")");
6193         if(!o) {
6194             throw {message: "JsonReader.read: Json object not found"};
6195         }
6196         
6197         if(o.metaData){
6198             
6199             delete this.ef;
6200             this.metaFromRemote = true;
6201             this.meta = o.metaData;
6202             this.recordType = Roo.data.Record.create(o.metaData.fields);
6203             this.onMetaChange(this.meta, this.recordType, o);
6204         }
6205         return this.readRecords(o);
6206     },
6207
6208     // private function a store will implement
6209     onMetaChange : function(meta, recordType, o){
6210
6211     },
6212
6213     /**
6214          * @ignore
6215          */
6216     simpleAccess: function(obj, subsc) {
6217         return obj[subsc];
6218     },
6219
6220         /**
6221          * @ignore
6222          */
6223     getJsonAccessor: function(){
6224         var re = /[\[\.]/;
6225         return function(expr) {
6226             try {
6227                 return(re.test(expr))
6228                     ? new Function("obj", "return obj." + expr)
6229                     : function(obj){
6230                         return obj[expr];
6231                     };
6232             } catch(e){}
6233             return Roo.emptyFn;
6234         };
6235     }(),
6236
6237     /**
6238      * Create a data block containing Roo.data.Records from an XML document.
6239      * @param {Object} o An object which contains an Array of row objects in the property specified
6240      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
6241      * which contains the total size of the dataset.
6242      * @return {Object} data A data block which is used by an Roo.data.Store object as
6243      * a cache of Roo.data.Records.
6244      */
6245     readRecords : function(o){
6246         /**
6247          * After any data loads, the raw JSON data is available for further custom processing.
6248          * @type Object
6249          */
6250         this.o = o;
6251         var s = this.meta, Record = this.recordType,
6252             f = Record.prototype.fields, fi = f.items, fl = f.length;
6253
6254 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
6255         if (!this.ef) {
6256             if(s.totalProperty) {
6257                     this.getTotal = this.getJsonAccessor(s.totalProperty);
6258                 }
6259                 if(s.successProperty) {
6260                     this.getSuccess = this.getJsonAccessor(s.successProperty);
6261                 }
6262                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
6263                 if (s.id) {
6264                         var g = this.getJsonAccessor(s.id);
6265                         this.getId = function(rec) {
6266                                 var r = g(rec);
6267                                 return (r === undefined || r === "") ? null : r;
6268                         };
6269                 } else {
6270                         this.getId = function(){return null;};
6271                 }
6272             this.ef = [];
6273             for(var jj = 0; jj < fl; jj++){
6274                 f = fi[jj];
6275                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
6276                 this.ef[jj] = this.getJsonAccessor(map);
6277             }
6278         }
6279
6280         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
6281         if(s.totalProperty){
6282             var vt = parseInt(this.getTotal(o), 10);
6283             if(!isNaN(vt)){
6284                 totalRecords = vt;
6285             }
6286         }
6287         if(s.successProperty){
6288             var vs = this.getSuccess(o);
6289             if(vs === false || vs === 'false'){
6290                 success = false;
6291             }
6292         }
6293         var records = [];
6294             for(var i = 0; i < c; i++){
6295                     var n = root[i];
6296                 var values = {};
6297                 var id = this.getId(n);
6298                 for(var j = 0; j < fl; j++){
6299                     f = fi[j];
6300                 var v = this.ef[j](n);
6301                 if (!f.convert) {
6302                     Roo.log('missing convert for ' + f.name);
6303                     Roo.log(f);
6304                     continue;
6305                 }
6306                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
6307                 }
6308                 var record = new Record(values, id);
6309                 record.json = n;
6310                 records[i] = record;
6311             }
6312             return {
6313             raw : o,
6314                 success : success,
6315                 records : records,
6316                 totalRecords : totalRecords
6317             };
6318     }
6319 });/*
6320  * Based on:
6321  * Ext JS Library 1.1.1
6322  * Copyright(c) 2006-2007, Ext JS, LLC.
6323  *
6324  * Originally Released Under LGPL - original licence link has changed is not relivant.
6325  *
6326  * Fork - LGPL
6327  * <script type="text/javascript">
6328  */
6329
6330 /**
6331  * @class Roo.data.XmlReader
6332  * @extends Roo.data.DataReader
6333  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
6334  * based on mappings in a provided Roo.data.Record constructor.<br><br>
6335  * <p>
6336  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
6337  * header in the HTTP response must be set to "text/xml".</em>
6338  * <p>
6339  * Example code:
6340  * <pre><code>
6341 var RecordDef = Roo.data.Record.create([
6342    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6343    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6344 ]);
6345 var myReader = new Roo.data.XmlReader({
6346    totalRecords: "results", // The element which contains the total dataset size (optional)
6347    record: "row",           // The repeated element which contains row information
6348    id: "id"                 // The element within the row that provides an ID for the record (optional)
6349 }, RecordDef);
6350 </code></pre>
6351  * <p>
6352  * This would consume an XML file like this:
6353  * <pre><code>
6354 &lt;?xml?>
6355 &lt;dataset>
6356  &lt;results>2&lt;/results>
6357  &lt;row>
6358    &lt;id>1&lt;/id>
6359    &lt;name>Bill&lt;/name>
6360    &lt;occupation>Gardener&lt;/occupation>
6361  &lt;/row>
6362  &lt;row>
6363    &lt;id>2&lt;/id>
6364    &lt;name>Ben&lt;/name>
6365    &lt;occupation>Horticulturalist&lt;/occupation>
6366  &lt;/row>
6367 &lt;/dataset>
6368 </code></pre>
6369  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
6370  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6371  * paged from the remote server.
6372  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
6373  * @cfg {String} success The DomQuery path to the success attribute used by forms.
6374  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
6375  * a record identifier value.
6376  * @constructor
6377  * Create a new XmlReader
6378  * @param {Object} meta Metadata configuration options
6379  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
6380  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
6381  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
6382  */
6383 Roo.data.XmlReader = function(meta, recordType){
6384     meta = meta || {};
6385     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6386 };
6387 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
6388     /**
6389      * This method is only used by a DataProxy which has retrieved data from a remote server.
6390          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
6391          * to contain a method called 'responseXML' that returns an XML document object.
6392      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6393      * a cache of Roo.data.Records.
6394      */
6395     read : function(response){
6396         var doc = response.responseXML;
6397         if(!doc) {
6398             throw {message: "XmlReader.read: XML Document not available"};
6399         }
6400         return this.readRecords(doc);
6401     },
6402
6403     /**
6404      * Create a data block containing Roo.data.Records from an XML document.
6405          * @param {Object} doc A parsed XML document.
6406      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6407      * a cache of Roo.data.Records.
6408      */
6409     readRecords : function(doc){
6410         /**
6411          * After any data loads/reads, the raw XML Document is available for further custom processing.
6412          * @type XMLDocument
6413          */
6414         this.xmlData = doc;
6415         var root = doc.documentElement || doc;
6416         var q = Roo.DomQuery;
6417         var recordType = this.recordType, fields = recordType.prototype.fields;
6418         var sid = this.meta.id;
6419         var totalRecords = 0, success = true;
6420         if(this.meta.totalRecords){
6421             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
6422         }
6423         
6424         if(this.meta.success){
6425             var sv = q.selectValue(this.meta.success, root, true);
6426             success = sv !== false && sv !== 'false';
6427         }
6428         var records = [];
6429         var ns = q.select(this.meta.record, root);
6430         for(var i = 0, len = ns.length; i < len; i++) {
6431                 var n = ns[i];
6432                 var values = {};
6433                 var id = sid ? q.selectValue(sid, n) : undefined;
6434                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6435                     var f = fields.items[j];
6436                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
6437                     v = f.convert(v);
6438                     values[f.name] = v;
6439                 }
6440                 var record = new recordType(values, id);
6441                 record.node = n;
6442                 records[records.length] = record;
6443             }
6444
6445             return {
6446                 success : success,
6447                 records : records,
6448                 totalRecords : totalRecords || records.length
6449             };
6450     }
6451 });/*
6452  * Based on:
6453  * Ext JS Library 1.1.1
6454  * Copyright(c) 2006-2007, Ext JS, LLC.
6455  *
6456  * Originally Released Under LGPL - original licence link has changed is not relivant.
6457  *
6458  * Fork - LGPL
6459  * <script type="text/javascript">
6460  */
6461
6462 /**
6463  * @class Roo.data.ArrayReader
6464  * @extends Roo.data.DataReader
6465  * Data reader class to create an Array of Roo.data.Record objects from an Array.
6466  * Each element of that Array represents a row of data fields. The
6467  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
6468  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
6469  * <p>
6470  * Example code:.
6471  * <pre><code>
6472 var RecordDef = Roo.data.Record.create([
6473     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
6474     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
6475 ]);
6476 var myReader = new Roo.data.ArrayReader({
6477     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
6478 }, RecordDef);
6479 </code></pre>
6480  * <p>
6481  * This would consume an Array like this:
6482  * <pre><code>
6483 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
6484   </code></pre>
6485  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
6486  * @constructor
6487  * Create a new JsonReader
6488  * @param {Object} meta Metadata configuration options.
6489  * @param {Object} recordType Either an Array of field definition objects
6490  * as specified to {@link Roo.data.Record#create},
6491  * or an {@link Roo.data.Record} object
6492  * created using {@link Roo.data.Record#create}.
6493  */
6494 Roo.data.ArrayReader = function(meta, recordType){
6495     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
6496 };
6497
6498 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
6499     /**
6500      * Create a data block containing Roo.data.Records from an XML document.
6501      * @param {Object} o An Array of row objects which represents the dataset.
6502      * @return {Object} data A data block which is used by an Roo.data.Store object as
6503      * a cache of Roo.data.Records.
6504      */
6505     readRecords : function(o){
6506         var sid = this.meta ? this.meta.id : null;
6507         var recordType = this.recordType, fields = recordType.prototype.fields;
6508         var records = [];
6509         var root = o;
6510             for(var i = 0; i < root.length; i++){
6511                     var n = root[i];
6512                 var values = {};
6513                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
6514                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6515                 var f = fields.items[j];
6516                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
6517                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
6518                 v = f.convert(v);
6519                 values[f.name] = v;
6520             }
6521                 var record = new recordType(values, id);
6522                 record.json = n;
6523                 records[records.length] = record;
6524             }
6525             return {
6526                 records : records,
6527                 totalRecords : records.length
6528             };
6529     }
6530 });/*
6531  * Based on:
6532  * Ext JS Library 1.1.1
6533  * Copyright(c) 2006-2007, Ext JS, LLC.
6534  *
6535  * Originally Released Under LGPL - original licence link has changed is not relivant.
6536  *
6537  * Fork - LGPL
6538  * <script type="text/javascript">
6539  */
6540
6541
6542 /**
6543  * @class Roo.data.Tree
6544  * @extends Roo.util.Observable
6545  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
6546  * in the tree have most standard DOM functionality.
6547  * @constructor
6548  * @param {Node} root (optional) The root node
6549  */
6550 Roo.data.Tree = function(root){
6551    this.nodeHash = {};
6552    /**
6553     * The root node for this tree
6554     * @type Node
6555     */
6556    this.root = null;
6557    if(root){
6558        this.setRootNode(root);
6559    }
6560    this.addEvents({
6561        /**
6562         * @event append
6563         * Fires when a new child node is appended to a node in this tree.
6564         * @param {Tree} tree The owner tree
6565         * @param {Node} parent The parent node
6566         * @param {Node} node The newly appended node
6567         * @param {Number} index The index of the newly appended node
6568         */
6569        "append" : true,
6570        /**
6571         * @event remove
6572         * Fires when a child node is removed from a node in this tree.
6573         * @param {Tree} tree The owner tree
6574         * @param {Node} parent The parent node
6575         * @param {Node} node The child node removed
6576         */
6577        "remove" : true,
6578        /**
6579         * @event move
6580         * Fires when a node is moved to a new location in the tree
6581         * @param {Tree} tree The owner tree
6582         * @param {Node} node The node moved
6583         * @param {Node} oldParent The old parent of this node
6584         * @param {Node} newParent The new parent of this node
6585         * @param {Number} index The index it was moved to
6586         */
6587        "move" : true,
6588        /**
6589         * @event insert
6590         * Fires when a new child node is inserted in a node in this tree.
6591         * @param {Tree} tree The owner tree
6592         * @param {Node} parent The parent node
6593         * @param {Node} node The child node inserted
6594         * @param {Node} refNode The child node the node was inserted before
6595         */
6596        "insert" : true,
6597        /**
6598         * @event beforeappend
6599         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
6600         * @param {Tree} tree The owner tree
6601         * @param {Node} parent The parent node
6602         * @param {Node} node The child node to be appended
6603         */
6604        "beforeappend" : true,
6605        /**
6606         * @event beforeremove
6607         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
6608         * @param {Tree} tree The owner tree
6609         * @param {Node} parent The parent node
6610         * @param {Node} node The child node to be removed
6611         */
6612        "beforeremove" : true,
6613        /**
6614         * @event beforemove
6615         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
6616         * @param {Tree} tree The owner tree
6617         * @param {Node} node The node being moved
6618         * @param {Node} oldParent The parent of the node
6619         * @param {Node} newParent The new parent the node is moving to
6620         * @param {Number} index The index it is being moved to
6621         */
6622        "beforemove" : true,
6623        /**
6624         * @event beforeinsert
6625         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
6626         * @param {Tree} tree The owner tree
6627         * @param {Node} parent The parent node
6628         * @param {Node} node The child node to be inserted
6629         * @param {Node} refNode The child node the node is being inserted before
6630         */
6631        "beforeinsert" : true
6632    });
6633
6634     Roo.data.Tree.superclass.constructor.call(this);
6635 };
6636
6637 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
6638     pathSeparator: "/",
6639
6640     proxyNodeEvent : function(){
6641         return this.fireEvent.apply(this, arguments);
6642     },
6643
6644     /**
6645      * Returns the root node for this tree.
6646      * @return {Node}
6647      */
6648     getRootNode : function(){
6649         return this.root;
6650     },
6651
6652     /**
6653      * Sets the root node for this tree.
6654      * @param {Node} node
6655      * @return {Node}
6656      */
6657     setRootNode : function(node){
6658         this.root = node;
6659         node.ownerTree = this;
6660         node.isRoot = true;
6661         this.registerNode(node);
6662         return node;
6663     },
6664
6665     /**
6666      * Gets a node in this tree by its id.
6667      * @param {String} id
6668      * @return {Node}
6669      */
6670     getNodeById : function(id){
6671         return this.nodeHash[id];
6672     },
6673
6674     registerNode : function(node){
6675         this.nodeHash[node.id] = node;
6676     },
6677
6678     unregisterNode : function(node){
6679         delete this.nodeHash[node.id];
6680     },
6681
6682     toString : function(){
6683         return "[Tree"+(this.id?" "+this.id:"")+"]";
6684     }
6685 });
6686
6687 /**
6688  * @class Roo.data.Node
6689  * @extends Roo.util.Observable
6690  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
6691  * @cfg {String} id The id for this node. If one is not specified, one is generated.
6692  * @constructor
6693  * @param {Object} attributes The attributes/config for the node
6694  */
6695 Roo.data.Node = function(attributes){
6696     /**
6697      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
6698      * @type {Object}
6699      */
6700     this.attributes = attributes || {};
6701     this.leaf = this.attributes.leaf;
6702     /**
6703      * The node id. @type String
6704      */
6705     this.id = this.attributes.id;
6706     if(!this.id){
6707         this.id = Roo.id(null, "ynode-");
6708         this.attributes.id = this.id;
6709     }
6710      
6711     
6712     /**
6713      * All child nodes of this node. @type Array
6714      */
6715     this.childNodes = [];
6716     if(!this.childNodes.indexOf){ // indexOf is a must
6717         this.childNodes.indexOf = function(o){
6718             for(var i = 0, len = this.length; i < len; i++){
6719                 if(this[i] == o) {
6720                     return i;
6721                 }
6722             }
6723             return -1;
6724         };
6725     }
6726     /**
6727      * The parent node for this node. @type Node
6728      */
6729     this.parentNode = null;
6730     /**
6731      * The first direct child node of this node, or null if this node has no child nodes. @type Node
6732      */
6733     this.firstChild = null;
6734     /**
6735      * The last direct child node of this node, or null if this node has no child nodes. @type Node
6736      */
6737     this.lastChild = null;
6738     /**
6739      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
6740      */
6741     this.previousSibling = null;
6742     /**
6743      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
6744      */
6745     this.nextSibling = null;
6746
6747     this.addEvents({
6748        /**
6749         * @event append
6750         * Fires when a new child node is appended
6751         * @param {Tree} tree The owner tree
6752         * @param {Node} this This node
6753         * @param {Node} node The newly appended node
6754         * @param {Number} index The index of the newly appended node
6755         */
6756        "append" : true,
6757        /**
6758         * @event remove
6759         * Fires when a child node is removed
6760         * @param {Tree} tree The owner tree
6761         * @param {Node} this This node
6762         * @param {Node} node The removed node
6763         */
6764        "remove" : true,
6765        /**
6766         * @event move
6767         * Fires when this node is moved to a new location in the tree
6768         * @param {Tree} tree The owner tree
6769         * @param {Node} this This node
6770         * @param {Node} oldParent The old parent of this node
6771         * @param {Node} newParent The new parent of this node
6772         * @param {Number} index The index it was moved to
6773         */
6774        "move" : true,
6775        /**
6776         * @event insert
6777         * Fires when a new child node is inserted.
6778         * @param {Tree} tree The owner tree
6779         * @param {Node} this This node
6780         * @param {Node} node The child node inserted
6781         * @param {Node} refNode The child node the node was inserted before
6782         */
6783        "insert" : true,
6784        /**
6785         * @event beforeappend
6786         * Fires before a new child is appended, return false to cancel the append.
6787         * @param {Tree} tree The owner tree
6788         * @param {Node} this This node
6789         * @param {Node} node The child node to be appended
6790         */
6791        "beforeappend" : true,
6792        /**
6793         * @event beforeremove
6794         * Fires before a child is removed, return false to cancel the remove.
6795         * @param {Tree} tree The owner tree
6796         * @param {Node} this This node
6797         * @param {Node} node The child node to be removed
6798         */
6799        "beforeremove" : true,
6800        /**
6801         * @event beforemove
6802         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
6803         * @param {Tree} tree The owner tree
6804         * @param {Node} this This node
6805         * @param {Node} oldParent The parent of this node
6806         * @param {Node} newParent The new parent this node is moving to
6807         * @param {Number} index The index it is being moved to
6808         */
6809        "beforemove" : true,
6810        /**
6811         * @event beforeinsert
6812         * Fires before a new child is inserted, return false to cancel the insert.
6813         * @param {Tree} tree The owner tree
6814         * @param {Node} this This node
6815         * @param {Node} node The child node to be inserted
6816         * @param {Node} refNode The child node the node is being inserted before
6817         */
6818        "beforeinsert" : true
6819    });
6820     this.listeners = this.attributes.listeners;
6821     Roo.data.Node.superclass.constructor.call(this);
6822 };
6823
6824 Roo.extend(Roo.data.Node, Roo.util.Observable, {
6825     fireEvent : function(evtName){
6826         // first do standard event for this node
6827         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
6828             return false;
6829         }
6830         // then bubble it up to the tree if the event wasn't cancelled
6831         var ot = this.getOwnerTree();
6832         if(ot){
6833             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
6834                 return false;
6835             }
6836         }
6837         return true;
6838     },
6839
6840     /**
6841      * Returns true if this node is a leaf
6842      * @return {Boolean}
6843      */
6844     isLeaf : function(){
6845         return this.leaf === true;
6846     },
6847
6848     // private
6849     setFirstChild : function(node){
6850         this.firstChild = node;
6851     },
6852
6853     //private
6854     setLastChild : function(node){
6855         this.lastChild = node;
6856     },
6857
6858
6859     /**
6860      * Returns true if this node is the last child of its parent
6861      * @return {Boolean}
6862      */
6863     isLast : function(){
6864        return (!this.parentNode ? true : this.parentNode.lastChild == this);
6865     },
6866
6867     /**
6868      * Returns true if this node is the first child of its parent
6869      * @return {Boolean}
6870      */
6871     isFirst : function(){
6872        return (!this.parentNode ? true : this.parentNode.firstChild == this);
6873     },
6874
6875     hasChildNodes : function(){
6876         return !this.isLeaf() && this.childNodes.length > 0;
6877     },
6878
6879     /**
6880      * Insert node(s) as the last child node of this node.
6881      * @param {Node/Array} node The node or Array of nodes to append
6882      * @return {Node} The appended node if single append, or null if an array was passed
6883      */
6884     appendChild : function(node){
6885         var multi = false;
6886         if(node instanceof Array){
6887             multi = node;
6888         }else if(arguments.length > 1){
6889             multi = arguments;
6890         }
6891         // if passed an array or multiple args do them one by one
6892         if(multi){
6893             for(var i = 0, len = multi.length; i < len; i++) {
6894                 this.appendChild(multi[i]);
6895             }
6896         }else{
6897             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
6898                 return false;
6899             }
6900             var index = this.childNodes.length;
6901             var oldParent = node.parentNode;
6902             // it's a move, make sure we move it cleanly
6903             if(oldParent){
6904                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
6905                     return false;
6906                 }
6907                 oldParent.removeChild(node);
6908             }
6909             index = this.childNodes.length;
6910             if(index == 0){
6911                 this.setFirstChild(node);
6912             }
6913             this.childNodes.push(node);
6914             node.parentNode = this;
6915             var ps = this.childNodes[index-1];
6916             if(ps){
6917                 node.previousSibling = ps;
6918                 ps.nextSibling = node;
6919             }else{
6920                 node.previousSibling = null;
6921             }
6922             node.nextSibling = null;
6923             this.setLastChild(node);
6924             node.setOwnerTree(this.getOwnerTree());
6925             this.fireEvent("append", this.ownerTree, this, node, index);
6926             if(oldParent){
6927                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
6928             }
6929             return node;
6930         }
6931     },
6932
6933     /**
6934      * Removes a child node from this node.
6935      * @param {Node} node The node to remove
6936      * @return {Node} The removed node
6937      */
6938     removeChild : function(node){
6939         var index = this.childNodes.indexOf(node);
6940         if(index == -1){
6941             return false;
6942         }
6943         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
6944             return false;
6945         }
6946
6947         // remove it from childNodes collection
6948         this.childNodes.splice(index, 1);
6949
6950         // update siblings
6951         if(node.previousSibling){
6952             node.previousSibling.nextSibling = node.nextSibling;
6953         }
6954         if(node.nextSibling){
6955             node.nextSibling.previousSibling = node.previousSibling;
6956         }
6957
6958         // update child refs
6959         if(this.firstChild == node){
6960             this.setFirstChild(node.nextSibling);
6961         }
6962         if(this.lastChild == node){
6963             this.setLastChild(node.previousSibling);
6964         }
6965
6966         node.setOwnerTree(null);
6967         // clear any references from the node
6968         node.parentNode = null;
6969         node.previousSibling = null;
6970         node.nextSibling = null;
6971         this.fireEvent("remove", this.ownerTree, this, node);
6972         return node;
6973     },
6974
6975     /**
6976      * Inserts the first node before the second node in this nodes childNodes collection.
6977      * @param {Node} node The node to insert
6978      * @param {Node} refNode The node to insert before (if null the node is appended)
6979      * @return {Node} The inserted node
6980      */
6981     insertBefore : function(node, refNode){
6982         if(!refNode){ // like standard Dom, refNode can be null for append
6983             return this.appendChild(node);
6984         }
6985         // nothing to do
6986         if(node == refNode){
6987             return false;
6988         }
6989
6990         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
6991             return false;
6992         }
6993         var index = this.childNodes.indexOf(refNode);
6994         var oldParent = node.parentNode;
6995         var refIndex = index;
6996
6997         // when moving internally, indexes will change after remove
6998         if(oldParent == this && this.childNodes.indexOf(node) < index){
6999             refIndex--;
7000         }
7001
7002         // it's a move, make sure we move it cleanly
7003         if(oldParent){
7004             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
7005                 return false;
7006             }
7007             oldParent.removeChild(node);
7008         }
7009         if(refIndex == 0){
7010             this.setFirstChild(node);
7011         }
7012         this.childNodes.splice(refIndex, 0, node);
7013         node.parentNode = this;
7014         var ps = this.childNodes[refIndex-1];
7015         if(ps){
7016             node.previousSibling = ps;
7017             ps.nextSibling = node;
7018         }else{
7019             node.previousSibling = null;
7020         }
7021         node.nextSibling = refNode;
7022         refNode.previousSibling = node;
7023         node.setOwnerTree(this.getOwnerTree());
7024         this.fireEvent("insert", this.ownerTree, this, node, refNode);
7025         if(oldParent){
7026             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
7027         }
7028         return node;
7029     },
7030
7031     /**
7032      * Returns the child node at the specified index.
7033      * @param {Number} index
7034      * @return {Node}
7035      */
7036     item : function(index){
7037         return this.childNodes[index];
7038     },
7039
7040     /**
7041      * Replaces one child node in this node with another.
7042      * @param {Node} newChild The replacement node
7043      * @param {Node} oldChild The node to replace
7044      * @return {Node} The replaced node
7045      */
7046     replaceChild : function(newChild, oldChild){
7047         this.insertBefore(newChild, oldChild);
7048         this.removeChild(oldChild);
7049         return oldChild;
7050     },
7051
7052     /**
7053      * Returns the index of a child node
7054      * @param {Node} node
7055      * @return {Number} The index of the node or -1 if it was not found
7056      */
7057     indexOf : function(child){
7058         return this.childNodes.indexOf(child);
7059     },
7060
7061     /**
7062      * Returns the tree this node is in.
7063      * @return {Tree}
7064      */
7065     getOwnerTree : function(){
7066         // if it doesn't have one, look for one
7067         if(!this.ownerTree){
7068             var p = this;
7069             while(p){
7070                 if(p.ownerTree){
7071                     this.ownerTree = p.ownerTree;
7072                     break;
7073                 }
7074                 p = p.parentNode;
7075             }
7076         }
7077         return this.ownerTree;
7078     },
7079
7080     /**
7081      * Returns depth of this node (the root node has a depth of 0)
7082      * @return {Number}
7083      */
7084     getDepth : function(){
7085         var depth = 0;
7086         var p = this;
7087         while(p.parentNode){
7088             ++depth;
7089             p = p.parentNode;
7090         }
7091         return depth;
7092     },
7093
7094     // private
7095     setOwnerTree : function(tree){
7096         // if it's move, we need to update everyone
7097         if(tree != this.ownerTree){
7098             if(this.ownerTree){
7099                 this.ownerTree.unregisterNode(this);
7100             }
7101             this.ownerTree = tree;
7102             var cs = this.childNodes;
7103             for(var i = 0, len = cs.length; i < len; i++) {
7104                 cs[i].setOwnerTree(tree);
7105             }
7106             if(tree){
7107                 tree.registerNode(this);
7108             }
7109         }
7110     },
7111
7112     /**
7113      * Returns the path for this node. The path can be used to expand or select this node programmatically.
7114      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
7115      * @return {String} The path
7116      */
7117     getPath : function(attr){
7118         attr = attr || "id";
7119         var p = this.parentNode;
7120         var b = [this.attributes[attr]];
7121         while(p){
7122             b.unshift(p.attributes[attr]);
7123             p = p.parentNode;
7124         }
7125         var sep = this.getOwnerTree().pathSeparator;
7126         return sep + b.join(sep);
7127     },
7128
7129     /**
7130      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7131      * function call will be the scope provided or the current node. The arguments to the function
7132      * will be the args provided or the current node. If the function returns false at any point,
7133      * the bubble is stopped.
7134      * @param {Function} fn The function to call
7135      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7136      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7137      */
7138     bubble : function(fn, scope, args){
7139         var p = this;
7140         while(p){
7141             if(fn.call(scope || p, args || p) === false){
7142                 break;
7143             }
7144             p = p.parentNode;
7145         }
7146     },
7147
7148     /**
7149      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7150      * function call will be the scope provided or the current node. The arguments to the function
7151      * will be the args provided or the current node. If the function returns false at any point,
7152      * the cascade is stopped on that branch.
7153      * @param {Function} fn The function to call
7154      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7155      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7156      */
7157     cascade : function(fn, scope, args){
7158         if(fn.call(scope || this, args || this) !== false){
7159             var cs = this.childNodes;
7160             for(var i = 0, len = cs.length; i < len; i++) {
7161                 cs[i].cascade(fn, scope, args);
7162             }
7163         }
7164     },
7165
7166     /**
7167      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
7168      * function call will be the scope provided or the current node. The arguments to the function
7169      * will be the args provided or the current node. If the function returns false at any point,
7170      * the iteration stops.
7171      * @param {Function} fn The function to call
7172      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7173      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7174      */
7175     eachChild : function(fn, scope, args){
7176         var cs = this.childNodes;
7177         for(var i = 0, len = cs.length; i < len; i++) {
7178                 if(fn.call(scope || this, args || cs[i]) === false){
7179                     break;
7180                 }
7181         }
7182     },
7183
7184     /**
7185      * Finds the first child that has the attribute with the specified value.
7186      * @param {String} attribute The attribute name
7187      * @param {Mixed} value The value to search for
7188      * @return {Node} The found child or null if none was found
7189      */
7190     findChild : function(attribute, value){
7191         var cs = this.childNodes;
7192         for(var i = 0, len = cs.length; i < len; i++) {
7193                 if(cs[i].attributes[attribute] == value){
7194                     return cs[i];
7195                 }
7196         }
7197         return null;
7198     },
7199
7200     /**
7201      * Finds the first child by a custom function. The child matches if the function passed
7202      * returns true.
7203      * @param {Function} fn
7204      * @param {Object} scope (optional)
7205      * @return {Node} The found child or null if none was found
7206      */
7207     findChildBy : function(fn, scope){
7208         var cs = this.childNodes;
7209         for(var i = 0, len = cs.length; i < len; i++) {
7210                 if(fn.call(scope||cs[i], cs[i]) === true){
7211                     return cs[i];
7212                 }
7213         }
7214         return null;
7215     },
7216
7217     /**
7218      * Sorts this nodes children using the supplied sort function
7219      * @param {Function} fn
7220      * @param {Object} scope (optional)
7221      */
7222     sort : function(fn, scope){
7223         var cs = this.childNodes;
7224         var len = cs.length;
7225         if(len > 0){
7226             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
7227             cs.sort(sortFn);
7228             for(var i = 0; i < len; i++){
7229                 var n = cs[i];
7230                 n.previousSibling = cs[i-1];
7231                 n.nextSibling = cs[i+1];
7232                 if(i == 0){
7233                     this.setFirstChild(n);
7234                 }
7235                 if(i == len-1){
7236                     this.setLastChild(n);
7237                 }
7238             }
7239         }
7240     },
7241
7242     /**
7243      * Returns true if this node is an ancestor (at any point) of the passed node.
7244      * @param {Node} node
7245      * @return {Boolean}
7246      */
7247     contains : function(node){
7248         return node.isAncestor(this);
7249     },
7250
7251     /**
7252      * Returns true if the passed node is an ancestor (at any point) of this node.
7253      * @param {Node} node
7254      * @return {Boolean}
7255      */
7256     isAncestor : function(node){
7257         var p = this.parentNode;
7258         while(p){
7259             if(p == node){
7260                 return true;
7261             }
7262             p = p.parentNode;
7263         }
7264         return false;
7265     },
7266
7267     toString : function(){
7268         return "[Node"+(this.id?" "+this.id:"")+"]";
7269     }
7270 });/*
7271  * Based on:
7272  * Ext JS Library 1.1.1
7273  * Copyright(c) 2006-2007, Ext JS, LLC.
7274  *
7275  * Originally Released Under LGPL - original licence link has changed is not relivant.
7276  *
7277  * Fork - LGPL
7278  * <script type="text/javascript">
7279  */
7280  
7281
7282 /**
7283  * @class Roo.ComponentMgr
7284  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
7285  * @singleton
7286  */
7287 Roo.ComponentMgr = function(){
7288     var all = new Roo.util.MixedCollection();
7289
7290     return {
7291         /**
7292          * Registers a component.
7293          * @param {Roo.Component} c The component
7294          */
7295         register : function(c){
7296             all.add(c);
7297         },
7298
7299         /**
7300          * Unregisters a component.
7301          * @param {Roo.Component} c The component
7302          */
7303         unregister : function(c){
7304             all.remove(c);
7305         },
7306
7307         /**
7308          * Returns a component by id
7309          * @param {String} id The component id
7310          */
7311         get : function(id){
7312             return all.get(id);
7313         },
7314
7315         /**
7316          * Registers a function that will be called when a specified component is added to ComponentMgr
7317          * @param {String} id The component id
7318          * @param {Funtction} fn The callback function
7319          * @param {Object} scope The scope of the callback
7320          */
7321         onAvailable : function(id, fn, scope){
7322             all.on("add", function(index, o){
7323                 if(o.id == id){
7324                     fn.call(scope || o, o);
7325                     all.un("add", fn, scope);
7326                 }
7327             });
7328         }
7329     };
7330 }();/*
7331  * Based on:
7332  * Ext JS Library 1.1.1
7333  * Copyright(c) 2006-2007, Ext JS, LLC.
7334  *
7335  * Originally Released Under LGPL - original licence link has changed is not relivant.
7336  *
7337  * Fork - LGPL
7338  * <script type="text/javascript">
7339  */
7340  
7341 /**
7342  * @class Roo.Component
7343  * @extends Roo.util.Observable
7344  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
7345  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
7346  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
7347  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
7348  * All visual components (widgets) that require rendering into a layout should subclass Component.
7349  * @constructor
7350  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
7351  * 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
7352  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
7353  */
7354 Roo.Component = function(config){
7355     config = config || {};
7356     if(config.tagName || config.dom || typeof config == "string"){ // element object
7357         config = {el: config, id: config.id || config};
7358     }
7359     this.initialConfig = config;
7360
7361     Roo.apply(this, config);
7362     this.addEvents({
7363         /**
7364          * @event disable
7365          * Fires after the component is disabled.
7366              * @param {Roo.Component} this
7367              */
7368         disable : true,
7369         /**
7370          * @event enable
7371          * Fires after the component is enabled.
7372              * @param {Roo.Component} this
7373              */
7374         enable : true,
7375         /**
7376          * @event beforeshow
7377          * Fires before the component is shown.  Return false to stop the show.
7378              * @param {Roo.Component} this
7379              */
7380         beforeshow : true,
7381         /**
7382          * @event show
7383          * Fires after the component is shown.
7384              * @param {Roo.Component} this
7385              */
7386         show : true,
7387         /**
7388          * @event beforehide
7389          * Fires before the component is hidden. Return false to stop the hide.
7390              * @param {Roo.Component} this
7391              */
7392         beforehide : true,
7393         /**
7394          * @event hide
7395          * Fires after the component is hidden.
7396              * @param {Roo.Component} this
7397              */
7398         hide : true,
7399         /**
7400          * @event beforerender
7401          * Fires before the component is rendered. Return false to stop the render.
7402              * @param {Roo.Component} this
7403              */
7404         beforerender : true,
7405         /**
7406          * @event render
7407          * Fires after the component is rendered.
7408              * @param {Roo.Component} this
7409              */
7410         render : true,
7411         /**
7412          * @event beforedestroy
7413          * Fires before the component is destroyed. Return false to stop the destroy.
7414              * @param {Roo.Component} this
7415              */
7416         beforedestroy : true,
7417         /**
7418          * @event destroy
7419          * Fires after the component is destroyed.
7420              * @param {Roo.Component} this
7421              */
7422         destroy : true
7423     });
7424     if(!this.id){
7425         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
7426     }
7427     Roo.ComponentMgr.register(this);
7428     Roo.Component.superclass.constructor.call(this);
7429     this.initComponent();
7430     if(this.renderTo){ // not supported by all components yet. use at your own risk!
7431         this.render(this.renderTo);
7432         delete this.renderTo;
7433     }
7434 };
7435
7436 /** @private */
7437 Roo.Component.AUTO_ID = 1000;
7438
7439 Roo.extend(Roo.Component, Roo.util.Observable, {
7440     /**
7441      * @scope Roo.Component.prototype
7442      * @type {Boolean}
7443      * true if this component is hidden. Read-only.
7444      */
7445     hidden : false,
7446     /**
7447      * @type {Boolean}
7448      * true if this component is disabled. Read-only.
7449      */
7450     disabled : false,
7451     /**
7452      * @type {Boolean}
7453      * true if this component has been rendered. Read-only.
7454      */
7455     rendered : false,
7456     
7457     /** @cfg {String} disableClass
7458      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
7459      */
7460     disabledClass : "x-item-disabled",
7461         /** @cfg {Boolean} allowDomMove
7462          * Whether the component can move the Dom node when rendering (defaults to true).
7463          */
7464     allowDomMove : true,
7465     /** @cfg {String} hideMode
7466      * How this component should hidden. Supported values are
7467      * "visibility" (css visibility), "offsets" (negative offset position) and
7468      * "display" (css display) - defaults to "display".
7469      */
7470     hideMode: 'display',
7471
7472     /** @private */
7473     ctype : "Roo.Component",
7474
7475     /**
7476      * @cfg {String} actionMode 
7477      * which property holds the element that used for  hide() / show() / disable() / enable()
7478      * default is 'el' 
7479      */
7480     actionMode : "el",
7481
7482     /** @private */
7483     getActionEl : function(){
7484         return this[this.actionMode];
7485     },
7486
7487     initComponent : Roo.emptyFn,
7488     /**
7489      * If this is a lazy rendering component, render it to its container element.
7490      * @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.
7491      */
7492     render : function(container, position){
7493         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
7494             if(!container && this.el){
7495                 this.el = Roo.get(this.el);
7496                 container = this.el.dom.parentNode;
7497                 this.allowDomMove = false;
7498             }
7499             this.container = Roo.get(container);
7500             this.rendered = true;
7501             if(position !== undefined){
7502                 if(typeof position == 'number'){
7503                     position = this.container.dom.childNodes[position];
7504                 }else{
7505                     position = Roo.getDom(position);
7506                 }
7507             }
7508             this.onRender(this.container, position || null);
7509             if(this.cls){
7510                 this.el.addClass(this.cls);
7511                 delete this.cls;
7512             }
7513             if(this.style){
7514                 this.el.applyStyles(this.style);
7515                 delete this.style;
7516             }
7517             this.fireEvent("render", this);
7518             this.afterRender(this.container);
7519             if(this.hidden){
7520                 this.hide();
7521             }
7522             if(this.disabled){
7523                 this.disable();
7524             }
7525         }
7526         return this;
7527     },
7528
7529     /** @private */
7530     // default function is not really useful
7531     onRender : function(ct, position){
7532         if(this.el){
7533             this.el = Roo.get(this.el);
7534             if(this.allowDomMove !== false){
7535                 ct.dom.insertBefore(this.el.dom, position);
7536             }
7537         }
7538     },
7539
7540     /** @private */
7541     getAutoCreate : function(){
7542         var cfg = typeof this.autoCreate == "object" ?
7543                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
7544         if(this.id && !cfg.id){
7545             cfg.id = this.id;
7546         }
7547         return cfg;
7548     },
7549
7550     /** @private */
7551     afterRender : Roo.emptyFn,
7552
7553     /**
7554      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
7555      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
7556      */
7557     destroy : function(){
7558         if(this.fireEvent("beforedestroy", this) !== false){
7559             this.purgeListeners();
7560             this.beforeDestroy();
7561             if(this.rendered){
7562                 this.el.removeAllListeners();
7563                 this.el.remove();
7564                 if(this.actionMode == "container"){
7565                     this.container.remove();
7566                 }
7567             }
7568             this.onDestroy();
7569             Roo.ComponentMgr.unregister(this);
7570             this.fireEvent("destroy", this);
7571         }
7572     },
7573
7574         /** @private */
7575     beforeDestroy : function(){
7576
7577     },
7578
7579         /** @private */
7580         onDestroy : function(){
7581
7582     },
7583
7584     /**
7585      * Returns the underlying {@link Roo.Element}.
7586      * @return {Roo.Element} The element
7587      */
7588     getEl : function(){
7589         return this.el;
7590     },
7591
7592     /**
7593      * Returns the id of this component.
7594      * @return {String}
7595      */
7596     getId : function(){
7597         return this.id;
7598     },
7599
7600     /**
7601      * Try to focus this component.
7602      * @param {Boolean} selectText True to also select the text in this component (if applicable)
7603      * @return {Roo.Component} this
7604      */
7605     focus : function(selectText){
7606         if(this.rendered){
7607             this.el.focus();
7608             if(selectText === true){
7609                 this.el.dom.select();
7610             }
7611         }
7612         return this;
7613     },
7614
7615     /** @private */
7616     blur : function(){
7617         if(this.rendered){
7618             this.el.blur();
7619         }
7620         return this;
7621     },
7622
7623     /**
7624      * Disable this component.
7625      * @return {Roo.Component} this
7626      */
7627     disable : function(){
7628         if(this.rendered){
7629             this.onDisable();
7630         }
7631         this.disabled = true;
7632         this.fireEvent("disable", this);
7633         return this;
7634     },
7635
7636         // private
7637     onDisable : function(){
7638         this.getActionEl().addClass(this.disabledClass);
7639         this.el.dom.disabled = true;
7640     },
7641
7642     /**
7643      * Enable this component.
7644      * @return {Roo.Component} this
7645      */
7646     enable : function(){
7647         if(this.rendered){
7648             this.onEnable();
7649         }
7650         this.disabled = false;
7651         this.fireEvent("enable", this);
7652         return this;
7653     },
7654
7655         // private
7656     onEnable : function(){
7657         this.getActionEl().removeClass(this.disabledClass);
7658         this.el.dom.disabled = false;
7659     },
7660
7661     /**
7662      * Convenience function for setting disabled/enabled by boolean.
7663      * @param {Boolean} disabled
7664      */
7665     setDisabled : function(disabled){
7666         this[disabled ? "disable" : "enable"]();
7667     },
7668
7669     /**
7670      * Show this component.
7671      * @return {Roo.Component} this
7672      */
7673     show: function(){
7674         if(this.fireEvent("beforeshow", this) !== false){
7675             this.hidden = false;
7676             if(this.rendered){
7677                 this.onShow();
7678             }
7679             this.fireEvent("show", this);
7680         }
7681         return this;
7682     },
7683
7684     // private
7685     onShow : function(){
7686         var ae = this.getActionEl();
7687         if(this.hideMode == 'visibility'){
7688             ae.dom.style.visibility = "visible";
7689         }else if(this.hideMode == 'offsets'){
7690             ae.removeClass('x-hidden');
7691         }else{
7692             ae.dom.style.display = "";
7693         }
7694     },
7695
7696     /**
7697      * Hide this component.
7698      * @return {Roo.Component} this
7699      */
7700     hide: function(){
7701         if(this.fireEvent("beforehide", this) !== false){
7702             this.hidden = true;
7703             if(this.rendered){
7704                 this.onHide();
7705             }
7706             this.fireEvent("hide", this);
7707         }
7708         return this;
7709     },
7710
7711     // private
7712     onHide : function(){
7713         var ae = this.getActionEl();
7714         if(this.hideMode == 'visibility'){
7715             ae.dom.style.visibility = "hidden";
7716         }else if(this.hideMode == 'offsets'){
7717             ae.addClass('x-hidden');
7718         }else{
7719             ae.dom.style.display = "none";
7720         }
7721     },
7722
7723     /**
7724      * Convenience function to hide or show this component by boolean.
7725      * @param {Boolean} visible True to show, false to hide
7726      * @return {Roo.Component} this
7727      */
7728     setVisible: function(visible){
7729         if(visible) {
7730             this.show();
7731         }else{
7732             this.hide();
7733         }
7734         return this;
7735     },
7736
7737     /**
7738      * Returns true if this component is visible.
7739      */
7740     isVisible : function(){
7741         return this.getActionEl().isVisible();
7742     },
7743
7744     cloneConfig : function(overrides){
7745         overrides = overrides || {};
7746         var id = overrides.id || Roo.id();
7747         var cfg = Roo.applyIf(overrides, this.initialConfig);
7748         cfg.id = id; // prevent dup id
7749         return new this.constructor(cfg);
7750     }
7751 });/*
7752  * Based on:
7753  * Ext JS Library 1.1.1
7754  * Copyright(c) 2006-2007, Ext JS, LLC.
7755  *
7756  * Originally Released Under LGPL - original licence link has changed is not relivant.
7757  *
7758  * Fork - LGPL
7759  * <script type="text/javascript">
7760  */
7761  (function(){ 
7762 /**
7763  * @class Roo.Layer
7764  * @extends Roo.Element
7765  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
7766  * automatic maintaining of shadow/shim positions.
7767  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
7768  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
7769  * you can pass a string with a CSS class name. False turns off the shadow.
7770  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
7771  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
7772  * @cfg {String} cls CSS class to add to the element
7773  * @cfg {Number} zindex Starting z-index (defaults to 11000)
7774  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
7775  * @constructor
7776  * @param {Object} config An object with config options.
7777  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
7778  */
7779
7780 Roo.Layer = function(config, existingEl){
7781     config = config || {};
7782     var dh = Roo.DomHelper;
7783     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
7784     if(existingEl){
7785         this.dom = Roo.getDom(existingEl);
7786     }
7787     if(!this.dom){
7788         var o = config.dh || {tag: "div", cls: "x-layer"};
7789         this.dom = dh.append(pel, o);
7790     }
7791     if(config.cls){
7792         this.addClass(config.cls);
7793     }
7794     this.constrain = config.constrain !== false;
7795     this.visibilityMode = Roo.Element.VISIBILITY;
7796     if(config.id){
7797         this.id = this.dom.id = config.id;
7798     }else{
7799         this.id = Roo.id(this.dom);
7800     }
7801     this.zindex = config.zindex || this.getZIndex();
7802     this.position("absolute", this.zindex);
7803     if(config.shadow){
7804         this.shadowOffset = config.shadowOffset || 4;
7805         this.shadow = new Roo.Shadow({
7806             offset : this.shadowOffset,
7807             mode : config.shadow
7808         });
7809     }else{
7810         this.shadowOffset = 0;
7811     }
7812     this.useShim = config.shim !== false && Roo.useShims;
7813     this.useDisplay = config.useDisplay;
7814     this.hide();
7815 };
7816
7817 var supr = Roo.Element.prototype;
7818
7819 // shims are shared among layer to keep from having 100 iframes
7820 var shims = [];
7821
7822 Roo.extend(Roo.Layer, Roo.Element, {
7823
7824     getZIndex : function(){
7825         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
7826     },
7827
7828     getShim : function(){
7829         if(!this.useShim){
7830             return null;
7831         }
7832         if(this.shim){
7833             return this.shim;
7834         }
7835         var shim = shims.shift();
7836         if(!shim){
7837             shim = this.createShim();
7838             shim.enableDisplayMode('block');
7839             shim.dom.style.display = 'none';
7840             shim.dom.style.visibility = 'visible';
7841         }
7842         var pn = this.dom.parentNode;
7843         if(shim.dom.parentNode != pn){
7844             pn.insertBefore(shim.dom, this.dom);
7845         }
7846         shim.setStyle('z-index', this.getZIndex()-2);
7847         this.shim = shim;
7848         return shim;
7849     },
7850
7851     hideShim : function(){
7852         if(this.shim){
7853             this.shim.setDisplayed(false);
7854             shims.push(this.shim);
7855             delete this.shim;
7856         }
7857     },
7858
7859     disableShadow : function(){
7860         if(this.shadow){
7861             this.shadowDisabled = true;
7862             this.shadow.hide();
7863             this.lastShadowOffset = this.shadowOffset;
7864             this.shadowOffset = 0;
7865         }
7866     },
7867
7868     enableShadow : function(show){
7869         if(this.shadow){
7870             this.shadowDisabled = false;
7871             this.shadowOffset = this.lastShadowOffset;
7872             delete this.lastShadowOffset;
7873             if(show){
7874                 this.sync(true);
7875             }
7876         }
7877     },
7878
7879     // private
7880     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
7881     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
7882     sync : function(doShow){
7883         var sw = this.shadow;
7884         if(!this.updating && this.isVisible() && (sw || this.useShim)){
7885             var sh = this.getShim();
7886
7887             var w = this.getWidth(),
7888                 h = this.getHeight();
7889
7890             var l = this.getLeft(true),
7891                 t = this.getTop(true);
7892
7893             if(sw && !this.shadowDisabled){
7894                 if(doShow && !sw.isVisible()){
7895                     sw.show(this);
7896                 }else{
7897                     sw.realign(l, t, w, h);
7898                 }
7899                 if(sh){
7900                     if(doShow){
7901                        sh.show();
7902                     }
7903                     // fit the shim behind the shadow, so it is shimmed too
7904                     var a = sw.adjusts, s = sh.dom.style;
7905                     s.left = (Math.min(l, l+a.l))+"px";
7906                     s.top = (Math.min(t, t+a.t))+"px";
7907                     s.width = (w+a.w)+"px";
7908                     s.height = (h+a.h)+"px";
7909                 }
7910             }else if(sh){
7911                 if(doShow){
7912                    sh.show();
7913                 }
7914                 sh.setSize(w, h);
7915                 sh.setLeftTop(l, t);
7916             }
7917             
7918         }
7919     },
7920
7921     // private
7922     destroy : function(){
7923         this.hideShim();
7924         if(this.shadow){
7925             this.shadow.hide();
7926         }
7927         this.removeAllListeners();
7928         var pn = this.dom.parentNode;
7929         if(pn){
7930             pn.removeChild(this.dom);
7931         }
7932         Roo.Element.uncache(this.id);
7933     },
7934
7935     remove : function(){
7936         this.destroy();
7937     },
7938
7939     // private
7940     beginUpdate : function(){
7941         this.updating = true;
7942     },
7943
7944     // private
7945     endUpdate : function(){
7946         this.updating = false;
7947         this.sync(true);
7948     },
7949
7950     // private
7951     hideUnders : function(negOffset){
7952         if(this.shadow){
7953             this.shadow.hide();
7954         }
7955         this.hideShim();
7956     },
7957
7958     // private
7959     constrainXY : function(){
7960         if(this.constrain){
7961             var vw = Roo.lib.Dom.getViewWidth(),
7962                 vh = Roo.lib.Dom.getViewHeight();
7963             var s = Roo.get(document).getScroll();
7964
7965             var xy = this.getXY();
7966             var x = xy[0], y = xy[1];   
7967             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
7968             // only move it if it needs it
7969             var moved = false;
7970             // first validate right/bottom
7971             if((x + w) > vw+s.left){
7972                 x = vw - w - this.shadowOffset;
7973                 moved = true;
7974             }
7975             if((y + h) > vh+s.top){
7976                 y = vh - h - this.shadowOffset;
7977                 moved = true;
7978             }
7979             // then make sure top/left isn't negative
7980             if(x < s.left){
7981                 x = s.left;
7982                 moved = true;
7983             }
7984             if(y < s.top){
7985                 y = s.top;
7986                 moved = true;
7987             }
7988             if(moved){
7989                 if(this.avoidY){
7990                     var ay = this.avoidY;
7991                     if(y <= ay && (y+h) >= ay){
7992                         y = ay-h-5;   
7993                     }
7994                 }
7995                 xy = [x, y];
7996                 this.storeXY(xy);
7997                 supr.setXY.call(this, xy);
7998                 this.sync();
7999             }
8000         }
8001     },
8002
8003     isVisible : function(){
8004         return this.visible;    
8005     },
8006
8007     // private
8008     showAction : function(){
8009         this.visible = true; // track visibility to prevent getStyle calls
8010         if(this.useDisplay === true){
8011             this.setDisplayed("");
8012         }else if(this.lastXY){
8013             supr.setXY.call(this, this.lastXY);
8014         }else if(this.lastLT){
8015             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
8016         }
8017     },
8018
8019     // private
8020     hideAction : function(){
8021         this.visible = false;
8022         if(this.useDisplay === true){
8023             this.setDisplayed(false);
8024         }else{
8025             this.setLeftTop(-10000,-10000);
8026         }
8027     },
8028
8029     // overridden Element method
8030     setVisible : function(v, a, d, c, e){
8031         if(v){
8032             this.showAction();
8033         }
8034         if(a && v){
8035             var cb = function(){
8036                 this.sync(true);
8037                 if(c){
8038                     c();
8039                 }
8040             }.createDelegate(this);
8041             supr.setVisible.call(this, true, true, d, cb, e);
8042         }else{
8043             if(!v){
8044                 this.hideUnders(true);
8045             }
8046             var cb = c;
8047             if(a){
8048                 cb = function(){
8049                     this.hideAction();
8050                     if(c){
8051                         c();
8052                     }
8053                 }.createDelegate(this);
8054             }
8055             supr.setVisible.call(this, v, a, d, cb, e);
8056             if(v){
8057                 this.sync(true);
8058             }else if(!a){
8059                 this.hideAction();
8060             }
8061         }
8062     },
8063
8064     storeXY : function(xy){
8065         delete this.lastLT;
8066         this.lastXY = xy;
8067     },
8068
8069     storeLeftTop : function(left, top){
8070         delete this.lastXY;
8071         this.lastLT = [left, top];
8072     },
8073
8074     // private
8075     beforeFx : function(){
8076         this.beforeAction();
8077         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
8078     },
8079
8080     // private
8081     afterFx : function(){
8082         Roo.Layer.superclass.afterFx.apply(this, arguments);
8083         this.sync(this.isVisible());
8084     },
8085
8086     // private
8087     beforeAction : function(){
8088         if(!this.updating && this.shadow){
8089             this.shadow.hide();
8090         }
8091     },
8092
8093     // overridden Element method
8094     setLeft : function(left){
8095         this.storeLeftTop(left, this.getTop(true));
8096         supr.setLeft.apply(this, arguments);
8097         this.sync();
8098     },
8099
8100     setTop : function(top){
8101         this.storeLeftTop(this.getLeft(true), top);
8102         supr.setTop.apply(this, arguments);
8103         this.sync();
8104     },
8105
8106     setLeftTop : function(left, top){
8107         this.storeLeftTop(left, top);
8108         supr.setLeftTop.apply(this, arguments);
8109         this.sync();
8110     },
8111
8112     setXY : function(xy, a, d, c, e){
8113         this.fixDisplay();
8114         this.beforeAction();
8115         this.storeXY(xy);
8116         var cb = this.createCB(c);
8117         supr.setXY.call(this, xy, a, d, cb, e);
8118         if(!a){
8119             cb();
8120         }
8121     },
8122
8123     // private
8124     createCB : function(c){
8125         var el = this;
8126         return function(){
8127             el.constrainXY();
8128             el.sync(true);
8129             if(c){
8130                 c();
8131             }
8132         };
8133     },
8134
8135     // overridden Element method
8136     setX : function(x, a, d, c, e){
8137         this.setXY([x, this.getY()], a, d, c, e);
8138     },
8139
8140     // overridden Element method
8141     setY : function(y, a, d, c, e){
8142         this.setXY([this.getX(), y], a, d, c, e);
8143     },
8144
8145     // overridden Element method
8146     setSize : function(w, h, a, d, c, e){
8147         this.beforeAction();
8148         var cb = this.createCB(c);
8149         supr.setSize.call(this, w, h, a, d, cb, e);
8150         if(!a){
8151             cb();
8152         }
8153     },
8154
8155     // overridden Element method
8156     setWidth : function(w, a, d, c, e){
8157         this.beforeAction();
8158         var cb = this.createCB(c);
8159         supr.setWidth.call(this, w, a, d, cb, e);
8160         if(!a){
8161             cb();
8162         }
8163     },
8164
8165     // overridden Element method
8166     setHeight : function(h, a, d, c, e){
8167         this.beforeAction();
8168         var cb = this.createCB(c);
8169         supr.setHeight.call(this, h, a, d, cb, e);
8170         if(!a){
8171             cb();
8172         }
8173     },
8174
8175     // overridden Element method
8176     setBounds : function(x, y, w, h, a, d, c, e){
8177         this.beforeAction();
8178         var cb = this.createCB(c);
8179         if(!a){
8180             this.storeXY([x, y]);
8181             supr.setXY.call(this, [x, y]);
8182             supr.setSize.call(this, w, h, a, d, cb, e);
8183             cb();
8184         }else{
8185             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
8186         }
8187         return this;
8188     },
8189     
8190     /**
8191      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
8192      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
8193      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
8194      * @param {Number} zindex The new z-index to set
8195      * @return {this} The Layer
8196      */
8197     setZIndex : function(zindex){
8198         this.zindex = zindex;
8199         this.setStyle("z-index", zindex + 2);
8200         if(this.shadow){
8201             this.shadow.setZIndex(zindex + 1);
8202         }
8203         if(this.shim){
8204             this.shim.setStyle("z-index", zindex);
8205         }
8206     }
8207 });
8208 })();/*
8209  * Based on:
8210  * Ext JS Library 1.1.1
8211  * Copyright(c) 2006-2007, Ext JS, LLC.
8212  *
8213  * Originally Released Under LGPL - original licence link has changed is not relivant.
8214  *
8215  * Fork - LGPL
8216  * <script type="text/javascript">
8217  */
8218
8219
8220 /**
8221  * @class Roo.Shadow
8222  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
8223  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
8224  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
8225  * @constructor
8226  * Create a new Shadow
8227  * @param {Object} config The config object
8228  */
8229 Roo.Shadow = function(config){
8230     Roo.apply(this, config);
8231     if(typeof this.mode != "string"){
8232         this.mode = this.defaultMode;
8233     }
8234     var o = this.offset, a = {h: 0};
8235     var rad = Math.floor(this.offset/2);
8236     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
8237         case "drop":
8238             a.w = 0;
8239             a.l = a.t = o;
8240             a.t -= 1;
8241             if(Roo.isIE){
8242                 a.l -= this.offset + rad;
8243                 a.t -= this.offset + rad;
8244                 a.w -= rad;
8245                 a.h -= rad;
8246                 a.t += 1;
8247             }
8248         break;
8249         case "sides":
8250             a.w = (o*2);
8251             a.l = -o;
8252             a.t = o-1;
8253             if(Roo.isIE){
8254                 a.l -= (this.offset - rad);
8255                 a.t -= this.offset + rad;
8256                 a.l += 1;
8257                 a.w -= (this.offset - rad)*2;
8258                 a.w -= rad + 1;
8259                 a.h -= 1;
8260             }
8261         break;
8262         case "frame":
8263             a.w = a.h = (o*2);
8264             a.l = a.t = -o;
8265             a.t += 1;
8266             a.h -= 2;
8267             if(Roo.isIE){
8268                 a.l -= (this.offset - rad);
8269                 a.t -= (this.offset - rad);
8270                 a.l += 1;
8271                 a.w -= (this.offset + rad + 1);
8272                 a.h -= (this.offset + rad);
8273                 a.h += 1;
8274             }
8275         break;
8276     };
8277
8278     this.adjusts = a;
8279 };
8280
8281 Roo.Shadow.prototype = {
8282     /**
8283      * @cfg {String} mode
8284      * The shadow display mode.  Supports the following options:<br />
8285      * sides: Shadow displays on both sides and bottom only<br />
8286      * frame: Shadow displays equally on all four sides<br />
8287      * drop: Traditional bottom-right drop shadow (default)
8288      */
8289     /**
8290      * @cfg {String} offset
8291      * The number of pixels to offset the shadow from the element (defaults to 4)
8292      */
8293     offset: 4,
8294
8295     // private
8296     defaultMode: "drop",
8297
8298     /**
8299      * Displays the shadow under the target element
8300      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
8301      */
8302     show : function(target){
8303         target = Roo.get(target);
8304         if(!this.el){
8305             this.el = Roo.Shadow.Pool.pull();
8306             if(this.el.dom.nextSibling != target.dom){
8307                 this.el.insertBefore(target);
8308             }
8309         }
8310         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
8311         if(Roo.isIE){
8312             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
8313         }
8314         this.realign(
8315             target.getLeft(true),
8316             target.getTop(true),
8317             target.getWidth(),
8318             target.getHeight()
8319         );
8320         this.el.dom.style.display = "block";
8321     },
8322
8323     /**
8324      * Returns true if the shadow is visible, else false
8325      */
8326     isVisible : function(){
8327         return this.el ? true : false;  
8328     },
8329
8330     /**
8331      * Direct alignment when values are already available. Show must be called at least once before
8332      * calling this method to ensure it is initialized.
8333      * @param {Number} left The target element left position
8334      * @param {Number} top The target element top position
8335      * @param {Number} width The target element width
8336      * @param {Number} height The target element height
8337      */
8338     realign : function(l, t, w, h){
8339         if(!this.el){
8340             return;
8341         }
8342         var a = this.adjusts, d = this.el.dom, s = d.style;
8343         var iea = 0;
8344         s.left = (l+a.l)+"px";
8345         s.top = (t+a.t)+"px";
8346         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
8347  
8348         if(s.width != sws || s.height != shs){
8349             s.width = sws;
8350             s.height = shs;
8351             if(!Roo.isIE){
8352                 var cn = d.childNodes;
8353                 var sww = Math.max(0, (sw-12))+"px";
8354                 cn[0].childNodes[1].style.width = sww;
8355                 cn[1].childNodes[1].style.width = sww;
8356                 cn[2].childNodes[1].style.width = sww;
8357                 cn[1].style.height = Math.max(0, (sh-12))+"px";
8358             }
8359         }
8360     },
8361
8362     /**
8363      * Hides this shadow
8364      */
8365     hide : function(){
8366         if(this.el){
8367             this.el.dom.style.display = "none";
8368             Roo.Shadow.Pool.push(this.el);
8369             delete this.el;
8370         }
8371     },
8372
8373     /**
8374      * Adjust the z-index of this shadow
8375      * @param {Number} zindex The new z-index
8376      */
8377     setZIndex : function(z){
8378         this.zIndex = z;
8379         if(this.el){
8380             this.el.setStyle("z-index", z);
8381         }
8382     }
8383 };
8384
8385 // Private utility class that manages the internal Shadow cache
8386 Roo.Shadow.Pool = function(){
8387     var p = [];
8388     var markup = Roo.isIE ?
8389                  '<div class="x-ie-shadow"></div>' :
8390                  '<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>';
8391     return {
8392         pull : function(){
8393             var sh = p.shift();
8394             if(!sh){
8395                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
8396                 sh.autoBoxAdjust = false;
8397             }
8398             return sh;
8399         },
8400
8401         push : function(sh){
8402             p.push(sh);
8403         }
8404     };
8405 }();/*
8406  * Based on:
8407  * Ext JS Library 1.1.1
8408  * Copyright(c) 2006-2007, Ext JS, LLC.
8409  *
8410  * Originally Released Under LGPL - original licence link has changed is not relivant.
8411  *
8412  * Fork - LGPL
8413  * <script type="text/javascript">
8414  */
8415
8416 /**
8417  * @class Roo.BoxComponent
8418  * @extends Roo.Component
8419  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
8420  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
8421  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
8422  * layout containers.
8423  * @constructor
8424  * @param {Roo.Element/String/Object} config The configuration options.
8425  */
8426 Roo.BoxComponent = function(config){
8427     Roo.Component.call(this, config);
8428     this.addEvents({
8429         /**
8430          * @event resize
8431          * Fires after the component is resized.
8432              * @param {Roo.Component} this
8433              * @param {Number} adjWidth The box-adjusted width that was set
8434              * @param {Number} adjHeight The box-adjusted height that was set
8435              * @param {Number} rawWidth The width that was originally specified
8436              * @param {Number} rawHeight The height that was originally specified
8437              */
8438         resize : true,
8439         /**
8440          * @event move
8441          * Fires after the component is moved.
8442              * @param {Roo.Component} this
8443              * @param {Number} x The new x position
8444              * @param {Number} y The new y position
8445              */
8446         move : true
8447     });
8448 };
8449
8450 Roo.extend(Roo.BoxComponent, Roo.Component, {
8451     // private, set in afterRender to signify that the component has been rendered
8452     boxReady : false,
8453     // private, used to defer height settings to subclasses
8454     deferHeight: false,
8455     /** @cfg {Number} width
8456      * width (optional) size of component
8457      */
8458      /** @cfg {Number} height
8459      * height (optional) size of component
8460      */
8461      
8462     /**
8463      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
8464      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
8465      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
8466      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
8467      * @return {Roo.BoxComponent} this
8468      */
8469     setSize : function(w, h){
8470         // support for standard size objects
8471         if(typeof w == 'object'){
8472             h = w.height;
8473             w = w.width;
8474         }
8475         // not rendered
8476         if(!this.boxReady){
8477             this.width = w;
8478             this.height = h;
8479             return this;
8480         }
8481
8482         // prevent recalcs when not needed
8483         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
8484             return this;
8485         }
8486         this.lastSize = {width: w, height: h};
8487
8488         var adj = this.adjustSize(w, h);
8489         var aw = adj.width, ah = adj.height;
8490         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
8491             var rz = this.getResizeEl();
8492             if(!this.deferHeight && aw !== undefined && ah !== undefined){
8493                 rz.setSize(aw, ah);
8494             }else if(!this.deferHeight && ah !== undefined){
8495                 rz.setHeight(ah);
8496             }else if(aw !== undefined){
8497                 rz.setWidth(aw);
8498             }
8499             this.onResize(aw, ah, w, h);
8500             this.fireEvent('resize', this, aw, ah, w, h);
8501         }
8502         return this;
8503     },
8504
8505     /**
8506      * Gets the current size of the component's underlying element.
8507      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8508      */
8509     getSize : function(){
8510         return this.el.getSize();
8511     },
8512
8513     /**
8514      * Gets the current XY position of the component's underlying element.
8515      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8516      * @return {Array} The XY position of the element (e.g., [100, 200])
8517      */
8518     getPosition : function(local){
8519         if(local === true){
8520             return [this.el.getLeft(true), this.el.getTop(true)];
8521         }
8522         return this.xy || this.el.getXY();
8523     },
8524
8525     /**
8526      * Gets the current box measurements of the component's underlying element.
8527      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8528      * @returns {Object} box An object in the format {x, y, width, height}
8529      */
8530     getBox : function(local){
8531         var s = this.el.getSize();
8532         if(local){
8533             s.x = this.el.getLeft(true);
8534             s.y = this.el.getTop(true);
8535         }else{
8536             var xy = this.xy || this.el.getXY();
8537             s.x = xy[0];
8538             s.y = xy[1];
8539         }
8540         return s;
8541     },
8542
8543     /**
8544      * Sets the current box measurements of the component's underlying element.
8545      * @param {Object} box An object in the format {x, y, width, height}
8546      * @returns {Roo.BoxComponent} this
8547      */
8548     updateBox : function(box){
8549         this.setSize(box.width, box.height);
8550         this.setPagePosition(box.x, box.y);
8551         return this;
8552     },
8553
8554     // protected
8555     getResizeEl : function(){
8556         return this.resizeEl || this.el;
8557     },
8558
8559     // protected
8560     getPositionEl : function(){
8561         return this.positionEl || this.el;
8562     },
8563
8564     /**
8565      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
8566      * This method fires the move event.
8567      * @param {Number} left The new left
8568      * @param {Number} top The new top
8569      * @returns {Roo.BoxComponent} this
8570      */
8571     setPosition : function(x, y){
8572         this.x = x;
8573         this.y = y;
8574         if(!this.boxReady){
8575             return this;
8576         }
8577         var adj = this.adjustPosition(x, y);
8578         var ax = adj.x, ay = adj.y;
8579
8580         var el = this.getPositionEl();
8581         if(ax !== undefined || ay !== undefined){
8582             if(ax !== undefined && ay !== undefined){
8583                 el.setLeftTop(ax, ay);
8584             }else if(ax !== undefined){
8585                 el.setLeft(ax);
8586             }else if(ay !== undefined){
8587                 el.setTop(ay);
8588             }
8589             this.onPosition(ax, ay);
8590             this.fireEvent('move', this, ax, ay);
8591         }
8592         return this;
8593     },
8594
8595     /**
8596      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
8597      * This method fires the move event.
8598      * @param {Number} x The new x position
8599      * @param {Number} y The new y position
8600      * @returns {Roo.BoxComponent} this
8601      */
8602     setPagePosition : function(x, y){
8603         this.pageX = x;
8604         this.pageY = y;
8605         if(!this.boxReady){
8606             return;
8607         }
8608         if(x === undefined || y === undefined){ // cannot translate undefined points
8609             return;
8610         }
8611         var p = this.el.translatePoints(x, y);
8612         this.setPosition(p.left, p.top);
8613         return this;
8614     },
8615
8616     // private
8617     onRender : function(ct, position){
8618         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
8619         if(this.resizeEl){
8620             this.resizeEl = Roo.get(this.resizeEl);
8621         }
8622         if(this.positionEl){
8623             this.positionEl = Roo.get(this.positionEl);
8624         }
8625     },
8626
8627     // private
8628     afterRender : function(){
8629         Roo.BoxComponent.superclass.afterRender.call(this);
8630         this.boxReady = true;
8631         this.setSize(this.width, this.height);
8632         if(this.x || this.y){
8633             this.setPosition(this.x, this.y);
8634         }
8635         if(this.pageX || this.pageY){
8636             this.setPagePosition(this.pageX, this.pageY);
8637         }
8638     },
8639
8640     /**
8641      * Force the component's size to recalculate based on the underlying element's current height and width.
8642      * @returns {Roo.BoxComponent} this
8643      */
8644     syncSize : function(){
8645         delete this.lastSize;
8646         this.setSize(this.el.getWidth(), this.el.getHeight());
8647         return this;
8648     },
8649
8650     /**
8651      * Called after the component is resized, this method is empty by default but can be implemented by any
8652      * subclass that needs to perform custom logic after a resize occurs.
8653      * @param {Number} adjWidth The box-adjusted width that was set
8654      * @param {Number} adjHeight The box-adjusted height that was set
8655      * @param {Number} rawWidth The width that was originally specified
8656      * @param {Number} rawHeight The height that was originally specified
8657      */
8658     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
8659
8660     },
8661
8662     /**
8663      * Called after the component is moved, this method is empty by default but can be implemented by any
8664      * subclass that needs to perform custom logic after a move occurs.
8665      * @param {Number} x The new x position
8666      * @param {Number} y The new y position
8667      */
8668     onPosition : function(x, y){
8669
8670     },
8671
8672     // private
8673     adjustSize : function(w, h){
8674         if(this.autoWidth){
8675             w = 'auto';
8676         }
8677         if(this.autoHeight){
8678             h = 'auto';
8679         }
8680         return {width : w, height: h};
8681     },
8682
8683     // private
8684     adjustPosition : function(x, y){
8685         return {x : x, y: y};
8686     }
8687 });/*
8688  * Based on:
8689  * Ext JS Library 1.1.1
8690  * Copyright(c) 2006-2007, Ext JS, LLC.
8691  *
8692  * Originally Released Under LGPL - original licence link has changed is not relivant.
8693  *
8694  * Fork - LGPL
8695  * <script type="text/javascript">
8696  */
8697
8698
8699 /**
8700  * @class Roo.SplitBar
8701  * @extends Roo.util.Observable
8702  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
8703  * <br><br>
8704  * Usage:
8705  * <pre><code>
8706 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
8707                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
8708 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
8709 split.minSize = 100;
8710 split.maxSize = 600;
8711 split.animate = true;
8712 split.on('moved', splitterMoved);
8713 </code></pre>
8714  * @constructor
8715  * Create a new SplitBar
8716  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
8717  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
8718  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8719  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
8720                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
8721                         position of the SplitBar).
8722  */
8723 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
8724     
8725     /** @private */
8726     this.el = Roo.get(dragElement, true);
8727     this.el.dom.unselectable = "on";
8728     /** @private */
8729     this.resizingEl = Roo.get(resizingElement, true);
8730
8731     /**
8732      * @private
8733      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8734      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
8735      * @type Number
8736      */
8737     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
8738     
8739     /**
8740      * The minimum size of the resizing element. (Defaults to 0)
8741      * @type Number
8742      */
8743     this.minSize = 0;
8744     
8745     /**
8746      * The maximum size of the resizing element. (Defaults to 2000)
8747      * @type Number
8748      */
8749     this.maxSize = 2000;
8750     
8751     /**
8752      * Whether to animate the transition to the new size
8753      * @type Boolean
8754      */
8755     this.animate = false;
8756     
8757     /**
8758      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
8759      * @type Boolean
8760      */
8761     this.useShim = false;
8762     
8763     /** @private */
8764     this.shim = null;
8765     
8766     if(!existingProxy){
8767         /** @private */
8768         this.proxy = Roo.SplitBar.createProxy(this.orientation);
8769     }else{
8770         this.proxy = Roo.get(existingProxy).dom;
8771     }
8772     /** @private */
8773     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
8774     
8775     /** @private */
8776     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
8777     
8778     /** @private */
8779     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
8780     
8781     /** @private */
8782     this.dragSpecs = {};
8783     
8784     /**
8785      * @private The adapter to use to positon and resize elements
8786      */
8787     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
8788     this.adapter.init(this);
8789     
8790     if(this.orientation == Roo.SplitBar.HORIZONTAL){
8791         /** @private */
8792         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
8793         this.el.addClass("x-splitbar-h");
8794     }else{
8795         /** @private */
8796         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
8797         this.el.addClass("x-splitbar-v");
8798     }
8799     
8800     this.addEvents({
8801         /**
8802          * @event resize
8803          * Fires when the splitter is moved (alias for {@link #event-moved})
8804          * @param {Roo.SplitBar} this
8805          * @param {Number} newSize the new width or height
8806          */
8807         "resize" : true,
8808         /**
8809          * @event moved
8810          * Fires when the splitter is moved
8811          * @param {Roo.SplitBar} this
8812          * @param {Number} newSize the new width or height
8813          */
8814         "moved" : true,
8815         /**
8816          * @event beforeresize
8817          * Fires before the splitter is dragged
8818          * @param {Roo.SplitBar} this
8819          */
8820         "beforeresize" : true,
8821
8822         "beforeapply" : true
8823     });
8824
8825     Roo.util.Observable.call(this);
8826 };
8827
8828 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
8829     onStartProxyDrag : function(x, y){
8830         this.fireEvent("beforeresize", this);
8831         if(!this.overlay){
8832             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
8833             o.unselectable();
8834             o.enableDisplayMode("block");
8835             // all splitbars share the same overlay
8836             Roo.SplitBar.prototype.overlay = o;
8837         }
8838         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8839         this.overlay.show();
8840         Roo.get(this.proxy).setDisplayed("block");
8841         var size = this.adapter.getElementSize(this);
8842         this.activeMinSize = this.getMinimumSize();;
8843         this.activeMaxSize = this.getMaximumSize();;
8844         var c1 = size - this.activeMinSize;
8845         var c2 = Math.max(this.activeMaxSize - size, 0);
8846         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8847             this.dd.resetConstraints();
8848             this.dd.setXConstraint(
8849                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
8850                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
8851             );
8852             this.dd.setYConstraint(0, 0);
8853         }else{
8854             this.dd.resetConstraints();
8855             this.dd.setXConstraint(0, 0);
8856             this.dd.setYConstraint(
8857                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
8858                 this.placement == Roo.SplitBar.TOP ? c2 : c1
8859             );
8860          }
8861         this.dragSpecs.startSize = size;
8862         this.dragSpecs.startPoint = [x, y];
8863         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
8864     },
8865     
8866     /** 
8867      * @private Called after the drag operation by the DDProxy
8868      */
8869     onEndProxyDrag : function(e){
8870         Roo.get(this.proxy).setDisplayed(false);
8871         var endPoint = Roo.lib.Event.getXY(e);
8872         if(this.overlay){
8873             this.overlay.hide();
8874         }
8875         var newSize;
8876         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8877             newSize = this.dragSpecs.startSize + 
8878                 (this.placement == Roo.SplitBar.LEFT ?
8879                     endPoint[0] - this.dragSpecs.startPoint[0] :
8880                     this.dragSpecs.startPoint[0] - endPoint[0]
8881                 );
8882         }else{
8883             newSize = this.dragSpecs.startSize + 
8884                 (this.placement == Roo.SplitBar.TOP ?
8885                     endPoint[1] - this.dragSpecs.startPoint[1] :
8886                     this.dragSpecs.startPoint[1] - endPoint[1]
8887                 );
8888         }
8889         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
8890         if(newSize != this.dragSpecs.startSize){
8891             if(this.fireEvent('beforeapply', this, newSize) !== false){
8892                 this.adapter.setElementSize(this, newSize);
8893                 this.fireEvent("moved", this, newSize);
8894                 this.fireEvent("resize", this, newSize);
8895             }
8896         }
8897     },
8898     
8899     /**
8900      * Get the adapter this SplitBar uses
8901      * @return The adapter object
8902      */
8903     getAdapter : function(){
8904         return this.adapter;
8905     },
8906     
8907     /**
8908      * Set the adapter this SplitBar uses
8909      * @param {Object} adapter A SplitBar adapter object
8910      */
8911     setAdapter : function(adapter){
8912         this.adapter = adapter;
8913         this.adapter.init(this);
8914     },
8915     
8916     /**
8917      * Gets the minimum size for the resizing element
8918      * @return {Number} The minimum size
8919      */
8920     getMinimumSize : function(){
8921         return this.minSize;
8922     },
8923     
8924     /**
8925      * Sets the minimum size for the resizing element
8926      * @param {Number} minSize The minimum size
8927      */
8928     setMinimumSize : function(minSize){
8929         this.minSize = minSize;
8930     },
8931     
8932     /**
8933      * Gets the maximum size for the resizing element
8934      * @return {Number} The maximum size
8935      */
8936     getMaximumSize : function(){
8937         return this.maxSize;
8938     },
8939     
8940     /**
8941      * Sets the maximum size for the resizing element
8942      * @param {Number} maxSize The maximum size
8943      */
8944     setMaximumSize : function(maxSize){
8945         this.maxSize = maxSize;
8946     },
8947     
8948     /**
8949      * Sets the initialize size for the resizing element
8950      * @param {Number} size The initial size
8951      */
8952     setCurrentSize : function(size){
8953         var oldAnimate = this.animate;
8954         this.animate = false;
8955         this.adapter.setElementSize(this, size);
8956         this.animate = oldAnimate;
8957     },
8958     
8959     /**
8960      * Destroy this splitbar. 
8961      * @param {Boolean} removeEl True to remove the element
8962      */
8963     destroy : function(removeEl){
8964         if(this.shim){
8965             this.shim.remove();
8966         }
8967         this.dd.unreg();
8968         this.proxy.parentNode.removeChild(this.proxy);
8969         if(removeEl){
8970             this.el.remove();
8971         }
8972     }
8973 });
8974
8975 /**
8976  * @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.
8977  */
8978 Roo.SplitBar.createProxy = function(dir){
8979     var proxy = new Roo.Element(document.createElement("div"));
8980     proxy.unselectable();
8981     var cls = 'x-splitbar-proxy';
8982     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
8983     document.body.appendChild(proxy.dom);
8984     return proxy.dom;
8985 };
8986
8987 /** 
8988  * @class Roo.SplitBar.BasicLayoutAdapter
8989  * Default Adapter. It assumes the splitter and resizing element are not positioned
8990  * elements and only gets/sets the width of the element. Generally used for table based layouts.
8991  */
8992 Roo.SplitBar.BasicLayoutAdapter = function(){
8993 };
8994
8995 Roo.SplitBar.BasicLayoutAdapter.prototype = {
8996     // do nothing for now
8997     init : function(s){
8998     
8999     },
9000     /**
9001      * Called before drag operations to get the current size of the resizing element. 
9002      * @param {Roo.SplitBar} s The SplitBar using this adapter
9003      */
9004      getElementSize : function(s){
9005         if(s.orientation == Roo.SplitBar.HORIZONTAL){
9006             return s.resizingEl.getWidth();
9007         }else{
9008             return s.resizingEl.getHeight();
9009         }
9010     },
9011     
9012     /**
9013      * Called after drag operations to set the size of the resizing element.
9014      * @param {Roo.SplitBar} s The SplitBar using this adapter
9015      * @param {Number} newSize The new size to set
9016      * @param {Function} onComplete A function to be invoked when resizing is complete
9017      */
9018     setElementSize : function(s, newSize, onComplete){
9019         if(s.orientation == Roo.SplitBar.HORIZONTAL){
9020             if(!s.animate){
9021                 s.resizingEl.setWidth(newSize);
9022                 if(onComplete){
9023                     onComplete(s, newSize);
9024                 }
9025             }else{
9026                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
9027             }
9028         }else{
9029             
9030             if(!s.animate){
9031                 s.resizingEl.setHeight(newSize);
9032                 if(onComplete){
9033                     onComplete(s, newSize);
9034                 }
9035             }else{
9036                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
9037             }
9038         }
9039     }
9040 };
9041
9042 /** 
9043  *@class Roo.SplitBar.AbsoluteLayoutAdapter
9044  * @extends Roo.SplitBar.BasicLayoutAdapter
9045  * Adapter that  moves the splitter element to align with the resized sizing element. 
9046  * Used with an absolute positioned SplitBar.
9047  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
9048  * document.body, make sure you assign an id to the body element.
9049  */
9050 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
9051     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
9052     this.container = Roo.get(container);
9053 };
9054
9055 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
9056     init : function(s){
9057         this.basic.init(s);
9058     },
9059     
9060     getElementSize : function(s){
9061         return this.basic.getElementSize(s);
9062     },
9063     
9064     setElementSize : function(s, newSize, onComplete){
9065         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
9066     },
9067     
9068     moveSplitter : function(s){
9069         var yes = Roo.SplitBar;
9070         switch(s.placement){
9071             case yes.LEFT:
9072                 s.el.setX(s.resizingEl.getRight());
9073                 break;
9074             case yes.RIGHT:
9075                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
9076                 break;
9077             case yes.TOP:
9078                 s.el.setY(s.resizingEl.getBottom());
9079                 break;
9080             case yes.BOTTOM:
9081                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
9082                 break;
9083         }
9084     }
9085 };
9086
9087 /**
9088  * Orientation constant - Create a vertical SplitBar
9089  * @static
9090  * @type Number
9091  */
9092 Roo.SplitBar.VERTICAL = 1;
9093
9094 /**
9095  * Orientation constant - Create a horizontal SplitBar
9096  * @static
9097  * @type Number
9098  */
9099 Roo.SplitBar.HORIZONTAL = 2;
9100
9101 /**
9102  * Placement constant - The resizing element is to the left of the splitter element
9103  * @static
9104  * @type Number
9105  */
9106 Roo.SplitBar.LEFT = 1;
9107
9108 /**
9109  * Placement constant - The resizing element is to the right of the splitter element
9110  * @static
9111  * @type Number
9112  */
9113 Roo.SplitBar.RIGHT = 2;
9114
9115 /**
9116  * Placement constant - The resizing element is positioned above the splitter element
9117  * @static
9118  * @type Number
9119  */
9120 Roo.SplitBar.TOP = 3;
9121
9122 /**
9123  * Placement constant - The resizing element is positioned under splitter element
9124  * @static
9125  * @type Number
9126  */
9127 Roo.SplitBar.BOTTOM = 4;
9128 /*
9129  * Based on:
9130  * Ext JS Library 1.1.1
9131  * Copyright(c) 2006-2007, Ext JS, LLC.
9132  *
9133  * Originally Released Under LGPL - original licence link has changed is not relivant.
9134  *
9135  * Fork - LGPL
9136  * <script type="text/javascript">
9137  */
9138
9139 /**
9140  * @class Roo.View
9141  * @extends Roo.util.Observable
9142  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
9143  * This class also supports single and multi selection modes. <br>
9144  * Create a data model bound view:
9145  <pre><code>
9146  var store = new Roo.data.Store(...);
9147
9148  var view = new Roo.View({
9149     el : "my-element",
9150     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
9151  
9152     singleSelect: true,
9153     selectedClass: "ydataview-selected",
9154     store: store
9155  });
9156
9157  // listen for node click?
9158  view.on("click", function(vw, index, node, e){
9159  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9160  });
9161
9162  // load XML data
9163  dataModel.load("foobar.xml");
9164  </code></pre>
9165  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9166  * <br><br>
9167  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9168  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9169  * 
9170  * Note: old style constructor is still suported (container, template, config)
9171  * 
9172  * @constructor
9173  * Create a new View
9174  * @param {Object} config The config object
9175  * 
9176  */
9177 Roo.View = function(config, depreciated_tpl, depreciated_config){
9178     
9179     if (typeof(depreciated_tpl) == 'undefined') {
9180         // new way.. - universal constructor.
9181         Roo.apply(this, config);
9182         this.el  = Roo.get(this.el);
9183     } else {
9184         // old format..
9185         this.el  = Roo.get(config);
9186         this.tpl = depreciated_tpl;
9187         Roo.apply(this, depreciated_config);
9188     }
9189     this.wrapEl  = this.el.wrap().wrap();
9190     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
9191     
9192     
9193     if(typeof(this.tpl) == "string"){
9194         this.tpl = new Roo.Template(this.tpl);
9195     } else {
9196         // support xtype ctors..
9197         this.tpl = new Roo.factory(this.tpl, Roo);
9198     }
9199     
9200     
9201     this.tpl.compile();
9202    
9203   
9204     
9205      
9206     /** @private */
9207     this.addEvents({
9208         /**
9209          * @event beforeclick
9210          * Fires before a click is processed. Returns false to cancel the default action.
9211          * @param {Roo.View} this
9212          * @param {Number} index The index of the target node
9213          * @param {HTMLElement} node The target node
9214          * @param {Roo.EventObject} e The raw event object
9215          */
9216             "beforeclick" : true,
9217         /**
9218          * @event click
9219          * Fires when a template node is clicked.
9220          * @param {Roo.View} this
9221          * @param {Number} index The index of the target node
9222          * @param {HTMLElement} node The target node
9223          * @param {Roo.EventObject} e The raw event object
9224          */
9225             "click" : true,
9226         /**
9227          * @event dblclick
9228          * Fires when a template node is double clicked.
9229          * @param {Roo.View} this
9230          * @param {Number} index The index of the target node
9231          * @param {HTMLElement} node The target node
9232          * @param {Roo.EventObject} e The raw event object
9233          */
9234             "dblclick" : true,
9235         /**
9236          * @event contextmenu
9237          * Fires when a template node is right clicked.
9238          * @param {Roo.View} this
9239          * @param {Number} index The index of the target node
9240          * @param {HTMLElement} node The target node
9241          * @param {Roo.EventObject} e The raw event object
9242          */
9243             "contextmenu" : true,
9244         /**
9245          * @event selectionchange
9246          * Fires when the selected nodes change.
9247          * @param {Roo.View} this
9248          * @param {Array} selections Array of the selected nodes
9249          */
9250             "selectionchange" : true,
9251     
9252         /**
9253          * @event beforeselect
9254          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9255          * @param {Roo.View} this
9256          * @param {HTMLElement} node The node to be selected
9257          * @param {Array} selections Array of currently selected nodes
9258          */
9259             "beforeselect" : true,
9260         /**
9261          * @event preparedata
9262          * Fires on every row to render, to allow you to change the data.
9263          * @param {Roo.View} this
9264          * @param {Object} data to be rendered (change this)
9265          */
9266           "preparedata" : true
9267           
9268           
9269         });
9270
9271
9272
9273     this.el.on({
9274         "click": this.onClick,
9275         "dblclick": this.onDblClick,
9276         "contextmenu": this.onContextMenu,
9277         scope:this
9278     });
9279
9280     this.selections = [];
9281     this.nodes = [];
9282     this.cmp = new Roo.CompositeElementLite([]);
9283     if(this.store){
9284         this.store = Roo.factory(this.store, Roo.data);
9285         this.setStore(this.store, true);
9286     }
9287     
9288     if ( this.footer && this.footer.xtype) {
9289            
9290          var fctr = this.wrapEl.appendChild(document.createElement("div"));
9291         
9292         this.footer.dataSource = this.store
9293         this.footer.container = fctr;
9294         this.footer = Roo.factory(this.footer, Roo);
9295         fctr.insertFirst(this.el);
9296         
9297         // this is a bit insane - as the paging toolbar seems to detach the el..
9298 //        dom.parentNode.parentNode.parentNode
9299          // they get detached?
9300     }
9301     
9302     
9303     Roo.View.superclass.constructor.call(this);
9304     
9305     
9306 };
9307
9308 Roo.extend(Roo.View, Roo.util.Observable, {
9309     
9310      /**
9311      * @cfg {Roo.data.Store} store Data store to load data from.
9312      */
9313     store : false,
9314     
9315     /**
9316      * @cfg {String|Roo.Element} el The container element.
9317      */
9318     el : '',
9319     
9320     /**
9321      * @cfg {String|Roo.Template} tpl The template used by this View 
9322      */
9323     tpl : false,
9324     /**
9325      * @cfg {String} dataName the named area of the template to use as the data area
9326      *                          Works with domtemplates roo-name="name"
9327      */
9328     dataName: false,
9329     /**
9330      * @cfg {String} selectedClass The css class to add to selected nodes
9331      */
9332     selectedClass : "x-view-selected",
9333      /**
9334      * @cfg {String} emptyText The empty text to show when nothing is loaded.
9335      */
9336     emptyText : "",
9337     
9338     /**
9339      * @cfg {String} text to display on mask (default Loading)
9340      */
9341     mask : false,
9342     /**
9343      * @cfg {Boolean} multiSelect Allow multiple selection
9344      */
9345     multiSelect : false,
9346     /**
9347      * @cfg {Boolean} singleSelect Allow single selection
9348      */
9349     singleSelect:  false,
9350     
9351     /**
9352      * @cfg {Boolean} toggleSelect - selecting 
9353      */
9354     toggleSelect : false,
9355     
9356     /**
9357      * Returns the element this view is bound to.
9358      * @return {Roo.Element}
9359      */
9360     getEl : function(){
9361         return this.wrapEl;
9362     },
9363     
9364     
9365
9366     /**
9367      * Refreshes the view. - called by datachanged on the store. - do not call directly.
9368      */
9369     refresh : function(){
9370         var t = this.tpl;
9371         
9372         // if we are using something like 'domtemplate', then
9373         // the what gets used is:
9374         // t.applySubtemplate(NAME, data, wrapping data..)
9375         // the outer template then get' applied with
9376         //     the store 'extra data'
9377         // and the body get's added to the
9378         //      roo-name="data" node?
9379         //      <span class='roo-tpl-{name}'></span> ?????
9380         
9381         
9382         
9383         this.clearSelections();
9384         this.el.update("");
9385         var html = [];
9386         var records = this.store.getRange();
9387         if(records.length < 1) {
9388             
9389             // is this valid??  = should it render a template??
9390             
9391             this.el.update(this.emptyText);
9392             return;
9393         }
9394         var el = this.el;
9395         if (this.dataName) {
9396             this.el.update(t.apply(this.store.meta)); //????
9397             el = this.el.child('.roo-tpl-' + this.dataName);
9398         }
9399         
9400         for(var i = 0, len = records.length; i < len; i++){
9401             var data = this.prepareData(records[i].data, i, records[i]);
9402             this.fireEvent("preparedata", this, data, i, records[i]);
9403             html[html.length] = Roo.util.Format.trim(
9404                 this.dataName ?
9405                     t.applySubtemplate(this.dataName, data, this.store.meta) :
9406                     t.apply(data)
9407             );
9408         }
9409         
9410         
9411         
9412         el.update(html.join(""));
9413         this.nodes = el.dom.childNodes;
9414         this.updateIndexes(0);
9415     },
9416
9417     /**
9418      * Function to override to reformat the data that is sent to
9419      * the template for each node.
9420      * DEPRICATED - use the preparedata event handler.
9421      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9422      * a JSON object for an UpdateManager bound view).
9423      */
9424     prepareData : function(data, index, record)
9425     {
9426         this.fireEvent("preparedata", this, data, index, record);
9427         return data;
9428     },
9429
9430     onUpdate : function(ds, record){
9431         this.clearSelections();
9432         var index = this.store.indexOf(record);
9433         var n = this.nodes[index];
9434         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
9435         n.parentNode.removeChild(n);
9436         this.updateIndexes(index, index);
9437     },
9438
9439     
9440     
9441 // --------- FIXME     
9442     onAdd : function(ds, records, index)
9443     {
9444         this.clearSelections();
9445         if(this.nodes.length == 0){
9446             this.refresh();
9447             return;
9448         }
9449         var n = this.nodes[index];
9450         for(var i = 0, len = records.length; i < len; i++){
9451             var d = this.prepareData(records[i].data, i, records[i]);
9452             if(n){
9453                 this.tpl.insertBefore(n, d);
9454             }else{
9455                 
9456                 this.tpl.append(this.el, d);
9457             }
9458         }
9459         this.updateIndexes(index);
9460     },
9461
9462     onRemove : function(ds, record, index){
9463         this.clearSelections();
9464         var el = this.dataName  ?
9465             this.el.child('.roo-tpl-' + this.dataName) :
9466             this.el; 
9467         el.dom.removeChild(this.nodes[index]);
9468         this.updateIndexes(index);
9469     },
9470
9471     /**
9472      * Refresh an individual node.
9473      * @param {Number} index
9474      */
9475     refreshNode : function(index){
9476         this.onUpdate(this.store, this.store.getAt(index));
9477     },
9478
9479     updateIndexes : function(startIndex, endIndex){
9480         var ns = this.nodes;
9481         startIndex = startIndex || 0;
9482         endIndex = endIndex || ns.length - 1;
9483         for(var i = startIndex; i <= endIndex; i++){
9484             ns[i].nodeIndex = i;
9485         }
9486     },
9487
9488     /**
9489      * Changes the data store this view uses and refresh the view.
9490      * @param {Store} store
9491      */
9492     setStore : function(store, initial){
9493         if(!initial && this.store){
9494             this.store.un("datachanged", this.refresh);
9495             this.store.un("add", this.onAdd);
9496             this.store.un("remove", this.onRemove);
9497             this.store.un("update", this.onUpdate);
9498             this.store.un("clear", this.refresh);
9499             this.store.un("beforeload", this.onBeforeLoad);
9500             this.store.un("load", this.onLoad);
9501             this.store.un("loadexception", this.onLoad);
9502         }
9503         if(store){
9504           
9505             store.on("datachanged", this.refresh, this);
9506             store.on("add", this.onAdd, this);
9507             store.on("remove", this.onRemove, this);
9508             store.on("update", this.onUpdate, this);
9509             store.on("clear", this.refresh, this);
9510             store.on("beforeload", this.onBeforeLoad, this);
9511             store.on("load", this.onLoad, this);
9512             store.on("loadexception", this.onLoad, this);
9513         }
9514         
9515         if(store){
9516             this.refresh();
9517         }
9518     },
9519     /**
9520      * onbeforeLoad - masks the loading area.
9521      *
9522      */
9523     onBeforeLoad : function()
9524     {
9525         this.el.update("");
9526         this.el.mask(this.mask ? this.mask : "Loading" ); 
9527     },
9528     onLoad : function ()
9529     {
9530         this.el.unmask();
9531     },
9532     
9533
9534     /**
9535      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9536      * @param {HTMLElement} node
9537      * @return {HTMLElement} The template node
9538      */
9539     findItemFromChild : function(node){
9540         var el = this.dataName  ?
9541             this.el.child('.roo-tpl-' + this.dataName,true) :
9542             this.el.dom; 
9543         
9544         if(!node || node.parentNode == el){
9545                     return node;
9546             }
9547             var p = node.parentNode;
9548             while(p && p != el){
9549             if(p.parentNode == el){
9550                 return p;
9551             }
9552             p = p.parentNode;
9553         }
9554             return null;
9555     },
9556
9557     /** @ignore */
9558     onClick : function(e){
9559         var item = this.findItemFromChild(e.getTarget());
9560         if(item){
9561             var index = this.indexOf(item);
9562             if(this.onItemClick(item, index, e) !== false){
9563                 this.fireEvent("click", this, index, item, e);
9564             }
9565         }else{
9566             this.clearSelections();
9567         }
9568     },
9569
9570     /** @ignore */
9571     onContextMenu : function(e){
9572         var item = this.findItemFromChild(e.getTarget());
9573         if(item){
9574             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9575         }
9576     },
9577
9578     /** @ignore */
9579     onDblClick : function(e){
9580         var item = this.findItemFromChild(e.getTarget());
9581         if(item){
9582             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9583         }
9584     },
9585
9586     onItemClick : function(item, index, e)
9587     {
9588         if(this.fireEvent("beforeclick", this, index, item, e) === false){
9589             return false;
9590         }
9591         if (this.toggleSelect) {
9592             var m = this.isSelected(item) ? 'unselect' : 'select';
9593             Roo.log(m);
9594             var _t = this;
9595             _t[m](item, true, false);
9596             return true;
9597         }
9598         if(this.multiSelect || this.singleSelect){
9599             if(this.multiSelect && e.shiftKey && this.lastSelection){
9600                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9601             }else{
9602                 this.select(item, this.multiSelect && e.ctrlKey);
9603                 this.lastSelection = item;
9604             }
9605             e.preventDefault();
9606         }
9607         return true;
9608     },
9609
9610     /**
9611      * Get the number of selected nodes.
9612      * @return {Number}
9613      */
9614     getSelectionCount : function(){
9615         return this.selections.length;
9616     },
9617
9618     /**
9619      * Get the currently selected nodes.
9620      * @return {Array} An array of HTMLElements
9621      */
9622     getSelectedNodes : function(){
9623         return this.selections;
9624     },
9625
9626     /**
9627      * Get the indexes of the selected nodes.
9628      * @return {Array}
9629      */
9630     getSelectedIndexes : function(){
9631         var indexes = [], s = this.selections;
9632         for(var i = 0, len = s.length; i < len; i++){
9633             indexes.push(s[i].nodeIndex);
9634         }
9635         return indexes;
9636     },
9637
9638     /**
9639      * Clear all selections
9640      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9641      */
9642     clearSelections : function(suppressEvent){
9643         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9644             this.cmp.elements = this.selections;
9645             this.cmp.removeClass(this.selectedClass);
9646             this.selections = [];
9647             if(!suppressEvent){
9648                 this.fireEvent("selectionchange", this, this.selections);
9649             }
9650         }
9651     },
9652
9653     /**
9654      * Returns true if the passed node is selected
9655      * @param {HTMLElement/Number} node The node or node index
9656      * @return {Boolean}
9657      */
9658     isSelected : function(node){
9659         var s = this.selections;
9660         if(s.length < 1){
9661             return false;
9662         }
9663         node = this.getNode(node);
9664         return s.indexOf(node) !== -1;
9665     },
9666
9667     /**
9668      * Selects nodes.
9669      * @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
9670      * @param {Boolean} keepExisting (optional) true to keep existing selections
9671      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9672      */
9673     select : function(nodeInfo, keepExisting, suppressEvent){
9674         if(nodeInfo instanceof Array){
9675             if(!keepExisting){
9676                 this.clearSelections(true);
9677             }
9678             for(var i = 0, len = nodeInfo.length; i < len; i++){
9679                 this.select(nodeInfo[i], true, true);
9680             }
9681             return;
9682         } 
9683         var node = this.getNode(nodeInfo);
9684         if(!node || this.isSelected(node)){
9685             return; // already selected.
9686         }
9687         if(!keepExisting){
9688             this.clearSelections(true);
9689         }
9690         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9691             Roo.fly(node).addClass(this.selectedClass);
9692             this.selections.push(node);
9693             if(!suppressEvent){
9694                 this.fireEvent("selectionchange", this, this.selections);
9695             }
9696         }
9697         
9698         
9699     },
9700       /**
9701      * Unselects nodes.
9702      * @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
9703      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
9704      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9705      */
9706     unselect : function(nodeInfo, keepExisting, suppressEvent)
9707     {
9708         if(nodeInfo instanceof Array){
9709             Roo.each(this.selections, function(s) {
9710                 this.unselect(s, nodeInfo);
9711             }, this);
9712             return;
9713         }
9714         var node = this.getNode(nodeInfo);
9715         if(!node || !this.isSelected(node)){
9716             Roo.log("not selected");
9717             return; // not selected.
9718         }
9719         // fireevent???
9720         var ns = [];
9721         Roo.each(this.selections, function(s) {
9722             if (s == node ) {
9723                 Roo.fly(node).removeClass(this.selectedClass);
9724
9725                 return;
9726             }
9727             ns.push(s);
9728         },this);
9729         
9730         this.selections= ns;
9731         this.fireEvent("selectionchange", this, this.selections);
9732     },
9733
9734     /**
9735      * Gets a template node.
9736      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9737      * @return {HTMLElement} The node or null if it wasn't found
9738      */
9739     getNode : function(nodeInfo){
9740         if(typeof nodeInfo == "string"){
9741             return document.getElementById(nodeInfo);
9742         }else if(typeof nodeInfo == "number"){
9743             return this.nodes[nodeInfo];
9744         }
9745         return nodeInfo;
9746     },
9747
9748     /**
9749      * Gets a range template nodes.
9750      * @param {Number} startIndex
9751      * @param {Number} endIndex
9752      * @return {Array} An array of nodes
9753      */
9754     getNodes : function(start, end){
9755         var ns = this.nodes;
9756         start = start || 0;
9757         end = typeof end == "undefined" ? ns.length - 1 : end;
9758         var nodes = [];
9759         if(start <= end){
9760             for(var i = start; i <= end; i++){
9761                 nodes.push(ns[i]);
9762             }
9763         } else{
9764             for(var i = start; i >= end; i--){
9765                 nodes.push(ns[i]);
9766             }
9767         }
9768         return nodes;
9769     },
9770
9771     /**
9772      * Finds the index of the passed node
9773      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9774      * @return {Number} The index of the node or -1
9775      */
9776     indexOf : function(node){
9777         node = this.getNode(node);
9778         if(typeof node.nodeIndex == "number"){
9779             return node.nodeIndex;
9780         }
9781         var ns = this.nodes;
9782         for(var i = 0, len = ns.length; i < len; i++){
9783             if(ns[i] == node){
9784                 return i;
9785             }
9786         }
9787         return -1;
9788     }
9789 });
9790 /*
9791  * Based on:
9792  * Ext JS Library 1.1.1
9793  * Copyright(c) 2006-2007, Ext JS, LLC.
9794  *
9795  * Originally Released Under LGPL - original licence link has changed is not relivant.
9796  *
9797  * Fork - LGPL
9798  * <script type="text/javascript">
9799  */
9800
9801 /**
9802  * @class Roo.JsonView
9803  * @extends Roo.View
9804  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
9805 <pre><code>
9806 var view = new Roo.JsonView({
9807     container: "my-element",
9808     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
9809     multiSelect: true, 
9810     jsonRoot: "data" 
9811 });
9812
9813 // listen for node click?
9814 view.on("click", function(vw, index, node, e){
9815     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9816 });
9817
9818 // direct load of JSON data
9819 view.load("foobar.php");
9820
9821 // Example from my blog list
9822 var tpl = new Roo.Template(
9823     '&lt;div class="entry"&gt;' +
9824     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
9825     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
9826     "&lt;/div&gt;&lt;hr /&gt;"
9827 );
9828
9829 var moreView = new Roo.JsonView({
9830     container :  "entry-list", 
9831     template : tpl,
9832     jsonRoot: "posts"
9833 });
9834 moreView.on("beforerender", this.sortEntries, this);
9835 moreView.load({
9836     url: "/blog/get-posts.php",
9837     params: "allposts=true",
9838     text: "Loading Blog Entries..."
9839 });
9840 </code></pre>
9841
9842 * Note: old code is supported with arguments : (container, template, config)
9843
9844
9845  * @constructor
9846  * Create a new JsonView
9847  * 
9848  * @param {Object} config The config object
9849  * 
9850  */
9851 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
9852     
9853     
9854     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
9855
9856     var um = this.el.getUpdateManager();
9857     um.setRenderer(this);
9858     um.on("update", this.onLoad, this);
9859     um.on("failure", this.onLoadException, this);
9860
9861     /**
9862      * @event beforerender
9863      * Fires before rendering of the downloaded JSON data.
9864      * @param {Roo.JsonView} this
9865      * @param {Object} data The JSON data loaded
9866      */
9867     /**
9868      * @event load
9869      * Fires when data is loaded.
9870      * @param {Roo.JsonView} this
9871      * @param {Object} data The JSON data loaded
9872      * @param {Object} response The raw Connect response object
9873      */
9874     /**
9875      * @event loadexception
9876      * Fires when loading fails.
9877      * @param {Roo.JsonView} this
9878      * @param {Object} response The raw Connect response object
9879      */
9880     this.addEvents({
9881         'beforerender' : true,
9882         'load' : true,
9883         'loadexception' : true
9884     });
9885 };
9886 Roo.extend(Roo.JsonView, Roo.View, {
9887     /**
9888      * @type {String} The root property in the loaded JSON object that contains the data
9889      */
9890     jsonRoot : "",
9891
9892     /**
9893      * Refreshes the view.
9894      */
9895     refresh : function(){
9896         this.clearSelections();
9897         this.el.update("");
9898         var html = [];
9899         var o = this.jsonData;
9900         if(o && o.length > 0){
9901             for(var i = 0, len = o.length; i < len; i++){
9902                 var data = this.prepareData(o[i], i, o);
9903                 html[html.length] = this.tpl.apply(data);
9904             }
9905         }else{
9906             html.push(this.emptyText);
9907         }
9908         this.el.update(html.join(""));
9909         this.nodes = this.el.dom.childNodes;
9910         this.updateIndexes(0);
9911     },
9912
9913     /**
9914      * 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.
9915      * @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:
9916      <pre><code>
9917      view.load({
9918          url: "your-url.php",
9919          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
9920          callback: yourFunction,
9921          scope: yourObject, //(optional scope)
9922          discardUrl: false,
9923          nocache: false,
9924          text: "Loading...",
9925          timeout: 30,
9926          scripts: false
9927      });
9928      </code></pre>
9929      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
9930      * 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.
9931      * @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}
9932      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9933      * @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.
9934      */
9935     load : function(){
9936         var um = this.el.getUpdateManager();
9937         um.update.apply(um, arguments);
9938     },
9939
9940     render : function(el, response){
9941         this.clearSelections();
9942         this.el.update("");
9943         var o;
9944         try{
9945             o = Roo.util.JSON.decode(response.responseText);
9946             if(this.jsonRoot){
9947                 
9948                 o = o[this.jsonRoot];
9949             }
9950         } catch(e){
9951         }
9952         /**
9953          * The current JSON data or null
9954          */
9955         this.jsonData = o;
9956         this.beforeRender();
9957         this.refresh();
9958     },
9959
9960 /**
9961  * Get the number of records in the current JSON dataset
9962  * @return {Number}
9963  */
9964     getCount : function(){
9965         return this.jsonData ? this.jsonData.length : 0;
9966     },
9967
9968 /**
9969  * Returns the JSON object for the specified node(s)
9970  * @param {HTMLElement/Array} node The node or an array of nodes
9971  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
9972  * you get the JSON object for the node
9973  */
9974     getNodeData : function(node){
9975         if(node instanceof Array){
9976             var data = [];
9977             for(var i = 0, len = node.length; i < len; i++){
9978                 data.push(this.getNodeData(node[i]));
9979             }
9980             return data;
9981         }
9982         return this.jsonData[this.indexOf(node)] || null;
9983     },
9984
9985     beforeRender : function(){
9986         this.snapshot = this.jsonData;
9987         if(this.sortInfo){
9988             this.sort.apply(this, this.sortInfo);
9989         }
9990         this.fireEvent("beforerender", this, this.jsonData);
9991     },
9992
9993     onLoad : function(el, o){
9994         this.fireEvent("load", this, this.jsonData, o);
9995     },
9996
9997     onLoadException : function(el, o){
9998         this.fireEvent("loadexception", this, o);
9999     },
10000
10001 /**
10002  * Filter the data by a specific property.
10003  * @param {String} property A property on your JSON objects
10004  * @param {String/RegExp} value Either string that the property values
10005  * should start with, or a RegExp to test against the property
10006  */
10007     filter : function(property, value){
10008         if(this.jsonData){
10009             var data = [];
10010             var ss = this.snapshot;
10011             if(typeof value == "string"){
10012                 var vlen = value.length;
10013                 if(vlen == 0){
10014                     this.clearFilter();
10015                     return;
10016                 }
10017                 value = value.toLowerCase();
10018                 for(var i = 0, len = ss.length; i < len; i++){
10019                     var o = ss[i];
10020                     if(o[property].substr(0, vlen).toLowerCase() == value){
10021                         data.push(o);
10022                     }
10023                 }
10024             } else if(value.exec){ // regex?
10025                 for(var i = 0, len = ss.length; i < len; i++){
10026                     var o = ss[i];
10027                     if(value.test(o[property])){
10028                         data.push(o);
10029                     }
10030                 }
10031             } else{
10032                 return;
10033             }
10034             this.jsonData = data;
10035             this.refresh();
10036         }
10037     },
10038
10039 /**
10040  * Filter by a function. The passed function will be called with each
10041  * object in the current dataset. If the function returns true the value is kept,
10042  * otherwise it is filtered.
10043  * @param {Function} fn
10044  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
10045  */
10046     filterBy : function(fn, scope){
10047         if(this.jsonData){
10048             var data = [];
10049             var ss = this.snapshot;
10050             for(var i = 0, len = ss.length; i < len; i++){
10051                 var o = ss[i];
10052                 if(fn.call(scope || this, o)){
10053                     data.push(o);
10054                 }
10055             }
10056             this.jsonData = data;
10057             this.refresh();
10058         }
10059     },
10060
10061 /**
10062  * Clears the current filter.
10063  */
10064     clearFilter : function(){
10065         if(this.snapshot && this.jsonData != this.snapshot){
10066             this.jsonData = this.snapshot;
10067             this.refresh();
10068         }
10069     },
10070
10071
10072 /**
10073  * Sorts the data for this view and refreshes it.
10074  * @param {String} property A property on your JSON objects to sort on
10075  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
10076  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
10077  */
10078     sort : function(property, dir, sortType){
10079         this.sortInfo = Array.prototype.slice.call(arguments, 0);
10080         if(this.jsonData){
10081             var p = property;
10082             var dsc = dir && dir.toLowerCase() == "desc";
10083             var f = function(o1, o2){
10084                 var v1 = sortType ? sortType(o1[p]) : o1[p];
10085                 var v2 = sortType ? sortType(o2[p]) : o2[p];
10086                 ;
10087                 if(v1 < v2){
10088                     return dsc ? +1 : -1;
10089                 } else if(v1 > v2){
10090                     return dsc ? -1 : +1;
10091                 } else{
10092                     return 0;
10093                 }
10094             };
10095             this.jsonData.sort(f);
10096             this.refresh();
10097             if(this.jsonData != this.snapshot){
10098                 this.snapshot.sort(f);
10099             }
10100         }
10101     }
10102 });/*
10103  * Based on:
10104  * Ext JS Library 1.1.1
10105  * Copyright(c) 2006-2007, Ext JS, LLC.
10106  *
10107  * Originally Released Under LGPL - original licence link has changed is not relivant.
10108  *
10109  * Fork - LGPL
10110  * <script type="text/javascript">
10111  */
10112  
10113
10114 /**
10115  * @class Roo.ColorPalette
10116  * @extends Roo.Component
10117  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
10118  * Here's an example of typical usage:
10119  * <pre><code>
10120 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
10121 cp.render('my-div');
10122
10123 cp.on('select', function(palette, selColor){
10124     // do something with selColor
10125 });
10126 </code></pre>
10127  * @constructor
10128  * Create a new ColorPalette
10129  * @param {Object} config The config object
10130  */
10131 Roo.ColorPalette = function(config){
10132     Roo.ColorPalette.superclass.constructor.call(this, config);
10133     this.addEvents({
10134         /**
10135              * @event select
10136              * Fires when a color is selected
10137              * @param {ColorPalette} this
10138              * @param {String} color The 6-digit color hex code (without the # symbol)
10139              */
10140         select: true
10141     });
10142
10143     if(this.handler){
10144         this.on("select", this.handler, this.scope, true);
10145     }
10146 };
10147 Roo.extend(Roo.ColorPalette, Roo.Component, {
10148     /**
10149      * @cfg {String} itemCls
10150      * The CSS class to apply to the containing element (defaults to "x-color-palette")
10151      */
10152     itemCls : "x-color-palette",
10153     /**
10154      * @cfg {String} value
10155      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
10156      * the hex codes are case-sensitive.
10157      */
10158     value : null,
10159     clickEvent:'click',
10160     // private
10161     ctype: "Roo.ColorPalette",
10162
10163     /**
10164      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
10165      */
10166     allowReselect : false,
10167
10168     /**
10169      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
10170      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
10171      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
10172      * of colors with the width setting until the box is symmetrical.</p>
10173      * <p>You can override individual colors if needed:</p>
10174      * <pre><code>
10175 var cp = new Roo.ColorPalette();
10176 cp.colors[0] = "FF0000";  // change the first box to red
10177 </code></pre>
10178
10179 Or you can provide a custom array of your own for complete control:
10180 <pre><code>
10181 var cp = new Roo.ColorPalette();
10182 cp.colors = ["000000", "993300", "333300"];
10183 </code></pre>
10184      * @type Array
10185      */
10186     colors : [
10187         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
10188         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
10189         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
10190         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
10191         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
10192     ],
10193
10194     // private
10195     onRender : function(container, position){
10196         var t = new Roo.MasterTemplate(
10197             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
10198         );
10199         var c = this.colors;
10200         for(var i = 0, len = c.length; i < len; i++){
10201             t.add([c[i]]);
10202         }
10203         var el = document.createElement("div");
10204         el.className = this.itemCls;
10205         t.overwrite(el);
10206         container.dom.insertBefore(el, position);
10207         this.el = Roo.get(el);
10208         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
10209         if(this.clickEvent != 'click'){
10210             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
10211         }
10212     },
10213
10214     // private
10215     afterRender : function(){
10216         Roo.ColorPalette.superclass.afterRender.call(this);
10217         if(this.value){
10218             var s = this.value;
10219             this.value = null;
10220             this.select(s);
10221         }
10222     },
10223
10224     // private
10225     handleClick : function(e, t){
10226         e.preventDefault();
10227         if(!this.disabled){
10228             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
10229             this.select(c.toUpperCase());
10230         }
10231     },
10232
10233     /**
10234      * Selects the specified color in the palette (fires the select event)
10235      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
10236      */
10237     select : function(color){
10238         color = color.replace("#", "");
10239         if(color != this.value || this.allowReselect){
10240             var el = this.el;
10241             if(this.value){
10242                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
10243             }
10244             el.child("a.color-"+color).addClass("x-color-palette-sel");
10245             this.value = color;
10246             this.fireEvent("select", this, color);
10247         }
10248     }
10249 });/*
10250  * Based on:
10251  * Ext JS Library 1.1.1
10252  * Copyright(c) 2006-2007, Ext JS, LLC.
10253  *
10254  * Originally Released Under LGPL - original licence link has changed is not relivant.
10255  *
10256  * Fork - LGPL
10257  * <script type="text/javascript">
10258  */
10259  
10260 /**
10261  * @class Roo.DatePicker
10262  * @extends Roo.Component
10263  * Simple date picker class.
10264  * @constructor
10265  * Create a new DatePicker
10266  * @param {Object} config The config object
10267  */
10268 Roo.DatePicker = function(config){
10269     Roo.DatePicker.superclass.constructor.call(this, config);
10270
10271     this.value = config && config.value ?
10272                  config.value.clearTime() : new Date().clearTime();
10273
10274     this.addEvents({
10275         /**
10276              * @event select
10277              * Fires when a date is selected
10278              * @param {DatePicker} this
10279              * @param {Date} date The selected date
10280              */
10281         'select': true,
10282         /**
10283              * @event monthchange
10284              * Fires when the displayed month changes 
10285              * @param {DatePicker} this
10286              * @param {Date} date The selected month
10287              */
10288         'monthchange': true
10289     });
10290
10291     if(this.handler){
10292         this.on("select", this.handler,  this.scope || this);
10293     }
10294     // build the disabledDatesRE
10295     if(!this.disabledDatesRE && this.disabledDates){
10296         var dd = this.disabledDates;
10297         var re = "(?:";
10298         for(var i = 0; i < dd.length; i++){
10299             re += dd[i];
10300             if(i != dd.length-1) re += "|";
10301         }
10302         this.disabledDatesRE = new RegExp(re + ")");
10303     }
10304 };
10305
10306 Roo.extend(Roo.DatePicker, Roo.Component, {
10307     /**
10308      * @cfg {String} todayText
10309      * The text to display on the button that selects the current date (defaults to "Today")
10310      */
10311     todayText : "Today",
10312     /**
10313      * @cfg {String} okText
10314      * The text to display on the ok button
10315      */
10316     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
10317     /**
10318      * @cfg {String} cancelText
10319      * The text to display on the cancel button
10320      */
10321     cancelText : "Cancel",
10322     /**
10323      * @cfg {String} todayTip
10324      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
10325      */
10326     todayTip : "{0} (Spacebar)",
10327     /**
10328      * @cfg {Date} minDate
10329      * Minimum allowable date (JavaScript date object, defaults to null)
10330      */
10331     minDate : null,
10332     /**
10333      * @cfg {Date} maxDate
10334      * Maximum allowable date (JavaScript date object, defaults to null)
10335      */
10336     maxDate : null,
10337     /**
10338      * @cfg {String} minText
10339      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
10340      */
10341     minText : "This date is before the minimum date",
10342     /**
10343      * @cfg {String} maxText
10344      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
10345      */
10346     maxText : "This date is after the maximum date",
10347     /**
10348      * @cfg {String} format
10349      * The default date format string which can be overriden for localization support.  The format must be
10350      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
10351      */
10352     format : "m/d/y",
10353     /**
10354      * @cfg {Array} disabledDays
10355      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
10356      */
10357     disabledDays : null,
10358     /**
10359      * @cfg {String} disabledDaysText
10360      * The tooltip to display when the date falls on a disabled day (defaults to "")
10361      */
10362     disabledDaysText : "",
10363     /**
10364      * @cfg {RegExp} disabledDatesRE
10365      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
10366      */
10367     disabledDatesRE : null,
10368     /**
10369      * @cfg {String} disabledDatesText
10370      * The tooltip text to display when the date falls on a disabled date (defaults to "")
10371      */
10372     disabledDatesText : "",
10373     /**
10374      * @cfg {Boolean} constrainToViewport
10375      * True to constrain the date picker to the viewport (defaults to true)
10376      */
10377     constrainToViewport : true,
10378     /**
10379      * @cfg {Array} monthNames
10380      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
10381      */
10382     monthNames : Date.monthNames,
10383     /**
10384      * @cfg {Array} dayNames
10385      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
10386      */
10387     dayNames : Date.dayNames,
10388     /**
10389      * @cfg {String} nextText
10390      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
10391      */
10392     nextText: 'Next Month (Control+Right)',
10393     /**
10394      * @cfg {String} prevText
10395      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
10396      */
10397     prevText: 'Previous Month (Control+Left)',
10398     /**
10399      * @cfg {String} monthYearText
10400      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
10401      */
10402     monthYearText: 'Choose a month (Control+Up/Down to move years)',
10403     /**
10404      * @cfg {Number} startDay
10405      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
10406      */
10407     startDay : 0,
10408     /**
10409      * @cfg {Bool} showClear
10410      * Show a clear button (usefull for date form elements that can be blank.)
10411      */
10412     
10413     showClear: false,
10414     
10415     /**
10416      * Sets the value of the date field
10417      * @param {Date} value The date to set
10418      */
10419     setValue : function(value){
10420         var old = this.value;
10421         
10422         if (typeof(value) == 'string') {
10423          
10424             value = Date.parseDate(value, this.format);
10425         }
10426         if (!value) {
10427             value = new Date();
10428         }
10429         
10430         this.value = value.clearTime(true);
10431         if(this.el){
10432             this.update(this.value);
10433         }
10434     },
10435
10436     /**
10437      * Gets the current selected value of the date field
10438      * @return {Date} The selected date
10439      */
10440     getValue : function(){
10441         return this.value;
10442     },
10443
10444     // private
10445     focus : function(){
10446         if(this.el){
10447             this.update(this.activeDate);
10448         }
10449     },
10450
10451     // privateval
10452     onRender : function(container, position){
10453         
10454         var m = [
10455              '<table cellspacing="0">',
10456                 '<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>',
10457                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
10458         var dn = this.dayNames;
10459         for(var i = 0; i < 7; i++){
10460             var d = this.startDay+i;
10461             if(d > 6){
10462                 d = d-7;
10463             }
10464             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
10465         }
10466         m[m.length] = "</tr></thead><tbody><tr>";
10467         for(var i = 0; i < 42; i++) {
10468             if(i % 7 == 0 && i != 0){
10469                 m[m.length] = "</tr><tr>";
10470             }
10471             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
10472         }
10473         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
10474             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
10475
10476         var el = document.createElement("div");
10477         el.className = "x-date-picker";
10478         el.innerHTML = m.join("");
10479
10480         container.dom.insertBefore(el, position);
10481
10482         this.el = Roo.get(el);
10483         this.eventEl = Roo.get(el.firstChild);
10484
10485         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
10486             handler: this.showPrevMonth,
10487             scope: this,
10488             preventDefault:true,
10489             stopDefault:true
10490         });
10491
10492         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
10493             handler: this.showNextMonth,
10494             scope: this,
10495             preventDefault:true,
10496             stopDefault:true
10497         });
10498
10499         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
10500
10501         this.monthPicker = this.el.down('div.x-date-mp');
10502         this.monthPicker.enableDisplayMode('block');
10503         
10504         var kn = new Roo.KeyNav(this.eventEl, {
10505             "left" : function(e){
10506                 e.ctrlKey ?
10507                     this.showPrevMonth() :
10508                     this.update(this.activeDate.add("d", -1));
10509             },
10510
10511             "right" : function(e){
10512                 e.ctrlKey ?
10513                     this.showNextMonth() :
10514                     this.update(this.activeDate.add("d", 1));
10515             },
10516
10517             "up" : function(e){
10518                 e.ctrlKey ?
10519                     this.showNextYear() :
10520                     this.update(this.activeDate.add("d", -7));
10521             },
10522
10523             "down" : function(e){
10524                 e.ctrlKey ?
10525                     this.showPrevYear() :
10526                     this.update(this.activeDate.add("d", 7));
10527             },
10528
10529             "pageUp" : function(e){
10530                 this.showNextMonth();
10531             },
10532
10533             "pageDown" : function(e){
10534                 this.showPrevMonth();
10535             },
10536
10537             "enter" : function(e){
10538                 e.stopPropagation();
10539                 return true;
10540             },
10541
10542             scope : this
10543         });
10544
10545         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
10546
10547         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
10548
10549         this.el.unselectable();
10550         
10551         this.cells = this.el.select("table.x-date-inner tbody td");
10552         this.textNodes = this.el.query("table.x-date-inner tbody span");
10553
10554         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
10555             text: "&#160;",
10556             tooltip: this.monthYearText
10557         });
10558
10559         this.mbtn.on('click', this.showMonthPicker, this);
10560         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
10561
10562
10563         var today = (new Date()).dateFormat(this.format);
10564         
10565         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
10566         if (this.showClear) {
10567             baseTb.add( new Roo.Toolbar.Fill());
10568         }
10569         baseTb.add({
10570             text: String.format(this.todayText, today),
10571             tooltip: String.format(this.todayTip, today),
10572             handler: this.selectToday,
10573             scope: this
10574         });
10575         
10576         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
10577             
10578         //});
10579         if (this.showClear) {
10580             
10581             baseTb.add( new Roo.Toolbar.Fill());
10582             baseTb.add({
10583                 text: '&#160;',
10584                 cls: 'x-btn-icon x-btn-clear',
10585                 handler: function() {
10586                     //this.value = '';
10587                     this.fireEvent("select", this, '');
10588                 },
10589                 scope: this
10590             });
10591         }
10592         
10593         
10594         if(Roo.isIE){
10595             this.el.repaint();
10596         }
10597         this.update(this.value);
10598     },
10599
10600     createMonthPicker : function(){
10601         if(!this.monthPicker.dom.firstChild){
10602             var buf = ['<table border="0" cellspacing="0">'];
10603             for(var i = 0; i < 6; i++){
10604                 buf.push(
10605                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
10606                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
10607                     i == 0 ?
10608                     '<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>' :
10609                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
10610                 );
10611             }
10612             buf.push(
10613                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
10614                     this.okText,
10615                     '</button><button type="button" class="x-date-mp-cancel">',
10616                     this.cancelText,
10617                     '</button></td></tr>',
10618                 '</table>'
10619             );
10620             this.monthPicker.update(buf.join(''));
10621             this.monthPicker.on('click', this.onMonthClick, this);
10622             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
10623
10624             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
10625             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
10626
10627             this.mpMonths.each(function(m, a, i){
10628                 i += 1;
10629                 if((i%2) == 0){
10630                     m.dom.xmonth = 5 + Math.round(i * .5);
10631                 }else{
10632                     m.dom.xmonth = Math.round((i-1) * .5);
10633                 }
10634             });
10635         }
10636     },
10637
10638     showMonthPicker : function(){
10639         this.createMonthPicker();
10640         var size = this.el.getSize();
10641         this.monthPicker.setSize(size);
10642         this.monthPicker.child('table').setSize(size);
10643
10644         this.mpSelMonth = (this.activeDate || this.value).getMonth();
10645         this.updateMPMonth(this.mpSelMonth);
10646         this.mpSelYear = (this.activeDate || this.value).getFullYear();
10647         this.updateMPYear(this.mpSelYear);
10648
10649         this.monthPicker.slideIn('t', {duration:.2});
10650     },
10651
10652     updateMPYear : function(y){
10653         this.mpyear = y;
10654         var ys = this.mpYears.elements;
10655         for(var i = 1; i <= 10; i++){
10656             var td = ys[i-1], y2;
10657             if((i%2) == 0){
10658                 y2 = y + Math.round(i * .5);
10659                 td.firstChild.innerHTML = y2;
10660                 td.xyear = y2;
10661             }else{
10662                 y2 = y - (5-Math.round(i * .5));
10663                 td.firstChild.innerHTML = y2;
10664                 td.xyear = y2;
10665             }
10666             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
10667         }
10668     },
10669
10670     updateMPMonth : function(sm){
10671         this.mpMonths.each(function(m, a, i){
10672             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
10673         });
10674     },
10675
10676     selectMPMonth: function(m){
10677         
10678     },
10679
10680     onMonthClick : function(e, t){
10681         e.stopEvent();
10682         var el = new Roo.Element(t), pn;
10683         if(el.is('button.x-date-mp-cancel')){
10684             this.hideMonthPicker();
10685         }
10686         else if(el.is('button.x-date-mp-ok')){
10687             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10688             this.hideMonthPicker();
10689         }
10690         else if(pn = el.up('td.x-date-mp-month', 2)){
10691             this.mpMonths.removeClass('x-date-mp-sel');
10692             pn.addClass('x-date-mp-sel');
10693             this.mpSelMonth = pn.dom.xmonth;
10694         }
10695         else if(pn = el.up('td.x-date-mp-year', 2)){
10696             this.mpYears.removeClass('x-date-mp-sel');
10697             pn.addClass('x-date-mp-sel');
10698             this.mpSelYear = pn.dom.xyear;
10699         }
10700         else if(el.is('a.x-date-mp-prev')){
10701             this.updateMPYear(this.mpyear-10);
10702         }
10703         else if(el.is('a.x-date-mp-next')){
10704             this.updateMPYear(this.mpyear+10);
10705         }
10706     },
10707
10708     onMonthDblClick : function(e, t){
10709         e.stopEvent();
10710         var el = new Roo.Element(t), pn;
10711         if(pn = el.up('td.x-date-mp-month', 2)){
10712             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
10713             this.hideMonthPicker();
10714         }
10715         else if(pn = el.up('td.x-date-mp-year', 2)){
10716             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10717             this.hideMonthPicker();
10718         }
10719     },
10720
10721     hideMonthPicker : function(disableAnim){
10722         if(this.monthPicker){
10723             if(disableAnim === true){
10724                 this.monthPicker.hide();
10725             }else{
10726                 this.monthPicker.slideOut('t', {duration:.2});
10727             }
10728         }
10729     },
10730
10731     // private
10732     showPrevMonth : function(e){
10733         this.update(this.activeDate.add("mo", -1));
10734     },
10735
10736     // private
10737     showNextMonth : function(e){
10738         this.update(this.activeDate.add("mo", 1));
10739     },
10740
10741     // private
10742     showPrevYear : function(){
10743         this.update(this.activeDate.add("y", -1));
10744     },
10745
10746     // private
10747     showNextYear : function(){
10748         this.update(this.activeDate.add("y", 1));
10749     },
10750
10751     // private
10752     handleMouseWheel : function(e){
10753         var delta = e.getWheelDelta();
10754         if(delta > 0){
10755             this.showPrevMonth();
10756             e.stopEvent();
10757         } else if(delta < 0){
10758             this.showNextMonth();
10759             e.stopEvent();
10760         }
10761     },
10762
10763     // private
10764     handleDateClick : function(e, t){
10765         e.stopEvent();
10766         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
10767             this.setValue(new Date(t.dateValue));
10768             this.fireEvent("select", this, this.value);
10769         }
10770     },
10771
10772     // private
10773     selectToday : function(){
10774         this.setValue(new Date().clearTime());
10775         this.fireEvent("select", this, this.value);
10776     },
10777
10778     // private
10779     update : function(date)
10780     {
10781         var vd = this.activeDate;
10782         this.activeDate = date;
10783         if(vd && this.el){
10784             var t = date.getTime();
10785             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10786                 this.cells.removeClass("x-date-selected");
10787                 this.cells.each(function(c){
10788                    if(c.dom.firstChild.dateValue == t){
10789                        c.addClass("x-date-selected");
10790                        setTimeout(function(){
10791                             try{c.dom.firstChild.focus();}catch(e){}
10792                        }, 50);
10793                        return false;
10794                    }
10795                 });
10796                 return;
10797             }
10798         }
10799         
10800         var days = date.getDaysInMonth();
10801         var firstOfMonth = date.getFirstDateOfMonth();
10802         var startingPos = firstOfMonth.getDay()-this.startDay;
10803
10804         if(startingPos <= this.startDay){
10805             startingPos += 7;
10806         }
10807
10808         var pm = date.add("mo", -1);
10809         var prevStart = pm.getDaysInMonth()-startingPos;
10810
10811         var cells = this.cells.elements;
10812         var textEls = this.textNodes;
10813         days += startingPos;
10814
10815         // convert everything to numbers so it's fast
10816         var day = 86400000;
10817         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10818         var today = new Date().clearTime().getTime();
10819         var sel = date.clearTime().getTime();
10820         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10821         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10822         var ddMatch = this.disabledDatesRE;
10823         var ddText = this.disabledDatesText;
10824         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10825         var ddaysText = this.disabledDaysText;
10826         var format = this.format;
10827
10828         var setCellClass = function(cal, cell){
10829             cell.title = "";
10830             var t = d.getTime();
10831             cell.firstChild.dateValue = t;
10832             if(t == today){
10833                 cell.className += " x-date-today";
10834                 cell.title = cal.todayText;
10835             }
10836             if(t == sel){
10837                 cell.className += " x-date-selected";
10838                 setTimeout(function(){
10839                     try{cell.firstChild.focus();}catch(e){}
10840                 }, 50);
10841             }
10842             // disabling
10843             if(t < min) {
10844                 cell.className = " x-date-disabled";
10845                 cell.title = cal.minText;
10846                 return;
10847             }
10848             if(t > max) {
10849                 cell.className = " x-date-disabled";
10850                 cell.title = cal.maxText;
10851                 return;
10852             }
10853             if(ddays){
10854                 if(ddays.indexOf(d.getDay()) != -1){
10855                     cell.title = ddaysText;
10856                     cell.className = " x-date-disabled";
10857                 }
10858             }
10859             if(ddMatch && format){
10860                 var fvalue = d.dateFormat(format);
10861                 if(ddMatch.test(fvalue)){
10862                     cell.title = ddText.replace("%0", fvalue);
10863                     cell.className = " x-date-disabled";
10864                 }
10865             }
10866         };
10867
10868         var i = 0;
10869         for(; i < startingPos; i++) {
10870             textEls[i].innerHTML = (++prevStart);
10871             d.setDate(d.getDate()+1);
10872             cells[i].className = "x-date-prevday";
10873             setCellClass(this, cells[i]);
10874         }
10875         for(; i < days; i++){
10876             intDay = i - startingPos + 1;
10877             textEls[i].innerHTML = (intDay);
10878             d.setDate(d.getDate()+1);
10879             cells[i].className = "x-date-active";
10880             setCellClass(this, cells[i]);
10881         }
10882         var extraDays = 0;
10883         for(; i < 42; i++) {
10884              textEls[i].innerHTML = (++extraDays);
10885              d.setDate(d.getDate()+1);
10886              cells[i].className = "x-date-nextday";
10887              setCellClass(this, cells[i]);
10888         }
10889
10890         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
10891         this.fireEvent('monthchange', this, date);
10892         
10893         if(!this.internalRender){
10894             var main = this.el.dom.firstChild;
10895             var w = main.offsetWidth;
10896             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10897             Roo.fly(main).setWidth(w);
10898             this.internalRender = true;
10899             // opera does not respect the auto grow header center column
10900             // then, after it gets a width opera refuses to recalculate
10901             // without a second pass
10902             if(Roo.isOpera && !this.secondPass){
10903                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10904                 this.secondPass = true;
10905                 this.update.defer(10, this, [date]);
10906             }
10907         }
10908         
10909         
10910     }
10911 });        /*
10912  * Based on:
10913  * Ext JS Library 1.1.1
10914  * Copyright(c) 2006-2007, Ext JS, LLC.
10915  *
10916  * Originally Released Under LGPL - original licence link has changed is not relivant.
10917  *
10918  * Fork - LGPL
10919  * <script type="text/javascript">
10920  */
10921 /**
10922  * @class Roo.TabPanel
10923  * @extends Roo.util.Observable
10924  * A lightweight tab container.
10925  * <br><br>
10926  * Usage:
10927  * <pre><code>
10928 // basic tabs 1, built from existing content
10929 var tabs = new Roo.TabPanel("tabs1");
10930 tabs.addTab("script", "View Script");
10931 tabs.addTab("markup", "View Markup");
10932 tabs.activate("script");
10933
10934 // more advanced tabs, built from javascript
10935 var jtabs = new Roo.TabPanel("jtabs");
10936 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
10937
10938 // set up the UpdateManager
10939 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
10940 var updater = tab2.getUpdateManager();
10941 updater.setDefaultUrl("ajax1.htm");
10942 tab2.on('activate', updater.refresh, updater, true);
10943
10944 // Use setUrl for Ajax loading
10945 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
10946 tab3.setUrl("ajax2.htm", null, true);
10947
10948 // Disabled tab
10949 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
10950 tab4.disable();
10951
10952 jtabs.activate("jtabs-1");
10953  * </code></pre>
10954  * @constructor
10955  * Create a new TabPanel.
10956  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
10957  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
10958  */
10959 Roo.TabPanel = function(container, config){
10960     /**
10961     * The container element for this TabPanel.
10962     * @type Roo.Element
10963     */
10964     this.el = Roo.get(container, true);
10965     if(config){
10966         if(typeof config == "boolean"){
10967             this.tabPosition = config ? "bottom" : "top";
10968         }else{
10969             Roo.apply(this, config);
10970         }
10971     }
10972     if(this.tabPosition == "bottom"){
10973         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10974         this.el.addClass("x-tabs-bottom");
10975     }
10976     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
10977     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
10978     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
10979     if(Roo.isIE){
10980         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
10981     }
10982     if(this.tabPosition != "bottom"){
10983         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
10984          * @type Roo.Element
10985          */
10986         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10987         this.el.addClass("x-tabs-top");
10988     }
10989     this.items = [];
10990
10991     this.bodyEl.setStyle("position", "relative");
10992
10993     this.active = null;
10994     this.activateDelegate = this.activate.createDelegate(this);
10995
10996     this.addEvents({
10997         /**
10998          * @event tabchange
10999          * Fires when the active tab changes
11000          * @param {Roo.TabPanel} this
11001          * @param {Roo.TabPanelItem} activePanel The new active tab
11002          */
11003         "tabchange": true,
11004         /**
11005          * @event beforetabchange
11006          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
11007          * @param {Roo.TabPanel} this
11008          * @param {Object} e Set cancel to true on this object to cancel the tab change
11009          * @param {Roo.TabPanelItem} tab The tab being changed to
11010          */
11011         "beforetabchange" : true
11012     });
11013
11014     Roo.EventManager.onWindowResize(this.onResize, this);
11015     this.cpad = this.el.getPadding("lr");
11016     this.hiddenCount = 0;
11017
11018
11019     // toolbar on the tabbar support...
11020     if (this.toolbar) {
11021         var tcfg = this.toolbar;
11022         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
11023         this.toolbar = new Roo.Toolbar(tcfg);
11024         if (Roo.isSafari) {
11025             var tbl = tcfg.container.child('table', true);
11026             tbl.setAttribute('width', '100%');
11027         }
11028         
11029     }
11030    
11031
11032
11033     Roo.TabPanel.superclass.constructor.call(this);
11034 };
11035
11036 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
11037     /*
11038      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
11039      */
11040     tabPosition : "top",
11041     /*
11042      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
11043      */
11044     currentTabWidth : 0,
11045     /*
11046      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
11047      */
11048     minTabWidth : 40,
11049     /*
11050      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
11051      */
11052     maxTabWidth : 250,
11053     /*
11054      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
11055      */
11056     preferredTabWidth : 175,
11057     /*
11058      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
11059      */
11060     resizeTabs : false,
11061     /*
11062      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
11063      */
11064     monitorResize : true,
11065     /*
11066      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
11067      */
11068     toolbar : false,
11069
11070     /**
11071      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
11072      * @param {String} id The id of the div to use <b>or create</b>
11073      * @param {String} text The text for the tab
11074      * @param {String} content (optional) Content to put in the TabPanelItem body
11075      * @param {Boolean} closable (optional) True to create a close icon on the tab
11076      * @return {Roo.TabPanelItem} The created TabPanelItem
11077      */
11078     addTab : function(id, text, content, closable){
11079         var item = new Roo.TabPanelItem(this, id, text, closable);
11080         this.addTabItem(item);
11081         if(content){
11082             item.setContent(content);
11083         }
11084         return item;
11085     },
11086
11087     /**
11088      * Returns the {@link Roo.TabPanelItem} with the specified id/index
11089      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
11090      * @return {Roo.TabPanelItem}
11091      */
11092     getTab : function(id){
11093         return this.items[id];
11094     },
11095
11096     /**
11097      * Hides the {@link Roo.TabPanelItem} with the specified id/index
11098      * @param {String/Number} id The id or index of the TabPanelItem to hide.
11099      */
11100     hideTab : function(id){
11101         var t = this.items[id];
11102         if(!t.isHidden()){
11103            t.setHidden(true);
11104            this.hiddenCount++;
11105            this.autoSizeTabs();
11106         }
11107     },
11108
11109     /**
11110      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
11111      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
11112      */
11113     unhideTab : function(id){
11114         var t = this.items[id];
11115         if(t.isHidden()){
11116            t.setHidden(false);
11117            this.hiddenCount--;
11118            this.autoSizeTabs();
11119         }
11120     },
11121
11122     /**
11123      * Adds an existing {@link Roo.TabPanelItem}.
11124      * @param {Roo.TabPanelItem} item The TabPanelItem to add
11125      */
11126     addTabItem : function(item){
11127         this.items[item.id] = item;
11128         this.items.push(item);
11129         if(this.resizeTabs){
11130            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
11131            this.autoSizeTabs();
11132         }else{
11133             item.autoSize();
11134         }
11135     },
11136
11137     /**
11138      * Removes a {@link Roo.TabPanelItem}.
11139      * @param {String/Number} id The id or index of the TabPanelItem to remove.
11140      */
11141     removeTab : function(id){
11142         var items = this.items;
11143         var tab = items[id];
11144         if(!tab) { return; }
11145         var index = items.indexOf(tab);
11146         if(this.active == tab && items.length > 1){
11147             var newTab = this.getNextAvailable(index);
11148             if(newTab) {
11149                 newTab.activate();
11150             }
11151         }
11152         this.stripEl.dom.removeChild(tab.pnode.dom);
11153         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
11154             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
11155         }
11156         items.splice(index, 1);
11157         delete this.items[tab.id];
11158         tab.fireEvent("close", tab);
11159         tab.purgeListeners();
11160         this.autoSizeTabs();
11161     },
11162
11163     getNextAvailable : function(start){
11164         var items = this.items;
11165         var index = start;
11166         // look for a next tab that will slide over to
11167         // replace the one being removed
11168         while(index < items.length){
11169             var item = items[++index];
11170             if(item && !item.isHidden()){
11171                 return item;
11172             }
11173         }
11174         // if one isn't found select the previous tab (on the left)
11175         index = start;
11176         while(index >= 0){
11177             var item = items[--index];
11178             if(item && !item.isHidden()){
11179                 return item;
11180             }
11181         }
11182         return null;
11183     },
11184
11185     /**
11186      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
11187      * @param {String/Number} id The id or index of the TabPanelItem to disable.
11188      */
11189     disableTab : function(id){
11190         var tab = this.items[id];
11191         if(tab && this.active != tab){
11192             tab.disable();
11193         }
11194     },
11195
11196     /**
11197      * Enables a {@link Roo.TabPanelItem} that is disabled.
11198      * @param {String/Number} id The id or index of the TabPanelItem to enable.
11199      */
11200     enableTab : function(id){
11201         var tab = this.items[id];
11202         tab.enable();
11203     },
11204
11205     /**
11206      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
11207      * @param {String/Number} id The id or index of the TabPanelItem to activate.
11208      * @return {Roo.TabPanelItem} The TabPanelItem.
11209      */
11210     activate : function(id){
11211         var tab = this.items[id];
11212         if(!tab){
11213             return null;
11214         }
11215         if(tab == this.active || tab.disabled){
11216             return tab;
11217         }
11218         var e = {};
11219         this.fireEvent("beforetabchange", this, e, tab);
11220         if(e.cancel !== true && !tab.disabled){
11221             if(this.active){
11222                 this.active.hide();
11223             }
11224             this.active = this.items[id];
11225             this.active.show();
11226             this.fireEvent("tabchange", this, this.active);
11227         }
11228         return tab;
11229     },
11230
11231     /**
11232      * Gets the active {@link Roo.TabPanelItem}.
11233      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
11234      */
11235     getActiveTab : function(){
11236         return this.active;
11237     },
11238
11239     /**
11240      * Updates the tab body element to fit the height of the container element
11241      * for overflow scrolling
11242      * @param {Number} targetHeight (optional) Override the starting height from the elements height
11243      */
11244     syncHeight : function(targetHeight){
11245         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
11246         var bm = this.bodyEl.getMargins();
11247         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
11248         this.bodyEl.setHeight(newHeight);
11249         return newHeight;
11250     },
11251
11252     onResize : function(){
11253         if(this.monitorResize){
11254             this.autoSizeTabs();
11255         }
11256     },
11257
11258     /**
11259      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
11260      */
11261     beginUpdate : function(){
11262         this.updating = true;
11263     },
11264
11265     /**
11266      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
11267      */
11268     endUpdate : function(){
11269         this.updating = false;
11270         this.autoSizeTabs();
11271     },
11272
11273     /**
11274      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
11275      */
11276     autoSizeTabs : function(){
11277         var count = this.items.length;
11278         var vcount = count - this.hiddenCount;
11279         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
11280         var w = Math.max(this.el.getWidth() - this.cpad, 10);
11281         var availWidth = Math.floor(w / vcount);
11282         var b = this.stripBody;
11283         if(b.getWidth() > w){
11284             var tabs = this.items;
11285             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
11286             if(availWidth < this.minTabWidth){
11287                 /*if(!this.sleft){    // incomplete scrolling code
11288                     this.createScrollButtons();
11289                 }
11290                 this.showScroll();
11291                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
11292             }
11293         }else{
11294             if(this.currentTabWidth < this.preferredTabWidth){
11295                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
11296             }
11297         }
11298     },
11299
11300     /**
11301      * Returns the number of tabs in this TabPanel.
11302      * @return {Number}
11303      */
11304      getCount : function(){
11305          return this.items.length;
11306      },
11307
11308     /**
11309      * Resizes all the tabs to the passed width
11310      * @param {Number} The new width
11311      */
11312     setTabWidth : function(width){
11313         this.currentTabWidth = width;
11314         for(var i = 0, len = this.items.length; i < len; i++) {
11315                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
11316         }
11317     },
11318
11319     /**
11320      * Destroys this TabPanel
11321      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
11322      */
11323     destroy : function(removeEl){
11324         Roo.EventManager.removeResizeListener(this.onResize, this);
11325         for(var i = 0, len = this.items.length; i < len; i++){
11326             this.items[i].purgeListeners();
11327         }
11328         if(removeEl === true){
11329             this.el.update("");
11330             this.el.remove();
11331         }
11332     }
11333 });
11334
11335 /**
11336  * @class Roo.TabPanelItem
11337  * @extends Roo.util.Observable
11338  * Represents an individual item (tab plus body) in a TabPanel.
11339  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
11340  * @param {String} id The id of this TabPanelItem
11341  * @param {String} text The text for the tab of this TabPanelItem
11342  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
11343  */
11344 Roo.TabPanelItem = function(tabPanel, id, text, closable){
11345     /**
11346      * The {@link Roo.TabPanel} this TabPanelItem belongs to
11347      * @type Roo.TabPanel
11348      */
11349     this.tabPanel = tabPanel;
11350     /**
11351      * The id for this TabPanelItem
11352      * @type String
11353      */
11354     this.id = id;
11355     /** @private */
11356     this.disabled = false;
11357     /** @private */
11358     this.text = text;
11359     /** @private */
11360     this.loaded = false;
11361     this.closable = closable;
11362
11363     /**
11364      * The body element for this TabPanelItem.
11365      * @type Roo.Element
11366      */
11367     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
11368     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
11369     this.bodyEl.setStyle("display", "block");
11370     this.bodyEl.setStyle("zoom", "1");
11371     this.hideAction();
11372
11373     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
11374     /** @private */
11375     this.el = Roo.get(els.el, true);
11376     this.inner = Roo.get(els.inner, true);
11377     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
11378     this.pnode = Roo.get(els.el.parentNode, true);
11379     this.el.on("mousedown", this.onTabMouseDown, this);
11380     this.el.on("click", this.onTabClick, this);
11381     /** @private */
11382     if(closable){
11383         var c = Roo.get(els.close, true);
11384         c.dom.title = this.closeText;
11385         c.addClassOnOver("close-over");
11386         c.on("click", this.closeClick, this);
11387      }
11388
11389     this.addEvents({
11390          /**
11391          * @event activate
11392          * Fires when this tab becomes the active tab.
11393          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11394          * @param {Roo.TabPanelItem} this
11395          */
11396         "activate": true,
11397         /**
11398          * @event beforeclose
11399          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
11400          * @param {Roo.TabPanelItem} this
11401          * @param {Object} e Set cancel to true on this object to cancel the close.
11402          */
11403         "beforeclose": true,
11404         /**
11405          * @event close
11406          * Fires when this tab is closed.
11407          * @param {Roo.TabPanelItem} this
11408          */
11409          "close": true,
11410         /**
11411          * @event deactivate
11412          * Fires when this tab is no longer the active tab.
11413          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11414          * @param {Roo.TabPanelItem} this
11415          */
11416          "deactivate" : true
11417     });
11418     this.hidden = false;
11419
11420     Roo.TabPanelItem.superclass.constructor.call(this);
11421 };
11422
11423 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
11424     purgeListeners : function(){
11425        Roo.util.Observable.prototype.purgeListeners.call(this);
11426        this.el.removeAllListeners();
11427     },
11428     /**
11429      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
11430      */
11431     show : function(){
11432         this.pnode.addClass("on");
11433         this.showAction();
11434         if(Roo.isOpera){
11435             this.tabPanel.stripWrap.repaint();
11436         }
11437         this.fireEvent("activate", this.tabPanel, this);
11438     },
11439
11440     /**
11441      * Returns true if this tab is the active tab.
11442      * @return {Boolean}
11443      */
11444     isActive : function(){
11445         return this.tabPanel.getActiveTab() == this;
11446     },
11447
11448     /**
11449      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
11450      */
11451     hide : function(){
11452         this.pnode.removeClass("on");
11453         this.hideAction();
11454         this.fireEvent("deactivate", this.tabPanel, this);
11455     },
11456
11457     hideAction : function(){
11458         this.bodyEl.hide();
11459         this.bodyEl.setStyle("position", "absolute");
11460         this.bodyEl.setLeft("-20000px");
11461         this.bodyEl.setTop("-20000px");
11462     },
11463
11464     showAction : function(){
11465         this.bodyEl.setStyle("position", "relative");
11466         this.bodyEl.setTop("");
11467         this.bodyEl.setLeft("");
11468         this.bodyEl.show();
11469     },
11470
11471     /**
11472      * Set the tooltip for the tab.
11473      * @param {String} tooltip The tab's tooltip
11474      */
11475     setTooltip : function(text){
11476         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
11477             this.textEl.dom.qtip = text;
11478             this.textEl.dom.removeAttribute('title');
11479         }else{
11480             this.textEl.dom.title = text;
11481         }
11482     },
11483
11484     onTabClick : function(e){
11485         e.preventDefault();
11486         this.tabPanel.activate(this.id);
11487     },
11488
11489     onTabMouseDown : function(e){
11490         e.preventDefault();
11491         this.tabPanel.activate(this.id);
11492     },
11493
11494     getWidth : function(){
11495         return this.inner.getWidth();
11496     },
11497
11498     setWidth : function(width){
11499         var iwidth = width - this.pnode.getPadding("lr");
11500         this.inner.setWidth(iwidth);
11501         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
11502         this.pnode.setWidth(width);
11503     },
11504
11505     /**
11506      * Show or hide the tab
11507      * @param {Boolean} hidden True to hide or false to show.
11508      */
11509     setHidden : function(hidden){
11510         this.hidden = hidden;
11511         this.pnode.setStyle("display", hidden ? "none" : "");
11512     },
11513
11514     /**
11515      * Returns true if this tab is "hidden"
11516      * @return {Boolean}
11517      */
11518     isHidden : function(){
11519         return this.hidden;
11520     },
11521
11522     /**
11523      * Returns the text for this tab
11524      * @return {String}
11525      */
11526     getText : function(){
11527         return this.text;
11528     },
11529
11530     autoSize : function(){
11531         //this.el.beginMeasure();
11532         this.textEl.setWidth(1);
11533         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
11534         //this.el.endMeasure();
11535     },
11536
11537     /**
11538      * Sets the text for the tab (Note: this also sets the tooltip text)
11539      * @param {String} text The tab's text and tooltip
11540      */
11541     setText : function(text){
11542         this.text = text;
11543         this.textEl.update(text);
11544         this.setTooltip(text);
11545         if(!this.tabPanel.resizeTabs){
11546             this.autoSize();
11547         }
11548     },
11549     /**
11550      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
11551      */
11552     activate : function(){
11553         this.tabPanel.activate(this.id);
11554     },
11555
11556     /**
11557      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
11558      */
11559     disable : function(){
11560         if(this.tabPanel.active != this){
11561             this.disabled = true;
11562             this.pnode.addClass("disabled");
11563         }
11564     },
11565
11566     /**
11567      * Enables this TabPanelItem if it was previously disabled.
11568      */
11569     enable : function(){
11570         this.disabled = false;
11571         this.pnode.removeClass("disabled");
11572     },
11573
11574     /**
11575      * Sets the content for this TabPanelItem.
11576      * @param {String} content The content
11577      * @param {Boolean} loadScripts true to look for and load scripts
11578      */
11579     setContent : function(content, loadScripts){
11580         this.bodyEl.update(content, loadScripts);
11581     },
11582
11583     /**
11584      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
11585      * @return {Roo.UpdateManager} The UpdateManager
11586      */
11587     getUpdateManager : function(){
11588         return this.bodyEl.getUpdateManager();
11589     },
11590
11591     /**
11592      * Set a URL to be used to load the content for this TabPanelItem.
11593      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
11594      * @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)
11595      * @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)
11596      * @return {Roo.UpdateManager} The UpdateManager
11597      */
11598     setUrl : function(url, params, loadOnce){
11599         if(this.refreshDelegate){
11600             this.un('activate', this.refreshDelegate);
11601         }
11602         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
11603         this.on("activate", this.refreshDelegate);
11604         return this.bodyEl.getUpdateManager();
11605     },
11606
11607     /** @private */
11608     _handleRefresh : function(url, params, loadOnce){
11609         if(!loadOnce || !this.loaded){
11610             var updater = this.bodyEl.getUpdateManager();
11611             updater.update(url, params, this._setLoaded.createDelegate(this));
11612         }
11613     },
11614
11615     /**
11616      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
11617      *   Will fail silently if the setUrl method has not been called.
11618      *   This does not activate the panel, just updates its content.
11619      */
11620     refresh : function(){
11621         if(this.refreshDelegate){
11622            this.loaded = false;
11623            this.refreshDelegate();
11624         }
11625     },
11626
11627     /** @private */
11628     _setLoaded : function(){
11629         this.loaded = true;
11630     },
11631
11632     /** @private */
11633     closeClick : function(e){
11634         var o = {};
11635         e.stopEvent();
11636         this.fireEvent("beforeclose", this, o);
11637         if(o.cancel !== true){
11638             this.tabPanel.removeTab(this.id);
11639         }
11640     },
11641     /**
11642      * The text displayed in the tooltip for the close icon.
11643      * @type String
11644      */
11645     closeText : "Close this tab"
11646 });
11647
11648 /** @private */
11649 Roo.TabPanel.prototype.createStrip = function(container){
11650     var strip = document.createElement("div");
11651     strip.className = "x-tabs-wrap";
11652     container.appendChild(strip);
11653     return strip;
11654 };
11655 /** @private */
11656 Roo.TabPanel.prototype.createStripList = function(strip){
11657     // div wrapper for retard IE
11658     // returns the "tr" element.
11659     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
11660         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
11661         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
11662     return strip.firstChild.firstChild.firstChild.firstChild;
11663 };
11664 /** @private */
11665 Roo.TabPanel.prototype.createBody = function(container){
11666     var body = document.createElement("div");
11667     Roo.id(body, "tab-body");
11668     Roo.fly(body).addClass("x-tabs-body");
11669     container.appendChild(body);
11670     return body;
11671 };
11672 /** @private */
11673 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
11674     var body = Roo.getDom(id);
11675     if(!body){
11676         body = document.createElement("div");
11677         body.id = id;
11678     }
11679     Roo.fly(body).addClass("x-tabs-item-body");
11680     bodyEl.insertBefore(body, bodyEl.firstChild);
11681     return body;
11682 };
11683 /** @private */
11684 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
11685     var td = document.createElement("td");
11686     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
11687     //stripEl.appendChild(td);
11688     if(closable){
11689         td.className = "x-tabs-closable";
11690         if(!this.closeTpl){
11691             this.closeTpl = new Roo.Template(
11692                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11693                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
11694                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
11695             );
11696         }
11697         var el = this.closeTpl.overwrite(td, {"text": text});
11698         var close = el.getElementsByTagName("div")[0];
11699         var inner = el.getElementsByTagName("em")[0];
11700         return {"el": el, "close": close, "inner": inner};
11701     } else {
11702         if(!this.tabTpl){
11703             this.tabTpl = new Roo.Template(
11704                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11705                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
11706             );
11707         }
11708         var el = this.tabTpl.overwrite(td, {"text": text});
11709         var inner = el.getElementsByTagName("em")[0];
11710         return {"el": el, "inner": inner};
11711     }
11712 };/*
11713  * Based on:
11714  * Ext JS Library 1.1.1
11715  * Copyright(c) 2006-2007, Ext JS, LLC.
11716  *
11717  * Originally Released Under LGPL - original licence link has changed is not relivant.
11718  *
11719  * Fork - LGPL
11720  * <script type="text/javascript">
11721  */
11722
11723 /**
11724  * @class Roo.Button
11725  * @extends Roo.util.Observable
11726  * Simple Button class
11727  * @cfg {String} text The button text
11728  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
11729  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
11730  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
11731  * @cfg {Object} scope The scope of the handler
11732  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
11733  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
11734  * @cfg {Boolean} hidden True to start hidden (defaults to false)
11735  * @cfg {Boolean} disabled True to start disabled (defaults to false)
11736  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
11737  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
11738    applies if enableToggle = true)
11739  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
11740  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
11741   an {@link Roo.util.ClickRepeater} config object (defaults to false).
11742  * @constructor
11743  * Create a new button
11744  * @param {Object} config The config object
11745  */
11746 Roo.Button = function(renderTo, config)
11747 {
11748     if (!config) {
11749         config = renderTo;
11750         renderTo = config.renderTo || false;
11751     }
11752     
11753     Roo.apply(this, config);
11754     this.addEvents({
11755         /**
11756              * @event click
11757              * Fires when this button is clicked
11758              * @param {Button} this
11759              * @param {EventObject} e The click event
11760              */
11761             "click" : true,
11762         /**
11763              * @event toggle
11764              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
11765              * @param {Button} this
11766              * @param {Boolean} pressed
11767              */
11768             "toggle" : true,
11769         /**
11770              * @event mouseover
11771              * Fires when the mouse hovers over the button
11772              * @param {Button} this
11773              * @param {Event} e The event object
11774              */
11775         'mouseover' : true,
11776         /**
11777              * @event mouseout
11778              * Fires when the mouse exits the button
11779              * @param {Button} this
11780              * @param {Event} e The event object
11781              */
11782         'mouseout': true,
11783          /**
11784              * @event render
11785              * Fires when the button is rendered
11786              * @param {Button} this
11787              */
11788         'render': true
11789     });
11790     if(this.menu){
11791         this.menu = Roo.menu.MenuMgr.get(this.menu);
11792     }
11793     // register listeners first!!  - so render can be captured..
11794     Roo.util.Observable.call(this);
11795     if(renderTo){
11796         this.render(renderTo);
11797     }
11798     
11799   
11800 };
11801
11802 Roo.extend(Roo.Button, Roo.util.Observable, {
11803     /**
11804      * 
11805      */
11806     
11807     /**
11808      * Read-only. True if this button is hidden
11809      * @type Boolean
11810      */
11811     hidden : false,
11812     /**
11813      * Read-only. True if this button is disabled
11814      * @type Boolean
11815      */
11816     disabled : false,
11817     /**
11818      * Read-only. True if this button is pressed (only if enableToggle = true)
11819      * @type Boolean
11820      */
11821     pressed : false,
11822
11823     /**
11824      * @cfg {Number} tabIndex 
11825      * The DOM tabIndex for this button (defaults to undefined)
11826      */
11827     tabIndex : undefined,
11828
11829     /**
11830      * @cfg {Boolean} enableToggle
11831      * True to enable pressed/not pressed toggling (defaults to false)
11832      */
11833     enableToggle: false,
11834     /**
11835      * @cfg {Mixed} menu
11836      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
11837      */
11838     menu : undefined,
11839     /**
11840      * @cfg {String} menuAlign
11841      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
11842      */
11843     menuAlign : "tl-bl?",
11844
11845     /**
11846      * @cfg {String} iconCls
11847      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
11848      */
11849     iconCls : undefined,
11850     /**
11851      * @cfg {String} type
11852      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
11853      */
11854     type : 'button',
11855
11856     // private
11857     menuClassTarget: 'tr',
11858
11859     /**
11860      * @cfg {String} clickEvent
11861      * The type of event to map to the button's event handler (defaults to 'click')
11862      */
11863     clickEvent : 'click',
11864
11865     /**
11866      * @cfg {Boolean} handleMouseEvents
11867      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
11868      */
11869     handleMouseEvents : true,
11870
11871     /**
11872      * @cfg {String} tooltipType
11873      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
11874      */
11875     tooltipType : 'qtip',
11876
11877     /**
11878      * @cfg {String} cls
11879      * A CSS class to apply to the button's main element.
11880      */
11881     
11882     /**
11883      * @cfg {Roo.Template} template (Optional)
11884      * An {@link Roo.Template} with which to create the Button's main element. This Template must
11885      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
11886      * require code modifications if required elements (e.g. a button) aren't present.
11887      */
11888
11889     // private
11890     render : function(renderTo){
11891         var btn;
11892         if(this.hideParent){
11893             this.parentEl = Roo.get(renderTo);
11894         }
11895         if(!this.dhconfig){
11896             if(!this.template){
11897                 if(!Roo.Button.buttonTemplate){
11898                     // hideous table template
11899                     Roo.Button.buttonTemplate = new Roo.Template(
11900                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
11901                         '<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>',
11902                         "</tr></tbody></table>");
11903                 }
11904                 this.template = Roo.Button.buttonTemplate;
11905             }
11906             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
11907             var btnEl = btn.child("button:first");
11908             btnEl.on('focus', this.onFocus, this);
11909             btnEl.on('blur', this.onBlur, this);
11910             if(this.cls){
11911                 btn.addClass(this.cls);
11912             }
11913             if(this.icon){
11914                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
11915             }
11916             if(this.iconCls){
11917                 btnEl.addClass(this.iconCls);
11918                 if(!this.cls){
11919                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11920                 }
11921             }
11922             if(this.tabIndex !== undefined){
11923                 btnEl.dom.tabIndex = this.tabIndex;
11924             }
11925             if(this.tooltip){
11926                 if(typeof this.tooltip == 'object'){
11927                     Roo.QuickTips.tips(Roo.apply({
11928                           target: btnEl.id
11929                     }, this.tooltip));
11930                 } else {
11931                     btnEl.dom[this.tooltipType] = this.tooltip;
11932                 }
11933             }
11934         }else{
11935             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
11936         }
11937         this.el = btn;
11938         if(this.id){
11939             this.el.dom.id = this.el.id = this.id;
11940         }
11941         if(this.menu){
11942             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
11943             this.menu.on("show", this.onMenuShow, this);
11944             this.menu.on("hide", this.onMenuHide, this);
11945         }
11946         btn.addClass("x-btn");
11947         if(Roo.isIE && !Roo.isIE7){
11948             this.autoWidth.defer(1, this);
11949         }else{
11950             this.autoWidth();
11951         }
11952         if(this.handleMouseEvents){
11953             btn.on("mouseover", this.onMouseOver, this);
11954             btn.on("mouseout", this.onMouseOut, this);
11955             btn.on("mousedown", this.onMouseDown, this);
11956         }
11957         btn.on(this.clickEvent, this.onClick, this);
11958         //btn.on("mouseup", this.onMouseUp, this);
11959         if(this.hidden){
11960             this.hide();
11961         }
11962         if(this.disabled){
11963             this.disable();
11964         }
11965         Roo.ButtonToggleMgr.register(this);
11966         if(this.pressed){
11967             this.el.addClass("x-btn-pressed");
11968         }
11969         if(this.repeat){
11970             var repeater = new Roo.util.ClickRepeater(btn,
11971                 typeof this.repeat == "object" ? this.repeat : {}
11972             );
11973             repeater.on("click", this.onClick,  this);
11974         }
11975         
11976         this.fireEvent('render', this);
11977         
11978     },
11979     /**
11980      * Returns the button's underlying element
11981      * @return {Roo.Element} The element
11982      */
11983     getEl : function(){
11984         return this.el;  
11985     },
11986     
11987     /**
11988      * Destroys this Button and removes any listeners.
11989      */
11990     destroy : function(){
11991         Roo.ButtonToggleMgr.unregister(this);
11992         this.el.removeAllListeners();
11993         this.purgeListeners();
11994         this.el.remove();
11995     },
11996
11997     // private
11998     autoWidth : function(){
11999         if(this.el){
12000             this.el.setWidth("auto");
12001             if(Roo.isIE7 && Roo.isStrict){
12002                 var ib = this.el.child('button');
12003                 if(ib && ib.getWidth() > 20){
12004                     ib.clip();
12005                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
12006                 }
12007             }
12008             if(this.minWidth){
12009                 if(this.hidden){
12010                     this.el.beginMeasure();
12011                 }
12012                 if(this.el.getWidth() < this.minWidth){
12013                     this.el.setWidth(this.minWidth);
12014                 }
12015                 if(this.hidden){
12016                     this.el.endMeasure();
12017                 }
12018             }
12019         }
12020     },
12021
12022     /**
12023      * Assigns this button's click handler
12024      * @param {Function} handler The function to call when the button is clicked
12025      * @param {Object} scope (optional) Scope for the function passed in
12026      */
12027     setHandler : function(handler, scope){
12028         this.handler = handler;
12029         this.scope = scope;  
12030     },
12031     
12032     /**
12033      * Sets this button's text
12034      * @param {String} text The button text
12035      */
12036     setText : function(text){
12037         this.text = text;
12038         if(this.el){
12039             this.el.child("td.x-btn-center button.x-btn-text").update(text);
12040         }
12041         this.autoWidth();
12042     },
12043     
12044     /**
12045      * Gets the text for this button
12046      * @return {String} The button text
12047      */
12048     getText : function(){
12049         return this.text;  
12050     },
12051     
12052     /**
12053      * Show this button
12054      */
12055     show: function(){
12056         this.hidden = false;
12057         if(this.el){
12058             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
12059         }
12060     },
12061     
12062     /**
12063      * Hide this button
12064      */
12065     hide: function(){
12066         this.hidden = true;
12067         if(this.el){
12068             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
12069         }
12070     },
12071     
12072     /**
12073      * Convenience function for boolean show/hide
12074      * @param {Boolean} visible True to show, false to hide
12075      */
12076     setVisible: function(visible){
12077         if(visible) {
12078             this.show();
12079         }else{
12080             this.hide();
12081         }
12082     },
12083     
12084     /**
12085      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
12086      * @param {Boolean} state (optional) Force a particular state
12087      */
12088     toggle : function(state){
12089         state = state === undefined ? !this.pressed : state;
12090         if(state != this.pressed){
12091             if(state){
12092                 this.el.addClass("x-btn-pressed");
12093                 this.pressed = true;
12094                 this.fireEvent("toggle", this, true);
12095             }else{
12096                 this.el.removeClass("x-btn-pressed");
12097                 this.pressed = false;
12098                 this.fireEvent("toggle", this, false);
12099             }
12100             if(this.toggleHandler){
12101                 this.toggleHandler.call(this.scope || this, this, state);
12102             }
12103         }
12104     },
12105     
12106     /**
12107      * Focus the button
12108      */
12109     focus : function(){
12110         this.el.child('button:first').focus();
12111     },
12112     
12113     /**
12114      * Disable this button
12115      */
12116     disable : function(){
12117         if(this.el){
12118             this.el.addClass("x-btn-disabled");
12119         }
12120         this.disabled = true;
12121     },
12122     
12123     /**
12124      * Enable this button
12125      */
12126     enable : function(){
12127         if(this.el){
12128             this.el.removeClass("x-btn-disabled");
12129         }
12130         this.disabled = false;
12131     },
12132
12133     /**
12134      * Convenience function for boolean enable/disable
12135      * @param {Boolean} enabled True to enable, false to disable
12136      */
12137     setDisabled : function(v){
12138         this[v !== true ? "enable" : "disable"]();
12139     },
12140
12141     // private
12142     onClick : function(e){
12143         if(e){
12144             e.preventDefault();
12145         }
12146         if(e.button != 0){
12147             return;
12148         }
12149         if(!this.disabled){
12150             if(this.enableToggle){
12151                 this.toggle();
12152             }
12153             if(this.menu && !this.menu.isVisible()){
12154                 this.menu.show(this.el, this.menuAlign);
12155             }
12156             this.fireEvent("click", this, e);
12157             if(this.handler){
12158                 this.el.removeClass("x-btn-over");
12159                 this.handler.call(this.scope || this, this, e);
12160             }
12161         }
12162     },
12163     // private
12164     onMouseOver : function(e){
12165         if(!this.disabled){
12166             this.el.addClass("x-btn-over");
12167             this.fireEvent('mouseover', this, e);
12168         }
12169     },
12170     // private
12171     onMouseOut : function(e){
12172         if(!e.within(this.el,  true)){
12173             this.el.removeClass("x-btn-over");
12174             this.fireEvent('mouseout', this, e);
12175         }
12176     },
12177     // private
12178     onFocus : function(e){
12179         if(!this.disabled){
12180             this.el.addClass("x-btn-focus");
12181         }
12182     },
12183     // private
12184     onBlur : function(e){
12185         this.el.removeClass("x-btn-focus");
12186     },
12187     // private
12188     onMouseDown : function(e){
12189         if(!this.disabled && e.button == 0){
12190             this.el.addClass("x-btn-click");
12191             Roo.get(document).on('mouseup', this.onMouseUp, this);
12192         }
12193     },
12194     // private
12195     onMouseUp : function(e){
12196         if(e.button == 0){
12197             this.el.removeClass("x-btn-click");
12198             Roo.get(document).un('mouseup', this.onMouseUp, this);
12199         }
12200     },
12201     // private
12202     onMenuShow : function(e){
12203         this.el.addClass("x-btn-menu-active");
12204     },
12205     // private
12206     onMenuHide : function(e){
12207         this.el.removeClass("x-btn-menu-active");
12208     }   
12209 });
12210
12211 // Private utility class used by Button
12212 Roo.ButtonToggleMgr = function(){
12213    var groups = {};
12214    
12215    function toggleGroup(btn, state){
12216        if(state){
12217            var g = groups[btn.toggleGroup];
12218            for(var i = 0, l = g.length; i < l; i++){
12219                if(g[i] != btn){
12220                    g[i].toggle(false);
12221                }
12222            }
12223        }
12224    }
12225    
12226    return {
12227        register : function(btn){
12228            if(!btn.toggleGroup){
12229                return;
12230            }
12231            var g = groups[btn.toggleGroup];
12232            if(!g){
12233                g = groups[btn.toggleGroup] = [];
12234            }
12235            g.push(btn);
12236            btn.on("toggle", toggleGroup);
12237        },
12238        
12239        unregister : function(btn){
12240            if(!btn.toggleGroup){
12241                return;
12242            }
12243            var g = groups[btn.toggleGroup];
12244            if(g){
12245                g.remove(btn);
12246                btn.un("toggle", toggleGroup);
12247            }
12248        }
12249    };
12250 }();/*
12251  * Based on:
12252  * Ext JS Library 1.1.1
12253  * Copyright(c) 2006-2007, Ext JS, LLC.
12254  *
12255  * Originally Released Under LGPL - original licence link has changed is not relivant.
12256  *
12257  * Fork - LGPL
12258  * <script type="text/javascript">
12259  */
12260  
12261 /**
12262  * @class Roo.SplitButton
12263  * @extends Roo.Button
12264  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
12265  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
12266  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
12267  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
12268  * @cfg {String} arrowTooltip The title attribute of the arrow
12269  * @constructor
12270  * Create a new menu button
12271  * @param {String/HTMLElement/Element} renderTo The element to append the button to
12272  * @param {Object} config The config object
12273  */
12274 Roo.SplitButton = function(renderTo, config){
12275     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
12276     /**
12277      * @event arrowclick
12278      * Fires when this button's arrow is clicked
12279      * @param {SplitButton} this
12280      * @param {EventObject} e The click event
12281      */
12282     this.addEvents({"arrowclick":true});
12283 };
12284
12285 Roo.extend(Roo.SplitButton, Roo.Button, {
12286     render : function(renderTo){
12287         // this is one sweet looking template!
12288         var tpl = new Roo.Template(
12289             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
12290             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
12291             '<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>',
12292             "</tbody></table></td><td>",
12293             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
12294             '<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>',
12295             "</tbody></table></td></tr></table>"
12296         );
12297         var btn = tpl.append(renderTo, [this.text, this.type], true);
12298         var btnEl = btn.child("button");
12299         if(this.cls){
12300             btn.addClass(this.cls);
12301         }
12302         if(this.icon){
12303             btnEl.setStyle('background-image', 'url(' +this.icon +')');
12304         }
12305         if(this.iconCls){
12306             btnEl.addClass(this.iconCls);
12307             if(!this.cls){
12308                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
12309             }
12310         }
12311         this.el = btn;
12312         if(this.handleMouseEvents){
12313             btn.on("mouseover", this.onMouseOver, this);
12314             btn.on("mouseout", this.onMouseOut, this);
12315             btn.on("mousedown", this.onMouseDown, this);
12316             btn.on("mouseup", this.onMouseUp, this);
12317         }
12318         btn.on(this.clickEvent, this.onClick, this);
12319         if(this.tooltip){
12320             if(typeof this.tooltip == 'object'){
12321                 Roo.QuickTips.tips(Roo.apply({
12322                       target: btnEl.id
12323                 }, this.tooltip));
12324             } else {
12325                 btnEl.dom[this.tooltipType] = this.tooltip;
12326             }
12327         }
12328         if(this.arrowTooltip){
12329             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
12330         }
12331         if(this.hidden){
12332             this.hide();
12333         }
12334         if(this.disabled){
12335             this.disable();
12336         }
12337         if(this.pressed){
12338             this.el.addClass("x-btn-pressed");
12339         }
12340         if(Roo.isIE && !Roo.isIE7){
12341             this.autoWidth.defer(1, this);
12342         }else{
12343             this.autoWidth();
12344         }
12345         if(this.menu){
12346             this.menu.on("show", this.onMenuShow, this);
12347             this.menu.on("hide", this.onMenuHide, this);
12348         }
12349         this.fireEvent('render', this);
12350     },
12351
12352     // private
12353     autoWidth : function(){
12354         if(this.el){
12355             var tbl = this.el.child("table:first");
12356             var tbl2 = this.el.child("table:last");
12357             this.el.setWidth("auto");
12358             tbl.setWidth("auto");
12359             if(Roo.isIE7 && Roo.isStrict){
12360                 var ib = this.el.child('button:first');
12361                 if(ib && ib.getWidth() > 20){
12362                     ib.clip();
12363                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
12364                 }
12365             }
12366             if(this.minWidth){
12367                 if(this.hidden){
12368                     this.el.beginMeasure();
12369                 }
12370                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
12371                     tbl.setWidth(this.minWidth-tbl2.getWidth());
12372                 }
12373                 if(this.hidden){
12374                     this.el.endMeasure();
12375                 }
12376             }
12377             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
12378         } 
12379     },
12380     /**
12381      * Sets this button's click handler
12382      * @param {Function} handler The function to call when the button is clicked
12383      * @param {Object} scope (optional) Scope for the function passed above
12384      */
12385     setHandler : function(handler, scope){
12386         this.handler = handler;
12387         this.scope = scope;  
12388     },
12389     
12390     /**
12391      * Sets this button's arrow click handler
12392      * @param {Function} handler The function to call when the arrow is clicked
12393      * @param {Object} scope (optional) Scope for the function passed above
12394      */
12395     setArrowHandler : function(handler, scope){
12396         this.arrowHandler = handler;
12397         this.scope = scope;  
12398     },
12399     
12400     /**
12401      * Focus the button
12402      */
12403     focus : function(){
12404         if(this.el){
12405             this.el.child("button:first").focus();
12406         }
12407     },
12408
12409     // private
12410     onClick : function(e){
12411         e.preventDefault();
12412         if(!this.disabled){
12413             if(e.getTarget(".x-btn-menu-arrow-wrap")){
12414                 if(this.menu && !this.menu.isVisible()){
12415                     this.menu.show(this.el, this.menuAlign);
12416                 }
12417                 this.fireEvent("arrowclick", this, e);
12418                 if(this.arrowHandler){
12419                     this.arrowHandler.call(this.scope || this, this, e);
12420                 }
12421             }else{
12422                 this.fireEvent("click", this, e);
12423                 if(this.handler){
12424                     this.handler.call(this.scope || this, this, e);
12425                 }
12426             }
12427         }
12428     },
12429     // private
12430     onMouseDown : function(e){
12431         if(!this.disabled){
12432             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
12433         }
12434     },
12435     // private
12436     onMouseUp : function(e){
12437         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
12438     }   
12439 });
12440
12441
12442 // backwards compat
12443 Roo.MenuButton = Roo.SplitButton;/*
12444  * Based on:
12445  * Ext JS Library 1.1.1
12446  * Copyright(c) 2006-2007, Ext JS, LLC.
12447  *
12448  * Originally Released Under LGPL - original licence link has changed is not relivant.
12449  *
12450  * Fork - LGPL
12451  * <script type="text/javascript">
12452  */
12453
12454 /**
12455  * @class Roo.Toolbar
12456  * Basic Toolbar class.
12457  * @constructor
12458  * Creates a new Toolbar
12459  * @param {Object} container The config object
12460  */ 
12461 Roo.Toolbar = function(container, buttons, config)
12462 {
12463     /// old consturctor format still supported..
12464     if(container instanceof Array){ // omit the container for later rendering
12465         buttons = container;
12466         config = buttons;
12467         container = null;
12468     }
12469     if (typeof(container) == 'object' && container.xtype) {
12470         config = container;
12471         container = config.container;
12472         buttons = config.buttons || []; // not really - use items!!
12473     }
12474     var xitems = [];
12475     if (config && config.items) {
12476         xitems = config.items;
12477         delete config.items;
12478     }
12479     Roo.apply(this, config);
12480     this.buttons = buttons;
12481     
12482     if(container){
12483         this.render(container);
12484     }
12485     this.xitems = xitems;
12486     Roo.each(xitems, function(b) {
12487         this.add(b);
12488     }, this);
12489     
12490 };
12491
12492 Roo.Toolbar.prototype = {
12493     /**
12494      * @cfg {Array} items
12495      * array of button configs or elements to add (will be converted to a MixedCollection)
12496      */
12497     
12498     /**
12499      * @cfg {String/HTMLElement/Element} container
12500      * The id or element that will contain the toolbar
12501      */
12502     // private
12503     render : function(ct){
12504         this.el = Roo.get(ct);
12505         if(this.cls){
12506             this.el.addClass(this.cls);
12507         }
12508         // using a table allows for vertical alignment
12509         // 100% width is needed by Safari...
12510         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
12511         this.tr = this.el.child("tr", true);
12512         var autoId = 0;
12513         this.items = new Roo.util.MixedCollection(false, function(o){
12514             return o.id || ("item" + (++autoId));
12515         });
12516         if(this.buttons){
12517             this.add.apply(this, this.buttons);
12518             delete this.buttons;
12519         }
12520     },
12521
12522     /**
12523      * Adds element(s) to the toolbar -- this function takes a variable number of 
12524      * arguments of mixed type and adds them to the toolbar.
12525      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
12526      * <ul>
12527      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
12528      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
12529      * <li>Field: Any form field (equivalent to {@link #addField})</li>
12530      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
12531      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
12532      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
12533      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
12534      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
12535      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
12536      * </ul>
12537      * @param {Mixed} arg2
12538      * @param {Mixed} etc.
12539      */
12540     add : function(){
12541         var a = arguments, l = a.length;
12542         for(var i = 0; i < l; i++){
12543             this._add(a[i]);
12544         }
12545     },
12546     // private..
12547     _add : function(el) {
12548         
12549         if (el.xtype) {
12550             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
12551         }
12552         
12553         if (el.applyTo){ // some kind of form field
12554             return this.addField(el);
12555         } 
12556         if (el.render){ // some kind of Toolbar.Item
12557             return this.addItem(el);
12558         }
12559         if (typeof el == "string"){ // string
12560             if(el == "separator" || el == "-"){
12561                 return this.addSeparator();
12562             }
12563             if (el == " "){
12564                 return this.addSpacer();
12565             }
12566             if(el == "->"){
12567                 return this.addFill();
12568             }
12569             return this.addText(el);
12570             
12571         }
12572         if(el.tagName){ // element
12573             return this.addElement(el);
12574         }
12575         if(typeof el == "object"){ // must be button config?
12576             return this.addButton(el);
12577         }
12578         // and now what?!?!
12579         return false;
12580         
12581     },
12582     
12583     /**
12584      * Add an Xtype element
12585      * @param {Object} xtype Xtype Object
12586      * @return {Object} created Object
12587      */
12588     addxtype : function(e){
12589         return this.add(e);  
12590     },
12591     
12592     /**
12593      * Returns the Element for this toolbar.
12594      * @return {Roo.Element}
12595      */
12596     getEl : function(){
12597         return this.el;  
12598     },
12599     
12600     /**
12601      * Adds a separator
12602      * @return {Roo.Toolbar.Item} The separator item
12603      */
12604     addSeparator : function(){
12605         return this.addItem(new Roo.Toolbar.Separator());
12606     },
12607
12608     /**
12609      * Adds a spacer element
12610      * @return {Roo.Toolbar.Spacer} The spacer item
12611      */
12612     addSpacer : function(){
12613         return this.addItem(new Roo.Toolbar.Spacer());
12614     },
12615
12616     /**
12617      * Adds a fill element that forces subsequent additions to the right side of the toolbar
12618      * @return {Roo.Toolbar.Fill} The fill item
12619      */
12620     addFill : function(){
12621         return this.addItem(new Roo.Toolbar.Fill());
12622     },
12623
12624     /**
12625      * Adds any standard HTML element to the toolbar
12626      * @param {String/HTMLElement/Element} el The element or id of the element to add
12627      * @return {Roo.Toolbar.Item} The element's item
12628      */
12629     addElement : function(el){
12630         return this.addItem(new Roo.Toolbar.Item(el));
12631     },
12632     /**
12633      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
12634      * @type Roo.util.MixedCollection  
12635      */
12636     items : false,
12637      
12638     /**
12639      * Adds any Toolbar.Item or subclass
12640      * @param {Roo.Toolbar.Item} item
12641      * @return {Roo.Toolbar.Item} The item
12642      */
12643     addItem : function(item){
12644         var td = this.nextBlock();
12645         item.render(td);
12646         this.items.add(item);
12647         return item;
12648     },
12649     
12650     /**
12651      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
12652      * @param {Object/Array} config A button config or array of configs
12653      * @return {Roo.Toolbar.Button/Array}
12654      */
12655     addButton : function(config){
12656         if(config instanceof Array){
12657             var buttons = [];
12658             for(var i = 0, len = config.length; i < len; i++) {
12659                 buttons.push(this.addButton(config[i]));
12660             }
12661             return buttons;
12662         }
12663         var b = config;
12664         if(!(config instanceof Roo.Toolbar.Button)){
12665             b = config.split ?
12666                 new Roo.Toolbar.SplitButton(config) :
12667                 new Roo.Toolbar.Button(config);
12668         }
12669         var td = this.nextBlock();
12670         b.render(td);
12671         this.items.add(b);
12672         return b;
12673     },
12674     
12675     /**
12676      * Adds text to the toolbar
12677      * @param {String} text The text to add
12678      * @return {Roo.Toolbar.Item} The element's item
12679      */
12680     addText : function(text){
12681         return this.addItem(new Roo.Toolbar.TextItem(text));
12682     },
12683     
12684     /**
12685      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
12686      * @param {Number} index The index where the item is to be inserted
12687      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
12688      * @return {Roo.Toolbar.Button/Item}
12689      */
12690     insertButton : function(index, item){
12691         if(item instanceof Array){
12692             var buttons = [];
12693             for(var i = 0, len = item.length; i < len; i++) {
12694                buttons.push(this.insertButton(index + i, item[i]));
12695             }
12696             return buttons;
12697         }
12698         if (!(item instanceof Roo.Toolbar.Button)){
12699            item = new Roo.Toolbar.Button(item);
12700         }
12701         var td = document.createElement("td");
12702         this.tr.insertBefore(td, this.tr.childNodes[index]);
12703         item.render(td);
12704         this.items.insert(index, item);
12705         return item;
12706     },
12707     
12708     /**
12709      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
12710      * @param {Object} config
12711      * @return {Roo.Toolbar.Item} The element's item
12712      */
12713     addDom : function(config, returnEl){
12714         var td = this.nextBlock();
12715         Roo.DomHelper.overwrite(td, config);
12716         var ti = new Roo.Toolbar.Item(td.firstChild);
12717         ti.render(td);
12718         this.items.add(ti);
12719         return ti;
12720     },
12721
12722     /**
12723      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
12724      * @type Roo.util.MixedCollection  
12725      */
12726     fields : false,
12727     
12728     /**
12729      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
12730      * Note: the field should not have been rendered yet. For a field that has already been
12731      * rendered, use {@link #addElement}.
12732      * @param {Roo.form.Field} field
12733      * @return {Roo.ToolbarItem}
12734      */
12735      
12736       
12737     addField : function(field) {
12738         if (!this.fields) {
12739             var autoId = 0;
12740             this.fields = new Roo.util.MixedCollection(false, function(o){
12741                 return o.id || ("item" + (++autoId));
12742             });
12743
12744         }
12745         
12746         var td = this.nextBlock();
12747         field.render(td);
12748         var ti = new Roo.Toolbar.Item(td.firstChild);
12749         ti.render(td);
12750         this.items.add(ti);
12751         this.fields.add(field);
12752         return ti;
12753     },
12754     /**
12755      * Hide the toolbar
12756      * @method hide
12757      */
12758      
12759       
12760     hide : function()
12761     {
12762         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
12763         this.el.child('div').hide();
12764     },
12765     /**
12766      * Show the toolbar
12767      * @method show
12768      */
12769     show : function()
12770     {
12771         this.el.child('div').show();
12772     },
12773       
12774     // private
12775     nextBlock : function(){
12776         var td = document.createElement("td");
12777         this.tr.appendChild(td);
12778         return td;
12779     },
12780
12781     // private
12782     destroy : function(){
12783         if(this.items){ // rendered?
12784             Roo.destroy.apply(Roo, this.items.items);
12785         }
12786         if(this.fields){ // rendered?
12787             Roo.destroy.apply(Roo, this.fields.items);
12788         }
12789         Roo.Element.uncache(this.el, this.tr);
12790     }
12791 };
12792
12793 /**
12794  * @class Roo.Toolbar.Item
12795  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
12796  * @constructor
12797  * Creates a new Item
12798  * @param {HTMLElement} el 
12799  */
12800 Roo.Toolbar.Item = function(el){
12801     this.el = Roo.getDom(el);
12802     this.id = Roo.id(this.el);
12803     this.hidden = false;
12804 };
12805
12806 Roo.Toolbar.Item.prototype = {
12807     
12808     /**
12809      * Get this item's HTML Element
12810      * @return {HTMLElement}
12811      */
12812     getEl : function(){
12813        return this.el;  
12814     },
12815
12816     // private
12817     render : function(td){
12818         this.td = td;
12819         td.appendChild(this.el);
12820     },
12821     
12822     /**
12823      * Removes and destroys this item.
12824      */
12825     destroy : function(){
12826         this.td.parentNode.removeChild(this.td);
12827     },
12828     
12829     /**
12830      * Shows this item.
12831      */
12832     show: function(){
12833         this.hidden = false;
12834         this.td.style.display = "";
12835     },
12836     
12837     /**
12838      * Hides this item.
12839      */
12840     hide: function(){
12841         this.hidden = true;
12842         this.td.style.display = "none";
12843     },
12844     
12845     /**
12846      * Convenience function for boolean show/hide.
12847      * @param {Boolean} visible true to show/false to hide
12848      */
12849     setVisible: function(visible){
12850         if(visible) {
12851             this.show();
12852         }else{
12853             this.hide();
12854         }
12855     },
12856     
12857     /**
12858      * Try to focus this item.
12859      */
12860     focus : function(){
12861         Roo.fly(this.el).focus();
12862     },
12863     
12864     /**
12865      * Disables this item.
12866      */
12867     disable : function(){
12868         Roo.fly(this.td).addClass("x-item-disabled");
12869         this.disabled = true;
12870         this.el.disabled = true;
12871     },
12872     
12873     /**
12874      * Enables this item.
12875      */
12876     enable : function(){
12877         Roo.fly(this.td).removeClass("x-item-disabled");
12878         this.disabled = false;
12879         this.el.disabled = false;
12880     }
12881 };
12882
12883
12884 /**
12885  * @class Roo.Toolbar.Separator
12886  * @extends Roo.Toolbar.Item
12887  * A simple toolbar separator class
12888  * @constructor
12889  * Creates a new Separator
12890  */
12891 Roo.Toolbar.Separator = function(){
12892     var s = document.createElement("span");
12893     s.className = "ytb-sep";
12894     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
12895 };
12896 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
12897     enable:Roo.emptyFn,
12898     disable:Roo.emptyFn,
12899     focus:Roo.emptyFn
12900 });
12901
12902 /**
12903  * @class Roo.Toolbar.Spacer
12904  * @extends Roo.Toolbar.Item
12905  * A simple element that adds extra horizontal space to a toolbar.
12906  * @constructor
12907  * Creates a new Spacer
12908  */
12909 Roo.Toolbar.Spacer = function(){
12910     var s = document.createElement("div");
12911     s.className = "ytb-spacer";
12912     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
12913 };
12914 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
12915     enable:Roo.emptyFn,
12916     disable:Roo.emptyFn,
12917     focus:Roo.emptyFn
12918 });
12919
12920 /**
12921  * @class Roo.Toolbar.Fill
12922  * @extends Roo.Toolbar.Spacer
12923  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
12924  * @constructor
12925  * Creates a new Spacer
12926  */
12927 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
12928     // private
12929     render : function(td){
12930         td.style.width = '100%';
12931         Roo.Toolbar.Fill.superclass.render.call(this, td);
12932     }
12933 });
12934
12935 /**
12936  * @class Roo.Toolbar.TextItem
12937  * @extends Roo.Toolbar.Item
12938  * A simple class that renders text directly into a toolbar.
12939  * @constructor
12940  * Creates a new TextItem
12941  * @param {String} text
12942  */
12943 Roo.Toolbar.TextItem = function(text){
12944     if (typeof(text) == 'object') {
12945         text = text.text;
12946     }
12947     var s = document.createElement("span");
12948     s.className = "ytb-text";
12949     s.innerHTML = text;
12950     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
12951 };
12952 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
12953     enable:Roo.emptyFn,
12954     disable:Roo.emptyFn,
12955     focus:Roo.emptyFn
12956 });
12957
12958 /**
12959  * @class Roo.Toolbar.Button
12960  * @extends Roo.Button
12961  * A button that renders into a toolbar.
12962  * @constructor
12963  * Creates a new Button
12964  * @param {Object} config A standard {@link Roo.Button} config object
12965  */
12966 Roo.Toolbar.Button = function(config){
12967     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
12968 };
12969 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
12970     render : function(td){
12971         this.td = td;
12972         Roo.Toolbar.Button.superclass.render.call(this, td);
12973     },
12974     
12975     /**
12976      * Removes and destroys this button
12977      */
12978     destroy : function(){
12979         Roo.Toolbar.Button.superclass.destroy.call(this);
12980         this.td.parentNode.removeChild(this.td);
12981     },
12982     
12983     /**
12984      * Shows this button
12985      */
12986     show: function(){
12987         this.hidden = false;
12988         this.td.style.display = "";
12989     },
12990     
12991     /**
12992      * Hides this button
12993      */
12994     hide: function(){
12995         this.hidden = true;
12996         this.td.style.display = "none";
12997     },
12998
12999     /**
13000      * Disables this item
13001      */
13002     disable : function(){
13003         Roo.fly(this.td).addClass("x-item-disabled");
13004         this.disabled = true;
13005     },
13006
13007     /**
13008      * Enables this item
13009      */
13010     enable : function(){
13011         Roo.fly(this.td).removeClass("x-item-disabled");
13012         this.disabled = false;
13013     }
13014 });
13015 // backwards compat
13016 Roo.ToolbarButton = Roo.Toolbar.Button;
13017
13018 /**
13019  * @class Roo.Toolbar.SplitButton
13020  * @extends Roo.SplitButton
13021  * A menu button that renders into a toolbar.
13022  * @constructor
13023  * Creates a new SplitButton
13024  * @param {Object} config A standard {@link Roo.SplitButton} config object
13025  */
13026 Roo.Toolbar.SplitButton = function(config){
13027     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
13028 };
13029 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
13030     render : function(td){
13031         this.td = td;
13032         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
13033     },
13034     
13035     /**
13036      * Removes and destroys this button
13037      */
13038     destroy : function(){
13039         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
13040         this.td.parentNode.removeChild(this.td);
13041     },
13042     
13043     /**
13044      * Shows this button
13045      */
13046     show: function(){
13047         this.hidden = false;
13048         this.td.style.display = "";
13049     },
13050     
13051     /**
13052      * Hides this button
13053      */
13054     hide: function(){
13055         this.hidden = true;
13056         this.td.style.display = "none";
13057     }
13058 });
13059
13060 // backwards compat
13061 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
13062  * Based on:
13063  * Ext JS Library 1.1.1
13064  * Copyright(c) 2006-2007, Ext JS, LLC.
13065  *
13066  * Originally Released Under LGPL - original licence link has changed is not relivant.
13067  *
13068  * Fork - LGPL
13069  * <script type="text/javascript">
13070  */
13071  
13072 /**
13073  * @class Roo.PagingToolbar
13074  * @extends Roo.Toolbar
13075  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
13076  * @constructor
13077  * Create a new PagingToolbar
13078  * @param {Object} config The config object
13079  */
13080 Roo.PagingToolbar = function(el, ds, config)
13081 {
13082     // old args format still supported... - xtype is prefered..
13083     if (typeof(el) == 'object' && el.xtype) {
13084         // created from xtype...
13085         config = el;
13086         ds = el.dataSource;
13087         el = config.container;
13088     }
13089     var items = [];
13090     if (config.items) {
13091         items = config.items;
13092         config.items = [];
13093     }
13094     
13095     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
13096     this.ds = ds;
13097     this.cursor = 0;
13098     this.renderButtons(this.el);
13099     this.bind(ds);
13100     
13101     // supprot items array.
13102    
13103     Roo.each(items, function(e) {
13104         this.add(Roo.factory(e));
13105     },this);
13106     
13107 };
13108
13109 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
13110     /**
13111      * @cfg {Roo.data.Store} dataSource
13112      * The underlying data store providing the paged data
13113      */
13114     /**
13115      * @cfg {String/HTMLElement/Element} container
13116      * container The id or element that will contain the toolbar
13117      */
13118     /**
13119      * @cfg {Boolean} displayInfo
13120      * True to display the displayMsg (defaults to false)
13121      */
13122     /**
13123      * @cfg {Number} pageSize
13124      * The number of records to display per page (defaults to 20)
13125      */
13126     pageSize: 20,
13127     /**
13128      * @cfg {String} displayMsg
13129      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
13130      */
13131     displayMsg : 'Displaying {0} - {1} of {2}',
13132     /**
13133      * @cfg {String} emptyMsg
13134      * The message to display when no records are found (defaults to "No data to display")
13135      */
13136     emptyMsg : 'No data to display',
13137     /**
13138      * Customizable piece of the default paging text (defaults to "Page")
13139      * @type String
13140      */
13141     beforePageText : "Page",
13142     /**
13143      * Customizable piece of the default paging text (defaults to "of %0")
13144      * @type String
13145      */
13146     afterPageText : "of {0}",
13147     /**
13148      * Customizable piece of the default paging text (defaults to "First Page")
13149      * @type String
13150      */
13151     firstText : "First Page",
13152     /**
13153      * Customizable piece of the default paging text (defaults to "Previous Page")
13154      * @type String
13155      */
13156     prevText : "Previous Page",
13157     /**
13158      * Customizable piece of the default paging text (defaults to "Next Page")
13159      * @type String
13160      */
13161     nextText : "Next Page",
13162     /**
13163      * Customizable piece of the default paging text (defaults to "Last Page")
13164      * @type String
13165      */
13166     lastText : "Last Page",
13167     /**
13168      * Customizable piece of the default paging text (defaults to "Refresh")
13169      * @type String
13170      */
13171     refreshText : "Refresh",
13172
13173     // private
13174     renderButtons : function(el){
13175         Roo.PagingToolbar.superclass.render.call(this, el);
13176         this.first = this.addButton({
13177             tooltip: this.firstText,
13178             cls: "x-btn-icon x-grid-page-first",
13179             disabled: true,
13180             handler: this.onClick.createDelegate(this, ["first"])
13181         });
13182         this.prev = this.addButton({
13183             tooltip: this.prevText,
13184             cls: "x-btn-icon x-grid-page-prev",
13185             disabled: true,
13186             handler: this.onClick.createDelegate(this, ["prev"])
13187         });
13188         //this.addSeparator();
13189         this.add(this.beforePageText);
13190         this.field = Roo.get(this.addDom({
13191            tag: "input",
13192            type: "text",
13193            size: "3",
13194            value: "1",
13195            cls: "x-grid-page-number"
13196         }).el);
13197         this.field.on("keydown", this.onPagingKeydown, this);
13198         this.field.on("focus", function(){this.dom.select();});
13199         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
13200         this.field.setHeight(18);
13201         //this.addSeparator();
13202         this.next = this.addButton({
13203             tooltip: this.nextText,
13204             cls: "x-btn-icon x-grid-page-next",
13205             disabled: true,
13206             handler: this.onClick.createDelegate(this, ["next"])
13207         });
13208         this.last = this.addButton({
13209             tooltip: this.lastText,
13210             cls: "x-btn-icon x-grid-page-last",
13211             disabled: true,
13212             handler: this.onClick.createDelegate(this, ["last"])
13213         });
13214         //this.addSeparator();
13215         this.loading = this.addButton({
13216             tooltip: this.refreshText,
13217             cls: "x-btn-icon x-grid-loading",
13218             handler: this.onClick.createDelegate(this, ["refresh"])
13219         });
13220
13221         if(this.displayInfo){
13222             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
13223         }
13224     },
13225
13226     // private
13227     updateInfo : function(){
13228         if(this.displayEl){
13229             var count = this.ds.getCount();
13230             var msg = count == 0 ?
13231                 this.emptyMsg :
13232                 String.format(
13233                     this.displayMsg,
13234                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
13235                 );
13236             this.displayEl.update(msg);
13237         }
13238     },
13239
13240     // private
13241     onLoad : function(ds, r, o){
13242        this.cursor = o.params ? o.params.start : 0;
13243        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
13244
13245        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
13246        this.field.dom.value = ap;
13247        this.first.setDisabled(ap == 1);
13248        this.prev.setDisabled(ap == 1);
13249        this.next.setDisabled(ap == ps);
13250        this.last.setDisabled(ap == ps);
13251        this.loading.enable();
13252        this.updateInfo();
13253     },
13254
13255     // private
13256     getPageData : function(){
13257         var total = this.ds.getTotalCount();
13258         return {
13259             total : total,
13260             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
13261             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
13262         };
13263     },
13264
13265     // private
13266     onLoadError : function(){
13267         this.loading.enable();
13268     },
13269
13270     // private
13271     onPagingKeydown : function(e){
13272         var k = e.getKey();
13273         var d = this.getPageData();
13274         if(k == e.RETURN){
13275             var v = this.field.dom.value, pageNum;
13276             if(!v || isNaN(pageNum = parseInt(v, 10))){
13277                 this.field.dom.value = d.activePage;
13278                 return;
13279             }
13280             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
13281             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13282             e.stopEvent();
13283         }
13284         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))
13285         {
13286           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
13287           this.field.dom.value = pageNum;
13288           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
13289           e.stopEvent();
13290         }
13291         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13292         {
13293           var v = this.field.dom.value, pageNum; 
13294           var increment = (e.shiftKey) ? 10 : 1;
13295           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13296             increment *= -1;
13297           if(!v || isNaN(pageNum = parseInt(v, 10))) {
13298             this.field.dom.value = d.activePage;
13299             return;
13300           }
13301           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
13302           {
13303             this.field.dom.value = parseInt(v, 10) + increment;
13304             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
13305             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13306           }
13307           e.stopEvent();
13308         }
13309     },
13310
13311     // private
13312     beforeLoad : function(){
13313         if(this.loading){
13314             this.loading.disable();
13315         }
13316     },
13317
13318     // private
13319     onClick : function(which){
13320         var ds = this.ds;
13321         switch(which){
13322             case "first":
13323                 ds.load({params:{start: 0, limit: this.pageSize}});
13324             break;
13325             case "prev":
13326                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
13327             break;
13328             case "next":
13329                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
13330             break;
13331             case "last":
13332                 var total = ds.getTotalCount();
13333                 var extra = total % this.pageSize;
13334                 var lastStart = extra ? (total - extra) : total-this.pageSize;
13335                 ds.load({params:{start: lastStart, limit: this.pageSize}});
13336             break;
13337             case "refresh":
13338                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
13339             break;
13340         }
13341     },
13342
13343     /**
13344      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
13345      * @param {Roo.data.Store} store The data store to unbind
13346      */
13347     unbind : function(ds){
13348         ds.un("beforeload", this.beforeLoad, this);
13349         ds.un("load", this.onLoad, this);
13350         ds.un("loadexception", this.onLoadError, this);
13351         ds.un("remove", this.updateInfo, this);
13352         ds.un("add", this.updateInfo, this);
13353         this.ds = undefined;
13354     },
13355
13356     /**
13357      * Binds the paging toolbar to the specified {@link Roo.data.Store}
13358      * @param {Roo.data.Store} store The data store to bind
13359      */
13360     bind : function(ds){
13361         ds.on("beforeload", this.beforeLoad, this);
13362         ds.on("load", this.onLoad, this);
13363         ds.on("loadexception", this.onLoadError, this);
13364         ds.on("remove", this.updateInfo, this);
13365         ds.on("add", this.updateInfo, this);
13366         this.ds = ds;
13367     }
13368 });/*
13369  * Based on:
13370  * Ext JS Library 1.1.1
13371  * Copyright(c) 2006-2007, Ext JS, LLC.
13372  *
13373  * Originally Released Under LGPL - original licence link has changed is not relivant.
13374  *
13375  * Fork - LGPL
13376  * <script type="text/javascript">
13377  */
13378
13379 /**
13380  * @class Roo.Resizable
13381  * @extends Roo.util.Observable
13382  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
13383  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
13384  * 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
13385  * the element will be wrapped for you automatically.</p>
13386  * <p>Here is the list of valid resize handles:</p>
13387  * <pre>
13388 Value   Description
13389 ------  -------------------
13390  'n'     north
13391  's'     south
13392  'e'     east
13393  'w'     west
13394  'nw'    northwest
13395  'sw'    southwest
13396  'se'    southeast
13397  'ne'    northeast
13398  'hd'    horizontal drag
13399  'all'   all
13400 </pre>
13401  * <p>Here's an example showing the creation of a typical Resizable:</p>
13402  * <pre><code>
13403 var resizer = new Roo.Resizable("element-id", {
13404     handles: 'all',
13405     minWidth: 200,
13406     minHeight: 100,
13407     maxWidth: 500,
13408     maxHeight: 400,
13409     pinned: true
13410 });
13411 resizer.on("resize", myHandler);
13412 </code></pre>
13413  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
13414  * resizer.east.setDisplayed(false);</p>
13415  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
13416  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
13417  * resize operation's new size (defaults to [0, 0])
13418  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
13419  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
13420  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
13421  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
13422  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
13423  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
13424  * @cfg {Number} width The width of the element in pixels (defaults to null)
13425  * @cfg {Number} height The height of the element in pixels (defaults to null)
13426  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
13427  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
13428  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
13429  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
13430  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
13431  * in favor of the handles config option (defaults to false)
13432  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
13433  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
13434  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
13435  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
13436  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
13437  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
13438  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
13439  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
13440  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
13441  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
13442  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
13443  * @constructor
13444  * Create a new resizable component
13445  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
13446  * @param {Object} config configuration options
13447   */
13448 Roo.Resizable = function(el, config)
13449 {
13450     this.el = Roo.get(el);
13451
13452     if(config && config.wrap){
13453         config.resizeChild = this.el;
13454         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
13455         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
13456         this.el.setStyle("overflow", "hidden");
13457         this.el.setPositioning(config.resizeChild.getPositioning());
13458         config.resizeChild.clearPositioning();
13459         if(!config.width || !config.height){
13460             var csize = config.resizeChild.getSize();
13461             this.el.setSize(csize.width, csize.height);
13462         }
13463         if(config.pinned && !config.adjustments){
13464             config.adjustments = "auto";
13465         }
13466     }
13467
13468     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
13469     this.proxy.unselectable();
13470     this.proxy.enableDisplayMode('block');
13471
13472     Roo.apply(this, config);
13473
13474     if(this.pinned){
13475         this.disableTrackOver = true;
13476         this.el.addClass("x-resizable-pinned");
13477     }
13478     // if the element isn't positioned, make it relative
13479     var position = this.el.getStyle("position");
13480     if(position != "absolute" && position != "fixed"){
13481         this.el.setStyle("position", "relative");
13482     }
13483     if(!this.handles){ // no handles passed, must be legacy style
13484         this.handles = 's,e,se';
13485         if(this.multiDirectional){
13486             this.handles += ',n,w';
13487         }
13488     }
13489     if(this.handles == "all"){
13490         this.handles = "n s e w ne nw se sw";
13491     }
13492     var hs = this.handles.split(/\s*?[,;]\s*?| /);
13493     var ps = Roo.Resizable.positions;
13494     for(var i = 0, len = hs.length; i < len; i++){
13495         if(hs[i] && ps[hs[i]]){
13496             var pos = ps[hs[i]];
13497             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
13498         }
13499     }
13500     // legacy
13501     this.corner = this.southeast;
13502     
13503     // updateBox = the box can move..
13504     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
13505         this.updateBox = true;
13506     }
13507
13508     this.activeHandle = null;
13509
13510     if(this.resizeChild){
13511         if(typeof this.resizeChild == "boolean"){
13512             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
13513         }else{
13514             this.resizeChild = Roo.get(this.resizeChild, true);
13515         }
13516     }
13517     
13518     if(this.adjustments == "auto"){
13519         var rc = this.resizeChild;
13520         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
13521         if(rc && (hw || hn)){
13522             rc.position("relative");
13523             rc.setLeft(hw ? hw.el.getWidth() : 0);
13524             rc.setTop(hn ? hn.el.getHeight() : 0);
13525         }
13526         this.adjustments = [
13527             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
13528             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
13529         ];
13530     }
13531
13532     if(this.draggable){
13533         this.dd = this.dynamic ?
13534             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
13535         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
13536     }
13537
13538     // public events
13539     this.addEvents({
13540         /**
13541          * @event beforeresize
13542          * Fired before resize is allowed. Set enabled to false to cancel resize.
13543          * @param {Roo.Resizable} this
13544          * @param {Roo.EventObject} e The mousedown event
13545          */
13546         "beforeresize" : true,
13547         /**
13548          * @event resizing
13549          * Fired a resizing.
13550          * @param {Roo.Resizable} this
13551          * @param {Number} x The new x position
13552          * @param {Number} y The new y position
13553          * @param {Number} w The new w width
13554          * @param {Number} h The new h hight
13555          * @param {Roo.EventObject} e The mouseup event
13556          */
13557         "resizing" : true,
13558         /**
13559          * @event resize
13560          * Fired after a resize.
13561          * @param {Roo.Resizable} this
13562          * @param {Number} width The new width
13563          * @param {Number} height The new height
13564          * @param {Roo.EventObject} e The mouseup event
13565          */
13566         "resize" : true
13567     });
13568
13569     if(this.width !== null && this.height !== null){
13570         this.resizeTo(this.width, this.height);
13571     }else{
13572         this.updateChildSize();
13573     }
13574     if(Roo.isIE){
13575         this.el.dom.style.zoom = 1;
13576     }
13577     Roo.Resizable.superclass.constructor.call(this);
13578 };
13579
13580 Roo.extend(Roo.Resizable, Roo.util.Observable, {
13581         resizeChild : false,
13582         adjustments : [0, 0],
13583         minWidth : 5,
13584         minHeight : 5,
13585         maxWidth : 10000,
13586         maxHeight : 10000,
13587         enabled : true,
13588         animate : false,
13589         duration : .35,
13590         dynamic : false,
13591         handles : false,
13592         multiDirectional : false,
13593         disableTrackOver : false,
13594         easing : 'easeOutStrong',
13595         widthIncrement : 0,
13596         heightIncrement : 0,
13597         pinned : false,
13598         width : null,
13599         height : null,
13600         preserveRatio : false,
13601         transparent: false,
13602         minX: 0,
13603         minY: 0,
13604         draggable: false,
13605
13606         /**
13607          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
13608          */
13609         constrainTo: undefined,
13610         /**
13611          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
13612          */
13613         resizeRegion: undefined,
13614
13615
13616     /**
13617      * Perform a manual resize
13618      * @param {Number} width
13619      * @param {Number} height
13620      */
13621     resizeTo : function(width, height){
13622         this.el.setSize(width, height);
13623         this.updateChildSize();
13624         this.fireEvent("resize", this, width, height, null);
13625     },
13626
13627     // private
13628     startSizing : function(e, handle){
13629         this.fireEvent("beforeresize", this, e);
13630         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
13631
13632             if(!this.overlay){
13633                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
13634                 this.overlay.unselectable();
13635                 this.overlay.enableDisplayMode("block");
13636                 this.overlay.on("mousemove", this.onMouseMove, this);
13637                 this.overlay.on("mouseup", this.onMouseUp, this);
13638             }
13639             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
13640
13641             this.resizing = true;
13642             this.startBox = this.el.getBox();
13643             this.startPoint = e.getXY();
13644             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
13645                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
13646
13647             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13648             this.overlay.show();
13649
13650             if(this.constrainTo) {
13651                 var ct = Roo.get(this.constrainTo);
13652                 this.resizeRegion = ct.getRegion().adjust(
13653                     ct.getFrameWidth('t'),
13654                     ct.getFrameWidth('l'),
13655                     -ct.getFrameWidth('b'),
13656                     -ct.getFrameWidth('r')
13657                 );
13658             }
13659
13660             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
13661             this.proxy.show();
13662             this.proxy.setBox(this.startBox);
13663             if(!this.dynamic){
13664                 this.proxy.setStyle('visibility', 'visible');
13665             }
13666         }
13667     },
13668
13669     // private
13670     onMouseDown : function(handle, e){
13671         if(this.enabled){
13672             e.stopEvent();
13673             this.activeHandle = handle;
13674             this.startSizing(e, handle);
13675         }
13676     },
13677
13678     // private
13679     onMouseUp : function(e){
13680         var size = this.resizeElement();
13681         this.resizing = false;
13682         this.handleOut();
13683         this.overlay.hide();
13684         this.proxy.hide();
13685         this.fireEvent("resize", this, size.width, size.height, e);
13686     },
13687
13688     // private
13689     updateChildSize : function(){
13690         
13691         if(this.resizeChild){
13692             var el = this.el;
13693             var child = this.resizeChild;
13694             var adj = this.adjustments;
13695             if(el.dom.offsetWidth){
13696                 var b = el.getSize(true);
13697                 child.setSize(b.width+adj[0], b.height+adj[1]);
13698             }
13699             // Second call here for IE
13700             // The first call enables instant resizing and
13701             // the second call corrects scroll bars if they
13702             // exist
13703             if(Roo.isIE){
13704                 setTimeout(function(){
13705                     if(el.dom.offsetWidth){
13706                         var b = el.getSize(true);
13707                         child.setSize(b.width+adj[0], b.height+adj[1]);
13708                     }
13709                 }, 10);
13710             }
13711         }
13712     },
13713
13714     // private
13715     snap : function(value, inc, min){
13716         if(!inc || !value) return value;
13717         var newValue = value;
13718         var m = value % inc;
13719         if(m > 0){
13720             if(m > (inc/2)){
13721                 newValue = value + (inc-m);
13722             }else{
13723                 newValue = value - m;
13724             }
13725         }
13726         return Math.max(min, newValue);
13727     },
13728
13729     // private
13730     resizeElement : function(){
13731         var box = this.proxy.getBox();
13732         if(this.updateBox){
13733             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
13734         }else{
13735             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
13736         }
13737         this.updateChildSize();
13738         if(!this.dynamic){
13739             this.proxy.hide();
13740         }
13741         return box;
13742     },
13743
13744     // private
13745     constrain : function(v, diff, m, mx){
13746         if(v - diff < m){
13747             diff = v - m;
13748         }else if(v - diff > mx){
13749             diff = mx - v;
13750         }
13751         return diff;
13752     },
13753
13754     // private
13755     onMouseMove : function(e){
13756         
13757         if(this.enabled){
13758             try{// try catch so if something goes wrong the user doesn't get hung
13759
13760             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
13761                 return;
13762             }
13763
13764             //var curXY = this.startPoint;
13765             var curSize = this.curSize || this.startBox;
13766             var x = this.startBox.x, y = this.startBox.y;
13767             var ox = x, oy = y;
13768             var w = curSize.width, h = curSize.height;
13769             var ow = w, oh = h;
13770             var mw = this.minWidth, mh = this.minHeight;
13771             var mxw = this.maxWidth, mxh = this.maxHeight;
13772             var wi = this.widthIncrement;
13773             var hi = this.heightIncrement;
13774
13775             var eventXY = e.getXY();
13776             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
13777             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
13778
13779             var pos = this.activeHandle.position;
13780
13781             switch(pos){
13782                 case "east":
13783                     w += diffX;
13784                     w = Math.min(Math.max(mw, w), mxw);
13785                     break;
13786              
13787                 case "south":
13788                     h += diffY;
13789                     h = Math.min(Math.max(mh, h), mxh);
13790                     break;
13791                 case "southeast":
13792                     w += diffX;
13793                     h += diffY;
13794                     w = Math.min(Math.max(mw, w), mxw);
13795                     h = Math.min(Math.max(mh, h), mxh);
13796                     break;
13797                 case "north":
13798                     diffY = this.constrain(h, diffY, mh, mxh);
13799                     y += diffY;
13800                     h -= diffY;
13801                     break;
13802                 case "hdrag":
13803                     
13804                     if (wi) {
13805                         var adiffX = Math.abs(diffX);
13806                         var sub = (adiffX % wi); // how much 
13807                         if (sub > (wi/2)) { // far enough to snap
13808                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
13809                         } else {
13810                             // remove difference.. 
13811                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
13812                         }
13813                     }
13814                     x += diffX;
13815                     x = Math.max(this.minX, x);
13816                     break;
13817                 case "west":
13818                     diffX = this.constrain(w, diffX, mw, mxw);
13819                     x += diffX;
13820                     w -= diffX;
13821                     break;
13822                 case "northeast":
13823                     w += diffX;
13824                     w = Math.min(Math.max(mw, w), mxw);
13825                     diffY = this.constrain(h, diffY, mh, mxh);
13826                     y += diffY;
13827                     h -= diffY;
13828                     break;
13829                 case "northwest":
13830                     diffX = this.constrain(w, diffX, mw, mxw);
13831                     diffY = this.constrain(h, diffY, mh, mxh);
13832                     y += diffY;
13833                     h -= diffY;
13834                     x += diffX;
13835                     w -= diffX;
13836                     break;
13837                case "southwest":
13838                     diffX = this.constrain(w, diffX, mw, mxw);
13839                     h += diffY;
13840                     h = Math.min(Math.max(mh, h), mxh);
13841                     x += diffX;
13842                     w -= diffX;
13843                     break;
13844             }
13845
13846             var sw = this.snap(w, wi, mw);
13847             var sh = this.snap(h, hi, mh);
13848             if(sw != w || sh != h){
13849                 switch(pos){
13850                     case "northeast":
13851                         y -= sh - h;
13852                     break;
13853                     case "north":
13854                         y -= sh - h;
13855                         break;
13856                     case "southwest":
13857                         x -= sw - w;
13858                     break;
13859                     case "west":
13860                         x -= sw - w;
13861                         break;
13862                     case "northwest":
13863                         x -= sw - w;
13864                         y -= sh - h;
13865                     break;
13866                 }
13867                 w = sw;
13868                 h = sh;
13869             }
13870
13871             if(this.preserveRatio){
13872                 switch(pos){
13873                     case "southeast":
13874                     case "east":
13875                         h = oh * (w/ow);
13876                         h = Math.min(Math.max(mh, h), mxh);
13877                         w = ow * (h/oh);
13878                        break;
13879                     case "south":
13880                         w = ow * (h/oh);
13881                         w = Math.min(Math.max(mw, w), mxw);
13882                         h = oh * (w/ow);
13883                         break;
13884                     case "northeast":
13885                         w = ow * (h/oh);
13886                         w = Math.min(Math.max(mw, w), mxw);
13887                         h = oh * (w/ow);
13888                     break;
13889                     case "north":
13890                         var tw = w;
13891                         w = ow * (h/oh);
13892                         w = Math.min(Math.max(mw, w), mxw);
13893                         h = oh * (w/ow);
13894                         x += (tw - w) / 2;
13895                         break;
13896                     case "southwest":
13897                         h = oh * (w/ow);
13898                         h = Math.min(Math.max(mh, h), mxh);
13899                         var tw = w;
13900                         w = ow * (h/oh);
13901                         x += tw - w;
13902                         break;
13903                     case "west":
13904                         var th = h;
13905                         h = oh * (w/ow);
13906                         h = Math.min(Math.max(mh, h), mxh);
13907                         y += (th - h) / 2;
13908                         var tw = w;
13909                         w = ow * (h/oh);
13910                         x += tw - w;
13911                        break;
13912                     case "northwest":
13913                         var tw = w;
13914                         var th = h;
13915                         h = oh * (w/ow);
13916                         h = Math.min(Math.max(mh, h), mxh);
13917                         w = ow * (h/oh);
13918                         y += th - h;
13919                         x += tw - w;
13920                        break;
13921
13922                 }
13923             }
13924             if (pos == 'hdrag') {
13925                 w = ow;
13926             }
13927             this.proxy.setBounds(x, y, w, h);
13928             if(this.dynamic){
13929                 this.resizeElement();
13930             }
13931             }catch(e){}
13932         }
13933         this.fireEvent("resizing", this, x, y, w, h, e);
13934     },
13935
13936     // private
13937     handleOver : function(){
13938         if(this.enabled){
13939             this.el.addClass("x-resizable-over");
13940         }
13941     },
13942
13943     // private
13944     handleOut : function(){
13945         if(!this.resizing){
13946             this.el.removeClass("x-resizable-over");
13947         }
13948     },
13949
13950     /**
13951      * Returns the element this component is bound to.
13952      * @return {Roo.Element}
13953      */
13954     getEl : function(){
13955         return this.el;
13956     },
13957
13958     /**
13959      * Returns the resizeChild element (or null).
13960      * @return {Roo.Element}
13961      */
13962     getResizeChild : function(){
13963         return this.resizeChild;
13964     },
13965     groupHandler : function()
13966     {
13967         
13968     },
13969     /**
13970      * Destroys this resizable. If the element was wrapped and
13971      * removeEl is not true then the element remains.
13972      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
13973      */
13974     destroy : function(removeEl){
13975         this.proxy.remove();
13976         if(this.overlay){
13977             this.overlay.removeAllListeners();
13978             this.overlay.remove();
13979         }
13980         var ps = Roo.Resizable.positions;
13981         for(var k in ps){
13982             if(typeof ps[k] != "function" && this[ps[k]]){
13983                 var h = this[ps[k]];
13984                 h.el.removeAllListeners();
13985                 h.el.remove();
13986             }
13987         }
13988         if(removeEl){
13989             this.el.update("");
13990             this.el.remove();
13991         }
13992     }
13993 });
13994
13995 // private
13996 // hash to map config positions to true positions
13997 Roo.Resizable.positions = {
13998     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
13999     hd: "hdrag"
14000 };
14001
14002 // private
14003 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
14004     if(!this.tpl){
14005         // only initialize the template if resizable is used
14006         var tpl = Roo.DomHelper.createTemplate(
14007             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
14008         );
14009         tpl.compile();
14010         Roo.Resizable.Handle.prototype.tpl = tpl;
14011     }
14012     this.position = pos;
14013     this.rz = rz;
14014     // show north drag fro topdra
14015     var handlepos = pos == 'hdrag' ? 'north' : pos;
14016     
14017     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
14018     if (pos == 'hdrag') {
14019         this.el.setStyle('cursor', 'pointer');
14020     }
14021     this.el.unselectable();
14022     if(transparent){
14023         this.el.setOpacity(0);
14024     }
14025     this.el.on("mousedown", this.onMouseDown, this);
14026     if(!disableTrackOver){
14027         this.el.on("mouseover", this.onMouseOver, this);
14028         this.el.on("mouseout", this.onMouseOut, this);
14029     }
14030 };
14031
14032 // private
14033 Roo.Resizable.Handle.prototype = {
14034     afterResize : function(rz){
14035         // do nothing
14036     },
14037     // private
14038     onMouseDown : function(e){
14039         this.rz.onMouseDown(this, e);
14040     },
14041     // private
14042     onMouseOver : function(e){
14043         this.rz.handleOver(this, e);
14044     },
14045     // private
14046     onMouseOut : function(e){
14047         this.rz.handleOut(this, e);
14048     }
14049 };/*
14050  * Based on:
14051  * Ext JS Library 1.1.1
14052  * Copyright(c) 2006-2007, Ext JS, LLC.
14053  *
14054  * Originally Released Under LGPL - original licence link has changed is not relivant.
14055  *
14056  * Fork - LGPL
14057  * <script type="text/javascript">
14058  */
14059
14060 /**
14061  * @class Roo.Editor
14062  * @extends Roo.Component
14063  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
14064  * @constructor
14065  * Create a new Editor
14066  * @param {Roo.form.Field} field The Field object (or descendant)
14067  * @param {Object} config The config object
14068  */
14069 Roo.Editor = function(field, config){
14070     Roo.Editor.superclass.constructor.call(this, config);
14071     this.field = field;
14072     this.addEvents({
14073         /**
14074              * @event beforestartedit
14075              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
14076              * false from the handler of this event.
14077              * @param {Editor} this
14078              * @param {Roo.Element} boundEl The underlying element bound to this editor
14079              * @param {Mixed} value The field value being set
14080              */
14081         "beforestartedit" : true,
14082         /**
14083              * @event startedit
14084              * Fires when this editor is displayed
14085              * @param {Roo.Element} boundEl The underlying element bound to this editor
14086              * @param {Mixed} value The starting field value
14087              */
14088         "startedit" : true,
14089         /**
14090              * @event beforecomplete
14091              * Fires after a change has been made to the field, but before the change is reflected in the underlying
14092              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
14093              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
14094              * event will not fire since no edit actually occurred.
14095              * @param {Editor} this
14096              * @param {Mixed} value The current field value
14097              * @param {Mixed} startValue The original field value
14098              */
14099         "beforecomplete" : true,
14100         /**
14101              * @event complete
14102              * Fires after editing is complete and any changed value has been written to the underlying field.
14103              * @param {Editor} this
14104              * @param {Mixed} value The current field value
14105              * @param {Mixed} startValue The original field value
14106              */
14107         "complete" : true,
14108         /**
14109          * @event specialkey
14110          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
14111          * {@link Roo.EventObject#getKey} to determine which key was pressed.
14112          * @param {Roo.form.Field} this
14113          * @param {Roo.EventObject} e The event object
14114          */
14115         "specialkey" : true
14116     });
14117 };
14118
14119 Roo.extend(Roo.Editor, Roo.Component, {
14120     /**
14121      * @cfg {Boolean/String} autosize
14122      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
14123      * or "height" to adopt the height only (defaults to false)
14124      */
14125     /**
14126      * @cfg {Boolean} revertInvalid
14127      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
14128      * validation fails (defaults to true)
14129      */
14130     /**
14131      * @cfg {Boolean} ignoreNoChange
14132      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
14133      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
14134      * will never be ignored.
14135      */
14136     /**
14137      * @cfg {Boolean} hideEl
14138      * False to keep the bound element visible while the editor is displayed (defaults to true)
14139      */
14140     /**
14141      * @cfg {Mixed} value
14142      * The data value of the underlying field (defaults to "")
14143      */
14144     value : "",
14145     /**
14146      * @cfg {String} alignment
14147      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
14148      */
14149     alignment: "c-c?",
14150     /**
14151      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
14152      * for bottom-right shadow (defaults to "frame")
14153      */
14154     shadow : "frame",
14155     /**
14156      * @cfg {Boolean} constrain True to constrain the editor to the viewport
14157      */
14158     constrain : false,
14159     /**
14160      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
14161      */
14162     completeOnEnter : false,
14163     /**
14164      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
14165      */
14166     cancelOnEsc : false,
14167     /**
14168      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
14169      */
14170     updateEl : false,
14171
14172     // private
14173     onRender : function(ct, position){
14174         this.el = new Roo.Layer({
14175             shadow: this.shadow,
14176             cls: "x-editor",
14177             parentEl : ct,
14178             shim : this.shim,
14179             shadowOffset:4,
14180             id: this.id,
14181             constrain: this.constrain
14182         });
14183         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
14184         if(this.field.msgTarget != 'title'){
14185             this.field.msgTarget = 'qtip';
14186         }
14187         this.field.render(this.el);
14188         if(Roo.isGecko){
14189             this.field.el.dom.setAttribute('autocomplete', 'off');
14190         }
14191         this.field.on("specialkey", this.onSpecialKey, this);
14192         if(this.swallowKeys){
14193             this.field.el.swallowEvent(['keydown','keypress']);
14194         }
14195         this.field.show();
14196         this.field.on("blur", this.onBlur, this);
14197         if(this.field.grow){
14198             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
14199         }
14200     },
14201
14202     onSpecialKey : function(field, e)
14203     {
14204         //Roo.log('editor onSpecialKey');
14205         if(this.completeOnEnter && e.getKey() == e.ENTER){
14206             e.stopEvent();
14207             this.completeEdit();
14208             return;
14209         }
14210         // do not fire special key otherwise it might hide close the editor...
14211         if(e.getKey() == e.ENTER){    
14212             return;
14213         }
14214         if(this.cancelOnEsc && e.getKey() == e.ESC){
14215             this.cancelEdit();
14216             return;
14217         } 
14218         this.fireEvent('specialkey', field, e);
14219     
14220     },
14221
14222     /**
14223      * Starts the editing process and shows the editor.
14224      * @param {String/HTMLElement/Element} el The element to edit
14225      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
14226       * to the innerHTML of el.
14227      */
14228     startEdit : function(el, value){
14229         if(this.editing){
14230             this.completeEdit();
14231         }
14232         this.boundEl = Roo.get(el);
14233         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
14234         if(!this.rendered){
14235             this.render(this.parentEl || document.body);
14236         }
14237         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
14238             return;
14239         }
14240         this.startValue = v;
14241         this.field.setValue(v);
14242         if(this.autoSize){
14243             var sz = this.boundEl.getSize();
14244             switch(this.autoSize){
14245                 case "width":
14246                 this.setSize(sz.width,  "");
14247                 break;
14248                 case "height":
14249                 this.setSize("",  sz.height);
14250                 break;
14251                 default:
14252                 this.setSize(sz.width,  sz.height);
14253             }
14254         }
14255         this.el.alignTo(this.boundEl, this.alignment);
14256         this.editing = true;
14257         if(Roo.QuickTips){
14258             Roo.QuickTips.disable();
14259         }
14260         this.show();
14261     },
14262
14263     /**
14264      * Sets the height and width of this editor.
14265      * @param {Number} width The new width
14266      * @param {Number} height The new height
14267      */
14268     setSize : function(w, h){
14269         this.field.setSize(w, h);
14270         if(this.el){
14271             this.el.sync();
14272         }
14273     },
14274
14275     /**
14276      * Realigns the editor to the bound field based on the current alignment config value.
14277      */
14278     realign : function(){
14279         this.el.alignTo(this.boundEl, this.alignment);
14280     },
14281
14282     /**
14283      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
14284      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
14285      */
14286     completeEdit : function(remainVisible){
14287         if(!this.editing){
14288             return;
14289         }
14290         var v = this.getValue();
14291         if(this.revertInvalid !== false && !this.field.isValid()){
14292             v = this.startValue;
14293             this.cancelEdit(true);
14294         }
14295         if(String(v) === String(this.startValue) && this.ignoreNoChange){
14296             this.editing = false;
14297             this.hide();
14298             return;
14299         }
14300         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
14301             this.editing = false;
14302             if(this.updateEl && this.boundEl){
14303                 this.boundEl.update(v);
14304             }
14305             if(remainVisible !== true){
14306                 this.hide();
14307             }
14308             this.fireEvent("complete", this, v, this.startValue);
14309         }
14310     },
14311
14312     // private
14313     onShow : function(){
14314         this.el.show();
14315         if(this.hideEl !== false){
14316             this.boundEl.hide();
14317         }
14318         this.field.show();
14319         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
14320             this.fixIEFocus = true;
14321             this.deferredFocus.defer(50, this);
14322         }else{
14323             this.field.focus();
14324         }
14325         this.fireEvent("startedit", this.boundEl, this.startValue);
14326     },
14327
14328     deferredFocus : function(){
14329         if(this.editing){
14330             this.field.focus();
14331         }
14332     },
14333
14334     /**
14335      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
14336      * reverted to the original starting value.
14337      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
14338      * cancel (defaults to false)
14339      */
14340     cancelEdit : function(remainVisible){
14341         if(this.editing){
14342             this.setValue(this.startValue);
14343             if(remainVisible !== true){
14344                 this.hide();
14345             }
14346         }
14347     },
14348
14349     // private
14350     onBlur : function(){
14351         if(this.allowBlur !== true && this.editing){
14352             this.completeEdit();
14353         }
14354     },
14355
14356     // private
14357     onHide : function(){
14358         if(this.editing){
14359             this.completeEdit();
14360             return;
14361         }
14362         this.field.blur();
14363         if(this.field.collapse){
14364             this.field.collapse();
14365         }
14366         this.el.hide();
14367         if(this.hideEl !== false){
14368             this.boundEl.show();
14369         }
14370         if(Roo.QuickTips){
14371             Roo.QuickTips.enable();
14372         }
14373     },
14374
14375     /**
14376      * Sets the data value of the editor
14377      * @param {Mixed} value Any valid value supported by the underlying field
14378      */
14379     setValue : function(v){
14380         this.field.setValue(v);
14381     },
14382
14383     /**
14384      * Gets the data value of the editor
14385      * @return {Mixed} The data value
14386      */
14387     getValue : function(){
14388         return this.field.getValue();
14389     }
14390 });/*
14391  * Based on:
14392  * Ext JS Library 1.1.1
14393  * Copyright(c) 2006-2007, Ext JS, LLC.
14394  *
14395  * Originally Released Under LGPL - original licence link has changed is not relivant.
14396  *
14397  * Fork - LGPL
14398  * <script type="text/javascript">
14399  */
14400  
14401 /**
14402  * @class Roo.BasicDialog
14403  * @extends Roo.util.Observable
14404  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
14405  * <pre><code>
14406 var dlg = new Roo.BasicDialog("my-dlg", {
14407     height: 200,
14408     width: 300,
14409     minHeight: 100,
14410     minWidth: 150,
14411     modal: true,
14412     proxyDrag: true,
14413     shadow: true
14414 });
14415 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
14416 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
14417 dlg.addButton('Cancel', dlg.hide, dlg);
14418 dlg.show();
14419 </code></pre>
14420   <b>A Dialog should always be a direct child of the body element.</b>
14421  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
14422  * @cfg {String} title Default text to display in the title bar (defaults to null)
14423  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14424  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14425  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
14426  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
14427  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
14428  * (defaults to null with no animation)
14429  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
14430  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
14431  * property for valid values (defaults to 'all')
14432  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
14433  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
14434  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
14435  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
14436  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
14437  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
14438  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
14439  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
14440  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
14441  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
14442  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
14443  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
14444  * draggable = true (defaults to false)
14445  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
14446  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14447  * shadow (defaults to false)
14448  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
14449  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
14450  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
14451  * @cfg {Array} buttons Array of buttons
14452  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
14453  * @constructor
14454  * Create a new BasicDialog.
14455  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
14456  * @param {Object} config Configuration options
14457  */
14458 Roo.BasicDialog = function(el, config){
14459     this.el = Roo.get(el);
14460     var dh = Roo.DomHelper;
14461     if(!this.el && config && config.autoCreate){
14462         if(typeof config.autoCreate == "object"){
14463             if(!config.autoCreate.id){
14464                 config.autoCreate.id = el;
14465             }
14466             this.el = dh.append(document.body,
14467                         config.autoCreate, true);
14468         }else{
14469             this.el = dh.append(document.body,
14470                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
14471         }
14472     }
14473     el = this.el;
14474     el.setDisplayed(true);
14475     el.hide = this.hideAction;
14476     this.id = el.id;
14477     el.addClass("x-dlg");
14478
14479     Roo.apply(this, config);
14480
14481     this.proxy = el.createProxy("x-dlg-proxy");
14482     this.proxy.hide = this.hideAction;
14483     this.proxy.setOpacity(.5);
14484     this.proxy.hide();
14485
14486     if(config.width){
14487         el.setWidth(config.width);
14488     }
14489     if(config.height){
14490         el.setHeight(config.height);
14491     }
14492     this.size = el.getSize();
14493     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
14494         this.xy = [config.x,config.y];
14495     }else{
14496         this.xy = el.getCenterXY(true);
14497     }
14498     /** The header element @type Roo.Element */
14499     this.header = el.child("> .x-dlg-hd");
14500     /** The body element @type Roo.Element */
14501     this.body = el.child("> .x-dlg-bd");
14502     /** The footer element @type Roo.Element */
14503     this.footer = el.child("> .x-dlg-ft");
14504
14505     if(!this.header){
14506         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
14507     }
14508     if(!this.body){
14509         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
14510     }
14511
14512     this.header.unselectable();
14513     if(this.title){
14514         this.header.update(this.title);
14515     }
14516     // this element allows the dialog to be focused for keyboard event
14517     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
14518     this.focusEl.swallowEvent("click", true);
14519
14520     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
14521
14522     // wrap the body and footer for special rendering
14523     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
14524     if(this.footer){
14525         this.bwrap.dom.appendChild(this.footer.dom);
14526     }
14527
14528     this.bg = this.el.createChild({
14529         tag: "div", cls:"x-dlg-bg",
14530         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
14531     });
14532     this.centerBg = this.bg.child("div.x-dlg-bg-center");
14533
14534
14535     if(this.autoScroll !== false && !this.autoTabs){
14536         this.body.setStyle("overflow", "auto");
14537     }
14538
14539     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
14540
14541     if(this.closable !== false){
14542         this.el.addClass("x-dlg-closable");
14543         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
14544         this.close.on("click", this.closeClick, this);
14545         this.close.addClassOnOver("x-dlg-close-over");
14546     }
14547     if(this.collapsible !== false){
14548         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
14549         this.collapseBtn.on("click", this.collapseClick, this);
14550         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
14551         this.header.on("dblclick", this.collapseClick, this);
14552     }
14553     if(this.resizable !== false){
14554         this.el.addClass("x-dlg-resizable");
14555         this.resizer = new Roo.Resizable(el, {
14556             minWidth: this.minWidth || 80,
14557             minHeight:this.minHeight || 80,
14558             handles: this.resizeHandles || "all",
14559             pinned: true
14560         });
14561         this.resizer.on("beforeresize", this.beforeResize, this);
14562         this.resizer.on("resize", this.onResize, this);
14563     }
14564     if(this.draggable !== false){
14565         el.addClass("x-dlg-draggable");
14566         if (!this.proxyDrag) {
14567             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
14568         }
14569         else {
14570             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
14571         }
14572         dd.setHandleElId(this.header.id);
14573         dd.endDrag = this.endMove.createDelegate(this);
14574         dd.startDrag = this.startMove.createDelegate(this);
14575         dd.onDrag = this.onDrag.createDelegate(this);
14576         dd.scroll = false;
14577         this.dd = dd;
14578     }
14579     if(this.modal){
14580         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
14581         this.mask.enableDisplayMode("block");
14582         this.mask.hide();
14583         this.el.addClass("x-dlg-modal");
14584     }
14585     if(this.shadow){
14586         this.shadow = new Roo.Shadow({
14587             mode : typeof this.shadow == "string" ? this.shadow : "sides",
14588             offset : this.shadowOffset
14589         });
14590     }else{
14591         this.shadowOffset = 0;
14592     }
14593     if(Roo.useShims && this.shim !== false){
14594         this.shim = this.el.createShim();
14595         this.shim.hide = this.hideAction;
14596         this.shim.hide();
14597     }else{
14598         this.shim = false;
14599     }
14600     if(this.autoTabs){
14601         this.initTabs();
14602     }
14603     if (this.buttons) { 
14604         var bts= this.buttons;
14605         this.buttons = [];
14606         Roo.each(bts, function(b) {
14607             this.addButton(b);
14608         }, this);
14609     }
14610     
14611     
14612     this.addEvents({
14613         /**
14614          * @event keydown
14615          * Fires when a key is pressed
14616          * @param {Roo.BasicDialog} this
14617          * @param {Roo.EventObject} e
14618          */
14619         "keydown" : true,
14620         /**
14621          * @event move
14622          * Fires when this dialog is moved by the user.
14623          * @param {Roo.BasicDialog} this
14624          * @param {Number} x The new page X
14625          * @param {Number} y The new page Y
14626          */
14627         "move" : true,
14628         /**
14629          * @event resize
14630          * Fires when this dialog is resized by the user.
14631          * @param {Roo.BasicDialog} this
14632          * @param {Number} width The new width
14633          * @param {Number} height The new height
14634          */
14635         "resize" : true,
14636         /**
14637          * @event beforehide
14638          * Fires before this dialog is hidden.
14639          * @param {Roo.BasicDialog} this
14640          */
14641         "beforehide" : true,
14642         /**
14643          * @event hide
14644          * Fires when this dialog is hidden.
14645          * @param {Roo.BasicDialog} this
14646          */
14647         "hide" : true,
14648         /**
14649          * @event beforeshow
14650          * Fires before this dialog is shown.
14651          * @param {Roo.BasicDialog} this
14652          */
14653         "beforeshow" : true,
14654         /**
14655          * @event show
14656          * Fires when this dialog is shown.
14657          * @param {Roo.BasicDialog} this
14658          */
14659         "show" : true
14660     });
14661     el.on("keydown", this.onKeyDown, this);
14662     el.on("mousedown", this.toFront, this);
14663     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
14664     this.el.hide();
14665     Roo.DialogManager.register(this);
14666     Roo.BasicDialog.superclass.constructor.call(this);
14667 };
14668
14669 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
14670     shadowOffset: Roo.isIE ? 6 : 5,
14671     minHeight: 80,
14672     minWidth: 200,
14673     minButtonWidth: 75,
14674     defaultButton: null,
14675     buttonAlign: "right",
14676     tabTag: 'div',
14677     firstShow: true,
14678
14679     /**
14680      * Sets the dialog title text
14681      * @param {String} text The title text to display
14682      * @return {Roo.BasicDialog} this
14683      */
14684     setTitle : function(text){
14685         this.header.update(text);
14686         return this;
14687     },
14688
14689     // private
14690     closeClick : function(){
14691         this.hide();
14692     },
14693
14694     // private
14695     collapseClick : function(){
14696         this[this.collapsed ? "expand" : "collapse"]();
14697     },
14698
14699     /**
14700      * Collapses the dialog to its minimized state (only the title bar is visible).
14701      * Equivalent to the user clicking the collapse dialog button.
14702      */
14703     collapse : function(){
14704         if(!this.collapsed){
14705             this.collapsed = true;
14706             this.el.addClass("x-dlg-collapsed");
14707             this.restoreHeight = this.el.getHeight();
14708             this.resizeTo(this.el.getWidth(), this.header.getHeight());
14709         }
14710     },
14711
14712     /**
14713      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
14714      * clicking the expand dialog button.
14715      */
14716     expand : function(){
14717         if(this.collapsed){
14718             this.collapsed = false;
14719             this.el.removeClass("x-dlg-collapsed");
14720             this.resizeTo(this.el.getWidth(), this.restoreHeight);
14721         }
14722     },
14723
14724     /**
14725      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
14726      * @return {Roo.TabPanel} The tabs component
14727      */
14728     initTabs : function(){
14729         var tabs = this.getTabs();
14730         while(tabs.getTab(0)){
14731             tabs.removeTab(0);
14732         }
14733         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
14734             var dom = el.dom;
14735             tabs.addTab(Roo.id(dom), dom.title);
14736             dom.title = "";
14737         });
14738         tabs.activate(0);
14739         return tabs;
14740     },
14741
14742     // private
14743     beforeResize : function(){
14744         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
14745     },
14746
14747     // private
14748     onResize : function(){
14749         this.refreshSize();
14750         this.syncBodyHeight();
14751         this.adjustAssets();
14752         this.focus();
14753         this.fireEvent("resize", this, this.size.width, this.size.height);
14754     },
14755
14756     // private
14757     onKeyDown : function(e){
14758         if(this.isVisible()){
14759             this.fireEvent("keydown", this, e);
14760         }
14761     },
14762
14763     /**
14764      * Resizes the dialog.
14765      * @param {Number} width
14766      * @param {Number} height
14767      * @return {Roo.BasicDialog} this
14768      */
14769     resizeTo : function(width, height){
14770         this.el.setSize(width, height);
14771         this.size = {width: width, height: height};
14772         this.syncBodyHeight();
14773         if(this.fixedcenter){
14774             this.center();
14775         }
14776         if(this.isVisible()){
14777             this.constrainXY();
14778             this.adjustAssets();
14779         }
14780         this.fireEvent("resize", this, width, height);
14781         return this;
14782     },
14783
14784
14785     /**
14786      * Resizes the dialog to fit the specified content size.
14787      * @param {Number} width
14788      * @param {Number} height
14789      * @return {Roo.BasicDialog} this
14790      */
14791     setContentSize : function(w, h){
14792         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
14793         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
14794         //if(!this.el.isBorderBox()){
14795             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
14796             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
14797         //}
14798         if(this.tabs){
14799             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
14800             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
14801         }
14802         this.resizeTo(w, h);
14803         return this;
14804     },
14805
14806     /**
14807      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
14808      * executed in response to a particular key being pressed while the dialog is active.
14809      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
14810      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14811      * @param {Function} fn The function to call
14812      * @param {Object} scope (optional) The scope of the function
14813      * @return {Roo.BasicDialog} this
14814      */
14815     addKeyListener : function(key, fn, scope){
14816         var keyCode, shift, ctrl, alt;
14817         if(typeof key == "object" && !(key instanceof Array)){
14818             keyCode = key["key"];
14819             shift = key["shift"];
14820             ctrl = key["ctrl"];
14821             alt = key["alt"];
14822         }else{
14823             keyCode = key;
14824         }
14825         var handler = function(dlg, e){
14826             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14827                 var k = e.getKey();
14828                 if(keyCode instanceof Array){
14829                     for(var i = 0, len = keyCode.length; i < len; i++){
14830                         if(keyCode[i] == k){
14831                           fn.call(scope || window, dlg, k, e);
14832                           return;
14833                         }
14834                     }
14835                 }else{
14836                     if(k == keyCode){
14837                         fn.call(scope || window, dlg, k, e);
14838                     }
14839                 }
14840             }
14841         };
14842         this.on("keydown", handler);
14843         return this;
14844     },
14845
14846     /**
14847      * Returns the TabPanel component (creates it if it doesn't exist).
14848      * Note: If you wish to simply check for the existence of tabs without creating them,
14849      * check for a null 'tabs' property.
14850      * @return {Roo.TabPanel} The tabs component
14851      */
14852     getTabs : function(){
14853         if(!this.tabs){
14854             this.el.addClass("x-dlg-auto-tabs");
14855             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
14856             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
14857         }
14858         return this.tabs;
14859     },
14860
14861     /**
14862      * Adds a button to the footer section of the dialog.
14863      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
14864      * object or a valid Roo.DomHelper element config
14865      * @param {Function} handler The function called when the button is clicked
14866      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
14867      * @return {Roo.Button} The new button
14868      */
14869     addButton : function(config, handler, scope){
14870         var dh = Roo.DomHelper;
14871         if(!this.footer){
14872             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
14873         }
14874         if(!this.btnContainer){
14875             var tb = this.footer.createChild({
14876
14877                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
14878                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
14879             }, null, true);
14880             this.btnContainer = tb.firstChild.firstChild.firstChild;
14881         }
14882         var bconfig = {
14883             handler: handler,
14884             scope: scope,
14885             minWidth: this.minButtonWidth,
14886             hideParent:true
14887         };
14888         if(typeof config == "string"){
14889             bconfig.text = config;
14890         }else{
14891             if(config.tag){
14892                 bconfig.dhconfig = config;
14893             }else{
14894                 Roo.apply(bconfig, config);
14895             }
14896         }
14897         var fc = false;
14898         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
14899             bconfig.position = Math.max(0, bconfig.position);
14900             fc = this.btnContainer.childNodes[bconfig.position];
14901         }
14902          
14903         var btn = new Roo.Button(
14904             fc ? 
14905                 this.btnContainer.insertBefore(document.createElement("td"),fc)
14906                 : this.btnContainer.appendChild(document.createElement("td")),
14907             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
14908             bconfig
14909         );
14910         this.syncBodyHeight();
14911         if(!this.buttons){
14912             /**
14913              * Array of all the buttons that have been added to this dialog via addButton
14914              * @type Array
14915              */
14916             this.buttons = [];
14917         }
14918         this.buttons.push(btn);
14919         return btn;
14920     },
14921
14922     /**
14923      * Sets the default button to be focused when the dialog is displayed.
14924      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
14925      * @return {Roo.BasicDialog} this
14926      */
14927     setDefaultButton : function(btn){
14928         this.defaultButton = btn;
14929         return this;
14930     },
14931
14932     // private
14933     getHeaderFooterHeight : function(safe){
14934         var height = 0;
14935         if(this.header){
14936            height += this.header.getHeight();
14937         }
14938         if(this.footer){
14939            var fm = this.footer.getMargins();
14940             height += (this.footer.getHeight()+fm.top+fm.bottom);
14941         }
14942         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
14943         height += this.centerBg.getPadding("tb");
14944         return height;
14945     },
14946
14947     // private
14948     syncBodyHeight : function()
14949     {
14950         var bd = this.body, // the text
14951             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
14952             bw = this.bwrap;
14953         var height = this.size.height - this.getHeaderFooterHeight(false);
14954         bd.setHeight(height-bd.getMargins("tb"));
14955         var hh = this.header.getHeight();
14956         var h = this.size.height-hh;
14957         cb.setHeight(h);
14958         
14959         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
14960         bw.setHeight(h-cb.getPadding("tb"));
14961         
14962         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
14963         bd.setWidth(bw.getWidth(true));
14964         if(this.tabs){
14965             this.tabs.syncHeight();
14966             if(Roo.isIE){
14967                 this.tabs.el.repaint();
14968             }
14969         }
14970     },
14971
14972     /**
14973      * Restores the previous state of the dialog if Roo.state is configured.
14974      * @return {Roo.BasicDialog} this
14975      */
14976     restoreState : function(){
14977         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
14978         if(box && box.width){
14979             this.xy = [box.x, box.y];
14980             this.resizeTo(box.width, box.height);
14981         }
14982         return this;
14983     },
14984
14985     // private
14986     beforeShow : function(){
14987         this.expand();
14988         if(this.fixedcenter){
14989             this.xy = this.el.getCenterXY(true);
14990         }
14991         if(this.modal){
14992             Roo.get(document.body).addClass("x-body-masked");
14993             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14994             this.mask.show();
14995         }
14996         this.constrainXY();
14997     },
14998
14999     // private
15000     animShow : function(){
15001         var b = Roo.get(this.animateTarget).getBox();
15002         this.proxy.setSize(b.width, b.height);
15003         this.proxy.setLocation(b.x, b.y);
15004         this.proxy.show();
15005         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
15006                     true, .35, this.showEl.createDelegate(this));
15007     },
15008
15009     /**
15010      * Shows the dialog.
15011      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
15012      * @return {Roo.BasicDialog} this
15013      */
15014     show : function(animateTarget){
15015         if (this.fireEvent("beforeshow", this) === false){
15016             return;
15017         }
15018         if(this.syncHeightBeforeShow){
15019             this.syncBodyHeight();
15020         }else if(this.firstShow){
15021             this.firstShow = false;
15022             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
15023         }
15024         this.animateTarget = animateTarget || this.animateTarget;
15025         if(!this.el.isVisible()){
15026             this.beforeShow();
15027             if(this.animateTarget && Roo.get(this.animateTarget)){
15028                 this.animShow();
15029             }else{
15030                 this.showEl();
15031             }
15032         }
15033         return this;
15034     },
15035
15036     // private
15037     showEl : function(){
15038         this.proxy.hide();
15039         this.el.setXY(this.xy);
15040         this.el.show();
15041         this.adjustAssets(true);
15042         this.toFront();
15043         this.focus();
15044         // IE peekaboo bug - fix found by Dave Fenwick
15045         if(Roo.isIE){
15046             this.el.repaint();
15047         }
15048         this.fireEvent("show", this);
15049     },
15050
15051     /**
15052      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
15053      * dialog itself will receive focus.
15054      */
15055     focus : function(){
15056         if(this.defaultButton){
15057             this.defaultButton.focus();
15058         }else{
15059             this.focusEl.focus();
15060         }
15061     },
15062
15063     // private
15064     constrainXY : function(){
15065         if(this.constraintoviewport !== false){
15066             if(!this.viewSize){
15067                 if(this.container){
15068                     var s = this.container.getSize();
15069                     this.viewSize = [s.width, s.height];
15070                 }else{
15071                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
15072                 }
15073             }
15074             var s = Roo.get(this.container||document).getScroll();
15075
15076             var x = this.xy[0], y = this.xy[1];
15077             var w = this.size.width, h = this.size.height;
15078             var vw = this.viewSize[0], vh = this.viewSize[1];
15079             // only move it if it needs it
15080             var moved = false;
15081             // first validate right/bottom
15082             if(x + w > vw+s.left){
15083                 x = vw - w;
15084                 moved = true;
15085             }
15086             if(y + h > vh+s.top){
15087                 y = vh - h;
15088                 moved = true;
15089             }
15090             // then make sure top/left isn't negative
15091             if(x < s.left){
15092                 x = s.left;
15093                 moved = true;
15094             }
15095             if(y < s.top){
15096                 y = s.top;
15097                 moved = true;
15098             }
15099             if(moved){
15100                 // cache xy
15101                 this.xy = [x, y];
15102                 if(this.isVisible()){
15103                     this.el.setLocation(x, y);
15104                     this.adjustAssets();
15105                 }
15106             }
15107         }
15108     },
15109
15110     // private
15111     onDrag : function(){
15112         if(!this.proxyDrag){
15113             this.xy = this.el.getXY();
15114             this.adjustAssets();
15115         }
15116     },
15117
15118     // private
15119     adjustAssets : function(doShow){
15120         var x = this.xy[0], y = this.xy[1];
15121         var w = this.size.width, h = this.size.height;
15122         if(doShow === true){
15123             if(this.shadow){
15124                 this.shadow.show(this.el);
15125             }
15126             if(this.shim){
15127                 this.shim.show();
15128             }
15129         }
15130         if(this.shadow && this.shadow.isVisible()){
15131             this.shadow.show(this.el);
15132         }
15133         if(this.shim && this.shim.isVisible()){
15134             this.shim.setBounds(x, y, w, h);
15135         }
15136     },
15137
15138     // private
15139     adjustViewport : function(w, h){
15140         if(!w || !h){
15141             w = Roo.lib.Dom.getViewWidth();
15142             h = Roo.lib.Dom.getViewHeight();
15143         }
15144         // cache the size
15145         this.viewSize = [w, h];
15146         if(this.modal && this.mask.isVisible()){
15147             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
15148             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15149         }
15150         if(this.isVisible()){
15151             this.constrainXY();
15152         }
15153     },
15154
15155     /**
15156      * Destroys this dialog and all its supporting elements (including any tabs, shim,
15157      * shadow, proxy, mask, etc.)  Also removes all event listeners.
15158      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
15159      */
15160     destroy : function(removeEl){
15161         if(this.isVisible()){
15162             this.animateTarget = null;
15163             this.hide();
15164         }
15165         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
15166         if(this.tabs){
15167             this.tabs.destroy(removeEl);
15168         }
15169         Roo.destroy(
15170              this.shim,
15171              this.proxy,
15172              this.resizer,
15173              this.close,
15174              this.mask
15175         );
15176         if(this.dd){
15177             this.dd.unreg();
15178         }
15179         if(this.buttons){
15180            for(var i = 0, len = this.buttons.length; i < len; i++){
15181                this.buttons[i].destroy();
15182            }
15183         }
15184         this.el.removeAllListeners();
15185         if(removeEl === true){
15186             this.el.update("");
15187             this.el.remove();
15188         }
15189         Roo.DialogManager.unregister(this);
15190     },
15191
15192     // private
15193     startMove : function(){
15194         if(this.proxyDrag){
15195             this.proxy.show();
15196         }
15197         if(this.constraintoviewport !== false){
15198             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
15199         }
15200     },
15201
15202     // private
15203     endMove : function(){
15204         if(!this.proxyDrag){
15205             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
15206         }else{
15207             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
15208             this.proxy.hide();
15209         }
15210         this.refreshSize();
15211         this.adjustAssets();
15212         this.focus();
15213         this.fireEvent("move", this, this.xy[0], this.xy[1]);
15214     },
15215
15216     /**
15217      * Brings this dialog to the front of any other visible dialogs
15218      * @return {Roo.BasicDialog} this
15219      */
15220     toFront : function(){
15221         Roo.DialogManager.bringToFront(this);
15222         return this;
15223     },
15224
15225     /**
15226      * Sends this dialog to the back (under) of any other visible dialogs
15227      * @return {Roo.BasicDialog} this
15228      */
15229     toBack : function(){
15230         Roo.DialogManager.sendToBack(this);
15231         return this;
15232     },
15233
15234     /**
15235      * Centers this dialog in the viewport
15236      * @return {Roo.BasicDialog} this
15237      */
15238     center : function(){
15239         var xy = this.el.getCenterXY(true);
15240         this.moveTo(xy[0], xy[1]);
15241         return this;
15242     },
15243
15244     /**
15245      * Moves the dialog's top-left corner to the specified point
15246      * @param {Number} x
15247      * @param {Number} y
15248      * @return {Roo.BasicDialog} this
15249      */
15250     moveTo : function(x, y){
15251         this.xy = [x,y];
15252         if(this.isVisible()){
15253             this.el.setXY(this.xy);
15254             this.adjustAssets();
15255         }
15256         return this;
15257     },
15258
15259     /**
15260      * Aligns the dialog to the specified element
15261      * @param {String/HTMLElement/Roo.Element} element The element to align to.
15262      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
15263      * @param {Array} offsets (optional) Offset the positioning by [x, y]
15264      * @return {Roo.BasicDialog} this
15265      */
15266     alignTo : function(element, position, offsets){
15267         this.xy = this.el.getAlignToXY(element, position, offsets);
15268         if(this.isVisible()){
15269             this.el.setXY(this.xy);
15270             this.adjustAssets();
15271         }
15272         return this;
15273     },
15274
15275     /**
15276      * Anchors an element to another element and realigns it when the window is resized.
15277      * @param {String/HTMLElement/Roo.Element} element The element to align to.
15278      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
15279      * @param {Array} offsets (optional) Offset the positioning by [x, y]
15280      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
15281      * is a number, it is used as the buffer delay (defaults to 50ms).
15282      * @return {Roo.BasicDialog} this
15283      */
15284     anchorTo : function(el, alignment, offsets, monitorScroll){
15285         var action = function(){
15286             this.alignTo(el, alignment, offsets);
15287         };
15288         Roo.EventManager.onWindowResize(action, this);
15289         var tm = typeof monitorScroll;
15290         if(tm != 'undefined'){
15291             Roo.EventManager.on(window, 'scroll', action, this,
15292                 {buffer: tm == 'number' ? monitorScroll : 50});
15293         }
15294         action.call(this);
15295         return this;
15296     },
15297
15298     /**
15299      * Returns true if the dialog is visible
15300      * @return {Boolean}
15301      */
15302     isVisible : function(){
15303         return this.el.isVisible();
15304     },
15305
15306     // private
15307     animHide : function(callback){
15308         var b = Roo.get(this.animateTarget).getBox();
15309         this.proxy.show();
15310         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
15311         this.el.hide();
15312         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
15313                     this.hideEl.createDelegate(this, [callback]));
15314     },
15315
15316     /**
15317      * Hides the dialog.
15318      * @param {Function} callback (optional) Function to call when the dialog is hidden
15319      * @return {Roo.BasicDialog} this
15320      */
15321     hide : function(callback){
15322         if (this.fireEvent("beforehide", this) === false){
15323             return;
15324         }
15325         if(this.shadow){
15326             this.shadow.hide();
15327         }
15328         if(this.shim) {
15329           this.shim.hide();
15330         }
15331         // sometimes animateTarget seems to get set.. causing problems...
15332         // this just double checks..
15333         if(this.animateTarget && Roo.get(this.animateTarget)) {
15334            this.animHide(callback);
15335         }else{
15336             this.el.hide();
15337             this.hideEl(callback);
15338         }
15339         return this;
15340     },
15341
15342     // private
15343     hideEl : function(callback){
15344         this.proxy.hide();
15345         if(this.modal){
15346             this.mask.hide();
15347             Roo.get(document.body).removeClass("x-body-masked");
15348         }
15349         this.fireEvent("hide", this);
15350         if(typeof callback == "function"){
15351             callback();
15352         }
15353     },
15354
15355     // private
15356     hideAction : function(){
15357         this.setLeft("-10000px");
15358         this.setTop("-10000px");
15359         this.setStyle("visibility", "hidden");
15360     },
15361
15362     // private
15363     refreshSize : function(){
15364         this.size = this.el.getSize();
15365         this.xy = this.el.getXY();
15366         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
15367     },
15368
15369     // private
15370     // z-index is managed by the DialogManager and may be overwritten at any time
15371     setZIndex : function(index){
15372         if(this.modal){
15373             this.mask.setStyle("z-index", index);
15374         }
15375         if(this.shim){
15376             this.shim.setStyle("z-index", ++index);
15377         }
15378         if(this.shadow){
15379             this.shadow.setZIndex(++index);
15380         }
15381         this.el.setStyle("z-index", ++index);
15382         if(this.proxy){
15383             this.proxy.setStyle("z-index", ++index);
15384         }
15385         if(this.resizer){
15386             this.resizer.proxy.setStyle("z-index", ++index);
15387         }
15388
15389         this.lastZIndex = index;
15390     },
15391
15392     /**
15393      * Returns the element for this dialog
15394      * @return {Roo.Element} The underlying dialog Element
15395      */
15396     getEl : function(){
15397         return this.el;
15398     }
15399 });
15400
15401 /**
15402  * @class Roo.DialogManager
15403  * Provides global access to BasicDialogs that have been created and
15404  * support for z-indexing (layering) multiple open dialogs.
15405  */
15406 Roo.DialogManager = function(){
15407     var list = {};
15408     var accessList = [];
15409     var front = null;
15410
15411     // private
15412     var sortDialogs = function(d1, d2){
15413         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
15414     };
15415
15416     // private
15417     var orderDialogs = function(){
15418         accessList.sort(sortDialogs);
15419         var seed = Roo.DialogManager.zseed;
15420         for(var i = 0, len = accessList.length; i < len; i++){
15421             var dlg = accessList[i];
15422             if(dlg){
15423                 dlg.setZIndex(seed + (i*10));
15424             }
15425         }
15426     };
15427
15428     return {
15429         /**
15430          * The starting z-index for BasicDialogs (defaults to 9000)
15431          * @type Number The z-index value
15432          */
15433         zseed : 9000,
15434
15435         // private
15436         register : function(dlg){
15437             list[dlg.id] = dlg;
15438             accessList.push(dlg);
15439         },
15440
15441         // private
15442         unregister : function(dlg){
15443             delete list[dlg.id];
15444             var i=0;
15445             var len=0;
15446             if(!accessList.indexOf){
15447                 for(  i = 0, len = accessList.length; i < len; i++){
15448                     if(accessList[i] == dlg){
15449                         accessList.splice(i, 1);
15450                         return;
15451                     }
15452                 }
15453             }else{
15454                  i = accessList.indexOf(dlg);
15455                 if(i != -1){
15456                     accessList.splice(i, 1);
15457                 }
15458             }
15459         },
15460
15461         /**
15462          * Gets a registered dialog by id
15463          * @param {String/Object} id The id of the dialog or a dialog
15464          * @return {Roo.BasicDialog} this
15465          */
15466         get : function(id){
15467             return typeof id == "object" ? id : list[id];
15468         },
15469
15470         /**
15471          * Brings the specified dialog to the front
15472          * @param {String/Object} dlg The id of the dialog or a dialog
15473          * @return {Roo.BasicDialog} this
15474          */
15475         bringToFront : function(dlg){
15476             dlg = this.get(dlg);
15477             if(dlg != front){
15478                 front = dlg;
15479                 dlg._lastAccess = new Date().getTime();
15480                 orderDialogs();
15481             }
15482             return dlg;
15483         },
15484
15485         /**
15486          * Sends the specified dialog to the back
15487          * @param {String/Object} dlg The id of the dialog or a dialog
15488          * @return {Roo.BasicDialog} this
15489          */
15490         sendToBack : function(dlg){
15491             dlg = this.get(dlg);
15492             dlg._lastAccess = -(new Date().getTime());
15493             orderDialogs();
15494             return dlg;
15495         },
15496
15497         /**
15498          * Hides all dialogs
15499          */
15500         hideAll : function(){
15501             for(var id in list){
15502                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
15503                     list[id].hide();
15504                 }
15505             }
15506         }
15507     };
15508 }();
15509
15510 /**
15511  * @class Roo.LayoutDialog
15512  * @extends Roo.BasicDialog
15513  * Dialog which provides adjustments for working with a layout in a Dialog.
15514  * Add your necessary layout config options to the dialog's config.<br>
15515  * Example usage (including a nested layout):
15516  * <pre><code>
15517 if(!dialog){
15518     dialog = new Roo.LayoutDialog("download-dlg", {
15519         modal: true,
15520         width:600,
15521         height:450,
15522         shadow:true,
15523         minWidth:500,
15524         minHeight:350,
15525         autoTabs:true,
15526         proxyDrag:true,
15527         // layout config merges with the dialog config
15528         center:{
15529             tabPosition: "top",
15530             alwaysShowTabs: true
15531         }
15532     });
15533     dialog.addKeyListener(27, dialog.hide, dialog);
15534     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
15535     dialog.addButton("Build It!", this.getDownload, this);
15536
15537     // we can even add nested layouts
15538     var innerLayout = new Roo.BorderLayout("dl-inner", {
15539         east: {
15540             initialSize: 200,
15541             autoScroll:true,
15542             split:true
15543         },
15544         center: {
15545             autoScroll:true
15546         }
15547     });
15548     innerLayout.beginUpdate();
15549     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
15550     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
15551     innerLayout.endUpdate(true);
15552
15553     var layout = dialog.getLayout();
15554     layout.beginUpdate();
15555     layout.add("center", new Roo.ContentPanel("standard-panel",
15556                         {title: "Download the Source", fitToFrame:true}));
15557     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
15558                {title: "Build your own roo.js"}));
15559     layout.getRegion("center").showPanel(sp);
15560     layout.endUpdate();
15561 }
15562 </code></pre>
15563     * @constructor
15564     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
15565     * @param {Object} config configuration options
15566   */
15567 Roo.LayoutDialog = function(el, cfg){
15568     
15569     var config=  cfg;
15570     if (typeof(cfg) == 'undefined') {
15571         config = Roo.apply({}, el);
15572         // not sure why we use documentElement here.. - it should always be body.
15573         // IE7 borks horribly if we use documentElement.
15574         // webkit also does not like documentElement - it creates a body element...
15575         el = Roo.get( document.body || document.documentElement ).createChild();
15576         //config.autoCreate = true;
15577     }
15578     
15579     
15580     config.autoTabs = false;
15581     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
15582     this.body.setStyle({overflow:"hidden", position:"relative"});
15583     this.layout = new Roo.BorderLayout(this.body.dom, config);
15584     this.layout.monitorWindowResize = false;
15585     this.el.addClass("x-dlg-auto-layout");
15586     // fix case when center region overwrites center function
15587     this.center = Roo.BasicDialog.prototype.center;
15588     this.on("show", this.layout.layout, this.layout, true);
15589     if (config.items) {
15590         var xitems = config.items;
15591         delete config.items;
15592         Roo.each(xitems, this.addxtype, this);
15593     }
15594     
15595     
15596 };
15597 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
15598     /**
15599      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
15600      * @deprecated
15601      */
15602     endUpdate : function(){
15603         this.layout.endUpdate();
15604     },
15605
15606     /**
15607      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
15608      *  @deprecated
15609      */
15610     beginUpdate : function(){
15611         this.layout.beginUpdate();
15612     },
15613
15614     /**
15615      * Get the BorderLayout for this dialog
15616      * @return {Roo.BorderLayout}
15617      */
15618     getLayout : function(){
15619         return this.layout;
15620     },
15621
15622     showEl : function(){
15623         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
15624         if(Roo.isIE7){
15625             this.layout.layout();
15626         }
15627     },
15628
15629     // private
15630     // Use the syncHeightBeforeShow config option to control this automatically
15631     syncBodyHeight : function(){
15632         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
15633         if(this.layout){this.layout.layout();}
15634     },
15635     
15636       /**
15637      * Add an xtype element (actually adds to the layout.)
15638      * @return {Object} xdata xtype object data.
15639      */
15640     
15641     addxtype : function(c) {
15642         return this.layout.addxtype(c);
15643     }
15644 });/*
15645  * Based on:
15646  * Ext JS Library 1.1.1
15647  * Copyright(c) 2006-2007, Ext JS, LLC.
15648  *
15649  * Originally Released Under LGPL - original licence link has changed is not relivant.
15650  *
15651  * Fork - LGPL
15652  * <script type="text/javascript">
15653  */
15654  
15655 /**
15656  * @class Roo.MessageBox
15657  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
15658  * Example usage:
15659  *<pre><code>
15660 // Basic alert:
15661 Roo.Msg.alert('Status', 'Changes saved successfully.');
15662
15663 // Prompt for user data:
15664 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
15665     if (btn == 'ok'){
15666         // process text value...
15667     }
15668 });
15669
15670 // Show a dialog using config options:
15671 Roo.Msg.show({
15672    title:'Save Changes?',
15673    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
15674    buttons: Roo.Msg.YESNOCANCEL,
15675    fn: processResult,
15676    animEl: 'elId'
15677 });
15678 </code></pre>
15679  * @singleton
15680  */
15681 Roo.MessageBox = function(){
15682     var dlg, opt, mask, waitTimer;
15683     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
15684     var buttons, activeTextEl, bwidth;
15685
15686     // private
15687     var handleButton = function(button){
15688         dlg.hide();
15689         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
15690     };
15691
15692     // private
15693     var handleHide = function(){
15694         if(opt && opt.cls){
15695             dlg.el.removeClass(opt.cls);
15696         }
15697         if(waitTimer){
15698             Roo.TaskMgr.stop(waitTimer);
15699             waitTimer = null;
15700         }
15701     };
15702
15703     // private
15704     var updateButtons = function(b){
15705         var width = 0;
15706         if(!b){
15707             buttons["ok"].hide();
15708             buttons["cancel"].hide();
15709             buttons["yes"].hide();
15710             buttons["no"].hide();
15711             dlg.footer.dom.style.display = 'none';
15712             return width;
15713         }
15714         dlg.footer.dom.style.display = '';
15715         for(var k in buttons){
15716             if(typeof buttons[k] != "function"){
15717                 if(b[k]){
15718                     buttons[k].show();
15719                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
15720                     width += buttons[k].el.getWidth()+15;
15721                 }else{
15722                     buttons[k].hide();
15723                 }
15724             }
15725         }
15726         return width;
15727     };
15728
15729     // private
15730     var handleEsc = function(d, k, e){
15731         if(opt && opt.closable !== false){
15732             dlg.hide();
15733         }
15734         if(e){
15735             e.stopEvent();
15736         }
15737     };
15738
15739     return {
15740         /**
15741          * Returns a reference to the underlying {@link Roo.BasicDialog} element
15742          * @return {Roo.BasicDialog} The BasicDialog element
15743          */
15744         getDialog : function(){
15745            if(!dlg){
15746                 dlg = new Roo.BasicDialog("x-msg-box", {
15747                     autoCreate : true,
15748                     shadow: true,
15749                     draggable: true,
15750                     resizable:false,
15751                     constraintoviewport:false,
15752                     fixedcenter:true,
15753                     collapsible : false,
15754                     shim:true,
15755                     modal: true,
15756                     width:400, height:100,
15757                     buttonAlign:"center",
15758                     closeClick : function(){
15759                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
15760                             handleButton("no");
15761                         }else{
15762                             handleButton("cancel");
15763                         }
15764                     }
15765                 });
15766                 dlg.on("hide", handleHide);
15767                 mask = dlg.mask;
15768                 dlg.addKeyListener(27, handleEsc);
15769                 buttons = {};
15770                 var bt = this.buttonText;
15771                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
15772                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
15773                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
15774                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
15775                 bodyEl = dlg.body.createChild({
15776
15777                     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>'
15778                 });
15779                 msgEl = bodyEl.dom.firstChild;
15780                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
15781                 textboxEl.enableDisplayMode();
15782                 textboxEl.addKeyListener([10,13], function(){
15783                     if(dlg.isVisible() && opt && opt.buttons){
15784                         if(opt.buttons.ok){
15785                             handleButton("ok");
15786                         }else if(opt.buttons.yes){
15787                             handleButton("yes");
15788                         }
15789                     }
15790                 });
15791                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
15792                 textareaEl.enableDisplayMode();
15793                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
15794                 progressEl.enableDisplayMode();
15795                 var pf = progressEl.dom.firstChild;
15796                 if (pf) {
15797                     pp = Roo.get(pf.firstChild);
15798                     pp.setHeight(pf.offsetHeight);
15799                 }
15800                 
15801             }
15802             return dlg;
15803         },
15804
15805         /**
15806          * Updates the message box body text
15807          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
15808          * the XHTML-compliant non-breaking space character '&amp;#160;')
15809          * @return {Roo.MessageBox} This message box
15810          */
15811         updateText : function(text){
15812             if(!dlg.isVisible() && !opt.width){
15813                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
15814             }
15815             msgEl.innerHTML = text || '&#160;';
15816       
15817             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
15818             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
15819             var w = Math.max(
15820                     Math.min(opt.width || cw , this.maxWidth), 
15821                     Math.max(opt.minWidth || this.minWidth, bwidth)
15822             );
15823             if(opt.prompt){
15824                 activeTextEl.setWidth(w);
15825             }
15826             if(dlg.isVisible()){
15827                 dlg.fixedcenter = false;
15828             }
15829             // to big, make it scroll. = But as usual stupid IE does not support
15830             // !important..
15831             
15832             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
15833                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
15834                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
15835             } else {
15836                 bodyEl.dom.style.height = '';
15837                 bodyEl.dom.style.overflowY = '';
15838             }
15839             if (cw > w) {
15840                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
15841             } else {
15842                 bodyEl.dom.style.overflowX = '';
15843             }
15844             
15845             dlg.setContentSize(w, bodyEl.getHeight());
15846             if(dlg.isVisible()){
15847                 dlg.fixedcenter = true;
15848             }
15849             return this;
15850         },
15851
15852         /**
15853          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
15854          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
15855          * @param {Number} value Any number between 0 and 1 (e.g., .5)
15856          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
15857          * @return {Roo.MessageBox} This message box
15858          */
15859         updateProgress : function(value, text){
15860             if(text){
15861                 this.updateText(text);
15862             }
15863             if (pp) { // weird bug on my firefox - for some reason this is not defined
15864                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
15865             }
15866             return this;
15867         },        
15868
15869         /**
15870          * Returns true if the message box is currently displayed
15871          * @return {Boolean} True if the message box is visible, else false
15872          */
15873         isVisible : function(){
15874             return dlg && dlg.isVisible();  
15875         },
15876
15877         /**
15878          * Hides the message box if it is displayed
15879          */
15880         hide : function(){
15881             if(this.isVisible()){
15882                 dlg.hide();
15883             }  
15884         },
15885
15886         /**
15887          * Displays a new message box, or reinitializes an existing message box, based on the config options
15888          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
15889          * The following config object properties are supported:
15890          * <pre>
15891 Property    Type             Description
15892 ----------  ---------------  ------------------------------------------------------------------------------------
15893 animEl            String/Element   An id or Element from which the message box should animate as it opens and
15894                                    closes (defaults to undefined)
15895 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
15896                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
15897 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
15898                                    progress and wait dialogs will ignore this property and always hide the
15899                                    close button as they can only be closed programmatically.
15900 cls               String           A custom CSS class to apply to the message box element
15901 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
15902                                    displayed (defaults to 75)
15903 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
15904                                    function will be btn (the name of the button that was clicked, if applicable,
15905                                    e.g. "ok"), and text (the value of the active text field, if applicable).
15906                                    Progress and wait dialogs will ignore this option since they do not respond to
15907                                    user actions and can only be closed programmatically, so any required function
15908                                    should be called by the same code after it closes the dialog.
15909 icon              String           A CSS class that provides a background image to be used as an icon for
15910                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
15911 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
15912 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
15913 modal             Boolean          False to allow user interaction with the page while the message box is
15914                                    displayed (defaults to true)
15915 msg               String           A string that will replace the existing message box body text (defaults
15916                                    to the XHTML-compliant non-breaking space character '&#160;')
15917 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
15918 progress          Boolean          True to display a progress bar (defaults to false)
15919 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
15920 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
15921 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
15922 title             String           The title text
15923 value             String           The string value to set into the active textbox element if displayed
15924 wait              Boolean          True to display a progress bar (defaults to false)
15925 width             Number           The width of the dialog in pixels
15926 </pre>
15927          *
15928          * Example usage:
15929          * <pre><code>
15930 Roo.Msg.show({
15931    title: 'Address',
15932    msg: 'Please enter your address:',
15933    width: 300,
15934    buttons: Roo.MessageBox.OKCANCEL,
15935    multiline: true,
15936    fn: saveAddress,
15937    animEl: 'addAddressBtn'
15938 });
15939 </code></pre>
15940          * @param {Object} config Configuration options
15941          * @return {Roo.MessageBox} This message box
15942          */
15943         show : function(options)
15944         {
15945             
15946             // this causes nightmares if you show one dialog after another
15947             // especially on callbacks..
15948              
15949             if(this.isVisible()){
15950                 
15951                 this.hide();
15952                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
15953                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
15954                 Roo.log("New Dialog Message:" +  options.msg )
15955                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
15956                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
15957                 
15958             }
15959             var d = this.getDialog();
15960             opt = options;
15961             d.setTitle(opt.title || "&#160;");
15962             d.close.setDisplayed(opt.closable !== false);
15963             activeTextEl = textboxEl;
15964             opt.prompt = opt.prompt || (opt.multiline ? true : false);
15965             if(opt.prompt){
15966                 if(opt.multiline){
15967                     textboxEl.hide();
15968                     textareaEl.show();
15969                     textareaEl.setHeight(typeof opt.multiline == "number" ?
15970                         opt.multiline : this.defaultTextHeight);
15971                     activeTextEl = textareaEl;
15972                 }else{
15973                     textboxEl.show();
15974                     textareaEl.hide();
15975                 }
15976             }else{
15977                 textboxEl.hide();
15978                 textareaEl.hide();
15979             }
15980             progressEl.setDisplayed(opt.progress === true);
15981             this.updateProgress(0);
15982             activeTextEl.dom.value = opt.value || "";
15983             if(opt.prompt){
15984                 dlg.setDefaultButton(activeTextEl);
15985             }else{
15986                 var bs = opt.buttons;
15987                 var db = null;
15988                 if(bs && bs.ok){
15989                     db = buttons["ok"];
15990                 }else if(bs && bs.yes){
15991                     db = buttons["yes"];
15992                 }
15993                 dlg.setDefaultButton(db);
15994             }
15995             bwidth = updateButtons(opt.buttons);
15996             this.updateText(opt.msg);
15997             if(opt.cls){
15998                 d.el.addClass(opt.cls);
15999             }
16000             d.proxyDrag = opt.proxyDrag === true;
16001             d.modal = opt.modal !== false;
16002             d.mask = opt.modal !== false ? mask : false;
16003             if(!d.isVisible()){
16004                 // force it to the end of the z-index stack so it gets a cursor in FF
16005                 document.body.appendChild(dlg.el.dom);
16006                 d.animateTarget = null;
16007                 d.show(options.animEl);
16008             }
16009             return this;
16010         },
16011
16012         /**
16013          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
16014          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
16015          * and closing the message box when the process is complete.
16016          * @param {String} title The title bar text
16017          * @param {String} msg The message box body text
16018          * @return {Roo.MessageBox} This message box
16019          */
16020         progress : function(title, msg){
16021             this.show({
16022                 title : title,
16023                 msg : msg,
16024                 buttons: false,
16025                 progress:true,
16026                 closable:false,
16027                 minWidth: this.minProgressWidth,
16028                 modal : true
16029             });
16030             return this;
16031         },
16032
16033         /**
16034          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
16035          * If a callback function is passed it will be called after the user clicks the button, and the
16036          * id of the button that was clicked will be passed as the only parameter to the callback
16037          * (could also be the top-right close button).
16038          * @param {String} title The title bar text
16039          * @param {String} msg The message box body text
16040          * @param {Function} fn (optional) The callback function invoked after the message box is closed
16041          * @param {Object} scope (optional) The scope of the callback function
16042          * @return {Roo.MessageBox} This message box
16043          */
16044         alert : function(title, msg, fn, scope){
16045             this.show({
16046                 title : title,
16047                 msg : msg,
16048                 buttons: this.OK,
16049                 fn: fn,
16050                 scope : scope,
16051                 modal : true
16052             });
16053             return this;
16054         },
16055
16056         /**
16057          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
16058          * interaction while waiting for a long-running process to complete that does not have defined intervals.
16059          * You are responsible for closing the message box when the process is complete.
16060          * @param {String} msg The message box body text
16061          * @param {String} title (optional) The title bar text
16062          * @return {Roo.MessageBox} This message box
16063          */
16064         wait : function(msg, title){
16065             this.show({
16066                 title : title,
16067                 msg : msg,
16068                 buttons: false,
16069                 closable:false,
16070                 progress:true,
16071                 modal:true,
16072                 width:300,
16073                 wait:true
16074             });
16075             waitTimer = Roo.TaskMgr.start({
16076                 run: function(i){
16077                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
16078                 },
16079                 interval: 1000
16080             });
16081             return this;
16082         },
16083
16084         /**
16085          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
16086          * If a callback function is passed it will be called after the user clicks either button, and the id of the
16087          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
16088          * @param {String} title The title bar text
16089          * @param {String} msg The message box body text
16090          * @param {Function} fn (optional) The callback function invoked after the message box is closed
16091          * @param {Object} scope (optional) The scope of the callback function
16092          * @return {Roo.MessageBox} This message box
16093          */
16094         confirm : function(title, msg, fn, scope){
16095             this.show({
16096                 title : title,
16097                 msg : msg,
16098                 buttons: this.YESNO,
16099                 fn: fn,
16100                 scope : scope,
16101                 modal : true
16102             });
16103             return this;
16104         },
16105
16106         /**
16107          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
16108          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
16109          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
16110          * (could also be the top-right close button) and the text that was entered will be passed as the two
16111          * parameters to the callback.
16112          * @param {String} title The title bar text
16113          * @param {String} msg The message box body text
16114          * @param {Function} fn (optional) The callback function invoked after the message box is closed
16115          * @param {Object} scope (optional) The scope of the callback function
16116          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
16117          * property, or the height in pixels to create the textbox (defaults to false / single-line)
16118          * @return {Roo.MessageBox} This message box
16119          */
16120         prompt : function(title, msg, fn, scope, multiline){
16121             this.show({
16122                 title : title,
16123                 msg : msg,
16124                 buttons: this.OKCANCEL,
16125                 fn: fn,
16126                 minWidth:250,
16127                 scope : scope,
16128                 prompt:true,
16129                 multiline: multiline,
16130                 modal : true
16131             });
16132             return this;
16133         },
16134
16135         /**
16136          * Button config that displays a single OK button
16137          * @type Object
16138          */
16139         OK : {ok:true},
16140         /**
16141          * Button config that displays Yes and No buttons
16142          * @type Object
16143          */
16144         YESNO : {yes:true, no:true},
16145         /**
16146          * Button config that displays OK and Cancel buttons
16147          * @type Object
16148          */
16149         OKCANCEL : {ok:true, cancel:true},
16150         /**
16151          * Button config that displays Yes, No and Cancel buttons
16152          * @type Object
16153          */
16154         YESNOCANCEL : {yes:true, no:true, cancel:true},
16155
16156         /**
16157          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
16158          * @type Number
16159          */
16160         defaultTextHeight : 75,
16161         /**
16162          * The maximum width in pixels of the message box (defaults to 600)
16163          * @type Number
16164          */
16165         maxWidth : 600,
16166         /**
16167          * The minimum width in pixels of the message box (defaults to 100)
16168          * @type Number
16169          */
16170         minWidth : 100,
16171         /**
16172          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
16173          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
16174          * @type Number
16175          */
16176         minProgressWidth : 250,
16177         /**
16178          * An object containing the default button text strings that can be overriden for localized language support.
16179          * Supported properties are: ok, cancel, yes and no.
16180          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
16181          * @type Object
16182          */
16183         buttonText : {
16184             ok : "OK",
16185             cancel : "Cancel",
16186             yes : "Yes",
16187             no : "No"
16188         }
16189     };
16190 }();
16191
16192 /**
16193  * Shorthand for {@link Roo.MessageBox}
16194  */
16195 Roo.Msg = Roo.MessageBox;/*
16196  * Based on:
16197  * Ext JS Library 1.1.1
16198  * Copyright(c) 2006-2007, Ext JS, LLC.
16199  *
16200  * Originally Released Under LGPL - original licence link has changed is not relivant.
16201  *
16202  * Fork - LGPL
16203  * <script type="text/javascript">
16204  */
16205 /**
16206  * @class Roo.QuickTips
16207  * Provides attractive and customizable tooltips for any element.
16208  * @singleton
16209  */
16210 Roo.QuickTips = function(){
16211     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
16212     var ce, bd, xy, dd;
16213     var visible = false, disabled = true, inited = false;
16214     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
16215     
16216     var onOver = function(e){
16217         if(disabled){
16218             return;
16219         }
16220         var t = e.getTarget();
16221         if(!t || t.nodeType !== 1 || t == document || t == document.body){
16222             return;
16223         }
16224         if(ce && t == ce.el){
16225             clearTimeout(hideProc);
16226             return;
16227         }
16228         if(t && tagEls[t.id]){
16229             tagEls[t.id].el = t;
16230             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
16231             return;
16232         }
16233         var ttp, et = Roo.fly(t);
16234         var ns = cfg.namespace;
16235         if(tm.interceptTitles && t.title){
16236             ttp = t.title;
16237             t.qtip = ttp;
16238             t.removeAttribute("title");
16239             e.preventDefault();
16240         }else{
16241             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
16242         }
16243         if(ttp){
16244             showProc = show.defer(tm.showDelay, tm, [{
16245                 el: t, 
16246                 text: ttp, 
16247                 width: et.getAttributeNS(ns, cfg.width),
16248                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
16249                 title: et.getAttributeNS(ns, cfg.title),
16250                     cls: et.getAttributeNS(ns, cfg.cls)
16251             }]);
16252         }
16253     };
16254     
16255     var onOut = function(e){
16256         clearTimeout(showProc);
16257         var t = e.getTarget();
16258         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
16259             hideProc = setTimeout(hide, tm.hideDelay);
16260         }
16261     };
16262     
16263     var onMove = function(e){
16264         if(disabled){
16265             return;
16266         }
16267         xy = e.getXY();
16268         xy[1] += 18;
16269         if(tm.trackMouse && ce){
16270             el.setXY(xy);
16271         }
16272     };
16273     
16274     var onDown = function(e){
16275         clearTimeout(showProc);
16276         clearTimeout(hideProc);
16277         if(!e.within(el)){
16278             if(tm.hideOnClick){
16279                 hide();
16280                 tm.disable();
16281                 tm.enable.defer(100, tm);
16282             }
16283         }
16284     };
16285     
16286     var getPad = function(){
16287         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
16288     };
16289
16290     var show = function(o){
16291         if(disabled){
16292             return;
16293         }
16294         clearTimeout(dismissProc);
16295         ce = o;
16296         if(removeCls){ // in case manually hidden
16297             el.removeClass(removeCls);
16298             removeCls = null;
16299         }
16300         if(ce.cls){
16301             el.addClass(ce.cls);
16302             removeCls = ce.cls;
16303         }
16304         if(ce.title){
16305             tipTitle.update(ce.title);
16306             tipTitle.show();
16307         }else{
16308             tipTitle.update('');
16309             tipTitle.hide();
16310         }
16311         el.dom.style.width  = tm.maxWidth+'px';
16312         //tipBody.dom.style.width = '';
16313         tipBodyText.update(o.text);
16314         var p = getPad(), w = ce.width;
16315         if(!w){
16316             var td = tipBodyText.dom;
16317             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
16318             if(aw > tm.maxWidth){
16319                 w = tm.maxWidth;
16320             }else if(aw < tm.minWidth){
16321                 w = tm.minWidth;
16322             }else{
16323                 w = aw;
16324             }
16325         }
16326         //tipBody.setWidth(w);
16327         el.setWidth(parseInt(w, 10) + p);
16328         if(ce.autoHide === false){
16329             close.setDisplayed(true);
16330             if(dd){
16331                 dd.unlock();
16332             }
16333         }else{
16334             close.setDisplayed(false);
16335             if(dd){
16336                 dd.lock();
16337             }
16338         }
16339         if(xy){
16340             el.avoidY = xy[1]-18;
16341             el.setXY(xy);
16342         }
16343         if(tm.animate){
16344             el.setOpacity(.1);
16345             el.setStyle("visibility", "visible");
16346             el.fadeIn({callback: afterShow});
16347         }else{
16348             afterShow();
16349         }
16350     };
16351     
16352     var afterShow = function(){
16353         if(ce){
16354             el.show();
16355             esc.enable();
16356             if(tm.autoDismiss && ce.autoHide !== false){
16357                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
16358             }
16359         }
16360     };
16361     
16362     var hide = function(noanim){
16363         clearTimeout(dismissProc);
16364         clearTimeout(hideProc);
16365         ce = null;
16366         if(el.isVisible()){
16367             esc.disable();
16368             if(noanim !== true && tm.animate){
16369                 el.fadeOut({callback: afterHide});
16370             }else{
16371                 afterHide();
16372             } 
16373         }
16374     };
16375     
16376     var afterHide = function(){
16377         el.hide();
16378         if(removeCls){
16379             el.removeClass(removeCls);
16380             removeCls = null;
16381         }
16382     };
16383     
16384     return {
16385         /**
16386         * @cfg {Number} minWidth
16387         * The minimum width of the quick tip (defaults to 40)
16388         */
16389        minWidth : 40,
16390         /**
16391         * @cfg {Number} maxWidth
16392         * The maximum width of the quick tip (defaults to 300)
16393         */
16394        maxWidth : 300,
16395         /**
16396         * @cfg {Boolean} interceptTitles
16397         * True to automatically use the element's DOM title value if available (defaults to false)
16398         */
16399        interceptTitles : false,
16400         /**
16401         * @cfg {Boolean} trackMouse
16402         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
16403         */
16404        trackMouse : false,
16405         /**
16406         * @cfg {Boolean} hideOnClick
16407         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
16408         */
16409        hideOnClick : true,
16410         /**
16411         * @cfg {Number} showDelay
16412         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
16413         */
16414        showDelay : 500,
16415         /**
16416         * @cfg {Number} hideDelay
16417         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
16418         */
16419        hideDelay : 200,
16420         /**
16421         * @cfg {Boolean} autoHide
16422         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
16423         * Used in conjunction with hideDelay.
16424         */
16425        autoHide : true,
16426         /**
16427         * @cfg {Boolean}
16428         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
16429         * (defaults to true).  Used in conjunction with autoDismissDelay.
16430         */
16431        autoDismiss : true,
16432         /**
16433         * @cfg {Number}
16434         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
16435         */
16436        autoDismissDelay : 5000,
16437        /**
16438         * @cfg {Boolean} animate
16439         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
16440         */
16441        animate : false,
16442
16443        /**
16444         * @cfg {String} title
16445         * Title text to display (defaults to '').  This can be any valid HTML markup.
16446         */
16447         title: '',
16448        /**
16449         * @cfg {String} text
16450         * Body text to display (defaults to '').  This can be any valid HTML markup.
16451         */
16452         text : '',
16453        /**
16454         * @cfg {String} cls
16455         * A CSS class to apply to the base quick tip element (defaults to '').
16456         */
16457         cls : '',
16458        /**
16459         * @cfg {Number} width
16460         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
16461         * minWidth or maxWidth.
16462         */
16463         width : null,
16464
16465     /**
16466      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
16467      * or display QuickTips in a page.
16468      */
16469        init : function(){
16470           tm = Roo.QuickTips;
16471           cfg = tm.tagConfig;
16472           if(!inited){
16473               if(!Roo.isReady){ // allow calling of init() before onReady
16474                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
16475                   return;
16476               }
16477               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
16478               el.fxDefaults = {stopFx: true};
16479               // maximum custom styling
16480               //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>');
16481               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>');              
16482               tipTitle = el.child('h3');
16483               tipTitle.enableDisplayMode("block");
16484               tipBody = el.child('div.x-tip-bd');
16485               tipBodyText = el.child('div.x-tip-bd-inner');
16486               //bdLeft = el.child('div.x-tip-bd-left');
16487               //bdRight = el.child('div.x-tip-bd-right');
16488               close = el.child('div.x-tip-close');
16489               close.enableDisplayMode("block");
16490               close.on("click", hide);
16491               var d = Roo.get(document);
16492               d.on("mousedown", onDown);
16493               d.on("mouseover", onOver);
16494               d.on("mouseout", onOut);
16495               d.on("mousemove", onMove);
16496               esc = d.addKeyListener(27, hide);
16497               esc.disable();
16498               if(Roo.dd.DD){
16499                   dd = el.initDD("default", null, {
16500                       onDrag : function(){
16501                           el.sync();  
16502                       }
16503                   });
16504                   dd.setHandleElId(tipTitle.id);
16505                   dd.lock();
16506               }
16507               inited = true;
16508           }
16509           this.enable(); 
16510        },
16511
16512     /**
16513      * Configures a new quick tip instance and assigns it to a target element.  The following config options
16514      * are supported:
16515      * <pre>
16516 Property    Type                   Description
16517 ----------  ---------------------  ------------------------------------------------------------------------
16518 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
16519      * </ul>
16520      * @param {Object} config The config object
16521      */
16522        register : function(config){
16523            var cs = config instanceof Array ? config : arguments;
16524            for(var i = 0, len = cs.length; i < len; i++) {
16525                var c = cs[i];
16526                var target = c.target;
16527                if(target){
16528                    if(target instanceof Array){
16529                        for(var j = 0, jlen = target.length; j < jlen; j++){
16530                            tagEls[target[j]] = c;
16531                        }
16532                    }else{
16533                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
16534                    }
16535                }
16536            }
16537        },
16538
16539     /**
16540      * Removes this quick tip from its element and destroys it.
16541      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
16542      */
16543        unregister : function(el){
16544            delete tagEls[Roo.id(el)];
16545        },
16546
16547     /**
16548      * Enable this quick tip.
16549      */
16550        enable : function(){
16551            if(inited && disabled){
16552                locks.pop();
16553                if(locks.length < 1){
16554                    disabled = false;
16555                }
16556            }
16557        },
16558
16559     /**
16560      * Disable this quick tip.
16561      */
16562        disable : function(){
16563           disabled = true;
16564           clearTimeout(showProc);
16565           clearTimeout(hideProc);
16566           clearTimeout(dismissProc);
16567           if(ce){
16568               hide(true);
16569           }
16570           locks.push(1);
16571        },
16572
16573     /**
16574      * Returns true if the quick tip is enabled, else false.
16575      */
16576        isEnabled : function(){
16577             return !disabled;
16578        },
16579
16580         // private
16581        tagConfig : {
16582            namespace : "ext",
16583            attribute : "qtip",
16584            width : "width",
16585            target : "target",
16586            title : "qtitle",
16587            hide : "hide",
16588            cls : "qclass"
16589        }
16590    };
16591 }();
16592
16593 // backwards compat
16594 Roo.QuickTips.tips = Roo.QuickTips.register;/*
16595  * Based on:
16596  * Ext JS Library 1.1.1
16597  * Copyright(c) 2006-2007, Ext JS, LLC.
16598  *
16599  * Originally Released Under LGPL - original licence link has changed is not relivant.
16600  *
16601  * Fork - LGPL
16602  * <script type="text/javascript">
16603  */
16604  
16605
16606 /**
16607  * @class Roo.tree.TreePanel
16608  * @extends Roo.data.Tree
16609
16610  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
16611  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
16612  * @cfg {Boolean} enableDD true to enable drag and drop
16613  * @cfg {Boolean} enableDrag true to enable just drag
16614  * @cfg {Boolean} enableDrop true to enable just drop
16615  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
16616  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
16617  * @cfg {String} ddGroup The DD group this TreePanel belongs to
16618  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
16619  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
16620  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
16621  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
16622  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
16623  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
16624  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
16625  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
16626  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
16627  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
16628  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
16629  * @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>
16630  * @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>
16631  * 
16632  * @constructor
16633  * @param {String/HTMLElement/Element} el The container element
16634  * @param {Object} config
16635  */
16636 Roo.tree.TreePanel = function(el, config){
16637     var root = false;
16638     var loader = false;
16639     if (config.root) {
16640         root = config.root;
16641         delete config.root;
16642     }
16643     if (config.loader) {
16644         loader = config.loader;
16645         delete config.loader;
16646     }
16647     
16648     Roo.apply(this, config);
16649     Roo.tree.TreePanel.superclass.constructor.call(this);
16650     this.el = Roo.get(el);
16651     this.el.addClass('x-tree');
16652     //console.log(root);
16653     if (root) {
16654         this.setRootNode( Roo.factory(root, Roo.tree));
16655     }
16656     if (loader) {
16657         this.loader = Roo.factory(loader, Roo.tree);
16658     }
16659    /**
16660     * Read-only. The id of the container element becomes this TreePanel's id.
16661     */
16662     this.id = this.el.id;
16663     this.addEvents({
16664         /**
16665         * @event beforeload
16666         * Fires before a node is loaded, return false to cancel
16667         * @param {Node} node The node being loaded
16668         */
16669         "beforeload" : true,
16670         /**
16671         * @event load
16672         * Fires when a node is loaded
16673         * @param {Node} node The node that was loaded
16674         */
16675         "load" : true,
16676         /**
16677         * @event textchange
16678         * Fires when the text for a node is changed
16679         * @param {Node} node The node
16680         * @param {String} text The new text
16681         * @param {String} oldText The old text
16682         */
16683         "textchange" : true,
16684         /**
16685         * @event beforeexpand
16686         * Fires before a node is expanded, return false to cancel.
16687         * @param {Node} node The node
16688         * @param {Boolean} deep
16689         * @param {Boolean} anim
16690         */
16691         "beforeexpand" : true,
16692         /**
16693         * @event beforecollapse
16694         * Fires before a node is collapsed, return false to cancel.
16695         * @param {Node} node The node
16696         * @param {Boolean} deep
16697         * @param {Boolean} anim
16698         */
16699         "beforecollapse" : true,
16700         /**
16701         * @event expand
16702         * Fires when a node is expanded
16703         * @param {Node} node The node
16704         */
16705         "expand" : true,
16706         /**
16707         * @event disabledchange
16708         * Fires when the disabled status of a node changes
16709         * @param {Node} node The node
16710         * @param {Boolean} disabled
16711         */
16712         "disabledchange" : true,
16713         /**
16714         * @event collapse
16715         * Fires when a node is collapsed
16716         * @param {Node} node The node
16717         */
16718         "collapse" : true,
16719         /**
16720         * @event beforeclick
16721         * Fires before click processing on a node. Return false to cancel the default action.
16722         * @param {Node} node The node
16723         * @param {Roo.EventObject} e The event object
16724         */
16725         "beforeclick":true,
16726         /**
16727         * @event checkchange
16728         * Fires when a node with a checkbox's checked property changes
16729         * @param {Node} this This node
16730         * @param {Boolean} checked
16731         */
16732         "checkchange":true,
16733         /**
16734         * @event click
16735         * Fires when a node is clicked
16736         * @param {Node} node The node
16737         * @param {Roo.EventObject} e The event object
16738         */
16739         "click":true,
16740         /**
16741         * @event dblclick
16742         * Fires when a node is double clicked
16743         * @param {Node} node The node
16744         * @param {Roo.EventObject} e The event object
16745         */
16746         "dblclick":true,
16747         /**
16748         * @event contextmenu
16749         * Fires when a node is right clicked
16750         * @param {Node} node The node
16751         * @param {Roo.EventObject} e The event object
16752         */
16753         "contextmenu":true,
16754         /**
16755         * @event beforechildrenrendered
16756         * Fires right before the child nodes for a node are rendered
16757         * @param {Node} node The node
16758         */
16759         "beforechildrenrendered":true,
16760         /**
16761         * @event startdrag
16762         * Fires when a node starts being dragged
16763         * @param {Roo.tree.TreePanel} this
16764         * @param {Roo.tree.TreeNode} node
16765         * @param {event} e The raw browser event
16766         */ 
16767        "startdrag" : true,
16768        /**
16769         * @event enddrag
16770         * Fires when a drag operation is complete
16771         * @param {Roo.tree.TreePanel} this
16772         * @param {Roo.tree.TreeNode} node
16773         * @param {event} e The raw browser event
16774         */
16775        "enddrag" : true,
16776        /**
16777         * @event dragdrop
16778         * Fires when a dragged node is dropped on a valid DD target
16779         * @param {Roo.tree.TreePanel} this
16780         * @param {Roo.tree.TreeNode} node
16781         * @param {DD} dd The dd it was dropped on
16782         * @param {event} e The raw browser event
16783         */
16784        "dragdrop" : true,
16785        /**
16786         * @event beforenodedrop
16787         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
16788         * passed to handlers has the following properties:<br />
16789         * <ul style="padding:5px;padding-left:16px;">
16790         * <li>tree - The TreePanel</li>
16791         * <li>target - The node being targeted for the drop</li>
16792         * <li>data - The drag data from the drag source</li>
16793         * <li>point - The point of the drop - append, above or below</li>
16794         * <li>source - The drag source</li>
16795         * <li>rawEvent - Raw mouse event</li>
16796         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
16797         * to be inserted by setting them on this object.</li>
16798         * <li>cancel - Set this to true to cancel the drop.</li>
16799         * </ul>
16800         * @param {Object} dropEvent
16801         */
16802        "beforenodedrop" : true,
16803        /**
16804         * @event nodedrop
16805         * Fires after a DD object is dropped on a node in this tree. The dropEvent
16806         * passed to handlers has the following properties:<br />
16807         * <ul style="padding:5px;padding-left:16px;">
16808         * <li>tree - The TreePanel</li>
16809         * <li>target - The node being targeted for the drop</li>
16810         * <li>data - The drag data from the drag source</li>
16811         * <li>point - The point of the drop - append, above or below</li>
16812         * <li>source - The drag source</li>
16813         * <li>rawEvent - Raw mouse event</li>
16814         * <li>dropNode - Dropped node(s).</li>
16815         * </ul>
16816         * @param {Object} dropEvent
16817         */
16818        "nodedrop" : true,
16819         /**
16820         * @event nodedragover
16821         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
16822         * passed to handlers has the following properties:<br />
16823         * <ul style="padding:5px;padding-left:16px;">
16824         * <li>tree - The TreePanel</li>
16825         * <li>target - The node being targeted for the drop</li>
16826         * <li>data - The drag data from the drag source</li>
16827         * <li>point - The point of the drop - append, above or below</li>
16828         * <li>source - The drag source</li>
16829         * <li>rawEvent - Raw mouse event</li>
16830         * <li>dropNode - Drop node(s) provided by the source.</li>
16831         * <li>cancel - Set this to true to signal drop not allowed.</li>
16832         * </ul>
16833         * @param {Object} dragOverEvent
16834         */
16835        "nodedragover" : true
16836         
16837     });
16838     if(this.singleExpand){
16839        this.on("beforeexpand", this.restrictExpand, this);
16840     }
16841     if (this.editor) {
16842         this.editor.tree = this;
16843         this.editor = Roo.factory(this.editor, Roo.tree);
16844     }
16845     
16846     if (this.selModel) {
16847         this.selModel = Roo.factory(this.selModel, Roo.tree);
16848     }
16849    
16850 };
16851 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
16852     rootVisible : true,
16853     animate: Roo.enableFx,
16854     lines : true,
16855     enableDD : false,
16856     hlDrop : Roo.enableFx,
16857   
16858     renderer: false,
16859     
16860     rendererTip: false,
16861     // private
16862     restrictExpand : function(node){
16863         var p = node.parentNode;
16864         if(p){
16865             if(p.expandedChild && p.expandedChild.parentNode == p){
16866                 p.expandedChild.collapse();
16867             }
16868             p.expandedChild = node;
16869         }
16870     },
16871
16872     // private override
16873     setRootNode : function(node){
16874         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
16875         if(!this.rootVisible){
16876             node.ui = new Roo.tree.RootTreeNodeUI(node);
16877         }
16878         return node;
16879     },
16880
16881     /**
16882      * Returns the container element for this TreePanel
16883      */
16884     getEl : function(){
16885         return this.el;
16886     },
16887
16888     /**
16889      * Returns the default TreeLoader for this TreePanel
16890      */
16891     getLoader : function(){
16892         return this.loader;
16893     },
16894
16895     /**
16896      * Expand all nodes
16897      */
16898     expandAll : function(){
16899         this.root.expand(true);
16900     },
16901
16902     /**
16903      * Collapse all nodes
16904      */
16905     collapseAll : function(){
16906         this.root.collapse(true);
16907     },
16908
16909     /**
16910      * Returns the selection model used by this TreePanel
16911      */
16912     getSelectionModel : function(){
16913         if(!this.selModel){
16914             this.selModel = new Roo.tree.DefaultSelectionModel();
16915         }
16916         return this.selModel;
16917     },
16918
16919     /**
16920      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
16921      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
16922      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
16923      * @return {Array}
16924      */
16925     getChecked : function(a, startNode){
16926         startNode = startNode || this.root;
16927         var r = [];
16928         var f = function(){
16929             if(this.attributes.checked){
16930                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
16931             }
16932         }
16933         startNode.cascade(f);
16934         return r;
16935     },
16936
16937     /**
16938      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16939      * @param {String} path
16940      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16941      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
16942      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
16943      */
16944     expandPath : function(path, attr, callback){
16945         attr = attr || "id";
16946         var keys = path.split(this.pathSeparator);
16947         var curNode = this.root;
16948         if(curNode.attributes[attr] != keys[1]){ // invalid root
16949             if(callback){
16950                 callback(false, null);
16951             }
16952             return;
16953         }
16954         var index = 1;
16955         var f = function(){
16956             if(++index == keys.length){
16957                 if(callback){
16958                     callback(true, curNode);
16959                 }
16960                 return;
16961             }
16962             var c = curNode.findChild(attr, keys[index]);
16963             if(!c){
16964                 if(callback){
16965                     callback(false, curNode);
16966                 }
16967                 return;
16968             }
16969             curNode = c;
16970             c.expand(false, false, f);
16971         };
16972         curNode.expand(false, false, f);
16973     },
16974
16975     /**
16976      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16977      * @param {String} path
16978      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16979      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
16980      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
16981      */
16982     selectPath : function(path, attr, callback){
16983         attr = attr || "id";
16984         var keys = path.split(this.pathSeparator);
16985         var v = keys.pop();
16986         if(keys.length > 0){
16987             var f = function(success, node){
16988                 if(success && node){
16989                     var n = node.findChild(attr, v);
16990                     if(n){
16991                         n.select();
16992                         if(callback){
16993                             callback(true, n);
16994                         }
16995                     }else if(callback){
16996                         callback(false, n);
16997                     }
16998                 }else{
16999                     if(callback){
17000                         callback(false, n);
17001                     }
17002                 }
17003             };
17004             this.expandPath(keys.join(this.pathSeparator), attr, f);
17005         }else{
17006             this.root.select();
17007             if(callback){
17008                 callback(true, this.root);
17009             }
17010         }
17011     },
17012
17013     getTreeEl : function(){
17014         return this.el;
17015     },
17016
17017     /**
17018      * Trigger rendering of this TreePanel
17019      */
17020     render : function(){
17021         if (this.innerCt) {
17022             return this; // stop it rendering more than once!!
17023         }
17024         
17025         this.innerCt = this.el.createChild({tag:"ul",
17026                cls:"x-tree-root-ct " +
17027                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
17028
17029         if(this.containerScroll){
17030             Roo.dd.ScrollManager.register(this.el);
17031         }
17032         if((this.enableDD || this.enableDrop) && !this.dropZone){
17033            /**
17034             * The dropZone used by this tree if drop is enabled
17035             * @type Roo.tree.TreeDropZone
17036             */
17037              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
17038                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
17039            });
17040         }
17041         if((this.enableDD || this.enableDrag) && !this.dragZone){
17042            /**
17043             * The dragZone used by this tree if drag is enabled
17044             * @type Roo.tree.TreeDragZone
17045             */
17046             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
17047                ddGroup: this.ddGroup || "TreeDD",
17048                scroll: this.ddScroll
17049            });
17050         }
17051         this.getSelectionModel().init(this);
17052         if (!this.root) {
17053             Roo.log("ROOT not set in tree");
17054             return this;
17055         }
17056         this.root.render();
17057         if(!this.rootVisible){
17058             this.root.renderChildren();
17059         }
17060         return this;
17061     }
17062 });/*
17063  * Based on:
17064  * Ext JS Library 1.1.1
17065  * Copyright(c) 2006-2007, Ext JS, LLC.
17066  *
17067  * Originally Released Under LGPL - original licence link has changed is not relivant.
17068  *
17069  * Fork - LGPL
17070  * <script type="text/javascript">
17071  */
17072  
17073
17074 /**
17075  * @class Roo.tree.DefaultSelectionModel
17076  * @extends Roo.util.Observable
17077  * The default single selection for a TreePanel.
17078  * @param {Object} cfg Configuration
17079  */
17080 Roo.tree.DefaultSelectionModel = function(cfg){
17081    this.selNode = null;
17082    
17083    
17084    
17085    this.addEvents({
17086        /**
17087         * @event selectionchange
17088         * Fires when the selected node changes
17089         * @param {DefaultSelectionModel} this
17090         * @param {TreeNode} node the new selection
17091         */
17092        "selectionchange" : true,
17093
17094        /**
17095         * @event beforeselect
17096         * Fires before the selected node changes, return false to cancel the change
17097         * @param {DefaultSelectionModel} this
17098         * @param {TreeNode} node the new selection
17099         * @param {TreeNode} node the old selection
17100         */
17101        "beforeselect" : true
17102    });
17103    
17104     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
17105 };
17106
17107 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
17108     init : function(tree){
17109         this.tree = tree;
17110         tree.getTreeEl().on("keydown", this.onKeyDown, this);
17111         tree.on("click", this.onNodeClick, this);
17112     },
17113     
17114     onNodeClick : function(node, e){
17115         if (e.ctrlKey && this.selNode == node)  {
17116             this.unselect(node);
17117             return;
17118         }
17119         this.select(node);
17120     },
17121     
17122     /**
17123      * Select a node.
17124      * @param {TreeNode} node The node to select
17125      * @return {TreeNode} The selected node
17126      */
17127     select : function(node){
17128         var last = this.selNode;
17129         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
17130             if(last){
17131                 last.ui.onSelectedChange(false);
17132             }
17133             this.selNode = node;
17134             node.ui.onSelectedChange(true);
17135             this.fireEvent("selectionchange", this, node, last);
17136         }
17137         return node;
17138     },
17139     
17140     /**
17141      * Deselect a node.
17142      * @param {TreeNode} node The node to unselect
17143      */
17144     unselect : function(node){
17145         if(this.selNode == node){
17146             this.clearSelections();
17147         }    
17148     },
17149     
17150     /**
17151      * Clear all selections
17152      */
17153     clearSelections : function(){
17154         var n = this.selNode;
17155         if(n){
17156             n.ui.onSelectedChange(false);
17157             this.selNode = null;
17158             this.fireEvent("selectionchange", this, null);
17159         }
17160         return n;
17161     },
17162     
17163     /**
17164      * Get the selected node
17165      * @return {TreeNode} The selected node
17166      */
17167     getSelectedNode : function(){
17168         return this.selNode;    
17169     },
17170     
17171     /**
17172      * Returns true if the node is selected
17173      * @param {TreeNode} node The node to check
17174      * @return {Boolean}
17175      */
17176     isSelected : function(node){
17177         return this.selNode == node;  
17178     },
17179
17180     /**
17181      * Selects the node above the selected node in the tree, intelligently walking the nodes
17182      * @return TreeNode The new selection
17183      */
17184     selectPrevious : function(){
17185         var s = this.selNode || this.lastSelNode;
17186         if(!s){
17187             return null;
17188         }
17189         var ps = s.previousSibling;
17190         if(ps){
17191             if(!ps.isExpanded() || ps.childNodes.length < 1){
17192                 return this.select(ps);
17193             } else{
17194                 var lc = ps.lastChild;
17195                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
17196                     lc = lc.lastChild;
17197                 }
17198                 return this.select(lc);
17199             }
17200         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
17201             return this.select(s.parentNode);
17202         }
17203         return null;
17204     },
17205
17206     /**
17207      * Selects the node above the selected node in the tree, intelligently walking the nodes
17208      * @return TreeNode The new selection
17209      */
17210     selectNext : function(){
17211         var s = this.selNode || this.lastSelNode;
17212         if(!s){
17213             return null;
17214         }
17215         if(s.firstChild && s.isExpanded()){
17216              return this.select(s.firstChild);
17217          }else if(s.nextSibling){
17218              return this.select(s.nextSibling);
17219          }else if(s.parentNode){
17220             var newS = null;
17221             s.parentNode.bubble(function(){
17222                 if(this.nextSibling){
17223                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
17224                     return false;
17225                 }
17226             });
17227             return newS;
17228          }
17229         return null;
17230     },
17231
17232     onKeyDown : function(e){
17233         var s = this.selNode || this.lastSelNode;
17234         // undesirable, but required
17235         var sm = this;
17236         if(!s){
17237             return;
17238         }
17239         var k = e.getKey();
17240         switch(k){
17241              case e.DOWN:
17242                  e.stopEvent();
17243                  this.selectNext();
17244              break;
17245              case e.UP:
17246                  e.stopEvent();
17247                  this.selectPrevious();
17248              break;
17249              case e.RIGHT:
17250                  e.preventDefault();
17251                  if(s.hasChildNodes()){
17252                      if(!s.isExpanded()){
17253                          s.expand();
17254                      }else if(s.firstChild){
17255                          this.select(s.firstChild, e);
17256                      }
17257                  }
17258              break;
17259              case e.LEFT:
17260                  e.preventDefault();
17261                  if(s.hasChildNodes() && s.isExpanded()){
17262                      s.collapse();
17263                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
17264                      this.select(s.parentNode, e);
17265                  }
17266              break;
17267         };
17268     }
17269 });
17270
17271 /**
17272  * @class Roo.tree.MultiSelectionModel
17273  * @extends Roo.util.Observable
17274  * Multi selection for a TreePanel.
17275  * @param {Object} cfg Configuration
17276  */
17277 Roo.tree.MultiSelectionModel = function(){
17278    this.selNodes = [];
17279    this.selMap = {};
17280    this.addEvents({
17281        /**
17282         * @event selectionchange
17283         * Fires when the selected nodes change
17284         * @param {MultiSelectionModel} this
17285         * @param {Array} nodes Array of the selected nodes
17286         */
17287        "selectionchange" : true
17288    });
17289    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
17290    
17291 };
17292
17293 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
17294     init : function(tree){
17295         this.tree = tree;
17296         tree.getTreeEl().on("keydown", this.onKeyDown, this);
17297         tree.on("click", this.onNodeClick, this);
17298     },
17299     
17300     onNodeClick : function(node, e){
17301         this.select(node, e, e.ctrlKey);
17302     },
17303     
17304     /**
17305      * Select a node.
17306      * @param {TreeNode} node The node to select
17307      * @param {EventObject} e (optional) An event associated with the selection
17308      * @param {Boolean} keepExisting True to retain existing selections
17309      * @return {TreeNode} The selected node
17310      */
17311     select : function(node, e, keepExisting){
17312         if(keepExisting !== true){
17313             this.clearSelections(true);
17314         }
17315         if(this.isSelected(node)){
17316             this.lastSelNode = node;
17317             return node;
17318         }
17319         this.selNodes.push(node);
17320         this.selMap[node.id] = node;
17321         this.lastSelNode = node;
17322         node.ui.onSelectedChange(true);
17323         this.fireEvent("selectionchange", this, this.selNodes);
17324         return node;
17325     },
17326     
17327     /**
17328      * Deselect a node.
17329      * @param {TreeNode} node The node to unselect
17330      */
17331     unselect : function(node){
17332         if(this.selMap[node.id]){
17333             node.ui.onSelectedChange(false);
17334             var sn = this.selNodes;
17335             var index = -1;
17336             if(sn.indexOf){
17337                 index = sn.indexOf(node);
17338             }else{
17339                 for(var i = 0, len = sn.length; i < len; i++){
17340                     if(sn[i] == node){
17341                         index = i;
17342                         break;
17343                     }
17344                 }
17345             }
17346             if(index != -1){
17347                 this.selNodes.splice(index, 1);
17348             }
17349             delete this.selMap[node.id];
17350             this.fireEvent("selectionchange", this, this.selNodes);
17351         }
17352     },
17353     
17354     /**
17355      * Clear all selections
17356      */
17357     clearSelections : function(suppressEvent){
17358         var sn = this.selNodes;
17359         if(sn.length > 0){
17360             for(var i = 0, len = sn.length; i < len; i++){
17361                 sn[i].ui.onSelectedChange(false);
17362             }
17363             this.selNodes = [];
17364             this.selMap = {};
17365             if(suppressEvent !== true){
17366                 this.fireEvent("selectionchange", this, this.selNodes);
17367             }
17368         }
17369     },
17370     
17371     /**
17372      * Returns true if the node is selected
17373      * @param {TreeNode} node The node to check
17374      * @return {Boolean}
17375      */
17376     isSelected : function(node){
17377         return this.selMap[node.id] ? true : false;  
17378     },
17379     
17380     /**
17381      * Returns an array of the selected nodes
17382      * @return {Array}
17383      */
17384     getSelectedNodes : function(){
17385         return this.selNodes;    
17386     },
17387
17388     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
17389
17390     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
17391
17392     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
17393 });/*
17394  * Based on:
17395  * Ext JS Library 1.1.1
17396  * Copyright(c) 2006-2007, Ext JS, LLC.
17397  *
17398  * Originally Released Under LGPL - original licence link has changed is not relivant.
17399  *
17400  * Fork - LGPL
17401  * <script type="text/javascript">
17402  */
17403  
17404 /**
17405  * @class Roo.tree.TreeNode
17406  * @extends Roo.data.Node
17407  * @cfg {String} text The text for this node
17408  * @cfg {Boolean} expanded true to start the node expanded
17409  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
17410  * @cfg {Boolean} allowDrop false if this node cannot be drop on
17411  * @cfg {Boolean} disabled true to start the node disabled
17412  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
17413  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
17414  * @cfg {String} cls A css class to be added to the node
17415  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
17416  * @cfg {String} href URL of the link used for the node (defaults to #)
17417  * @cfg {String} hrefTarget target frame for the link
17418  * @cfg {String} qtip An Ext QuickTip for the node
17419  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
17420  * @cfg {Boolean} singleClickExpand True for single click expand on this node
17421  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
17422  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
17423  * (defaults to undefined with no checkbox rendered)
17424  * @constructor
17425  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
17426  */
17427 Roo.tree.TreeNode = function(attributes){
17428     attributes = attributes || {};
17429     if(typeof attributes == "string"){
17430         attributes = {text: attributes};
17431     }
17432     this.childrenRendered = false;
17433     this.rendered = false;
17434     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
17435     this.expanded = attributes.expanded === true;
17436     this.isTarget = attributes.isTarget !== false;
17437     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
17438     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
17439
17440     /**
17441      * Read-only. The text for this node. To change it use setText().
17442      * @type String
17443      */
17444     this.text = attributes.text;
17445     /**
17446      * True if this node is disabled.
17447      * @type Boolean
17448      */
17449     this.disabled = attributes.disabled === true;
17450
17451     this.addEvents({
17452         /**
17453         * @event textchange
17454         * Fires when the text for this node is changed
17455         * @param {Node} this This node
17456         * @param {String} text The new text
17457         * @param {String} oldText The old text
17458         */
17459         "textchange" : true,
17460         /**
17461         * @event beforeexpand
17462         * Fires before this node is expanded, return false to cancel.
17463         * @param {Node} this This node
17464         * @param {Boolean} deep
17465         * @param {Boolean} anim
17466         */
17467         "beforeexpand" : true,
17468         /**
17469         * @event beforecollapse
17470         * Fires before this node is collapsed, return false to cancel.
17471         * @param {Node} this This node
17472         * @param {Boolean} deep
17473         * @param {Boolean} anim
17474         */
17475         "beforecollapse" : true,
17476         /**
17477         * @event expand
17478         * Fires when this node is expanded
17479         * @param {Node} this This node
17480         */
17481         "expand" : true,
17482         /**
17483         * @event disabledchange
17484         * Fires when the disabled status of this node changes
17485         * @param {Node} this This node
17486         * @param {Boolean} disabled
17487         */
17488         "disabledchange" : true,
17489         /**
17490         * @event collapse
17491         * Fires when this node is collapsed
17492         * @param {Node} this This node
17493         */
17494         "collapse" : true,
17495         /**
17496         * @event beforeclick
17497         * Fires before click processing. Return false to cancel the default action.
17498         * @param {Node} this This node
17499         * @param {Roo.EventObject} e The event object
17500         */
17501         "beforeclick":true,
17502         /**
17503         * @event checkchange
17504         * Fires when a node with a checkbox's checked property changes
17505         * @param {Node} this This node
17506         * @param {Boolean} checked
17507         */
17508         "checkchange":true,
17509         /**
17510         * @event click
17511         * Fires when this node is clicked
17512         * @param {Node} this This node
17513         * @param {Roo.EventObject} e The event object
17514         */
17515         "click":true,
17516         /**
17517         * @event dblclick
17518         * Fires when this node is double clicked
17519         * @param {Node} this This node
17520         * @param {Roo.EventObject} e The event object
17521         */
17522         "dblclick":true,
17523         /**
17524         * @event contextmenu
17525         * Fires when this node is right clicked
17526         * @param {Node} this This node
17527         * @param {Roo.EventObject} e The event object
17528         */
17529         "contextmenu":true,
17530         /**
17531         * @event beforechildrenrendered
17532         * Fires right before the child nodes for this node are rendered
17533         * @param {Node} this This node
17534         */
17535         "beforechildrenrendered":true
17536     });
17537
17538     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
17539
17540     /**
17541      * Read-only. The UI for this node
17542      * @type TreeNodeUI
17543      */
17544     this.ui = new uiClass(this);
17545     
17546     // finally support items[]
17547     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
17548         return;
17549     }
17550     
17551     
17552     Roo.each(this.attributes.items, function(c) {
17553         this.appendChild(Roo.factory(c,Roo.Tree));
17554     }, this);
17555     delete this.attributes.items;
17556     
17557     
17558     
17559 };
17560 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
17561     preventHScroll: true,
17562     /**
17563      * Returns true if this node is expanded
17564      * @return {Boolean}
17565      */
17566     isExpanded : function(){
17567         return this.expanded;
17568     },
17569
17570     /**
17571      * Returns the UI object for this node
17572      * @return {TreeNodeUI}
17573      */
17574     getUI : function(){
17575         return this.ui;
17576     },
17577
17578     // private override
17579     setFirstChild : function(node){
17580         var of = this.firstChild;
17581         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
17582         if(this.childrenRendered && of && node != of){
17583             of.renderIndent(true, true);
17584         }
17585         if(this.rendered){
17586             this.renderIndent(true, true);
17587         }
17588     },
17589
17590     // private override
17591     setLastChild : function(node){
17592         var ol = this.lastChild;
17593         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
17594         if(this.childrenRendered && ol && node != ol){
17595             ol.renderIndent(true, true);
17596         }
17597         if(this.rendered){
17598             this.renderIndent(true, true);
17599         }
17600     },
17601
17602     // these methods are overridden to provide lazy rendering support
17603     // private override
17604     appendChild : function()
17605     {
17606         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
17607         if(node && this.childrenRendered){
17608             node.render();
17609         }
17610         this.ui.updateExpandIcon();
17611         return node;
17612     },
17613
17614     // private override
17615     removeChild : function(node){
17616         this.ownerTree.getSelectionModel().unselect(node);
17617         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
17618         // if it's been rendered remove dom node
17619         if(this.childrenRendered){
17620             node.ui.remove();
17621         }
17622         if(this.childNodes.length < 1){
17623             this.collapse(false, false);
17624         }else{
17625             this.ui.updateExpandIcon();
17626         }
17627         if(!this.firstChild) {
17628             this.childrenRendered = false;
17629         }
17630         return node;
17631     },
17632
17633     // private override
17634     insertBefore : function(node, refNode){
17635         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
17636         if(newNode && refNode && this.childrenRendered){
17637             node.render();
17638         }
17639         this.ui.updateExpandIcon();
17640         return newNode;
17641     },
17642
17643     /**
17644      * Sets the text for this node
17645      * @param {String} text
17646      */
17647     setText : function(text){
17648         var oldText = this.text;
17649         this.text = text;
17650         this.attributes.text = text;
17651         if(this.rendered){ // event without subscribing
17652             this.ui.onTextChange(this, text, oldText);
17653         }
17654         this.fireEvent("textchange", this, text, oldText);
17655     },
17656
17657     /**
17658      * Triggers selection of this node
17659      */
17660     select : function(){
17661         this.getOwnerTree().getSelectionModel().select(this);
17662     },
17663
17664     /**
17665      * Triggers deselection of this node
17666      */
17667     unselect : function(){
17668         this.getOwnerTree().getSelectionModel().unselect(this);
17669     },
17670
17671     /**
17672      * Returns true if this node is selected
17673      * @return {Boolean}
17674      */
17675     isSelected : function(){
17676         return this.getOwnerTree().getSelectionModel().isSelected(this);
17677     },
17678
17679     /**
17680      * Expand this node.
17681      * @param {Boolean} deep (optional) True to expand all children as well
17682      * @param {Boolean} anim (optional) false to cancel the default animation
17683      * @param {Function} callback (optional) A callback to be called when
17684      * expanding this node completes (does not wait for deep expand to complete).
17685      * Called with 1 parameter, this node.
17686      */
17687     expand : function(deep, anim, callback){
17688         if(!this.expanded){
17689             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
17690                 return;
17691             }
17692             if(!this.childrenRendered){
17693                 this.renderChildren();
17694             }
17695             this.expanded = true;
17696             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
17697                 this.ui.animExpand(function(){
17698                     this.fireEvent("expand", this);
17699                     if(typeof callback == "function"){
17700                         callback(this);
17701                     }
17702                     if(deep === true){
17703                         this.expandChildNodes(true);
17704                     }
17705                 }.createDelegate(this));
17706                 return;
17707             }else{
17708                 this.ui.expand();
17709                 this.fireEvent("expand", this);
17710                 if(typeof callback == "function"){
17711                     callback(this);
17712                 }
17713             }
17714         }else{
17715            if(typeof callback == "function"){
17716                callback(this);
17717            }
17718         }
17719         if(deep === true){
17720             this.expandChildNodes(true);
17721         }
17722     },
17723
17724     isHiddenRoot : function(){
17725         return this.isRoot && !this.getOwnerTree().rootVisible;
17726     },
17727
17728     /**
17729      * Collapse this node.
17730      * @param {Boolean} deep (optional) True to collapse all children as well
17731      * @param {Boolean} anim (optional) false to cancel the default animation
17732      */
17733     collapse : function(deep, anim){
17734         if(this.expanded && !this.isHiddenRoot()){
17735             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
17736                 return;
17737             }
17738             this.expanded = false;
17739             if((this.getOwnerTree().animate && anim !== false) || anim){
17740                 this.ui.animCollapse(function(){
17741                     this.fireEvent("collapse", this);
17742                     if(deep === true){
17743                         this.collapseChildNodes(true);
17744                     }
17745                 }.createDelegate(this));
17746                 return;
17747             }else{
17748                 this.ui.collapse();
17749                 this.fireEvent("collapse", this);
17750             }
17751         }
17752         if(deep === true){
17753             var cs = this.childNodes;
17754             for(var i = 0, len = cs.length; i < len; i++) {
17755                 cs[i].collapse(true, false);
17756             }
17757         }
17758     },
17759
17760     // private
17761     delayedExpand : function(delay){
17762         if(!this.expandProcId){
17763             this.expandProcId = this.expand.defer(delay, this);
17764         }
17765     },
17766
17767     // private
17768     cancelExpand : function(){
17769         if(this.expandProcId){
17770             clearTimeout(this.expandProcId);
17771         }
17772         this.expandProcId = false;
17773     },
17774
17775     /**
17776      * Toggles expanded/collapsed state of the node
17777      */
17778     toggle : function(){
17779         if(this.expanded){
17780             this.collapse();
17781         }else{
17782             this.expand();
17783         }
17784     },
17785
17786     /**
17787      * Ensures all parent nodes are expanded
17788      */
17789     ensureVisible : function(callback){
17790         var tree = this.getOwnerTree();
17791         tree.expandPath(this.parentNode.getPath(), false, function(){
17792             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
17793             Roo.callback(callback);
17794         }.createDelegate(this));
17795     },
17796
17797     /**
17798      * Expand all child nodes
17799      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
17800      */
17801     expandChildNodes : function(deep){
17802         var cs = this.childNodes;
17803         for(var i = 0, len = cs.length; i < len; i++) {
17804                 cs[i].expand(deep);
17805         }
17806     },
17807
17808     /**
17809      * Collapse all child nodes
17810      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
17811      */
17812     collapseChildNodes : function(deep){
17813         var cs = this.childNodes;
17814         for(var i = 0, len = cs.length; i < len; i++) {
17815                 cs[i].collapse(deep);
17816         }
17817     },
17818
17819     /**
17820      * Disables this node
17821      */
17822     disable : function(){
17823         this.disabled = true;
17824         this.unselect();
17825         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17826             this.ui.onDisableChange(this, true);
17827         }
17828         this.fireEvent("disabledchange", this, true);
17829     },
17830
17831     /**
17832      * Enables this node
17833      */
17834     enable : function(){
17835         this.disabled = false;
17836         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17837             this.ui.onDisableChange(this, false);
17838         }
17839         this.fireEvent("disabledchange", this, false);
17840     },
17841
17842     // private
17843     renderChildren : function(suppressEvent){
17844         if(suppressEvent !== false){
17845             this.fireEvent("beforechildrenrendered", this);
17846         }
17847         var cs = this.childNodes;
17848         for(var i = 0, len = cs.length; i < len; i++){
17849             cs[i].render(true);
17850         }
17851         this.childrenRendered = true;
17852     },
17853
17854     // private
17855     sort : function(fn, scope){
17856         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
17857         if(this.childrenRendered){
17858             var cs = this.childNodes;
17859             for(var i = 0, len = cs.length; i < len; i++){
17860                 cs[i].render(true);
17861             }
17862         }
17863     },
17864
17865     // private
17866     render : function(bulkRender){
17867         this.ui.render(bulkRender);
17868         if(!this.rendered){
17869             this.rendered = true;
17870             if(this.expanded){
17871                 this.expanded = false;
17872                 this.expand(false, false);
17873             }
17874         }
17875     },
17876
17877     // private
17878     renderIndent : function(deep, refresh){
17879         if(refresh){
17880             this.ui.childIndent = null;
17881         }
17882         this.ui.renderIndent();
17883         if(deep === true && this.childrenRendered){
17884             var cs = this.childNodes;
17885             for(var i = 0, len = cs.length; i < len; i++){
17886                 cs[i].renderIndent(true, refresh);
17887             }
17888         }
17889     }
17890 });/*
17891  * Based on:
17892  * Ext JS Library 1.1.1
17893  * Copyright(c) 2006-2007, Ext JS, LLC.
17894  *
17895  * Originally Released Under LGPL - original licence link has changed is not relivant.
17896  *
17897  * Fork - LGPL
17898  * <script type="text/javascript">
17899  */
17900  
17901 /**
17902  * @class Roo.tree.AsyncTreeNode
17903  * @extends Roo.tree.TreeNode
17904  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
17905  * @constructor
17906  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
17907  */
17908  Roo.tree.AsyncTreeNode = function(config){
17909     this.loaded = false;
17910     this.loading = false;
17911     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
17912     /**
17913     * @event beforeload
17914     * Fires before this node is loaded, return false to cancel
17915     * @param {Node} this This node
17916     */
17917     this.addEvents({'beforeload':true, 'load': true});
17918     /**
17919     * @event load
17920     * Fires when this node is loaded
17921     * @param {Node} this This node
17922     */
17923     /**
17924      * The loader used by this node (defaults to using the tree's defined loader)
17925      * @type TreeLoader
17926      * @property loader
17927      */
17928 };
17929 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
17930     expand : function(deep, anim, callback){
17931         if(this.loading){ // if an async load is already running, waiting til it's done
17932             var timer;
17933             var f = function(){
17934                 if(!this.loading){ // done loading
17935                     clearInterval(timer);
17936                     this.expand(deep, anim, callback);
17937                 }
17938             }.createDelegate(this);
17939             timer = setInterval(f, 200);
17940             return;
17941         }
17942         if(!this.loaded){
17943             if(this.fireEvent("beforeload", this) === false){
17944                 return;
17945             }
17946             this.loading = true;
17947             this.ui.beforeLoad(this);
17948             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
17949             if(loader){
17950                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
17951                 return;
17952             }
17953         }
17954         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
17955     },
17956     
17957     /**
17958      * Returns true if this node is currently loading
17959      * @return {Boolean}
17960      */
17961     isLoading : function(){
17962         return this.loading;  
17963     },
17964     
17965     loadComplete : function(deep, anim, callback){
17966         this.loading = false;
17967         this.loaded = true;
17968         this.ui.afterLoad(this);
17969         this.fireEvent("load", this);
17970         this.expand(deep, anim, callback);
17971     },
17972     
17973     /**
17974      * Returns true if this node has been loaded
17975      * @return {Boolean}
17976      */
17977     isLoaded : function(){
17978         return this.loaded;
17979     },
17980     
17981     hasChildNodes : function(){
17982         if(!this.isLeaf() && !this.loaded){
17983             return true;
17984         }else{
17985             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
17986         }
17987     },
17988
17989     /**
17990      * Trigger a reload for this node
17991      * @param {Function} callback
17992      */
17993     reload : function(callback){
17994         this.collapse(false, false);
17995         while(this.firstChild){
17996             this.removeChild(this.firstChild);
17997         }
17998         this.childrenRendered = false;
17999         this.loaded = false;
18000         if(this.isHiddenRoot()){
18001             this.expanded = false;
18002         }
18003         this.expand(false, false, callback);
18004     }
18005 });/*
18006  * Based on:
18007  * Ext JS Library 1.1.1
18008  * Copyright(c) 2006-2007, Ext JS, LLC.
18009  *
18010  * Originally Released Under LGPL - original licence link has changed is not relivant.
18011  *
18012  * Fork - LGPL
18013  * <script type="text/javascript">
18014  */
18015  
18016 /**
18017  * @class Roo.tree.TreeNodeUI
18018  * @constructor
18019  * @param {Object} node The node to render
18020  * The TreeNode UI implementation is separate from the
18021  * tree implementation. Unless you are customizing the tree UI,
18022  * you should never have to use this directly.
18023  */
18024 Roo.tree.TreeNodeUI = function(node){
18025     this.node = node;
18026     this.rendered = false;
18027     this.animating = false;
18028     this.emptyIcon = Roo.BLANK_IMAGE_URL;
18029 };
18030
18031 Roo.tree.TreeNodeUI.prototype = {
18032     removeChild : function(node){
18033         if(this.rendered){
18034             this.ctNode.removeChild(node.ui.getEl());
18035         }
18036     },
18037
18038     beforeLoad : function(){
18039          this.addClass("x-tree-node-loading");
18040     },
18041
18042     afterLoad : function(){
18043          this.removeClass("x-tree-node-loading");
18044     },
18045
18046     onTextChange : function(node, text, oldText){
18047         if(this.rendered){
18048             this.textNode.innerHTML = text;
18049         }
18050     },
18051
18052     onDisableChange : function(node, state){
18053         this.disabled = state;
18054         if(state){
18055             this.addClass("x-tree-node-disabled");
18056         }else{
18057             this.removeClass("x-tree-node-disabled");
18058         }
18059     },
18060
18061     onSelectedChange : function(state){
18062         if(state){
18063             this.focus();
18064             this.addClass("x-tree-selected");
18065         }else{
18066             //this.blur();
18067             this.removeClass("x-tree-selected");
18068         }
18069     },
18070
18071     onMove : function(tree, node, oldParent, newParent, index, refNode){
18072         this.childIndent = null;
18073         if(this.rendered){
18074             var targetNode = newParent.ui.getContainer();
18075             if(!targetNode){//target not rendered
18076                 this.holder = document.createElement("div");
18077                 this.holder.appendChild(this.wrap);
18078                 return;
18079             }
18080             var insertBefore = refNode ? refNode.ui.getEl() : null;
18081             if(insertBefore){
18082                 targetNode.insertBefore(this.wrap, insertBefore);
18083             }else{
18084                 targetNode.appendChild(this.wrap);
18085             }
18086             this.node.renderIndent(true);
18087         }
18088     },
18089
18090     addClass : function(cls){
18091         if(this.elNode){
18092             Roo.fly(this.elNode).addClass(cls);
18093         }
18094     },
18095
18096     removeClass : function(cls){
18097         if(this.elNode){
18098             Roo.fly(this.elNode).removeClass(cls);
18099         }
18100     },
18101
18102     remove : function(){
18103         if(this.rendered){
18104             this.holder = document.createElement("div");
18105             this.holder.appendChild(this.wrap);
18106         }
18107     },
18108
18109     fireEvent : function(){
18110         return this.node.fireEvent.apply(this.node, arguments);
18111     },
18112
18113     initEvents : function(){
18114         this.node.on("move", this.onMove, this);
18115         var E = Roo.EventManager;
18116         var a = this.anchor;
18117
18118         var el = Roo.fly(a, '_treeui');
18119
18120         if(Roo.isOpera){ // opera render bug ignores the CSS
18121             el.setStyle("text-decoration", "none");
18122         }
18123
18124         el.on("click", this.onClick, this);
18125         el.on("dblclick", this.onDblClick, this);
18126
18127         if(this.checkbox){
18128             Roo.EventManager.on(this.checkbox,
18129                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
18130         }
18131
18132         el.on("contextmenu", this.onContextMenu, this);
18133
18134         var icon = Roo.fly(this.iconNode);
18135         icon.on("click", this.onClick, this);
18136         icon.on("dblclick", this.onDblClick, this);
18137         icon.on("contextmenu", this.onContextMenu, this);
18138         E.on(this.ecNode, "click", this.ecClick, this, true);
18139
18140         if(this.node.disabled){
18141             this.addClass("x-tree-node-disabled");
18142         }
18143         if(this.node.hidden){
18144             this.addClass("x-tree-node-disabled");
18145         }
18146         var ot = this.node.getOwnerTree();
18147         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
18148         if(dd && (!this.node.isRoot || ot.rootVisible)){
18149             Roo.dd.Registry.register(this.elNode, {
18150                 node: this.node,
18151                 handles: this.getDDHandles(),
18152                 isHandle: false
18153             });
18154         }
18155     },
18156
18157     getDDHandles : function(){
18158         return [this.iconNode, this.textNode];
18159     },
18160
18161     hide : function(){
18162         if(this.rendered){
18163             this.wrap.style.display = "none";
18164         }
18165     },
18166
18167     show : function(){
18168         if(this.rendered){
18169             this.wrap.style.display = "";
18170         }
18171     },
18172
18173     onContextMenu : function(e){
18174         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
18175             e.preventDefault();
18176             this.focus();
18177             this.fireEvent("contextmenu", this.node, e);
18178         }
18179     },
18180
18181     onClick : function(e){
18182         if(this.dropping){
18183             e.stopEvent();
18184             return;
18185         }
18186         if(this.fireEvent("beforeclick", this.node, e) !== false){
18187             if(!this.disabled && this.node.attributes.href){
18188                 this.fireEvent("click", this.node, e);
18189                 return;
18190             }
18191             e.preventDefault();
18192             if(this.disabled){
18193                 return;
18194             }
18195
18196             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
18197                 this.node.toggle();
18198             }
18199
18200             this.fireEvent("click", this.node, e);
18201         }else{
18202             e.stopEvent();
18203         }
18204     },
18205
18206     onDblClick : function(e){
18207         e.preventDefault();
18208         if(this.disabled){
18209             return;
18210         }
18211         if(this.checkbox){
18212             this.toggleCheck();
18213         }
18214         if(!this.animating && this.node.hasChildNodes()){
18215             this.node.toggle();
18216         }
18217         this.fireEvent("dblclick", this.node, e);
18218     },
18219
18220     onCheckChange : function(){
18221         var checked = this.checkbox.checked;
18222         this.node.attributes.checked = checked;
18223         this.fireEvent('checkchange', this.node, checked);
18224     },
18225
18226     ecClick : function(e){
18227         if(!this.animating && this.node.hasChildNodes()){
18228             this.node.toggle();
18229         }
18230     },
18231
18232     startDrop : function(){
18233         this.dropping = true;
18234     },
18235
18236     // delayed drop so the click event doesn't get fired on a drop
18237     endDrop : function(){
18238        setTimeout(function(){
18239            this.dropping = false;
18240        }.createDelegate(this), 50);
18241     },
18242
18243     expand : function(){
18244         this.updateExpandIcon();
18245         this.ctNode.style.display = "";
18246     },
18247
18248     focus : function(){
18249         if(!this.node.preventHScroll){
18250             try{this.anchor.focus();
18251             }catch(e){}
18252         }else if(!Roo.isIE){
18253             try{
18254                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
18255                 var l = noscroll.scrollLeft;
18256                 this.anchor.focus();
18257                 noscroll.scrollLeft = l;
18258             }catch(e){}
18259         }
18260     },
18261
18262     toggleCheck : function(value){
18263         var cb = this.checkbox;
18264         if(cb){
18265             cb.checked = (value === undefined ? !cb.checked : value);
18266         }
18267     },
18268
18269     blur : function(){
18270         try{
18271             this.anchor.blur();
18272         }catch(e){}
18273     },
18274
18275     animExpand : function(callback){
18276         var ct = Roo.get(this.ctNode);
18277         ct.stopFx();
18278         if(!this.node.hasChildNodes()){
18279             this.updateExpandIcon();
18280             this.ctNode.style.display = "";
18281             Roo.callback(callback);
18282             return;
18283         }
18284         this.animating = true;
18285         this.updateExpandIcon();
18286
18287         ct.slideIn('t', {
18288            callback : function(){
18289                this.animating = false;
18290                Roo.callback(callback);
18291             },
18292             scope: this,
18293             duration: this.node.ownerTree.duration || .25
18294         });
18295     },
18296
18297     highlight : function(){
18298         var tree = this.node.getOwnerTree();
18299         Roo.fly(this.wrap).highlight(
18300             tree.hlColor || "C3DAF9",
18301             {endColor: tree.hlBaseColor}
18302         );
18303     },
18304
18305     collapse : function(){
18306         this.updateExpandIcon();
18307         this.ctNode.style.display = "none";
18308     },
18309
18310     animCollapse : function(callback){
18311         var ct = Roo.get(this.ctNode);
18312         ct.enableDisplayMode('block');
18313         ct.stopFx();
18314
18315         this.animating = true;
18316         this.updateExpandIcon();
18317
18318         ct.slideOut('t', {
18319             callback : function(){
18320                this.animating = false;
18321                Roo.callback(callback);
18322             },
18323             scope: this,
18324             duration: this.node.ownerTree.duration || .25
18325         });
18326     },
18327
18328     getContainer : function(){
18329         return this.ctNode;
18330     },
18331
18332     getEl : function(){
18333         return this.wrap;
18334     },
18335
18336     appendDDGhost : function(ghostNode){
18337         ghostNode.appendChild(this.elNode.cloneNode(true));
18338     },
18339
18340     getDDRepairXY : function(){
18341         return Roo.lib.Dom.getXY(this.iconNode);
18342     },
18343
18344     onRender : function(){
18345         this.render();
18346     },
18347
18348     render : function(bulkRender){
18349         var n = this.node, a = n.attributes;
18350         var targetNode = n.parentNode ?
18351               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
18352
18353         if(!this.rendered){
18354             this.rendered = true;
18355
18356             this.renderElements(n, a, targetNode, bulkRender);
18357
18358             if(a.qtip){
18359                if(this.textNode.setAttributeNS){
18360                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
18361                    if(a.qtipTitle){
18362                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
18363                    }
18364                }else{
18365                    this.textNode.setAttribute("ext:qtip", a.qtip);
18366                    if(a.qtipTitle){
18367                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
18368                    }
18369                }
18370             }else if(a.qtipCfg){
18371                 a.qtipCfg.target = Roo.id(this.textNode);
18372                 Roo.QuickTips.register(a.qtipCfg);
18373             }
18374             this.initEvents();
18375             if(!this.node.expanded){
18376                 this.updateExpandIcon();
18377             }
18378         }else{
18379             if(bulkRender === true) {
18380                 targetNode.appendChild(this.wrap);
18381             }
18382         }
18383     },
18384
18385     renderElements : function(n, a, targetNode, bulkRender)
18386     {
18387         // add some indent caching, this helps performance when rendering a large tree
18388         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
18389         var t = n.getOwnerTree();
18390         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
18391         if (typeof(n.attributes.html) != 'undefined') {
18392             txt = n.attributes.html;
18393         }
18394         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
18395         var cb = typeof a.checked == 'boolean';
18396         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
18397         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
18398             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
18399             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
18400             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
18401             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
18402             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
18403              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
18404                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
18405             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
18406             "</li>"];
18407
18408         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
18409             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
18410                                 n.nextSibling.ui.getEl(), buf.join(""));
18411         }else{
18412             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
18413         }
18414
18415         this.elNode = this.wrap.childNodes[0];
18416         this.ctNode = this.wrap.childNodes[1];
18417         var cs = this.elNode.childNodes;
18418         this.indentNode = cs[0];
18419         this.ecNode = cs[1];
18420         this.iconNode = cs[2];
18421         var index = 3;
18422         if(cb){
18423             this.checkbox = cs[3];
18424             index++;
18425         }
18426         this.anchor = cs[index];
18427         this.textNode = cs[index].firstChild;
18428     },
18429
18430     getAnchor : function(){
18431         return this.anchor;
18432     },
18433
18434     getTextEl : function(){
18435         return this.textNode;
18436     },
18437
18438     getIconEl : function(){
18439         return this.iconNode;
18440     },
18441
18442     isChecked : function(){
18443         return this.checkbox ? this.checkbox.checked : false;
18444     },
18445
18446     updateExpandIcon : function(){
18447         if(this.rendered){
18448             var n = this.node, c1, c2;
18449             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
18450             var hasChild = n.hasChildNodes();
18451             if(hasChild){
18452                 if(n.expanded){
18453                     cls += "-minus";
18454                     c1 = "x-tree-node-collapsed";
18455                     c2 = "x-tree-node-expanded";
18456                 }else{
18457                     cls += "-plus";
18458                     c1 = "x-tree-node-expanded";
18459                     c2 = "x-tree-node-collapsed";
18460                 }
18461                 if(this.wasLeaf){
18462                     this.removeClass("x-tree-node-leaf");
18463                     this.wasLeaf = false;
18464                 }
18465                 if(this.c1 != c1 || this.c2 != c2){
18466                     Roo.fly(this.elNode).replaceClass(c1, c2);
18467                     this.c1 = c1; this.c2 = c2;
18468                 }
18469             }else{
18470                 // this changes non-leafs into leafs if they have no children.
18471                 // it's not very rational behaviour..
18472                 
18473                 if(!this.wasLeaf && this.node.leaf){
18474                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
18475                     delete this.c1;
18476                     delete this.c2;
18477                     this.wasLeaf = true;
18478                 }
18479             }
18480             var ecc = "x-tree-ec-icon "+cls;
18481             if(this.ecc != ecc){
18482                 this.ecNode.className = ecc;
18483                 this.ecc = ecc;
18484             }
18485         }
18486     },
18487
18488     getChildIndent : function(){
18489         if(!this.childIndent){
18490             var buf = [];
18491             var p = this.node;
18492             while(p){
18493                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
18494                     if(!p.isLast()) {
18495                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
18496                     } else {
18497                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
18498                     }
18499                 }
18500                 p = p.parentNode;
18501             }
18502             this.childIndent = buf.join("");
18503         }
18504         return this.childIndent;
18505     },
18506
18507     renderIndent : function(){
18508         if(this.rendered){
18509             var indent = "";
18510             var p = this.node.parentNode;
18511             if(p){
18512                 indent = p.ui.getChildIndent();
18513             }
18514             if(this.indentMarkup != indent){ // don't rerender if not required
18515                 this.indentNode.innerHTML = indent;
18516                 this.indentMarkup = indent;
18517             }
18518             this.updateExpandIcon();
18519         }
18520     }
18521 };
18522
18523 Roo.tree.RootTreeNodeUI = function(){
18524     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
18525 };
18526 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
18527     render : function(){
18528         if(!this.rendered){
18529             var targetNode = this.node.ownerTree.innerCt.dom;
18530             this.node.expanded = true;
18531             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
18532             this.wrap = this.ctNode = targetNode.firstChild;
18533         }
18534     },
18535     collapse : function(){
18536     },
18537     expand : function(){
18538     }
18539 });/*
18540  * Based on:
18541  * Ext JS Library 1.1.1
18542  * Copyright(c) 2006-2007, Ext JS, LLC.
18543  *
18544  * Originally Released Under LGPL - original licence link has changed is not relivant.
18545  *
18546  * Fork - LGPL
18547  * <script type="text/javascript">
18548  */
18549 /**
18550  * @class Roo.tree.TreeLoader
18551  * @extends Roo.util.Observable
18552  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
18553  * nodes from a specified URL. The response must be a javascript Array definition
18554  * who's elements are node definition objects. eg:
18555  * <pre><code>
18556 {  success : true,
18557    data :      [
18558    
18559     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
18560     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
18561     ]
18562 }
18563
18564
18565 </code></pre>
18566  * <br><br>
18567  * The old style respose with just an array is still supported, but not recommended.
18568  * <br><br>
18569  *
18570  * A server request is sent, and child nodes are loaded only when a node is expanded.
18571  * The loading node's id is passed to the server under the parameter name "node" to
18572  * enable the server to produce the correct child nodes.
18573  * <br><br>
18574  * To pass extra parameters, an event handler may be attached to the "beforeload"
18575  * event, and the parameters specified in the TreeLoader's baseParams property:
18576  * <pre><code>
18577     myTreeLoader.on("beforeload", function(treeLoader, node) {
18578         this.baseParams.category = node.attributes.category;
18579     }, this);
18580 </code></pre><
18581  * This would pass an HTTP parameter called "category" to the server containing
18582  * the value of the Node's "category" attribute.
18583  * @constructor
18584  * Creates a new Treeloader.
18585  * @param {Object} config A config object containing config properties.
18586  */
18587 Roo.tree.TreeLoader = function(config){
18588     this.baseParams = {};
18589     this.requestMethod = "POST";
18590     Roo.apply(this, config);
18591
18592     this.addEvents({
18593     
18594         /**
18595          * @event beforeload
18596          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
18597          * @param {Object} This TreeLoader object.
18598          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18599          * @param {Object} callback The callback function specified in the {@link #load} call.
18600          */
18601         beforeload : true,
18602         /**
18603          * @event load
18604          * Fires when the node has been successfuly loaded.
18605          * @param {Object} This TreeLoader object.
18606          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18607          * @param {Object} response The response object containing the data from the server.
18608          */
18609         load : true,
18610         /**
18611          * @event loadexception
18612          * Fires if the network request failed.
18613          * @param {Object} This TreeLoader object.
18614          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18615          * @param {Object} response The response object containing the data from the server.
18616          */
18617         loadexception : true,
18618         /**
18619          * @event create
18620          * Fires before a node is created, enabling you to return custom Node types 
18621          * @param {Object} This TreeLoader object.
18622          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
18623          */
18624         create : true
18625     });
18626
18627     Roo.tree.TreeLoader.superclass.constructor.call(this);
18628 };
18629
18630 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
18631     /**
18632     * @cfg {String} dataUrl The URL from which to request a Json string which
18633     * specifies an array of node definition object representing the child nodes
18634     * to be loaded.
18635     */
18636     /**
18637     * @cfg {String} requestMethod either GET or POST
18638     * defaults to POST (due to BC)
18639     * to be loaded.
18640     */
18641     /**
18642     * @cfg {Object} baseParams (optional) An object containing properties which
18643     * specify HTTP parameters to be passed to each request for child nodes.
18644     */
18645     /**
18646     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
18647     * created by this loader. If the attributes sent by the server have an attribute in this object,
18648     * they take priority.
18649     */
18650     /**
18651     * @cfg {Object} uiProviders (optional) An object containing properties which
18652     * 
18653     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
18654     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
18655     * <i>uiProvider</i> attribute of a returned child node is a string rather
18656     * than a reference to a TreeNodeUI implementation, this that string value
18657     * is used as a property name in the uiProviders object. You can define the provider named
18658     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
18659     */
18660     uiProviders : {},
18661
18662     /**
18663     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
18664     * child nodes before loading.
18665     */
18666     clearOnLoad : true,
18667
18668     /**
18669     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
18670     * property on loading, rather than expecting an array. (eg. more compatible to a standard
18671     * Grid query { data : [ .....] }
18672     */
18673     
18674     root : false,
18675      /**
18676     * @cfg {String} queryParam (optional) 
18677     * Name of the query as it will be passed on the querystring (defaults to 'node')
18678     * eg. the request will be ?node=[id]
18679     */
18680     
18681     
18682     queryParam: false,
18683     
18684     /**
18685      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
18686      * This is called automatically when a node is expanded, but may be used to reload
18687      * a node (or append new children if the {@link #clearOnLoad} option is false.)
18688      * @param {Roo.tree.TreeNode} node
18689      * @param {Function} callback
18690      */
18691     load : function(node, callback){
18692         if(this.clearOnLoad){
18693             while(node.firstChild){
18694                 node.removeChild(node.firstChild);
18695             }
18696         }
18697         if(node.attributes.children){ // preloaded json children
18698             var cs = node.attributes.children;
18699             for(var i = 0, len = cs.length; i < len; i++){
18700                 node.appendChild(this.createNode(cs[i]));
18701             }
18702             if(typeof callback == "function"){
18703                 callback();
18704             }
18705         }else if(this.dataUrl){
18706             this.requestData(node, callback);
18707         }
18708     },
18709
18710     getParams: function(node){
18711         var buf = [], bp = this.baseParams;
18712         for(var key in bp){
18713             if(typeof bp[key] != "function"){
18714                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
18715             }
18716         }
18717         var n = this.queryParam === false ? 'node' : this.queryParam;
18718         buf.push(n + "=", encodeURIComponent(node.id));
18719         return buf.join("");
18720     },
18721
18722     requestData : function(node, callback){
18723         if(this.fireEvent("beforeload", this, node, callback) !== false){
18724             this.transId = Roo.Ajax.request({
18725                 method:this.requestMethod,
18726                 url: this.dataUrl||this.url,
18727                 success: this.handleResponse,
18728                 failure: this.handleFailure,
18729                 scope: this,
18730                 argument: {callback: callback, node: node},
18731                 params: this.getParams(node)
18732             });
18733         }else{
18734             // if the load is cancelled, make sure we notify
18735             // the node that we are done
18736             if(typeof callback == "function"){
18737                 callback();
18738             }
18739         }
18740     },
18741
18742     isLoading : function(){
18743         return this.transId ? true : false;
18744     },
18745
18746     abort : function(){
18747         if(this.isLoading()){
18748             Roo.Ajax.abort(this.transId);
18749         }
18750     },
18751
18752     // private
18753     createNode : function(attr)
18754     {
18755         // apply baseAttrs, nice idea Corey!
18756         if(this.baseAttrs){
18757             Roo.applyIf(attr, this.baseAttrs);
18758         }
18759         if(this.applyLoader !== false){
18760             attr.loader = this;
18761         }
18762         // uiProvider = depreciated..
18763         
18764         if(typeof(attr.uiProvider) == 'string'){
18765            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
18766                 /**  eval:var:attr */ eval(attr.uiProvider);
18767         }
18768         if(typeof(this.uiProviders['default']) != 'undefined') {
18769             attr.uiProvider = this.uiProviders['default'];
18770         }
18771         
18772         this.fireEvent('create', this, attr);
18773         
18774         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
18775         return(attr.leaf ?
18776                         new Roo.tree.TreeNode(attr) :
18777                         new Roo.tree.AsyncTreeNode(attr));
18778     },
18779
18780     processResponse : function(response, node, callback)
18781     {
18782         var json = response.responseText;
18783         try {
18784             
18785             var o = Roo.decode(json);
18786             
18787             if (this.root === false && typeof(o.success) != undefined) {
18788                 this.root = 'data'; // the default behaviour for list like data..
18789                 }
18790                 
18791             if (this.root !== false &&  !o.success) {
18792                 // it's a failure condition.
18793                 var a = response.argument;
18794                 this.fireEvent("loadexception", this, a.node, response);
18795                 Roo.log("Load failed - should have a handler really");
18796                 return;
18797             }
18798             
18799             
18800             
18801             if (this.root !== false) {
18802                  o = o[this.root];
18803             }
18804             
18805             for(var i = 0, len = o.length; i < len; i++){
18806                 var n = this.createNode(o[i]);
18807                 if(n){
18808                     node.appendChild(n);
18809                 }
18810             }
18811             if(typeof callback == "function"){
18812                 callback(this, node);
18813             }
18814         }catch(e){
18815             this.handleFailure(response);
18816         }
18817     },
18818
18819     handleResponse : function(response){
18820         this.transId = false;
18821         var a = response.argument;
18822         this.processResponse(response, a.node, a.callback);
18823         this.fireEvent("load", this, a.node, response);
18824     },
18825
18826     handleFailure : function(response)
18827     {
18828         // should handle failure better..
18829         this.transId = false;
18830         var a = response.argument;
18831         this.fireEvent("loadexception", this, a.node, response);
18832         if(typeof a.callback == "function"){
18833             a.callback(this, a.node);
18834         }
18835     }
18836 });/*
18837  * Based on:
18838  * Ext JS Library 1.1.1
18839  * Copyright(c) 2006-2007, Ext JS, LLC.
18840  *
18841  * Originally Released Under LGPL - original licence link has changed is not relivant.
18842  *
18843  * Fork - LGPL
18844  * <script type="text/javascript">
18845  */
18846
18847 /**
18848 * @class Roo.tree.TreeFilter
18849 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
18850 * @param {TreePanel} tree
18851 * @param {Object} config (optional)
18852  */
18853 Roo.tree.TreeFilter = function(tree, config){
18854     this.tree = tree;
18855     this.filtered = {};
18856     Roo.apply(this, config);
18857 };
18858
18859 Roo.tree.TreeFilter.prototype = {
18860     clearBlank:false,
18861     reverse:false,
18862     autoClear:false,
18863     remove:false,
18864
18865      /**
18866      * Filter the data by a specific attribute.
18867      * @param {String/RegExp} value Either string that the attribute value
18868      * should start with or a RegExp to test against the attribute
18869      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
18870      * @param {TreeNode} startNode (optional) The node to start the filter at.
18871      */
18872     filter : function(value, attr, startNode){
18873         attr = attr || "text";
18874         var f;
18875         if(typeof value == "string"){
18876             var vlen = value.length;
18877             // auto clear empty filter
18878             if(vlen == 0 && this.clearBlank){
18879                 this.clear();
18880                 return;
18881             }
18882             value = value.toLowerCase();
18883             f = function(n){
18884                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
18885             };
18886         }else if(value.exec){ // regex?
18887             f = function(n){
18888                 return value.test(n.attributes[attr]);
18889             };
18890         }else{
18891             throw 'Illegal filter type, must be string or regex';
18892         }
18893         this.filterBy(f, null, startNode);
18894         },
18895
18896     /**
18897      * Filter by a function. The passed function will be called with each
18898      * node in the tree (or from the startNode). If the function returns true, the node is kept
18899      * otherwise it is filtered. If a node is filtered, its children are also filtered.
18900      * @param {Function} fn The filter function
18901      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
18902      */
18903     filterBy : function(fn, scope, startNode){
18904         startNode = startNode || this.tree.root;
18905         if(this.autoClear){
18906             this.clear();
18907         }
18908         var af = this.filtered, rv = this.reverse;
18909         var f = function(n){
18910             if(n == startNode){
18911                 return true;
18912             }
18913             if(af[n.id]){
18914                 return false;
18915             }
18916             var m = fn.call(scope || n, n);
18917             if(!m || rv){
18918                 af[n.id] = n;
18919                 n.ui.hide();
18920                 return false;
18921             }
18922             return true;
18923         };
18924         startNode.cascade(f);
18925         if(this.remove){
18926            for(var id in af){
18927                if(typeof id != "function"){
18928                    var n = af[id];
18929                    if(n && n.parentNode){
18930                        n.parentNode.removeChild(n);
18931                    }
18932                }
18933            }
18934         }
18935     },
18936
18937     /**
18938      * Clears the current filter. Note: with the "remove" option
18939      * set a filter cannot be cleared.
18940      */
18941     clear : function(){
18942         var t = this.tree;
18943         var af = this.filtered;
18944         for(var id in af){
18945             if(typeof id != "function"){
18946                 var n = af[id];
18947                 if(n){
18948                     n.ui.show();
18949                 }
18950             }
18951         }
18952         this.filtered = {};
18953     }
18954 };
18955 /*
18956  * Based on:
18957  * Ext JS Library 1.1.1
18958  * Copyright(c) 2006-2007, Ext JS, LLC.
18959  *
18960  * Originally Released Under LGPL - original licence link has changed is not relivant.
18961  *
18962  * Fork - LGPL
18963  * <script type="text/javascript">
18964  */
18965  
18966
18967 /**
18968  * @class Roo.tree.TreeSorter
18969  * Provides sorting of nodes in a TreePanel
18970  * 
18971  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
18972  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
18973  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
18974  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
18975  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
18976  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
18977  * @constructor
18978  * @param {TreePanel} tree
18979  * @param {Object} config
18980  */
18981 Roo.tree.TreeSorter = function(tree, config){
18982     Roo.apply(this, config);
18983     tree.on("beforechildrenrendered", this.doSort, this);
18984     tree.on("append", this.updateSort, this);
18985     tree.on("insert", this.updateSort, this);
18986     
18987     var dsc = this.dir && this.dir.toLowerCase() == "desc";
18988     var p = this.property || "text";
18989     var sortType = this.sortType;
18990     var fs = this.folderSort;
18991     var cs = this.caseSensitive === true;
18992     var leafAttr = this.leafAttr || 'leaf';
18993
18994     this.sortFn = function(n1, n2){
18995         if(fs){
18996             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
18997                 return 1;
18998             }
18999             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
19000                 return -1;
19001             }
19002         }
19003         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
19004         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
19005         if(v1 < v2){
19006                         return dsc ? +1 : -1;
19007                 }else if(v1 > v2){
19008                         return dsc ? -1 : +1;
19009         }else{
19010                 return 0;
19011         }
19012     };
19013 };
19014
19015 Roo.tree.TreeSorter.prototype = {
19016     doSort : function(node){
19017         node.sort(this.sortFn);
19018     },
19019     
19020     compareNodes : function(n1, n2){
19021         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
19022     },
19023     
19024     updateSort : function(tree, node){
19025         if(node.childrenRendered){
19026             this.doSort.defer(1, this, [node]);
19027         }
19028     }
19029 };/*
19030  * Based on:
19031  * Ext JS Library 1.1.1
19032  * Copyright(c) 2006-2007, Ext JS, LLC.
19033  *
19034  * Originally Released Under LGPL - original licence link has changed is not relivant.
19035  *
19036  * Fork - LGPL
19037  * <script type="text/javascript">
19038  */
19039
19040 if(Roo.dd.DropZone){
19041     
19042 Roo.tree.TreeDropZone = function(tree, config){
19043     this.allowParentInsert = false;
19044     this.allowContainerDrop = false;
19045     this.appendOnly = false;
19046     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
19047     this.tree = tree;
19048     this.lastInsertClass = "x-tree-no-status";
19049     this.dragOverData = {};
19050 };
19051
19052 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
19053     ddGroup : "TreeDD",
19054     scroll:  true,
19055     
19056     expandDelay : 1000,
19057     
19058     expandNode : function(node){
19059         if(node.hasChildNodes() && !node.isExpanded()){
19060             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
19061         }
19062     },
19063     
19064     queueExpand : function(node){
19065         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
19066     },
19067     
19068     cancelExpand : function(){
19069         if(this.expandProcId){
19070             clearTimeout(this.expandProcId);
19071             this.expandProcId = false;
19072         }
19073     },
19074     
19075     isValidDropPoint : function(n, pt, dd, e, data){
19076         if(!n || !data){ return false; }
19077         var targetNode = n.node;
19078         var dropNode = data.node;
19079         // default drop rules
19080         if(!(targetNode && targetNode.isTarget && pt)){
19081             return false;
19082         }
19083         if(pt == "append" && targetNode.allowChildren === false){
19084             return false;
19085         }
19086         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
19087             return false;
19088         }
19089         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
19090             return false;
19091         }
19092         // reuse the object
19093         var overEvent = this.dragOverData;
19094         overEvent.tree = this.tree;
19095         overEvent.target = targetNode;
19096         overEvent.data = data;
19097         overEvent.point = pt;
19098         overEvent.source = dd;
19099         overEvent.rawEvent = e;
19100         overEvent.dropNode = dropNode;
19101         overEvent.cancel = false;  
19102         var result = this.tree.fireEvent("nodedragover", overEvent);
19103         return overEvent.cancel === false && result !== false;
19104     },
19105     
19106     getDropPoint : function(e, n, dd)
19107     {
19108         var tn = n.node;
19109         if(tn.isRoot){
19110             return tn.allowChildren !== false ? "append" : false; // always append for root
19111         }
19112         var dragEl = n.ddel;
19113         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
19114         var y = Roo.lib.Event.getPageY(e);
19115         //var noAppend = tn.allowChildren === false || tn.isLeaf();
19116         
19117         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
19118         var noAppend = tn.allowChildren === false;
19119         if(this.appendOnly || tn.parentNode.allowChildren === false){
19120             return noAppend ? false : "append";
19121         }
19122         var noBelow = false;
19123         if(!this.allowParentInsert){
19124             noBelow = tn.hasChildNodes() && tn.isExpanded();
19125         }
19126         var q = (b - t) / (noAppend ? 2 : 3);
19127         if(y >= t && y < (t + q)){
19128             return "above";
19129         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
19130             return "below";
19131         }else{
19132             return "append";
19133         }
19134     },
19135     
19136     onNodeEnter : function(n, dd, e, data)
19137     {
19138         this.cancelExpand();
19139     },
19140     
19141     onNodeOver : function(n, dd, e, data)
19142     {
19143        
19144         var pt = this.getDropPoint(e, n, dd);
19145         var node = n.node;
19146         
19147         // auto node expand check
19148         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
19149             this.queueExpand(node);
19150         }else if(pt != "append"){
19151             this.cancelExpand();
19152         }
19153         
19154         // set the insert point style on the target node
19155         var returnCls = this.dropNotAllowed;
19156         if(this.isValidDropPoint(n, pt, dd, e, data)){
19157            if(pt){
19158                var el = n.ddel;
19159                var cls;
19160                if(pt == "above"){
19161                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
19162                    cls = "x-tree-drag-insert-above";
19163                }else if(pt == "below"){
19164                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
19165                    cls = "x-tree-drag-insert-below";
19166                }else{
19167                    returnCls = "x-tree-drop-ok-append";
19168                    cls = "x-tree-drag-append";
19169                }
19170                if(this.lastInsertClass != cls){
19171                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
19172                    this.lastInsertClass = cls;
19173                }
19174            }
19175        }
19176        return returnCls;
19177     },
19178     
19179     onNodeOut : function(n, dd, e, data){
19180         
19181         this.cancelExpand();
19182         this.removeDropIndicators(n);
19183     },
19184     
19185     onNodeDrop : function(n, dd, e, data){
19186         var point = this.getDropPoint(e, n, dd);
19187         var targetNode = n.node;
19188         targetNode.ui.startDrop();
19189         if(!this.isValidDropPoint(n, point, dd, e, data)){
19190             targetNode.ui.endDrop();
19191             return false;
19192         }
19193         // first try to find the drop node
19194         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
19195         var dropEvent = {
19196             tree : this.tree,
19197             target: targetNode,
19198             data: data,
19199             point: point,
19200             source: dd,
19201             rawEvent: e,
19202             dropNode: dropNode,
19203             cancel: !dropNode   
19204         };
19205         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
19206         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
19207             targetNode.ui.endDrop();
19208             return false;
19209         }
19210         // allow target changing
19211         targetNode = dropEvent.target;
19212         if(point == "append" && !targetNode.isExpanded()){
19213             targetNode.expand(false, null, function(){
19214                 this.completeDrop(dropEvent);
19215             }.createDelegate(this));
19216         }else{
19217             this.completeDrop(dropEvent);
19218         }
19219         return true;
19220     },
19221     
19222     completeDrop : function(de){
19223         var ns = de.dropNode, p = de.point, t = de.target;
19224         if(!(ns instanceof Array)){
19225             ns = [ns];
19226         }
19227         var n;
19228         for(var i = 0, len = ns.length; i < len; i++){
19229             n = ns[i];
19230             if(p == "above"){
19231                 t.parentNode.insertBefore(n, t);
19232             }else if(p == "below"){
19233                 t.parentNode.insertBefore(n, t.nextSibling);
19234             }else{
19235                 t.appendChild(n);
19236             }
19237         }
19238         n.ui.focus();
19239         if(this.tree.hlDrop){
19240             n.ui.highlight();
19241         }
19242         t.ui.endDrop();
19243         this.tree.fireEvent("nodedrop", de);
19244     },
19245     
19246     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
19247         if(this.tree.hlDrop){
19248             dropNode.ui.focus();
19249             dropNode.ui.highlight();
19250         }
19251         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
19252     },
19253     
19254     getTree : function(){
19255         return this.tree;
19256     },
19257     
19258     removeDropIndicators : function(n){
19259         if(n && n.ddel){
19260             var el = n.ddel;
19261             Roo.fly(el).removeClass([
19262                     "x-tree-drag-insert-above",
19263                     "x-tree-drag-insert-below",
19264                     "x-tree-drag-append"]);
19265             this.lastInsertClass = "_noclass";
19266         }
19267     },
19268     
19269     beforeDragDrop : function(target, e, id){
19270         this.cancelExpand();
19271         return true;
19272     },
19273     
19274     afterRepair : function(data){
19275         if(data && Roo.enableFx){
19276             data.node.ui.highlight();
19277         }
19278         this.hideProxy();
19279     } 
19280     
19281 });
19282
19283 }
19284 /*
19285  * Based on:
19286  * Ext JS Library 1.1.1
19287  * Copyright(c) 2006-2007, Ext JS, LLC.
19288  *
19289  * Originally Released Under LGPL - original licence link has changed is not relivant.
19290  *
19291  * Fork - LGPL
19292  * <script type="text/javascript">
19293  */
19294  
19295
19296 if(Roo.dd.DragZone){
19297 Roo.tree.TreeDragZone = function(tree, config){
19298     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
19299     this.tree = tree;
19300 };
19301
19302 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
19303     ddGroup : "TreeDD",
19304    
19305     onBeforeDrag : function(data, e){
19306         var n = data.node;
19307         return n && n.draggable && !n.disabled;
19308     },
19309      
19310     
19311     onInitDrag : function(e){
19312         var data = this.dragData;
19313         this.tree.getSelectionModel().select(data.node);
19314         this.proxy.update("");
19315         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
19316         this.tree.fireEvent("startdrag", this.tree, data.node, e);
19317     },
19318     
19319     getRepairXY : function(e, data){
19320         return data.node.ui.getDDRepairXY();
19321     },
19322     
19323     onEndDrag : function(data, e){
19324         this.tree.fireEvent("enddrag", this.tree, data.node, e);
19325         
19326         
19327     },
19328     
19329     onValidDrop : function(dd, e, id){
19330         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
19331         this.hideProxy();
19332     },
19333     
19334     beforeInvalidDrop : function(e, id){
19335         // this scrolls the original position back into view
19336         var sm = this.tree.getSelectionModel();
19337         sm.clearSelections();
19338         sm.select(this.dragData.node);
19339     }
19340 });
19341 }/*
19342  * Based on:
19343  * Ext JS Library 1.1.1
19344  * Copyright(c) 2006-2007, Ext JS, LLC.
19345  *
19346  * Originally Released Under LGPL - original licence link has changed is not relivant.
19347  *
19348  * Fork - LGPL
19349  * <script type="text/javascript">
19350  */
19351 /**
19352  * @class Roo.tree.TreeEditor
19353  * @extends Roo.Editor
19354  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
19355  * as the editor field.
19356  * @constructor
19357  * @param {Object} config (used to be the tree panel.)
19358  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
19359  * 
19360  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
19361  * @cfg {Roo.form.TextField|Object} field The field configuration
19362  *
19363  * 
19364  */
19365 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
19366     var tree = config;
19367     var field;
19368     if (oldconfig) { // old style..
19369         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
19370     } else {
19371         // new style..
19372         tree = config.tree;
19373         config.field = config.field  || {};
19374         config.field.xtype = 'TextField';
19375         field = Roo.factory(config.field, Roo.form);
19376     }
19377     config = config || {};
19378     
19379     
19380     this.addEvents({
19381         /**
19382          * @event beforenodeedit
19383          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
19384          * false from the handler of this event.
19385          * @param {Editor} this
19386          * @param {Roo.tree.Node} node 
19387          */
19388         "beforenodeedit" : true
19389     });
19390     
19391     //Roo.log(config);
19392     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
19393
19394     this.tree = tree;
19395
19396     tree.on('beforeclick', this.beforeNodeClick, this);
19397     tree.getTreeEl().on('mousedown', this.hide, this);
19398     this.on('complete', this.updateNode, this);
19399     this.on('beforestartedit', this.fitToTree, this);
19400     this.on('startedit', this.bindScroll, this, {delay:10});
19401     this.on('specialkey', this.onSpecialKey, this);
19402 };
19403
19404 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
19405     /**
19406      * @cfg {String} alignment
19407      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
19408      */
19409     alignment: "l-l",
19410     // inherit
19411     autoSize: false,
19412     /**
19413      * @cfg {Boolean} hideEl
19414      * True to hide the bound element while the editor is displayed (defaults to false)
19415      */
19416     hideEl : false,
19417     /**
19418      * @cfg {String} cls
19419      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
19420      */
19421     cls: "x-small-editor x-tree-editor",
19422     /**
19423      * @cfg {Boolean} shim
19424      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
19425      */
19426     shim:false,
19427     // inherit
19428     shadow:"frame",
19429     /**
19430      * @cfg {Number} maxWidth
19431      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
19432      * the containing tree element's size, it will be automatically limited for you to the container width, taking
19433      * scroll and client offsets into account prior to each edit.
19434      */
19435     maxWidth: 250,
19436
19437     editDelay : 350,
19438
19439     // private
19440     fitToTree : function(ed, el){
19441         var td = this.tree.getTreeEl().dom, nd = el.dom;
19442         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
19443             td.scrollLeft = nd.offsetLeft;
19444         }
19445         var w = Math.min(
19446                 this.maxWidth,
19447                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
19448         this.setSize(w, '');
19449         
19450         return this.fireEvent('beforenodeedit', this, this.editNode);
19451         
19452     },
19453
19454     // private
19455     triggerEdit : function(node){
19456         this.completeEdit();
19457         this.editNode = node;
19458         this.startEdit(node.ui.textNode, node.text);
19459     },
19460
19461     // private
19462     bindScroll : function(){
19463         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
19464     },
19465
19466     // private
19467     beforeNodeClick : function(node, e){
19468         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
19469         this.lastClick = new Date();
19470         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
19471             e.stopEvent();
19472             this.triggerEdit(node);
19473             return false;
19474         }
19475         return true;
19476     },
19477
19478     // private
19479     updateNode : function(ed, value){
19480         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
19481         this.editNode.setText(value);
19482     },
19483
19484     // private
19485     onHide : function(){
19486         Roo.tree.TreeEditor.superclass.onHide.call(this);
19487         if(this.editNode){
19488             this.editNode.ui.focus();
19489         }
19490     },
19491
19492     // private
19493     onSpecialKey : function(field, e){
19494         var k = e.getKey();
19495         if(k == e.ESC){
19496             e.stopEvent();
19497             this.cancelEdit();
19498         }else if(k == e.ENTER && !e.hasModifier()){
19499             e.stopEvent();
19500             this.completeEdit();
19501         }
19502     }
19503 });//<Script type="text/javascript">
19504 /*
19505  * Based on:
19506  * Ext JS Library 1.1.1
19507  * Copyright(c) 2006-2007, Ext JS, LLC.
19508  *
19509  * Originally Released Under LGPL - original licence link has changed is not relivant.
19510  *
19511  * Fork - LGPL
19512  * <script type="text/javascript">
19513  */
19514  
19515 /**
19516  * Not documented??? - probably should be...
19517  */
19518
19519 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
19520     //focus: Roo.emptyFn, // prevent odd scrolling behavior
19521     
19522     renderElements : function(n, a, targetNode, bulkRender){
19523         //consel.log("renderElements?");
19524         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
19525
19526         var t = n.getOwnerTree();
19527         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
19528         
19529         var cols = t.columns;
19530         var bw = t.borderWidth;
19531         var c = cols[0];
19532         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
19533          var cb = typeof a.checked == "boolean";
19534         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19535         var colcls = 'x-t-' + tid + '-c0';
19536         var buf = [
19537             '<li class="x-tree-node">',
19538             
19539                 
19540                 '<div class="x-tree-node-el ', a.cls,'">',
19541                     // extran...
19542                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
19543                 
19544                 
19545                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
19546                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
19547                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
19548                            (a.icon ? ' x-tree-node-inline-icon' : ''),
19549                            (a.iconCls ? ' '+a.iconCls : ''),
19550                            '" unselectable="on" />',
19551                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
19552                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
19553                              
19554                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19555                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
19556                             '<span unselectable="on" qtip="' + tx + '">',
19557                              tx,
19558                              '</span></a>' ,
19559                     '</div>',
19560                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19561                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
19562                  ];
19563         for(var i = 1, len = cols.length; i < len; i++){
19564             c = cols[i];
19565             colcls = 'x-t-' + tid + '-c' +i;
19566             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19567             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
19568                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
19569                       "</div>");
19570          }
19571          
19572          buf.push(
19573             '</a>',
19574             '<div class="x-clear"></div></div>',
19575             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
19576             "</li>");
19577         
19578         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
19579             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
19580                                 n.nextSibling.ui.getEl(), buf.join(""));
19581         }else{
19582             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
19583         }
19584         var el = this.wrap.firstChild;
19585         this.elRow = el;
19586         this.elNode = el.firstChild;
19587         this.ranchor = el.childNodes[1];
19588         this.ctNode = this.wrap.childNodes[1];
19589         var cs = el.firstChild.childNodes;
19590         this.indentNode = cs[0];
19591         this.ecNode = cs[1];
19592         this.iconNode = cs[2];
19593         var index = 3;
19594         if(cb){
19595             this.checkbox = cs[3];
19596             index++;
19597         }
19598         this.anchor = cs[index];
19599         
19600         this.textNode = cs[index].firstChild;
19601         
19602         //el.on("click", this.onClick, this);
19603         //el.on("dblclick", this.onDblClick, this);
19604         
19605         
19606        // console.log(this);
19607     },
19608     initEvents : function(){
19609         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
19610         
19611             
19612         var a = this.ranchor;
19613
19614         var el = Roo.get(a);
19615
19616         if(Roo.isOpera){ // opera render bug ignores the CSS
19617             el.setStyle("text-decoration", "none");
19618         }
19619
19620         el.on("click", this.onClick, this);
19621         el.on("dblclick", this.onDblClick, this);
19622         el.on("contextmenu", this.onContextMenu, this);
19623         
19624     },
19625     
19626     /*onSelectedChange : function(state){
19627         if(state){
19628             this.focus();
19629             this.addClass("x-tree-selected");
19630         }else{
19631             //this.blur();
19632             this.removeClass("x-tree-selected");
19633         }
19634     },*/
19635     addClass : function(cls){
19636         if(this.elRow){
19637             Roo.fly(this.elRow).addClass(cls);
19638         }
19639         
19640     },
19641     
19642     
19643     removeClass : function(cls){
19644         if(this.elRow){
19645             Roo.fly(this.elRow).removeClass(cls);
19646         }
19647     }
19648
19649     
19650     
19651 });//<Script type="text/javascript">
19652
19653 /*
19654  * Based on:
19655  * Ext JS Library 1.1.1
19656  * Copyright(c) 2006-2007, Ext JS, LLC.
19657  *
19658  * Originally Released Under LGPL - original licence link has changed is not relivant.
19659  *
19660  * Fork - LGPL
19661  * <script type="text/javascript">
19662  */
19663  
19664
19665 /**
19666  * @class Roo.tree.ColumnTree
19667  * @extends Roo.data.TreePanel
19668  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
19669  * @cfg {int} borderWidth  compined right/left border allowance
19670  * @constructor
19671  * @param {String/HTMLElement/Element} el The container element
19672  * @param {Object} config
19673  */
19674 Roo.tree.ColumnTree =  function(el, config)
19675 {
19676    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
19677    this.addEvents({
19678         /**
19679         * @event resize
19680         * Fire this event on a container when it resizes
19681         * @param {int} w Width
19682         * @param {int} h Height
19683         */
19684        "resize" : true
19685     });
19686     this.on('resize', this.onResize, this);
19687 };
19688
19689 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
19690     //lines:false,
19691     
19692     
19693     borderWidth: Roo.isBorderBox ? 0 : 2, 
19694     headEls : false,
19695     
19696     render : function(){
19697         // add the header.....
19698        
19699         Roo.tree.ColumnTree.superclass.render.apply(this);
19700         
19701         this.el.addClass('x-column-tree');
19702         
19703         this.headers = this.el.createChild(
19704             {cls:'x-tree-headers'},this.innerCt.dom);
19705    
19706         var cols = this.columns, c;
19707         var totalWidth = 0;
19708         this.headEls = [];
19709         var  len = cols.length;
19710         for(var i = 0; i < len; i++){
19711              c = cols[i];
19712              totalWidth += c.width;
19713             this.headEls.push(this.headers.createChild({
19714                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
19715                  cn: {
19716                      cls:'x-tree-hd-text',
19717                      html: c.header
19718                  },
19719                  style:'width:'+(c.width-this.borderWidth)+'px;'
19720              }));
19721         }
19722         this.headers.createChild({cls:'x-clear'});
19723         // prevent floats from wrapping when clipped
19724         this.headers.setWidth(totalWidth);
19725         //this.innerCt.setWidth(totalWidth);
19726         this.innerCt.setStyle({ overflow: 'auto' });
19727         this.onResize(this.width, this.height);
19728              
19729         
19730     },
19731     onResize : function(w,h)
19732     {
19733         this.height = h;
19734         this.width = w;
19735         // resize cols..
19736         this.innerCt.setWidth(this.width);
19737         this.innerCt.setHeight(this.height-20);
19738         
19739         // headers...
19740         var cols = this.columns, c;
19741         var totalWidth = 0;
19742         var expEl = false;
19743         var len = cols.length;
19744         for(var i = 0; i < len; i++){
19745             c = cols[i];
19746             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
19747                 // it's the expander..
19748                 expEl  = this.headEls[i];
19749                 continue;
19750             }
19751             totalWidth += c.width;
19752             
19753         }
19754         if (expEl) {
19755             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
19756         }
19757         this.headers.setWidth(w-20);
19758
19759         
19760         
19761         
19762     }
19763 });
19764 /*
19765  * Based on:
19766  * Ext JS Library 1.1.1
19767  * Copyright(c) 2006-2007, Ext JS, LLC.
19768  *
19769  * Originally Released Under LGPL - original licence link has changed is not relivant.
19770  *
19771  * Fork - LGPL
19772  * <script type="text/javascript">
19773  */
19774  
19775 /**
19776  * @class Roo.menu.Menu
19777  * @extends Roo.util.Observable
19778  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
19779  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
19780  * @constructor
19781  * Creates a new Menu
19782  * @param {Object} config Configuration options
19783  */
19784 Roo.menu.Menu = function(config){
19785     Roo.apply(this, config);
19786     this.id = this.id || Roo.id();
19787     this.addEvents({
19788         /**
19789          * @event beforeshow
19790          * Fires before this menu is displayed
19791          * @param {Roo.menu.Menu} this
19792          */
19793         beforeshow : true,
19794         /**
19795          * @event beforehide
19796          * Fires before this menu is hidden
19797          * @param {Roo.menu.Menu} this
19798          */
19799         beforehide : true,
19800         /**
19801          * @event show
19802          * Fires after this menu is displayed
19803          * @param {Roo.menu.Menu} this
19804          */
19805         show : true,
19806         /**
19807          * @event hide
19808          * Fires after this menu is hidden
19809          * @param {Roo.menu.Menu} this
19810          */
19811         hide : true,
19812         /**
19813          * @event click
19814          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19815          * @param {Roo.menu.Menu} this
19816          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19817          * @param {Roo.EventObject} e
19818          */
19819         click : true,
19820         /**
19821          * @event mouseover
19822          * Fires when the mouse is hovering over this menu
19823          * @param {Roo.menu.Menu} this
19824          * @param {Roo.EventObject} e
19825          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19826          */
19827         mouseover : true,
19828         /**
19829          * @event mouseout
19830          * Fires when the mouse exits this menu
19831          * @param {Roo.menu.Menu} this
19832          * @param {Roo.EventObject} e
19833          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19834          */
19835         mouseout : true,
19836         /**
19837          * @event itemclick
19838          * Fires when a menu item contained in this menu is clicked
19839          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
19840          * @param {Roo.EventObject} e
19841          */
19842         itemclick: true
19843     });
19844     if (this.registerMenu) {
19845         Roo.menu.MenuMgr.register(this);
19846     }
19847     
19848     var mis = this.items;
19849     this.items = new Roo.util.MixedCollection();
19850     if(mis){
19851         this.add.apply(this, mis);
19852     }
19853 };
19854
19855 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
19856     /**
19857      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
19858      */
19859     minWidth : 120,
19860     /**
19861      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
19862      * for bottom-right shadow (defaults to "sides")
19863      */
19864     shadow : "sides",
19865     /**
19866      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
19867      * this menu (defaults to "tl-tr?")
19868      */
19869     subMenuAlign : "tl-tr?",
19870     /**
19871      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
19872      * relative to its element of origin (defaults to "tl-bl?")
19873      */
19874     defaultAlign : "tl-bl?",
19875     /**
19876      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
19877      */
19878     allowOtherMenus : false,
19879     /**
19880      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
19881      */
19882     registerMenu : true,
19883
19884     hidden:true,
19885
19886     // private
19887     render : function(){
19888         if(this.el){
19889             return;
19890         }
19891         var el = this.el = new Roo.Layer({
19892             cls: "x-menu",
19893             shadow:this.shadow,
19894             constrain: false,
19895             parentEl: this.parentEl || document.body,
19896             zindex:15000
19897         });
19898
19899         this.keyNav = new Roo.menu.MenuNav(this);
19900
19901         if(this.plain){
19902             el.addClass("x-menu-plain");
19903         }
19904         if(this.cls){
19905             el.addClass(this.cls);
19906         }
19907         // generic focus element
19908         this.focusEl = el.createChild({
19909             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
19910         });
19911         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
19912         ul.on("click", this.onClick, this);
19913         ul.on("mouseover", this.onMouseOver, this);
19914         ul.on("mouseout", this.onMouseOut, this);
19915         this.items.each(function(item){
19916             if (item.hidden) {
19917                 return;
19918             }
19919             
19920             var li = document.createElement("li");
19921             li.className = "x-menu-list-item";
19922             ul.dom.appendChild(li);
19923             item.render(li, this);
19924         }, this);
19925         this.ul = ul;
19926         this.autoWidth();
19927     },
19928
19929     // private
19930     autoWidth : function(){
19931         var el = this.el, ul = this.ul;
19932         if(!el){
19933             return;
19934         }
19935         var w = this.width;
19936         if(w){
19937             el.setWidth(w);
19938         }else if(Roo.isIE){
19939             el.setWidth(this.minWidth);
19940             var t = el.dom.offsetWidth; // force recalc
19941             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
19942         }
19943     },
19944
19945     // private
19946     delayAutoWidth : function(){
19947         if(this.rendered){
19948             if(!this.awTask){
19949                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
19950             }
19951             this.awTask.delay(20);
19952         }
19953     },
19954
19955     // private
19956     findTargetItem : function(e){
19957         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
19958         if(t && t.menuItemId){
19959             return this.items.get(t.menuItemId);
19960         }
19961     },
19962
19963     // private
19964     onClick : function(e){
19965         var t;
19966         if(t = this.findTargetItem(e)){
19967             t.onClick(e);
19968             this.fireEvent("click", this, t, e);
19969         }
19970     },
19971
19972     // private
19973     setActiveItem : function(item, autoExpand){
19974         if(item != this.activeItem){
19975             if(this.activeItem){
19976                 this.activeItem.deactivate();
19977             }
19978             this.activeItem = item;
19979             item.activate(autoExpand);
19980         }else if(autoExpand){
19981             item.expandMenu();
19982         }
19983     },
19984
19985     // private
19986     tryActivate : function(start, step){
19987         var items = this.items;
19988         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
19989             var item = items.get(i);
19990             if(!item.disabled && item.canActivate){
19991                 this.setActiveItem(item, false);
19992                 return item;
19993             }
19994         }
19995         return false;
19996     },
19997
19998     // private
19999     onMouseOver : function(e){
20000         var t;
20001         if(t = this.findTargetItem(e)){
20002             if(t.canActivate && !t.disabled){
20003                 this.setActiveItem(t, true);
20004             }
20005         }
20006         this.fireEvent("mouseover", this, e, t);
20007     },
20008
20009     // private
20010     onMouseOut : function(e){
20011         var t;
20012         if(t = this.findTargetItem(e)){
20013             if(t == this.activeItem && t.shouldDeactivate(e)){
20014                 this.activeItem.deactivate();
20015                 delete this.activeItem;
20016             }
20017         }
20018         this.fireEvent("mouseout", this, e, t);
20019     },
20020
20021     /**
20022      * Read-only.  Returns true if the menu is currently displayed, else false.
20023      * @type Boolean
20024      */
20025     isVisible : function(){
20026         return this.el && !this.hidden;
20027     },
20028
20029     /**
20030      * Displays this menu relative to another element
20031      * @param {String/HTMLElement/Roo.Element} element The element to align to
20032      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
20033      * the element (defaults to this.defaultAlign)
20034      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
20035      */
20036     show : function(el, pos, parentMenu){
20037         this.parentMenu = parentMenu;
20038         if(!this.el){
20039             this.render();
20040         }
20041         this.fireEvent("beforeshow", this);
20042         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
20043     },
20044
20045     /**
20046      * Displays this menu at a specific xy position
20047      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
20048      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
20049      */
20050     showAt : function(xy, parentMenu, /* private: */_e){
20051         this.parentMenu = parentMenu;
20052         if(!this.el){
20053             this.render();
20054         }
20055         if(_e !== false){
20056             this.fireEvent("beforeshow", this);
20057             xy = this.el.adjustForConstraints(xy);
20058         }
20059         this.el.setXY(xy);
20060         this.el.show();
20061         this.hidden = false;
20062         this.focus();
20063         this.fireEvent("show", this);
20064     },
20065
20066     focus : function(){
20067         if(!this.hidden){
20068             this.doFocus.defer(50, this);
20069         }
20070     },
20071
20072     doFocus : function(){
20073         if(!this.hidden){
20074             this.focusEl.focus();
20075         }
20076     },
20077
20078     /**
20079      * Hides this menu and optionally all parent menus
20080      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
20081      */
20082     hide : function(deep){
20083         if(this.el && this.isVisible()){
20084             this.fireEvent("beforehide", this);
20085             if(this.activeItem){
20086                 this.activeItem.deactivate();
20087                 this.activeItem = null;
20088             }
20089             this.el.hide();
20090             this.hidden = true;
20091             this.fireEvent("hide", this);
20092         }
20093         if(deep === true && this.parentMenu){
20094             this.parentMenu.hide(true);
20095         }
20096     },
20097
20098     /**
20099      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
20100      * Any of the following are valid:
20101      * <ul>
20102      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
20103      * <li>An HTMLElement object which will be converted to a menu item</li>
20104      * <li>A menu item config object that will be created as a new menu item</li>
20105      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
20106      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
20107      * </ul>
20108      * Usage:
20109      * <pre><code>
20110 // Create the menu
20111 var menu = new Roo.menu.Menu();
20112
20113 // Create a menu item to add by reference
20114 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
20115
20116 // Add a bunch of items at once using different methods.
20117 // Only the last item added will be returned.
20118 var item = menu.add(
20119     menuItem,                // add existing item by ref
20120     'Dynamic Item',          // new TextItem
20121     '-',                     // new separator
20122     { text: 'Config Item' }  // new item by config
20123 );
20124 </code></pre>
20125      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
20126      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
20127      */
20128     add : function(){
20129         var a = arguments, l = a.length, item;
20130         for(var i = 0; i < l; i++){
20131             var el = a[i];
20132             if ((typeof(el) == "object") && el.xtype && el.xns) {
20133                 el = Roo.factory(el, Roo.menu);
20134             }
20135             
20136             if(el.render){ // some kind of Item
20137                 item = this.addItem(el);
20138             }else if(typeof el == "string"){ // string
20139                 if(el == "separator" || el == "-"){
20140                     item = this.addSeparator();
20141                 }else{
20142                     item = this.addText(el);
20143                 }
20144             }else if(el.tagName || el.el){ // element
20145                 item = this.addElement(el);
20146             }else if(typeof el == "object"){ // must be menu item config?
20147                 item = this.addMenuItem(el);
20148             }
20149         }
20150         return item;
20151     },
20152
20153     /**
20154      * Returns this menu's underlying {@link Roo.Element} object
20155      * @return {Roo.Element} The element
20156      */
20157     getEl : function(){
20158         if(!this.el){
20159             this.render();
20160         }
20161         return this.el;
20162     },
20163
20164     /**
20165      * Adds a separator bar to the menu
20166      * @return {Roo.menu.Item} The menu item that was added
20167      */
20168     addSeparator : function(){
20169         return this.addItem(new Roo.menu.Separator());
20170     },
20171
20172     /**
20173      * Adds an {@link Roo.Element} object to the menu
20174      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
20175      * @return {Roo.menu.Item} The menu item that was added
20176      */
20177     addElement : function(el){
20178         return this.addItem(new Roo.menu.BaseItem(el));
20179     },
20180
20181     /**
20182      * Adds an existing object based on {@link Roo.menu.Item} to the menu
20183      * @param {Roo.menu.Item} item The menu item to add
20184      * @return {Roo.menu.Item} The menu item that was added
20185      */
20186     addItem : function(item){
20187         this.items.add(item);
20188         if(this.ul){
20189             var li = document.createElement("li");
20190             li.className = "x-menu-list-item";
20191             this.ul.dom.appendChild(li);
20192             item.render(li, this);
20193             this.delayAutoWidth();
20194         }
20195         return item;
20196     },
20197
20198     /**
20199      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
20200      * @param {Object} config A MenuItem config object
20201      * @return {Roo.menu.Item} The menu item that was added
20202      */
20203     addMenuItem : function(config){
20204         if(!(config instanceof Roo.menu.Item)){
20205             if(typeof config.checked == "boolean"){ // must be check menu item config?
20206                 config = new Roo.menu.CheckItem(config);
20207             }else{
20208                 config = new Roo.menu.Item(config);
20209             }
20210         }
20211         return this.addItem(config);
20212     },
20213
20214     /**
20215      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
20216      * @param {String} text The text to display in the menu item
20217      * @return {Roo.menu.Item} The menu item that was added
20218      */
20219     addText : function(text){
20220         return this.addItem(new Roo.menu.TextItem({ text : text }));
20221     },
20222
20223     /**
20224      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
20225      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
20226      * @param {Roo.menu.Item} item The menu item to add
20227      * @return {Roo.menu.Item} The menu item that was added
20228      */
20229     insert : function(index, item){
20230         this.items.insert(index, item);
20231         if(this.ul){
20232             var li = document.createElement("li");
20233             li.className = "x-menu-list-item";
20234             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
20235             item.render(li, this);
20236             this.delayAutoWidth();
20237         }
20238         return item;
20239     },
20240
20241     /**
20242      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
20243      * @param {Roo.menu.Item} item The menu item to remove
20244      */
20245     remove : function(item){
20246         this.items.removeKey(item.id);
20247         item.destroy();
20248     },
20249
20250     /**
20251      * Removes and destroys all items in the menu
20252      */
20253     removeAll : function(){
20254         var f;
20255         while(f = this.items.first()){
20256             this.remove(f);
20257         }
20258     }
20259 });
20260
20261 // MenuNav is a private utility class used internally by the Menu
20262 Roo.menu.MenuNav = function(menu){
20263     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
20264     this.scope = this.menu = menu;
20265 };
20266
20267 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
20268     doRelay : function(e, h){
20269         var k = e.getKey();
20270         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
20271             this.menu.tryActivate(0, 1);
20272             return false;
20273         }
20274         return h.call(this.scope || this, e, this.menu);
20275     },
20276
20277     up : function(e, m){
20278         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
20279             m.tryActivate(m.items.length-1, -1);
20280         }
20281     },
20282
20283     down : function(e, m){
20284         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
20285             m.tryActivate(0, 1);
20286         }
20287     },
20288
20289     right : function(e, m){
20290         if(m.activeItem){
20291             m.activeItem.expandMenu(true);
20292         }
20293     },
20294
20295     left : function(e, m){
20296         m.hide();
20297         if(m.parentMenu && m.parentMenu.activeItem){
20298             m.parentMenu.activeItem.activate();
20299         }
20300     },
20301
20302     enter : function(e, m){
20303         if(m.activeItem){
20304             e.stopPropagation();
20305             m.activeItem.onClick(e);
20306             m.fireEvent("click", this, m.activeItem);
20307             return true;
20308         }
20309     }
20310 });/*
20311  * Based on:
20312  * Ext JS Library 1.1.1
20313  * Copyright(c) 2006-2007, Ext JS, LLC.
20314  *
20315  * Originally Released Under LGPL - original licence link has changed is not relivant.
20316  *
20317  * Fork - LGPL
20318  * <script type="text/javascript">
20319  */
20320  
20321 /**
20322  * @class Roo.menu.MenuMgr
20323  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
20324  * @singleton
20325  */
20326 Roo.menu.MenuMgr = function(){
20327    var menus, active, groups = {}, attached = false, lastShow = new Date();
20328
20329    // private - called when first menu is created
20330    function init(){
20331        menus = {};
20332        active = new Roo.util.MixedCollection();
20333        Roo.get(document).addKeyListener(27, function(){
20334            if(active.length > 0){
20335                hideAll();
20336            }
20337        });
20338    }
20339
20340    // private
20341    function hideAll(){
20342        if(active && active.length > 0){
20343            var c = active.clone();
20344            c.each(function(m){
20345                m.hide();
20346            });
20347        }
20348    }
20349
20350    // private
20351    function onHide(m){
20352        active.remove(m);
20353        if(active.length < 1){
20354            Roo.get(document).un("mousedown", onMouseDown);
20355            attached = false;
20356        }
20357    }
20358
20359    // private
20360    function onShow(m){
20361        var last = active.last();
20362        lastShow = new Date();
20363        active.add(m);
20364        if(!attached){
20365            Roo.get(document).on("mousedown", onMouseDown);
20366            attached = true;
20367        }
20368        if(m.parentMenu){
20369           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
20370           m.parentMenu.activeChild = m;
20371        }else if(last && last.isVisible()){
20372           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
20373        }
20374    }
20375
20376    // private
20377    function onBeforeHide(m){
20378        if(m.activeChild){
20379            m.activeChild.hide();
20380        }
20381        if(m.autoHideTimer){
20382            clearTimeout(m.autoHideTimer);
20383            delete m.autoHideTimer;
20384        }
20385    }
20386
20387    // private
20388    function onBeforeShow(m){
20389        var pm = m.parentMenu;
20390        if(!pm && !m.allowOtherMenus){
20391            hideAll();
20392        }else if(pm && pm.activeChild && active != m){
20393            pm.activeChild.hide();
20394        }
20395    }
20396
20397    // private
20398    function onMouseDown(e){
20399        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
20400            hideAll();
20401        }
20402    }
20403
20404    // private
20405    function onBeforeCheck(mi, state){
20406        if(state){
20407            var g = groups[mi.group];
20408            for(var i = 0, l = g.length; i < l; i++){
20409                if(g[i] != mi){
20410                    g[i].setChecked(false);
20411                }
20412            }
20413        }
20414    }
20415
20416    return {
20417
20418        /**
20419         * Hides all menus that are currently visible
20420         */
20421        hideAll : function(){
20422             hideAll();  
20423        },
20424
20425        // private
20426        register : function(menu){
20427            if(!menus){
20428                init();
20429            }
20430            menus[menu.id] = menu;
20431            menu.on("beforehide", onBeforeHide);
20432            menu.on("hide", onHide);
20433            menu.on("beforeshow", onBeforeShow);
20434            menu.on("show", onShow);
20435            var g = menu.group;
20436            if(g && menu.events["checkchange"]){
20437                if(!groups[g]){
20438                    groups[g] = [];
20439                }
20440                groups[g].push(menu);
20441                menu.on("checkchange", onCheck);
20442            }
20443        },
20444
20445         /**
20446          * Returns a {@link Roo.menu.Menu} object
20447          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
20448          * be used to generate and return a new Menu instance.
20449          */
20450        get : function(menu){
20451            if(typeof menu == "string"){ // menu id
20452                return menus[menu];
20453            }else if(menu.events){  // menu instance
20454                return menu;
20455            }else if(typeof menu.length == 'number'){ // array of menu items?
20456                return new Roo.menu.Menu({items:menu});
20457            }else{ // otherwise, must be a config
20458                return new Roo.menu.Menu(menu);
20459            }
20460        },
20461
20462        // private
20463        unregister : function(menu){
20464            delete menus[menu.id];
20465            menu.un("beforehide", onBeforeHide);
20466            menu.un("hide", onHide);
20467            menu.un("beforeshow", onBeforeShow);
20468            menu.un("show", onShow);
20469            var g = menu.group;
20470            if(g && menu.events["checkchange"]){
20471                groups[g].remove(menu);
20472                menu.un("checkchange", onCheck);
20473            }
20474        },
20475
20476        // private
20477        registerCheckable : function(menuItem){
20478            var g = menuItem.group;
20479            if(g){
20480                if(!groups[g]){
20481                    groups[g] = [];
20482                }
20483                groups[g].push(menuItem);
20484                menuItem.on("beforecheckchange", onBeforeCheck);
20485            }
20486        },
20487
20488        // private
20489        unregisterCheckable : function(menuItem){
20490            var g = menuItem.group;
20491            if(g){
20492                groups[g].remove(menuItem);
20493                menuItem.un("beforecheckchange", onBeforeCheck);
20494            }
20495        }
20496    };
20497 }();/*
20498  * Based on:
20499  * Ext JS Library 1.1.1
20500  * Copyright(c) 2006-2007, Ext JS, LLC.
20501  *
20502  * Originally Released Under LGPL - original licence link has changed is not relivant.
20503  *
20504  * Fork - LGPL
20505  * <script type="text/javascript">
20506  */
20507  
20508
20509 /**
20510  * @class Roo.menu.BaseItem
20511  * @extends Roo.Component
20512  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
20513  * management and base configuration options shared by all menu components.
20514  * @constructor
20515  * Creates a new BaseItem
20516  * @param {Object} config Configuration options
20517  */
20518 Roo.menu.BaseItem = function(config){
20519     Roo.menu.BaseItem.superclass.constructor.call(this, config);
20520
20521     this.addEvents({
20522         /**
20523          * @event click
20524          * Fires when this item is clicked
20525          * @param {Roo.menu.BaseItem} this
20526          * @param {Roo.EventObject} e
20527          */
20528         click: true,
20529         /**
20530          * @event activate
20531          * Fires when this item is activated
20532          * @param {Roo.menu.BaseItem} this
20533          */
20534         activate : true,
20535         /**
20536          * @event deactivate
20537          * Fires when this item is deactivated
20538          * @param {Roo.menu.BaseItem} this
20539          */
20540         deactivate : true
20541     });
20542
20543     if(this.handler){
20544         this.on("click", this.handler, this.scope, true);
20545     }
20546 };
20547
20548 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
20549     /**
20550      * @cfg {Function} handler
20551      * A function that will handle the click event of this menu item (defaults to undefined)
20552      */
20553     /**
20554      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
20555      */
20556     canActivate : false,
20557     
20558      /**
20559      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
20560      */
20561     hidden: false,
20562     
20563     /**
20564      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
20565      */
20566     activeClass : "x-menu-item-active",
20567     /**
20568      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
20569      */
20570     hideOnClick : true,
20571     /**
20572      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
20573      */
20574     hideDelay : 100,
20575
20576     // private
20577     ctype: "Roo.menu.BaseItem",
20578
20579     // private
20580     actionMode : "container",
20581
20582     // private
20583     render : function(container, parentMenu){
20584         this.parentMenu = parentMenu;
20585         Roo.menu.BaseItem.superclass.render.call(this, container);
20586         this.container.menuItemId = this.id;
20587     },
20588
20589     // private
20590     onRender : function(container, position){
20591         this.el = Roo.get(this.el);
20592         container.dom.appendChild(this.el.dom);
20593     },
20594
20595     // private
20596     onClick : function(e){
20597         if(!this.disabled && this.fireEvent("click", this, e) !== false
20598                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
20599             this.handleClick(e);
20600         }else{
20601             e.stopEvent();
20602         }
20603     },
20604
20605     // private
20606     activate : function(){
20607         if(this.disabled){
20608             return false;
20609         }
20610         var li = this.container;
20611         li.addClass(this.activeClass);
20612         this.region = li.getRegion().adjust(2, 2, -2, -2);
20613         this.fireEvent("activate", this);
20614         return true;
20615     },
20616
20617     // private
20618     deactivate : function(){
20619         this.container.removeClass(this.activeClass);
20620         this.fireEvent("deactivate", this);
20621     },
20622
20623     // private
20624     shouldDeactivate : function(e){
20625         return !this.region || !this.region.contains(e.getPoint());
20626     },
20627
20628     // private
20629     handleClick : function(e){
20630         if(this.hideOnClick){
20631             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
20632         }
20633     },
20634
20635     // private
20636     expandMenu : function(autoActivate){
20637         // do nothing
20638     },
20639
20640     // private
20641     hideMenu : function(){
20642         // do nothing
20643     }
20644 });/*
20645  * Based on:
20646  * Ext JS Library 1.1.1
20647  * Copyright(c) 2006-2007, Ext JS, LLC.
20648  *
20649  * Originally Released Under LGPL - original licence link has changed is not relivant.
20650  *
20651  * Fork - LGPL
20652  * <script type="text/javascript">
20653  */
20654  
20655 /**
20656  * @class Roo.menu.Adapter
20657  * @extends Roo.menu.BaseItem
20658  * 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.
20659  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
20660  * @constructor
20661  * Creates a new Adapter
20662  * @param {Object} config Configuration options
20663  */
20664 Roo.menu.Adapter = function(component, config){
20665     Roo.menu.Adapter.superclass.constructor.call(this, config);
20666     this.component = component;
20667 };
20668 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
20669     // private
20670     canActivate : true,
20671
20672     // private
20673     onRender : function(container, position){
20674         this.component.render(container);
20675         this.el = this.component.getEl();
20676     },
20677
20678     // private
20679     activate : function(){
20680         if(this.disabled){
20681             return false;
20682         }
20683         this.component.focus();
20684         this.fireEvent("activate", this);
20685         return true;
20686     },
20687
20688     // private
20689     deactivate : function(){
20690         this.fireEvent("deactivate", this);
20691     },
20692
20693     // private
20694     disable : function(){
20695         this.component.disable();
20696         Roo.menu.Adapter.superclass.disable.call(this);
20697     },
20698
20699     // private
20700     enable : function(){
20701         this.component.enable();
20702         Roo.menu.Adapter.superclass.enable.call(this);
20703     }
20704 });/*
20705  * Based on:
20706  * Ext JS Library 1.1.1
20707  * Copyright(c) 2006-2007, Ext JS, LLC.
20708  *
20709  * Originally Released Under LGPL - original licence link has changed is not relivant.
20710  *
20711  * Fork - LGPL
20712  * <script type="text/javascript">
20713  */
20714
20715 /**
20716  * @class Roo.menu.TextItem
20717  * @extends Roo.menu.BaseItem
20718  * Adds a static text string to a menu, usually used as either a heading or group separator.
20719  * Note: old style constructor with text is still supported.
20720  * 
20721  * @constructor
20722  * Creates a new TextItem
20723  * @param {Object} cfg Configuration
20724  */
20725 Roo.menu.TextItem = function(cfg){
20726     if (typeof(cfg) == 'string') {
20727         this.text = cfg;
20728     } else {
20729         Roo.apply(this,cfg);
20730     }
20731     
20732     Roo.menu.TextItem.superclass.constructor.call(this);
20733 };
20734
20735 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
20736     /**
20737      * @cfg {Boolean} text Text to show on item.
20738      */
20739     text : '',
20740     
20741     /**
20742      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20743      */
20744     hideOnClick : false,
20745     /**
20746      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
20747      */
20748     itemCls : "x-menu-text",
20749
20750     // private
20751     onRender : function(){
20752         var s = document.createElement("span");
20753         s.className = this.itemCls;
20754         s.innerHTML = this.text;
20755         this.el = s;
20756         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
20757     }
20758 });/*
20759  * Based on:
20760  * Ext JS Library 1.1.1
20761  * Copyright(c) 2006-2007, Ext JS, LLC.
20762  *
20763  * Originally Released Under LGPL - original licence link has changed is not relivant.
20764  *
20765  * Fork - LGPL
20766  * <script type="text/javascript">
20767  */
20768
20769 /**
20770  * @class Roo.menu.Separator
20771  * @extends Roo.menu.BaseItem
20772  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
20773  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
20774  * @constructor
20775  * @param {Object} config Configuration options
20776  */
20777 Roo.menu.Separator = function(config){
20778     Roo.menu.Separator.superclass.constructor.call(this, config);
20779 };
20780
20781 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
20782     /**
20783      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
20784      */
20785     itemCls : "x-menu-sep",
20786     /**
20787      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20788      */
20789     hideOnClick : false,
20790
20791     // private
20792     onRender : function(li){
20793         var s = document.createElement("span");
20794         s.className = this.itemCls;
20795         s.innerHTML = "&#160;";
20796         this.el = s;
20797         li.addClass("x-menu-sep-li");
20798         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
20799     }
20800 });/*
20801  * Based on:
20802  * Ext JS Library 1.1.1
20803  * Copyright(c) 2006-2007, Ext JS, LLC.
20804  *
20805  * Originally Released Under LGPL - original licence link has changed is not relivant.
20806  *
20807  * Fork - LGPL
20808  * <script type="text/javascript">
20809  */
20810 /**
20811  * @class Roo.menu.Item
20812  * @extends Roo.menu.BaseItem
20813  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
20814  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
20815  * activation and click handling.
20816  * @constructor
20817  * Creates a new Item
20818  * @param {Object} config Configuration options
20819  */
20820 Roo.menu.Item = function(config){
20821     Roo.menu.Item.superclass.constructor.call(this, config);
20822     if(this.menu){
20823         this.menu = Roo.menu.MenuMgr.get(this.menu);
20824     }
20825 };
20826 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
20827     
20828     /**
20829      * @cfg {String} text
20830      * The text to show on the menu item.
20831      */
20832     text: '',
20833      /**
20834      * @cfg {String} HTML to render in menu
20835      * The text to show on the menu item (HTML version).
20836      */
20837     html: '',
20838     /**
20839      * @cfg {String} icon
20840      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
20841      */
20842     icon: undefined,
20843     /**
20844      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
20845      */
20846     itemCls : "x-menu-item",
20847     /**
20848      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
20849      */
20850     canActivate : true,
20851     /**
20852      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
20853      */
20854     showDelay: 200,
20855     // doc'd in BaseItem
20856     hideDelay: 200,
20857
20858     // private
20859     ctype: "Roo.menu.Item",
20860     
20861     // private
20862     onRender : function(container, position){
20863         var el = document.createElement("a");
20864         el.hideFocus = true;
20865         el.unselectable = "on";
20866         el.href = this.href || "#";
20867         if(this.hrefTarget){
20868             el.target = this.hrefTarget;
20869         }
20870         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
20871         
20872         var html = this.html.length ? this.html  : String.format('{0}',this.text);
20873         
20874         el.innerHTML = String.format(
20875                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
20876                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
20877         this.el = el;
20878         Roo.menu.Item.superclass.onRender.call(this, container, position);
20879     },
20880
20881     /**
20882      * Sets the text to display in this menu item
20883      * @param {String} text The text to display
20884      * @param {Boolean} isHTML true to indicate text is pure html.
20885      */
20886     setText : function(text, isHTML){
20887         if (isHTML) {
20888             this.html = text;
20889         } else {
20890             this.text = text;
20891             this.html = '';
20892         }
20893         if(this.rendered){
20894             var html = this.html.length ? this.html  : String.format('{0}',this.text);
20895      
20896             this.el.update(String.format(
20897                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
20898                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
20899             this.parentMenu.autoWidth();
20900         }
20901     },
20902
20903     // private
20904     handleClick : function(e){
20905         if(!this.href){ // if no link defined, stop the event automatically
20906             e.stopEvent();
20907         }
20908         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
20909     },
20910
20911     // private
20912     activate : function(autoExpand){
20913         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
20914             this.focus();
20915             if(autoExpand){
20916                 this.expandMenu();
20917             }
20918         }
20919         return true;
20920     },
20921
20922     // private
20923     shouldDeactivate : function(e){
20924         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
20925             if(this.menu && this.menu.isVisible()){
20926                 return !this.menu.getEl().getRegion().contains(e.getPoint());
20927             }
20928             return true;
20929         }
20930         return false;
20931     },
20932
20933     // private
20934     deactivate : function(){
20935         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
20936         this.hideMenu();
20937     },
20938
20939     // private
20940     expandMenu : function(autoActivate){
20941         if(!this.disabled && this.menu){
20942             clearTimeout(this.hideTimer);
20943             delete this.hideTimer;
20944             if(!this.menu.isVisible() && !this.showTimer){
20945                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
20946             }else if (this.menu.isVisible() && autoActivate){
20947                 this.menu.tryActivate(0, 1);
20948             }
20949         }
20950     },
20951
20952     // private
20953     deferExpand : function(autoActivate){
20954         delete this.showTimer;
20955         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
20956         if(autoActivate){
20957             this.menu.tryActivate(0, 1);
20958         }
20959     },
20960
20961     // private
20962     hideMenu : function(){
20963         clearTimeout(this.showTimer);
20964         delete this.showTimer;
20965         if(!this.hideTimer && this.menu && this.menu.isVisible()){
20966             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
20967         }
20968     },
20969
20970     // private
20971     deferHide : function(){
20972         delete this.hideTimer;
20973         this.menu.hide();
20974     }
20975 });/*
20976  * Based on:
20977  * Ext JS Library 1.1.1
20978  * Copyright(c) 2006-2007, Ext JS, LLC.
20979  *
20980  * Originally Released Under LGPL - original licence link has changed is not relivant.
20981  *
20982  * Fork - LGPL
20983  * <script type="text/javascript">
20984  */
20985  
20986 /**
20987  * @class Roo.menu.CheckItem
20988  * @extends Roo.menu.Item
20989  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
20990  * @constructor
20991  * Creates a new CheckItem
20992  * @param {Object} config Configuration options
20993  */
20994 Roo.menu.CheckItem = function(config){
20995     Roo.menu.CheckItem.superclass.constructor.call(this, config);
20996     this.addEvents({
20997         /**
20998          * @event beforecheckchange
20999          * Fires before the checked value is set, providing an opportunity to cancel if needed
21000          * @param {Roo.menu.CheckItem} this
21001          * @param {Boolean} checked The new checked value that will be set
21002          */
21003         "beforecheckchange" : true,
21004         /**
21005          * @event checkchange
21006          * Fires after the checked value has been set
21007          * @param {Roo.menu.CheckItem} this
21008          * @param {Boolean} checked The checked value that was set
21009          */
21010         "checkchange" : true
21011     });
21012     if(this.checkHandler){
21013         this.on('checkchange', this.checkHandler, this.scope);
21014     }
21015 };
21016 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
21017     /**
21018      * @cfg {String} group
21019      * All check items with the same group name will automatically be grouped into a single-select
21020      * radio button group (defaults to '')
21021      */
21022     /**
21023      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
21024      */
21025     itemCls : "x-menu-item x-menu-check-item",
21026     /**
21027      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
21028      */
21029     groupClass : "x-menu-group-item",
21030
21031     /**
21032      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
21033      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
21034      * initialized with checked = true will be rendered as checked.
21035      */
21036     checked: false,
21037
21038     // private
21039     ctype: "Roo.menu.CheckItem",
21040
21041     // private
21042     onRender : function(c){
21043         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
21044         if(this.group){
21045             this.el.addClass(this.groupClass);
21046         }
21047         Roo.menu.MenuMgr.registerCheckable(this);
21048         if(this.checked){
21049             this.checked = false;
21050             this.setChecked(true, true);
21051         }
21052     },
21053
21054     // private
21055     destroy : function(){
21056         if(this.rendered){
21057             Roo.menu.MenuMgr.unregisterCheckable(this);
21058         }
21059         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
21060     },
21061
21062     /**
21063      * Set the checked state of this item
21064      * @param {Boolean} checked The new checked value
21065      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
21066      */
21067     setChecked : function(state, suppressEvent){
21068         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
21069             if(this.container){
21070                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
21071             }
21072             this.checked = state;
21073             if(suppressEvent !== true){
21074                 this.fireEvent("checkchange", this, state);
21075             }
21076         }
21077     },
21078
21079     // private
21080     handleClick : function(e){
21081        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
21082            this.setChecked(!this.checked);
21083        }
21084        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
21085     }
21086 });/*
21087  * Based on:
21088  * Ext JS Library 1.1.1
21089  * Copyright(c) 2006-2007, Ext JS, LLC.
21090  *
21091  * Originally Released Under LGPL - original licence link has changed is not relivant.
21092  *
21093  * Fork - LGPL
21094  * <script type="text/javascript">
21095  */
21096  
21097 /**
21098  * @class Roo.menu.DateItem
21099  * @extends Roo.menu.Adapter
21100  * A menu item that wraps the {@link Roo.DatPicker} component.
21101  * @constructor
21102  * Creates a new DateItem
21103  * @param {Object} config Configuration options
21104  */
21105 Roo.menu.DateItem = function(config){
21106     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
21107     /** The Roo.DatePicker object @type Roo.DatePicker */
21108     this.picker = this.component;
21109     this.addEvents({select: true});
21110     
21111     this.picker.on("render", function(picker){
21112         picker.getEl().swallowEvent("click");
21113         picker.container.addClass("x-menu-date-item");
21114     });
21115
21116     this.picker.on("select", this.onSelect, this);
21117 };
21118
21119 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
21120     // private
21121     onSelect : function(picker, date){
21122         this.fireEvent("select", this, date, picker);
21123         Roo.menu.DateItem.superclass.handleClick.call(this);
21124     }
21125 });/*
21126  * Based on:
21127  * Ext JS Library 1.1.1
21128  * Copyright(c) 2006-2007, Ext JS, LLC.
21129  *
21130  * Originally Released Under LGPL - original licence link has changed is not relivant.
21131  *
21132  * Fork - LGPL
21133  * <script type="text/javascript">
21134  */
21135  
21136 /**
21137  * @class Roo.menu.ColorItem
21138  * @extends Roo.menu.Adapter
21139  * A menu item that wraps the {@link Roo.ColorPalette} component.
21140  * @constructor
21141  * Creates a new ColorItem
21142  * @param {Object} config Configuration options
21143  */
21144 Roo.menu.ColorItem = function(config){
21145     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
21146     /** The Roo.ColorPalette object @type Roo.ColorPalette */
21147     this.palette = this.component;
21148     this.relayEvents(this.palette, ["select"]);
21149     if(this.selectHandler){
21150         this.on('select', this.selectHandler, this.scope);
21151     }
21152 };
21153 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
21154  * Based on:
21155  * Ext JS Library 1.1.1
21156  * Copyright(c) 2006-2007, Ext JS, LLC.
21157  *
21158  * Originally Released Under LGPL - original licence link has changed is not relivant.
21159  *
21160  * Fork - LGPL
21161  * <script type="text/javascript">
21162  */
21163  
21164
21165 /**
21166  * @class Roo.menu.DateMenu
21167  * @extends Roo.menu.Menu
21168  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
21169  * @constructor
21170  * Creates a new DateMenu
21171  * @param {Object} config Configuration options
21172  */
21173 Roo.menu.DateMenu = function(config){
21174     Roo.menu.DateMenu.superclass.constructor.call(this, config);
21175     this.plain = true;
21176     var di = new Roo.menu.DateItem(config);
21177     this.add(di);
21178     /**
21179      * The {@link Roo.DatePicker} instance for this DateMenu
21180      * @type DatePicker
21181      */
21182     this.picker = di.picker;
21183     /**
21184      * @event select
21185      * @param {DatePicker} picker
21186      * @param {Date} date
21187      */
21188     this.relayEvents(di, ["select"]);
21189     this.on('beforeshow', function(){
21190         if(this.picker){
21191             this.picker.hideMonthPicker(false);
21192         }
21193     }, this);
21194 };
21195 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
21196     cls:'x-date-menu'
21197 });/*
21198  * Based on:
21199  * Ext JS Library 1.1.1
21200  * Copyright(c) 2006-2007, Ext JS, LLC.
21201  *
21202  * Originally Released Under LGPL - original licence link has changed is not relivant.
21203  *
21204  * Fork - LGPL
21205  * <script type="text/javascript">
21206  */
21207  
21208
21209 /**
21210  * @class Roo.menu.ColorMenu
21211  * @extends Roo.menu.Menu
21212  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
21213  * @constructor
21214  * Creates a new ColorMenu
21215  * @param {Object} config Configuration options
21216  */
21217 Roo.menu.ColorMenu = function(config){
21218     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
21219     this.plain = true;
21220     var ci = new Roo.menu.ColorItem(config);
21221     this.add(ci);
21222     /**
21223      * The {@link Roo.ColorPalette} instance for this ColorMenu
21224      * @type ColorPalette
21225      */
21226     this.palette = ci.palette;
21227     /**
21228      * @event select
21229      * @param {ColorPalette} palette
21230      * @param {String} color
21231      */
21232     this.relayEvents(ci, ["select"]);
21233 };
21234 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
21235  * Based on:
21236  * Ext JS Library 1.1.1
21237  * Copyright(c) 2006-2007, Ext JS, LLC.
21238  *
21239  * Originally Released Under LGPL - original licence link has changed is not relivant.
21240  *
21241  * Fork - LGPL
21242  * <script type="text/javascript">
21243  */
21244  
21245 /**
21246  * @class Roo.form.Field
21247  * @extends Roo.BoxComponent
21248  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
21249  * @constructor
21250  * Creates a new Field
21251  * @param {Object} config Configuration options
21252  */
21253 Roo.form.Field = function(config){
21254     Roo.form.Field.superclass.constructor.call(this, config);
21255 };
21256
21257 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
21258     /**
21259      * @cfg {String} fieldLabel Label to use when rendering a form.
21260      */
21261        /**
21262      * @cfg {String} qtip Mouse over tip
21263      */
21264      
21265     /**
21266      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
21267      */
21268     invalidClass : "x-form-invalid",
21269     /**
21270      * @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")
21271      */
21272     invalidText : "The value in this field is invalid",
21273     /**
21274      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
21275      */
21276     focusClass : "x-form-focus",
21277     /**
21278      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
21279       automatic validation (defaults to "keyup").
21280      */
21281     validationEvent : "keyup",
21282     /**
21283      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
21284      */
21285     validateOnBlur : true,
21286     /**
21287      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
21288      */
21289     validationDelay : 250,
21290     /**
21291      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21292      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
21293      */
21294     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
21295     /**
21296      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
21297      */
21298     fieldClass : "x-form-field",
21299     /**
21300      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
21301      *<pre>
21302 Value         Description
21303 -----------   ----------------------------------------------------------------------
21304 qtip          Display a quick tip when the user hovers over the field
21305 title         Display a default browser title attribute popup
21306 under         Add a block div beneath the field containing the error text
21307 side          Add an error icon to the right of the field with a popup on hover
21308 [element id]  Add the error text directly to the innerHTML of the specified element
21309 </pre>
21310      */
21311     msgTarget : 'qtip',
21312     /**
21313      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
21314      */
21315     msgFx : 'normal',
21316
21317     /**
21318      * @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.
21319      */
21320     readOnly : false,
21321
21322     /**
21323      * @cfg {Boolean} disabled True to disable the field (defaults to false).
21324      */
21325     disabled : false,
21326
21327     /**
21328      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
21329      */
21330     inputType : undefined,
21331     
21332     /**
21333      * @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).
21334          */
21335         tabIndex : undefined,
21336         
21337     // private
21338     isFormField : true,
21339
21340     // private
21341     hasFocus : false,
21342     /**
21343      * @property {Roo.Element} fieldEl
21344      * Element Containing the rendered Field (with label etc.)
21345      */
21346     /**
21347      * @cfg {Mixed} value A value to initialize this field with.
21348      */
21349     value : undefined,
21350
21351     /**
21352      * @cfg {String} name The field's HTML name attribute.
21353      */
21354     /**
21355      * @cfg {String} cls A CSS class to apply to the field's underlying element.
21356      */
21357
21358         // private ??
21359         initComponent : function(){
21360         Roo.form.Field.superclass.initComponent.call(this);
21361         this.addEvents({
21362             /**
21363              * @event focus
21364              * Fires when this field receives input focus.
21365              * @param {Roo.form.Field} this
21366              */
21367             focus : true,
21368             /**
21369              * @event blur
21370              * Fires when this field loses input focus.
21371              * @param {Roo.form.Field} this
21372              */
21373             blur : true,
21374             /**
21375              * @event specialkey
21376              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
21377              * {@link Roo.EventObject#getKey} to determine which key was pressed.
21378              * @param {Roo.form.Field} this
21379              * @param {Roo.EventObject} e The event object
21380              */
21381             specialkey : true,
21382             /**
21383              * @event change
21384              * Fires just before the field blurs if the field value has changed.
21385              * @param {Roo.form.Field} this
21386              * @param {Mixed} newValue The new value
21387              * @param {Mixed} oldValue The original value
21388              */
21389             change : true,
21390             /**
21391              * @event invalid
21392              * Fires after the field has been marked as invalid.
21393              * @param {Roo.form.Field} this
21394              * @param {String} msg The validation message
21395              */
21396             invalid : true,
21397             /**
21398              * @event valid
21399              * Fires after the field has been validated with no errors.
21400              * @param {Roo.form.Field} this
21401              */
21402             valid : true,
21403              /**
21404              * @event keyup
21405              * Fires after the key up
21406              * @param {Roo.form.Field} this
21407              * @param {Roo.EventObject}  e The event Object
21408              */
21409             keyup : true
21410         });
21411     },
21412
21413     /**
21414      * Returns the name attribute of the field if available
21415      * @return {String} name The field name
21416      */
21417     getName: function(){
21418          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
21419     },
21420
21421     // private
21422     onRender : function(ct, position){
21423         Roo.form.Field.superclass.onRender.call(this, ct, position);
21424         if(!this.el){
21425             var cfg = this.getAutoCreate();
21426             if(!cfg.name){
21427                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
21428             }
21429             if (!cfg.name.length) {
21430                 delete cfg.name;
21431             }
21432             if(this.inputType){
21433                 cfg.type = this.inputType;
21434             }
21435             this.el = ct.createChild(cfg, position);
21436         }
21437         var type = this.el.dom.type;
21438         if(type){
21439             if(type == 'password'){
21440                 type = 'text';
21441             }
21442             this.el.addClass('x-form-'+type);
21443         }
21444         if(this.readOnly){
21445             this.el.dom.readOnly = true;
21446         }
21447         if(this.tabIndex !== undefined){
21448             this.el.dom.setAttribute('tabIndex', this.tabIndex);
21449         }
21450
21451         this.el.addClass([this.fieldClass, this.cls]);
21452         this.initValue();
21453     },
21454
21455     /**
21456      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
21457      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
21458      * @return {Roo.form.Field} this
21459      */
21460     applyTo : function(target){
21461         this.allowDomMove = false;
21462         this.el = Roo.get(target);
21463         this.render(this.el.dom.parentNode);
21464         return this;
21465     },
21466
21467     // private
21468     initValue : function(){
21469         if(this.value !== undefined){
21470             this.setValue(this.value);
21471         }else if(this.el.dom.value.length > 0){
21472             this.setValue(this.el.dom.value);
21473         }
21474     },
21475
21476     /**
21477      * Returns true if this field has been changed since it was originally loaded and is not disabled.
21478      */
21479     isDirty : function() {
21480         if(this.disabled) {
21481             return false;
21482         }
21483         return String(this.getValue()) !== String(this.originalValue);
21484     },
21485
21486     // private
21487     afterRender : function(){
21488         Roo.form.Field.superclass.afterRender.call(this);
21489         this.initEvents();
21490     },
21491
21492     // private
21493     fireKey : function(e){
21494         //Roo.log('field ' + e.getKey());
21495         if(e.isNavKeyPress()){
21496             this.fireEvent("specialkey", this, e);
21497         }
21498     },
21499
21500     /**
21501      * Resets the current field value to the originally loaded value and clears any validation messages
21502      */
21503     reset : function(){
21504         this.setValue(this.originalValue);
21505         this.clearInvalid();
21506     },
21507
21508     // private
21509     initEvents : function(){
21510         // safari killled keypress - so keydown is now used..
21511         this.el.on("keydown" , this.fireKey,  this);
21512         this.el.on("focus", this.onFocus,  this);
21513         this.el.on("blur", this.onBlur,  this);
21514         this.el.relayEvent('keyup', this);
21515
21516         // reference to original value for reset
21517         this.originalValue = this.getValue();
21518     },
21519
21520     // private
21521     onFocus : function(){
21522         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21523             this.el.addClass(this.focusClass);
21524         }
21525         if(!this.hasFocus){
21526             this.hasFocus = true;
21527             this.startValue = this.getValue();
21528             this.fireEvent("focus", this);
21529         }
21530     },
21531
21532     beforeBlur : Roo.emptyFn,
21533
21534     // private
21535     onBlur : function(){
21536         this.beforeBlur();
21537         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21538             this.el.removeClass(this.focusClass);
21539         }
21540         this.hasFocus = false;
21541         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
21542             this.validate();
21543         }
21544         var v = this.getValue();
21545         if(String(v) !== String(this.startValue)){
21546             this.fireEvent('change', this, v, this.startValue);
21547         }
21548         this.fireEvent("blur", this);
21549     },
21550
21551     /**
21552      * Returns whether or not the field value is currently valid
21553      * @param {Boolean} preventMark True to disable marking the field invalid
21554      * @return {Boolean} True if the value is valid, else false
21555      */
21556     isValid : function(preventMark){
21557         if(this.disabled){
21558             return true;
21559         }
21560         var restore = this.preventMark;
21561         this.preventMark = preventMark === true;
21562         var v = this.validateValue(this.processValue(this.getRawValue()));
21563         this.preventMark = restore;
21564         return v;
21565     },
21566
21567     /**
21568      * Validates the field value
21569      * @return {Boolean} True if the value is valid, else false
21570      */
21571     validate : function(){
21572         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
21573             this.clearInvalid();
21574             return true;
21575         }
21576         return false;
21577     },
21578
21579     processValue : function(value){
21580         return value;
21581     },
21582
21583     // private
21584     // Subclasses should provide the validation implementation by overriding this
21585     validateValue : function(value){
21586         return true;
21587     },
21588
21589     /**
21590      * Mark this field as invalid
21591      * @param {String} msg The validation message
21592      */
21593     markInvalid : function(msg){
21594         if(!this.rendered || this.preventMark){ // not rendered
21595             return;
21596         }
21597         this.el.addClass(this.invalidClass);
21598         msg = msg || this.invalidText;
21599         switch(this.msgTarget){
21600             case 'qtip':
21601                 this.el.dom.qtip = msg;
21602                 this.el.dom.qclass = 'x-form-invalid-tip';
21603                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
21604                     Roo.QuickTips.enable();
21605                 }
21606                 break;
21607             case 'title':
21608                 this.el.dom.title = msg;
21609                 break;
21610             case 'under':
21611                 if(!this.errorEl){
21612                     var elp = this.el.findParent('.x-form-element', 5, true);
21613                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
21614                     this.errorEl.setWidth(elp.getWidth(true)-20);
21615                 }
21616                 this.errorEl.update(msg);
21617                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
21618                 break;
21619             case 'side':
21620                 if(!this.errorIcon){
21621                     var elp = this.el.findParent('.x-form-element', 5, true);
21622                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
21623                 }
21624                 this.alignErrorIcon();
21625                 this.errorIcon.dom.qtip = msg;
21626                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
21627                 this.errorIcon.show();
21628                 this.on('resize', this.alignErrorIcon, this);
21629                 break;
21630             default:
21631                 var t = Roo.getDom(this.msgTarget);
21632                 t.innerHTML = msg;
21633                 t.style.display = this.msgDisplay;
21634                 break;
21635         }
21636         this.fireEvent('invalid', this, msg);
21637     },
21638
21639     // private
21640     alignErrorIcon : function(){
21641         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
21642     },
21643
21644     /**
21645      * Clear any invalid styles/messages for this field
21646      */
21647     clearInvalid : function(){
21648         if(!this.rendered || this.preventMark){ // not rendered
21649             return;
21650         }
21651         this.el.removeClass(this.invalidClass);
21652         switch(this.msgTarget){
21653             case 'qtip':
21654                 this.el.dom.qtip = '';
21655                 break;
21656             case 'title':
21657                 this.el.dom.title = '';
21658                 break;
21659             case 'under':
21660                 if(this.errorEl){
21661                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
21662                 }
21663                 break;
21664             case 'side':
21665                 if(this.errorIcon){
21666                     this.errorIcon.dom.qtip = '';
21667                     this.errorIcon.hide();
21668                     this.un('resize', this.alignErrorIcon, this);
21669                 }
21670                 break;
21671             default:
21672                 var t = Roo.getDom(this.msgTarget);
21673                 t.innerHTML = '';
21674                 t.style.display = 'none';
21675                 break;
21676         }
21677         this.fireEvent('valid', this);
21678     },
21679
21680     /**
21681      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
21682      * @return {Mixed} value The field value
21683      */
21684     getRawValue : function(){
21685         var v = this.el.getValue();
21686         
21687         return v;
21688     },
21689
21690     /**
21691      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
21692      * @return {Mixed} value The field value
21693      */
21694     getValue : function(){
21695         var v = this.el.getValue();
21696          
21697         return v;
21698     },
21699
21700     /**
21701      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
21702      * @param {Mixed} value The value to set
21703      */
21704     setRawValue : function(v){
21705         return this.el.dom.value = (v === null || v === undefined ? '' : v);
21706     },
21707
21708     /**
21709      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
21710      * @param {Mixed} value The value to set
21711      */
21712     setValue : function(v){
21713         this.value = v;
21714         if(this.rendered){
21715             this.el.dom.value = (v === null || v === undefined ? '' : v);
21716              this.validate();
21717         }
21718     },
21719
21720     adjustSize : function(w, h){
21721         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
21722         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
21723         return s;
21724     },
21725
21726     adjustWidth : function(tag, w){
21727         tag = tag.toLowerCase();
21728         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
21729             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
21730                 if(tag == 'input'){
21731                     return w + 2;
21732                 }
21733                 if(tag == 'textarea'){
21734                     return w-2;
21735                 }
21736             }else if(Roo.isOpera){
21737                 if(tag == 'input'){
21738                     return w + 2;
21739                 }
21740                 if(tag == 'textarea'){
21741                     return w-2;
21742                 }
21743             }
21744         }
21745         return w;
21746     }
21747 });
21748
21749
21750 // anything other than normal should be considered experimental
21751 Roo.form.Field.msgFx = {
21752     normal : {
21753         show: function(msgEl, f){
21754             msgEl.setDisplayed('block');
21755         },
21756
21757         hide : function(msgEl, f){
21758             msgEl.setDisplayed(false).update('');
21759         }
21760     },
21761
21762     slide : {
21763         show: function(msgEl, f){
21764             msgEl.slideIn('t', {stopFx:true});
21765         },
21766
21767         hide : function(msgEl, f){
21768             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
21769         }
21770     },
21771
21772     slideRight : {
21773         show: function(msgEl, f){
21774             msgEl.fixDisplay();
21775             msgEl.alignTo(f.el, 'tl-tr');
21776             msgEl.slideIn('l', {stopFx:true});
21777         },
21778
21779         hide : function(msgEl, f){
21780             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
21781         }
21782     }
21783 };/*
21784  * Based on:
21785  * Ext JS Library 1.1.1
21786  * Copyright(c) 2006-2007, Ext JS, LLC.
21787  *
21788  * Originally Released Under LGPL - original licence link has changed is not relivant.
21789  *
21790  * Fork - LGPL
21791  * <script type="text/javascript">
21792  */
21793  
21794
21795 /**
21796  * @class Roo.form.TextField
21797  * @extends Roo.form.Field
21798  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
21799  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
21800  * @constructor
21801  * Creates a new TextField
21802  * @param {Object} config Configuration options
21803  */
21804 Roo.form.TextField = function(config){
21805     Roo.form.TextField.superclass.constructor.call(this, config);
21806     this.addEvents({
21807         /**
21808          * @event autosize
21809          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
21810          * according to the default logic, but this event provides a hook for the developer to apply additional
21811          * logic at runtime to resize the field if needed.
21812              * @param {Roo.form.Field} this This text field
21813              * @param {Number} width The new field width
21814              */
21815         autosize : true
21816     });
21817 };
21818
21819 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
21820     /**
21821      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
21822      */
21823     grow : false,
21824     /**
21825      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
21826      */
21827     growMin : 30,
21828     /**
21829      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
21830      */
21831     growMax : 800,
21832     /**
21833      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
21834      */
21835     vtype : null,
21836     /**
21837      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
21838      */
21839     maskRe : null,
21840     /**
21841      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
21842      */
21843     disableKeyFilter : false,
21844     /**
21845      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
21846      */
21847     allowBlank : true,
21848     /**
21849      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
21850      */
21851     minLength : 0,
21852     /**
21853      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
21854      */
21855     maxLength : Number.MAX_VALUE,
21856     /**
21857      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
21858      */
21859     minLengthText : "The minimum length for this field is {0}",
21860     /**
21861      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
21862      */
21863     maxLengthText : "The maximum length for this field is {0}",
21864     /**
21865      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
21866      */
21867     selectOnFocus : false,
21868     /**
21869      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
21870      */
21871     blankText : "This field is required",
21872     /**
21873      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
21874      * If available, this function will be called only after the basic validators all return true, and will be passed the
21875      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
21876      */
21877     validator : null,
21878     /**
21879      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
21880      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
21881      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
21882      */
21883     regex : null,
21884     /**
21885      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
21886      */
21887     regexText : "",
21888     /**
21889      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
21890      */
21891     emptyText : null,
21892    
21893
21894     // private
21895     initEvents : function()
21896     {
21897         if (this.emptyText) {
21898             this.el.attr('placeholder', this.emptyText);
21899         }
21900         
21901         Roo.form.TextField.superclass.initEvents.call(this);
21902         if(this.validationEvent == 'keyup'){
21903             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
21904             this.el.on('keyup', this.filterValidation, this);
21905         }
21906         else if(this.validationEvent !== false){
21907             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
21908         }
21909         
21910         if(this.selectOnFocus){
21911             this.on("focus", this.preFocus, this);
21912             
21913         }
21914         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
21915             this.el.on("keypress", this.filterKeys, this);
21916         }
21917         if(this.grow){
21918             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
21919             this.el.on("click", this.autoSize,  this);
21920         }
21921         if(this.el.is('input[type=password]') && Roo.isSafari){
21922             this.el.on('keydown', this.SafariOnKeyDown, this);
21923         }
21924     },
21925
21926     processValue : function(value){
21927         if(this.stripCharsRe){
21928             var newValue = value.replace(this.stripCharsRe, '');
21929             if(newValue !== value){
21930                 this.setRawValue(newValue);
21931                 return newValue;
21932             }
21933         }
21934         return value;
21935     },
21936
21937     filterValidation : function(e){
21938         if(!e.isNavKeyPress()){
21939             this.validationTask.delay(this.validationDelay);
21940         }
21941     },
21942
21943     // private
21944     onKeyUp : function(e){
21945         if(!e.isNavKeyPress()){
21946             this.autoSize();
21947         }
21948     },
21949
21950     /**
21951      * Resets the current field value to the originally-loaded value and clears any validation messages.
21952      *  
21953      */
21954     reset : function(){
21955         Roo.form.TextField.superclass.reset.call(this);
21956        
21957     },
21958
21959     
21960     // private
21961     preFocus : function(){
21962         
21963         if(this.selectOnFocus){
21964             this.el.dom.select();
21965         }
21966     },
21967
21968     
21969     // private
21970     filterKeys : function(e){
21971         var k = e.getKey();
21972         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
21973             return;
21974         }
21975         var c = e.getCharCode(), cc = String.fromCharCode(c);
21976         if(Roo.isIE && (e.isSpecialKey() || !cc)){
21977             return;
21978         }
21979         if(!this.maskRe.test(cc)){
21980             e.stopEvent();
21981         }
21982     },
21983
21984     setValue : function(v){
21985         
21986         Roo.form.TextField.superclass.setValue.apply(this, arguments);
21987         
21988         this.autoSize();
21989     },
21990
21991     /**
21992      * Validates a value according to the field's validation rules and marks the field as invalid
21993      * if the validation fails
21994      * @param {Mixed} value The value to validate
21995      * @return {Boolean} True if the value is valid, else false
21996      */
21997     validateValue : function(value){
21998         if(value.length < 1)  { // if it's blank
21999              if(this.allowBlank){
22000                 this.clearInvalid();
22001                 return true;
22002              }else{
22003                 this.markInvalid(this.blankText);
22004                 return false;
22005              }
22006         }
22007         if(value.length < this.minLength){
22008             this.markInvalid(String.format(this.minLengthText, this.minLength));
22009             return false;
22010         }
22011         if(value.length > this.maxLength){
22012             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
22013             return false;
22014         }
22015         if(this.vtype){
22016             var vt = Roo.form.VTypes;
22017             if(!vt[this.vtype](value, this)){
22018                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
22019                 return false;
22020             }
22021         }
22022         if(typeof this.validator == "function"){
22023             var msg = this.validator(value);
22024             if(msg !== true){
22025                 this.markInvalid(msg);
22026                 return false;
22027             }
22028         }
22029         if(this.regex && !this.regex.test(value)){
22030             this.markInvalid(this.regexText);
22031             return false;
22032         }
22033         return true;
22034     },
22035
22036     /**
22037      * Selects text in this field
22038      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
22039      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
22040      */
22041     selectText : function(start, end){
22042         var v = this.getRawValue();
22043         if(v.length > 0){
22044             start = start === undefined ? 0 : start;
22045             end = end === undefined ? v.length : end;
22046             var d = this.el.dom;
22047             if(d.setSelectionRange){
22048                 d.setSelectionRange(start, end);
22049             }else if(d.createTextRange){
22050                 var range = d.createTextRange();
22051                 range.moveStart("character", start);
22052                 range.moveEnd("character", v.length-end);
22053                 range.select();
22054             }
22055         }
22056     },
22057
22058     /**
22059      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
22060      * This only takes effect if grow = true, and fires the autosize event.
22061      */
22062     autoSize : function(){
22063         if(!this.grow || !this.rendered){
22064             return;
22065         }
22066         if(!this.metrics){
22067             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
22068         }
22069         var el = this.el;
22070         var v = el.dom.value;
22071         var d = document.createElement('div');
22072         d.appendChild(document.createTextNode(v));
22073         v = d.innerHTML;
22074         d = null;
22075         v += "&#160;";
22076         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
22077         this.el.setWidth(w);
22078         this.fireEvent("autosize", this, w);
22079     },
22080     
22081     // private
22082     SafariOnKeyDown : function(event)
22083     {
22084         // this is a workaround for a password hang bug on chrome/ webkit.
22085         
22086         var isSelectAll = false;
22087         
22088         if(this.el.dom.selectionEnd > 0){
22089             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
22090         }
22091         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
22092             event.preventDefault();
22093             this.setValue('');
22094             return;
22095         }
22096         
22097         if(isSelectAll){ // backspace and delete key
22098             
22099             event.preventDefault();
22100             // this is very hacky as keydown always get's upper case.
22101             //
22102             var cc = String.fromCharCode(event.getCharCode());
22103             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
22104             
22105         }
22106         
22107         
22108     }
22109 });/*
22110  * Based on:
22111  * Ext JS Library 1.1.1
22112  * Copyright(c) 2006-2007, Ext JS, LLC.
22113  *
22114  * Originally Released Under LGPL - original licence link has changed is not relivant.
22115  *
22116  * Fork - LGPL
22117  * <script type="text/javascript">
22118  */
22119  
22120 /**
22121  * @class Roo.form.Hidden
22122  * @extends Roo.form.TextField
22123  * Simple Hidden element used on forms 
22124  * 
22125  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
22126  * 
22127  * @constructor
22128  * Creates a new Hidden form element.
22129  * @param {Object} config Configuration options
22130  */
22131
22132
22133
22134 // easy hidden field...
22135 Roo.form.Hidden = function(config){
22136     Roo.form.Hidden.superclass.constructor.call(this, config);
22137 };
22138   
22139 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
22140     fieldLabel:      '',
22141     inputType:      'hidden',
22142     width:          50,
22143     allowBlank:     true,
22144     labelSeparator: '',
22145     hidden:         true,
22146     itemCls :       'x-form-item-display-none'
22147
22148
22149 });
22150
22151
22152 /*
22153  * Based on:
22154  * Ext JS Library 1.1.1
22155  * Copyright(c) 2006-2007, Ext JS, LLC.
22156  *
22157  * Originally Released Under LGPL - original licence link has changed is not relivant.
22158  *
22159  * Fork - LGPL
22160  * <script type="text/javascript">
22161  */
22162  
22163 /**
22164  * @class Roo.form.TriggerField
22165  * @extends Roo.form.TextField
22166  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
22167  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
22168  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
22169  * for which you can provide a custom implementation.  For example:
22170  * <pre><code>
22171 var trigger = new Roo.form.TriggerField();
22172 trigger.onTriggerClick = myTriggerFn;
22173 trigger.applyTo('my-field');
22174 </code></pre>
22175  *
22176  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
22177  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
22178  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
22179  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
22180  * @constructor
22181  * Create a new TriggerField.
22182  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
22183  * to the base TextField)
22184  */
22185 Roo.form.TriggerField = function(config){
22186     this.mimicing = false;
22187     Roo.form.TriggerField.superclass.constructor.call(this, config);
22188 };
22189
22190 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
22191     /**
22192      * @cfg {String} triggerClass A CSS class to apply to the trigger
22193      */
22194     /**
22195      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
22196      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
22197      */
22198     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
22199     /**
22200      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
22201      */
22202     hideTrigger:false,
22203
22204     /** @cfg {Boolean} grow @hide */
22205     /** @cfg {Number} growMin @hide */
22206     /** @cfg {Number} growMax @hide */
22207
22208     /**
22209      * @hide 
22210      * @method
22211      */
22212     autoSize: Roo.emptyFn,
22213     // private
22214     monitorTab : true,
22215     // private
22216     deferHeight : true,
22217
22218     
22219     actionMode : 'wrap',
22220     // private
22221     onResize : function(w, h){
22222         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
22223         if(typeof w == 'number'){
22224             var x = w - this.trigger.getWidth();
22225             this.el.setWidth(this.adjustWidth('input', x));
22226             this.trigger.setStyle('left', x+'px');
22227         }
22228     },
22229
22230     // private
22231     adjustSize : Roo.BoxComponent.prototype.adjustSize,
22232
22233     // private
22234     getResizeEl : function(){
22235         return this.wrap;
22236     },
22237
22238     // private
22239     getPositionEl : function(){
22240         return this.wrap;
22241     },
22242
22243     // private
22244     alignErrorIcon : function(){
22245         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
22246     },
22247
22248     // private
22249     onRender : function(ct, position){
22250         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
22251         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
22252         this.trigger = this.wrap.createChild(this.triggerConfig ||
22253                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
22254         if(this.hideTrigger){
22255             this.trigger.setDisplayed(false);
22256         }
22257         this.initTrigger();
22258         if(!this.width){
22259             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
22260         }
22261     },
22262
22263     // private
22264     initTrigger : function(){
22265         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
22266         this.trigger.addClassOnOver('x-form-trigger-over');
22267         this.trigger.addClassOnClick('x-form-trigger-click');
22268     },
22269
22270     // private
22271     onDestroy : function(){
22272         if(this.trigger){
22273             this.trigger.removeAllListeners();
22274             this.trigger.remove();
22275         }
22276         if(this.wrap){
22277             this.wrap.remove();
22278         }
22279         Roo.form.TriggerField.superclass.onDestroy.call(this);
22280     },
22281
22282     // private
22283     onFocus : function(){
22284         Roo.form.TriggerField.superclass.onFocus.call(this);
22285         if(!this.mimicing){
22286             this.wrap.addClass('x-trigger-wrap-focus');
22287             this.mimicing = true;
22288             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
22289             if(this.monitorTab){
22290                 this.el.on("keydown", this.checkTab, this);
22291             }
22292         }
22293     },
22294
22295     // private
22296     checkTab : function(e){
22297         if(e.getKey() == e.TAB){
22298             this.triggerBlur();
22299         }
22300     },
22301
22302     // private
22303     onBlur : function(){
22304         // do nothing
22305     },
22306
22307     // private
22308     mimicBlur : function(e, t){
22309         if(!this.wrap.contains(t) && this.validateBlur()){
22310             this.triggerBlur();
22311         }
22312     },
22313
22314     // private
22315     triggerBlur : function(){
22316         this.mimicing = false;
22317         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
22318         if(this.monitorTab){
22319             this.el.un("keydown", this.checkTab, this);
22320         }
22321         this.wrap.removeClass('x-trigger-wrap-focus');
22322         Roo.form.TriggerField.superclass.onBlur.call(this);
22323     },
22324
22325     // private
22326     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
22327     validateBlur : function(e, t){
22328         return true;
22329     },
22330
22331     // private
22332     onDisable : function(){
22333         Roo.form.TriggerField.superclass.onDisable.call(this);
22334         if(this.wrap){
22335             this.wrap.addClass('x-item-disabled');
22336         }
22337     },
22338
22339     // private
22340     onEnable : function(){
22341         Roo.form.TriggerField.superclass.onEnable.call(this);
22342         if(this.wrap){
22343             this.wrap.removeClass('x-item-disabled');
22344         }
22345     },
22346
22347     // private
22348     onShow : function(){
22349         var ae = this.getActionEl();
22350         
22351         if(ae){
22352             ae.dom.style.display = '';
22353             ae.dom.style.visibility = 'visible';
22354         }
22355     },
22356
22357     // private
22358     
22359     onHide : function(){
22360         var ae = this.getActionEl();
22361         ae.dom.style.display = 'none';
22362     },
22363
22364     /**
22365      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
22366      * by an implementing function.
22367      * @method
22368      * @param {EventObject} e
22369      */
22370     onTriggerClick : Roo.emptyFn
22371 });
22372
22373 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
22374 // to be extended by an implementing class.  For an example of implementing this class, see the custom
22375 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
22376 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
22377     initComponent : function(){
22378         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
22379
22380         this.triggerConfig = {
22381             tag:'span', cls:'x-form-twin-triggers', cn:[
22382             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
22383             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
22384         ]};
22385     },
22386
22387     getTrigger : function(index){
22388         return this.triggers[index];
22389     },
22390
22391     initTrigger : function(){
22392         var ts = this.trigger.select('.x-form-trigger', true);
22393         this.wrap.setStyle('overflow', 'hidden');
22394         var triggerField = this;
22395         ts.each(function(t, all, index){
22396             t.hide = function(){
22397                 var w = triggerField.wrap.getWidth();
22398                 this.dom.style.display = 'none';
22399                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
22400             };
22401             t.show = function(){
22402                 var w = triggerField.wrap.getWidth();
22403                 this.dom.style.display = '';
22404                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
22405             };
22406             var triggerIndex = 'Trigger'+(index+1);
22407
22408             if(this['hide'+triggerIndex]){
22409                 t.dom.style.display = 'none';
22410             }
22411             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
22412             t.addClassOnOver('x-form-trigger-over');
22413             t.addClassOnClick('x-form-trigger-click');
22414         }, this);
22415         this.triggers = ts.elements;
22416     },
22417
22418     onTrigger1Click : Roo.emptyFn,
22419     onTrigger2Click : Roo.emptyFn
22420 });/*
22421  * Based on:
22422  * Ext JS Library 1.1.1
22423  * Copyright(c) 2006-2007, Ext JS, LLC.
22424  *
22425  * Originally Released Under LGPL - original licence link has changed is not relivant.
22426  *
22427  * Fork - LGPL
22428  * <script type="text/javascript">
22429  */
22430  
22431 /**
22432  * @class Roo.form.TextArea
22433  * @extends Roo.form.TextField
22434  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
22435  * support for auto-sizing.
22436  * @constructor
22437  * Creates a new TextArea
22438  * @param {Object} config Configuration options
22439  */
22440 Roo.form.TextArea = function(config){
22441     Roo.form.TextArea.superclass.constructor.call(this, config);
22442     // these are provided exchanges for backwards compat
22443     // minHeight/maxHeight were replaced by growMin/growMax to be
22444     // compatible with TextField growing config values
22445     if(this.minHeight !== undefined){
22446         this.growMin = this.minHeight;
22447     }
22448     if(this.maxHeight !== undefined){
22449         this.growMax = this.maxHeight;
22450     }
22451 };
22452
22453 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
22454     /**
22455      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
22456      */
22457     growMin : 60,
22458     /**
22459      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
22460      */
22461     growMax: 1000,
22462     /**
22463      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
22464      * in the field (equivalent to setting overflow: hidden, defaults to false)
22465      */
22466     preventScrollbars: false,
22467     /**
22468      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
22469      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
22470      */
22471
22472     // private
22473     onRender : function(ct, position){
22474         if(!this.el){
22475             this.defaultAutoCreate = {
22476                 tag: "textarea",
22477                 style:"width:300px;height:60px;",
22478                 autocomplete: "off"
22479             };
22480         }
22481         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
22482         if(this.grow){
22483             this.textSizeEl = Roo.DomHelper.append(document.body, {
22484                 tag: "pre", cls: "x-form-grow-sizer"
22485             });
22486             if(this.preventScrollbars){
22487                 this.el.setStyle("overflow", "hidden");
22488             }
22489             this.el.setHeight(this.growMin);
22490         }
22491     },
22492
22493     onDestroy : function(){
22494         if(this.textSizeEl){
22495             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
22496         }
22497         Roo.form.TextArea.superclass.onDestroy.call(this);
22498     },
22499
22500     // private
22501     onKeyUp : function(e){
22502         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
22503             this.autoSize();
22504         }
22505     },
22506
22507     /**
22508      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
22509      * This only takes effect if grow = true, and fires the autosize event if the height changes.
22510      */
22511     autoSize : function(){
22512         if(!this.grow || !this.textSizeEl){
22513             return;
22514         }
22515         var el = this.el;
22516         var v = el.dom.value;
22517         var ts = this.textSizeEl;
22518
22519         ts.innerHTML = '';
22520         ts.appendChild(document.createTextNode(v));
22521         v = ts.innerHTML;
22522
22523         Roo.fly(ts).setWidth(this.el.getWidth());
22524         if(v.length < 1){
22525             v = "&#160;&#160;";
22526         }else{
22527             if(Roo.isIE){
22528                 v = v.replace(/\n/g, '<p>&#160;</p>');
22529             }
22530             v += "&#160;\n&#160;";
22531         }
22532         ts.innerHTML = v;
22533         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
22534         if(h != this.lastHeight){
22535             this.lastHeight = h;
22536             this.el.setHeight(h);
22537             this.fireEvent("autosize", this, h);
22538         }
22539     }
22540 });/*
22541  * Based on:
22542  * Ext JS Library 1.1.1
22543  * Copyright(c) 2006-2007, Ext JS, LLC.
22544  *
22545  * Originally Released Under LGPL - original licence link has changed is not relivant.
22546  *
22547  * Fork - LGPL
22548  * <script type="text/javascript">
22549  */
22550  
22551
22552 /**
22553  * @class Roo.form.NumberField
22554  * @extends Roo.form.TextField
22555  * Numeric text field that provides automatic keystroke filtering and numeric validation.
22556  * @constructor
22557  * Creates a new NumberField
22558  * @param {Object} config Configuration options
22559  */
22560 Roo.form.NumberField = function(config){
22561     Roo.form.NumberField.superclass.constructor.call(this, config);
22562 };
22563
22564 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
22565     /**
22566      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
22567      */
22568     fieldClass: "x-form-field x-form-num-field",
22569     /**
22570      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
22571      */
22572     allowDecimals : true,
22573     /**
22574      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
22575      */
22576     decimalSeparator : ".",
22577     /**
22578      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
22579      */
22580     decimalPrecision : 2,
22581     /**
22582      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
22583      */
22584     allowNegative : true,
22585     /**
22586      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
22587      */
22588     minValue : Number.NEGATIVE_INFINITY,
22589     /**
22590      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
22591      */
22592     maxValue : Number.MAX_VALUE,
22593     /**
22594      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
22595      */
22596     minText : "The minimum value for this field is {0}",
22597     /**
22598      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
22599      */
22600     maxText : "The maximum value for this field is {0}",
22601     /**
22602      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
22603      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
22604      */
22605     nanText : "{0} is not a valid number",
22606
22607     // private
22608     initEvents : function(){
22609         Roo.form.NumberField.superclass.initEvents.call(this);
22610         var allowed = "0123456789";
22611         if(this.allowDecimals){
22612             allowed += this.decimalSeparator;
22613         }
22614         if(this.allowNegative){
22615             allowed += "-";
22616         }
22617         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
22618         var keyPress = function(e){
22619             var k = e.getKey();
22620             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
22621                 return;
22622             }
22623             var c = e.getCharCode();
22624             if(allowed.indexOf(String.fromCharCode(c)) === -1){
22625                 e.stopEvent();
22626             }
22627         };
22628         this.el.on("keypress", keyPress, this);
22629     },
22630
22631     // private
22632     validateValue : function(value){
22633         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
22634             return false;
22635         }
22636         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22637              return true;
22638         }
22639         var num = this.parseValue(value);
22640         if(isNaN(num)){
22641             this.markInvalid(String.format(this.nanText, value));
22642             return false;
22643         }
22644         if(num < this.minValue){
22645             this.markInvalid(String.format(this.minText, this.minValue));
22646             return false;
22647         }
22648         if(num > this.maxValue){
22649             this.markInvalid(String.format(this.maxText, this.maxValue));
22650             return false;
22651         }
22652         return true;
22653     },
22654
22655     getValue : function(){
22656         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
22657     },
22658
22659     // private
22660     parseValue : function(value){
22661         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
22662         return isNaN(value) ? '' : value;
22663     },
22664
22665     // private
22666     fixPrecision : function(value){
22667         var nan = isNaN(value);
22668         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
22669             return nan ? '' : value;
22670         }
22671         return parseFloat(value).toFixed(this.decimalPrecision);
22672     },
22673
22674     setValue : function(v){
22675         v = this.fixPrecision(v);
22676         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
22677     },
22678
22679     // private
22680     decimalPrecisionFcn : function(v){
22681         return Math.floor(v);
22682     },
22683
22684     beforeBlur : function(){
22685         var v = this.parseValue(this.getRawValue());
22686         if(v){
22687             this.setValue(v);
22688         }
22689     }
22690 });/*
22691  * Based on:
22692  * Ext JS Library 1.1.1
22693  * Copyright(c) 2006-2007, Ext JS, LLC.
22694  *
22695  * Originally Released Under LGPL - original licence link has changed is not relivant.
22696  *
22697  * Fork - LGPL
22698  * <script type="text/javascript">
22699  */
22700  
22701 /**
22702  * @class Roo.form.DateField
22703  * @extends Roo.form.TriggerField
22704  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22705 * @constructor
22706 * Create a new DateField
22707 * @param {Object} config
22708  */
22709 Roo.form.DateField = function(config){
22710     Roo.form.DateField.superclass.constructor.call(this, config);
22711     
22712       this.addEvents({
22713          
22714         /**
22715          * @event select
22716          * Fires when a date is selected
22717              * @param {Roo.form.DateField} combo This combo box
22718              * @param {Date} date The date selected
22719              */
22720         'select' : true
22721          
22722     });
22723     
22724     
22725     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22726     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22727     this.ddMatch = null;
22728     if(this.disabledDates){
22729         var dd = this.disabledDates;
22730         var re = "(?:";
22731         for(var i = 0; i < dd.length; i++){
22732             re += dd[i];
22733             if(i != dd.length-1) re += "|";
22734         }
22735         this.ddMatch = new RegExp(re + ")");
22736     }
22737 };
22738
22739 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
22740     /**
22741      * @cfg {String} format
22742      * The default date format string which can be overriden for localization support.  The format must be
22743      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22744      */
22745     format : "m/d/y",
22746     /**
22747      * @cfg {String} altFormats
22748      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22749      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22750      */
22751     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22752     /**
22753      * @cfg {Array} disabledDays
22754      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22755      */
22756     disabledDays : null,
22757     /**
22758      * @cfg {String} disabledDaysText
22759      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22760      */
22761     disabledDaysText : "Disabled",
22762     /**
22763      * @cfg {Array} disabledDates
22764      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22765      * expression so they are very powerful. Some examples:
22766      * <ul>
22767      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22768      * <li>["03/08", "09/16"] would disable those days for every year</li>
22769      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22770      * <li>["03/../2006"] would disable every day in March 2006</li>
22771      * <li>["^03"] would disable every day in every March</li>
22772      * </ul>
22773      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22774      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22775      */
22776     disabledDates : null,
22777     /**
22778      * @cfg {String} disabledDatesText
22779      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22780      */
22781     disabledDatesText : "Disabled",
22782     /**
22783      * @cfg {Date/String} minValue
22784      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22785      * valid format (defaults to null).
22786      */
22787     minValue : null,
22788     /**
22789      * @cfg {Date/String} maxValue
22790      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22791      * valid format (defaults to null).
22792      */
22793     maxValue : null,
22794     /**
22795      * @cfg {String} minText
22796      * The error text to display when the date in the cell is before minValue (defaults to
22797      * 'The date in this field must be after {minValue}').
22798      */
22799     minText : "The date in this field must be equal to or after {0}",
22800     /**
22801      * @cfg {String} maxText
22802      * The error text to display when the date in the cell is after maxValue (defaults to
22803      * 'The date in this field must be before {maxValue}').
22804      */
22805     maxText : "The date in this field must be equal to or before {0}",
22806     /**
22807      * @cfg {String} invalidText
22808      * The error text to display when the date in the field is invalid (defaults to
22809      * '{value} is not a valid date - it must be in the format {format}').
22810      */
22811     invalidText : "{0} is not a valid date - it must be in the format {1}",
22812     /**
22813      * @cfg {String} triggerClass
22814      * An additional CSS class used to style the trigger button.  The trigger will always get the
22815      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22816      * which displays a calendar icon).
22817      */
22818     triggerClass : 'x-form-date-trigger',
22819     
22820
22821     /**
22822      * @cfg {Boolean} useIso
22823      * if enabled, then the date field will use a hidden field to store the 
22824      * real value as iso formated date. default (false)
22825      */ 
22826     useIso : false,
22827     /**
22828      * @cfg {String/Object} autoCreate
22829      * A DomHelper element spec, or true for a default element spec (defaults to
22830      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22831      */ 
22832     // private
22833     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
22834     
22835     // private
22836     hiddenField: false,
22837     
22838     onRender : function(ct, position)
22839     {
22840         Roo.form.DateField.superclass.onRender.call(this, ct, position);
22841         if (this.useIso) {
22842             //this.el.dom.removeAttribute('name'); 
22843             Roo.log("Changing name?");
22844             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
22845             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22846                     'before', true);
22847             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
22848             // prevent input submission
22849             this.hiddenName = this.name;
22850         }
22851             
22852             
22853     },
22854     
22855     // private
22856     validateValue : function(value)
22857     {
22858         value = this.formatDate(value);
22859         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
22860             Roo.log('super failed');
22861             return false;
22862         }
22863         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22864              return true;
22865         }
22866         var svalue = value;
22867         value = this.parseDate(value);
22868         if(!value){
22869             Roo.log('parse date failed' + svalue);
22870             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22871             return false;
22872         }
22873         var time = value.getTime();
22874         if(this.minValue && time < this.minValue.getTime()){
22875             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22876             return false;
22877         }
22878         if(this.maxValue && time > this.maxValue.getTime()){
22879             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22880             return false;
22881         }
22882         if(this.disabledDays){
22883             var day = value.getDay();
22884             for(var i = 0; i < this.disabledDays.length; i++) {
22885                 if(day === this.disabledDays[i]){
22886                     this.markInvalid(this.disabledDaysText);
22887                     return false;
22888                 }
22889             }
22890         }
22891         var fvalue = this.formatDate(value);
22892         if(this.ddMatch && this.ddMatch.test(fvalue)){
22893             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22894             return false;
22895         }
22896         return true;
22897     },
22898
22899     // private
22900     // Provides logic to override the default TriggerField.validateBlur which just returns true
22901     validateBlur : function(){
22902         return !this.menu || !this.menu.isVisible();
22903     },
22904     
22905     getName: function()
22906     {
22907         // returns hidden if it's set..
22908         if (!this.rendered) {return ''};
22909         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
22910         
22911     },
22912
22913     /**
22914      * Returns the current date value of the date field.
22915      * @return {Date} The date value
22916      */
22917     getValue : function(){
22918         
22919         return  this.hiddenField ?
22920                 this.hiddenField.value :
22921                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
22922     },
22923
22924     /**
22925      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22926      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
22927      * (the default format used is "m/d/y").
22928      * <br />Usage:
22929      * <pre><code>
22930 //All of these calls set the same date value (May 4, 2006)
22931
22932 //Pass a date object:
22933 var dt = new Date('5/4/06');
22934 dateField.setValue(dt);
22935
22936 //Pass a date string (default format):
22937 dateField.setValue('5/4/06');
22938
22939 //Pass a date string (custom format):
22940 dateField.format = 'Y-m-d';
22941 dateField.setValue('2006-5-4');
22942 </code></pre>
22943      * @param {String/Date} date The date or valid date string
22944      */
22945     setValue : function(date){
22946         if (this.hiddenField) {
22947             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22948         }
22949         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22950         // make sure the value field is always stored as a date..
22951         this.value = this.parseDate(date);
22952         
22953         
22954     },
22955
22956     // private
22957     parseDate : function(value){
22958         if(!value || value instanceof Date){
22959             return value;
22960         }
22961         var v = Date.parseDate(value, this.format);
22962          if (!v && this.useIso) {
22963             v = Date.parseDate(value, 'Y-m-d');
22964         }
22965         if(!v && this.altFormats){
22966             if(!this.altFormatsArray){
22967                 this.altFormatsArray = this.altFormats.split("|");
22968             }
22969             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22970                 v = Date.parseDate(value, this.altFormatsArray[i]);
22971             }
22972         }
22973         return v;
22974     },
22975
22976     // private
22977     formatDate : function(date, fmt){
22978         return (!date || !(date instanceof Date)) ?
22979                date : date.dateFormat(fmt || this.format);
22980     },
22981
22982     // private
22983     menuListeners : {
22984         select: function(m, d){
22985             
22986             this.setValue(d);
22987             this.fireEvent('select', this, d);
22988         },
22989         show : function(){ // retain focus styling
22990             this.onFocus();
22991         },
22992         hide : function(){
22993             this.focus.defer(10, this);
22994             var ml = this.menuListeners;
22995             this.menu.un("select", ml.select,  this);
22996             this.menu.un("show", ml.show,  this);
22997             this.menu.un("hide", ml.hide,  this);
22998         }
22999     },
23000
23001     // private
23002     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
23003     onTriggerClick : function(){
23004         if(this.disabled){
23005             return;
23006         }
23007         if(this.menu == null){
23008             this.menu = new Roo.menu.DateMenu();
23009         }
23010         Roo.apply(this.menu.picker,  {
23011             showClear: this.allowBlank,
23012             minDate : this.minValue,
23013             maxDate : this.maxValue,
23014             disabledDatesRE : this.ddMatch,
23015             disabledDatesText : this.disabledDatesText,
23016             disabledDays : this.disabledDays,
23017             disabledDaysText : this.disabledDaysText,
23018             format : this.useIso ? 'Y-m-d' : this.format,
23019             minText : String.format(this.minText, this.formatDate(this.minValue)),
23020             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
23021         });
23022         this.menu.on(Roo.apply({}, this.menuListeners, {
23023             scope:this
23024         }));
23025         this.menu.picker.setValue(this.getValue() || new Date());
23026         this.menu.show(this.el, "tl-bl?");
23027     },
23028
23029     beforeBlur : function(){
23030         var v = this.parseDate(this.getRawValue());
23031         if(v){
23032             this.setValue(v);
23033         }
23034     }
23035
23036     /** @cfg {Boolean} grow @hide */
23037     /** @cfg {Number} growMin @hide */
23038     /** @cfg {Number} growMax @hide */
23039     /**
23040      * @hide
23041      * @method autoSize
23042      */
23043 });/*
23044  * Based on:
23045  * Ext JS Library 1.1.1
23046  * Copyright(c) 2006-2007, Ext JS, LLC.
23047  *
23048  * Originally Released Under LGPL - original licence link has changed is not relivant.
23049  *
23050  * Fork - LGPL
23051  * <script type="text/javascript">
23052  */
23053  
23054 /**
23055  * @class Roo.form.MonthField
23056  * @extends Roo.form.TriggerField
23057  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
23058 * @constructor
23059 * Create a new MonthField
23060 * @param {Object} config
23061  */
23062 Roo.form.MonthField = function(config){
23063     
23064     Roo.form.MonthField.superclass.constructor.call(this, config);
23065     
23066       this.addEvents({
23067          
23068         /**
23069          * @event select
23070          * Fires when a date is selected
23071              * @param {Roo.form.MonthFieeld} combo This combo box
23072              * @param {Date} date The date selected
23073              */
23074         'select' : true
23075          
23076     });
23077     
23078     
23079     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
23080     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
23081     this.ddMatch = null;
23082     if(this.disabledDates){
23083         var dd = this.disabledDates;
23084         var re = "(?:";
23085         for(var i = 0; i < dd.length; i++){
23086             re += dd[i];
23087             if(i != dd.length-1) re += "|";
23088         }
23089         this.ddMatch = new RegExp(re + ")");
23090     }
23091 };
23092
23093 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
23094     /**
23095      * @cfg {String} format
23096      * The default date format string which can be overriden for localization support.  The format must be
23097      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
23098      */
23099     format : "M Y",
23100     /**
23101      * @cfg {String} altFormats
23102      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
23103      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
23104      */
23105     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
23106     /**
23107      * @cfg {Array} disabledDays
23108      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
23109      */
23110     disabledDays : [0,1,2,3,4,5,6],
23111     /**
23112      * @cfg {String} disabledDaysText
23113      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
23114      */
23115     disabledDaysText : "Disabled",
23116     /**
23117      * @cfg {Array} disabledDates
23118      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
23119      * expression so they are very powerful. Some examples:
23120      * <ul>
23121      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
23122      * <li>["03/08", "09/16"] would disable those days for every year</li>
23123      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
23124      * <li>["03/../2006"] would disable every day in March 2006</li>
23125      * <li>["^03"] would disable every day in every March</li>
23126      * </ul>
23127      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
23128      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
23129      */
23130     disabledDates : null,
23131     /**
23132      * @cfg {String} disabledDatesText
23133      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
23134      */
23135     disabledDatesText : "Disabled",
23136     /**
23137      * @cfg {Date/String} minValue
23138      * The minimum allowed date. Can be either a Javascript date object or a string date in a
23139      * valid format (defaults to null).
23140      */
23141     minValue : null,
23142     /**
23143      * @cfg {Date/String} maxValue
23144      * The maximum allowed date. Can be either a Javascript date object or a string date in a
23145      * valid format (defaults to null).
23146      */
23147     maxValue : null,
23148     /**
23149      * @cfg {String} minText
23150      * The error text to display when the date in the cell is before minValue (defaults to
23151      * 'The date in this field must be after {minValue}').
23152      */
23153     minText : "The date in this field must be equal to or after {0}",
23154     /**
23155      * @cfg {String} maxTextf
23156      * The error text to display when the date in the cell is after maxValue (defaults to
23157      * 'The date in this field must be before {maxValue}').
23158      */
23159     maxText : "The date in this field must be equal to or before {0}",
23160     /**
23161      * @cfg {String} invalidText
23162      * The error text to display when the date in the field is invalid (defaults to
23163      * '{value} is not a valid date - it must be in the format {format}').
23164      */
23165     invalidText : "{0} is not a valid date - it must be in the format {1}",
23166     /**
23167      * @cfg {String} triggerClass
23168      * An additional CSS class used to style the trigger button.  The trigger will always get the
23169      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
23170      * which displays a calendar icon).
23171      */
23172     triggerClass : 'x-form-date-trigger',
23173     
23174
23175     /**
23176      * @cfg {Boolean} useIso
23177      * if enabled, then the date field will use a hidden field to store the 
23178      * real value as iso formated date. default (true)
23179      */ 
23180     useIso : true,
23181     /**
23182      * @cfg {String/Object} autoCreate
23183      * A DomHelper element spec, or true for a default element spec (defaults to
23184      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
23185      */ 
23186     // private
23187     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
23188     
23189     // private
23190     hiddenField: false,
23191     
23192     hideMonthPicker : false,
23193     
23194     onRender : function(ct, position)
23195     {
23196         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
23197         if (this.useIso) {
23198             this.el.dom.removeAttribute('name'); 
23199             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
23200                     'before', true);
23201             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
23202             // prevent input submission
23203             this.hiddenName = this.name;
23204         }
23205             
23206             
23207     },
23208     
23209     // private
23210     validateValue : function(value)
23211     {
23212         value = this.formatDate(value);
23213         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
23214             return false;
23215         }
23216         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
23217              return true;
23218         }
23219         var svalue = value;
23220         value = this.parseDate(value);
23221         if(!value){
23222             this.markInvalid(String.format(this.invalidText, svalue, this.format));
23223             return false;
23224         }
23225         var time = value.getTime();
23226         if(this.minValue && time < this.minValue.getTime()){
23227             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
23228             return false;
23229         }
23230         if(this.maxValue && time > this.maxValue.getTime()){
23231             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
23232             return false;
23233         }
23234         /*if(this.disabledDays){
23235             var day = value.getDay();
23236             for(var i = 0; i < this.disabledDays.length; i++) {
23237                 if(day === this.disabledDays[i]){
23238                     this.markInvalid(this.disabledDaysText);
23239                     return false;
23240                 }
23241             }
23242         }
23243         */
23244         var fvalue = this.formatDate(value);
23245         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
23246             this.markInvalid(String.format(this.disabledDatesText, fvalue));
23247             return false;
23248         }
23249         */
23250         return true;
23251     },
23252
23253     // private
23254     // Provides logic to override the default TriggerField.validateBlur which just returns true
23255     validateBlur : function(){
23256         return !this.menu || !this.menu.isVisible();
23257     },
23258
23259     /**
23260      * Returns the current date value of the date field.
23261      * @return {Date} The date value
23262      */
23263     getValue : function(){
23264         
23265         
23266         
23267         return  this.hiddenField ?
23268                 this.hiddenField.value :
23269                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
23270     },
23271
23272     /**
23273      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
23274      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
23275      * (the default format used is "m/d/y").
23276      * <br />Usage:
23277      * <pre><code>
23278 //All of these calls set the same date value (May 4, 2006)
23279
23280 //Pass a date object:
23281 var dt = new Date('5/4/06');
23282 monthField.setValue(dt);
23283
23284 //Pass a date string (default format):
23285 monthField.setValue('5/4/06');
23286
23287 //Pass a date string (custom format):
23288 monthField.format = 'Y-m-d';
23289 monthField.setValue('2006-5-4');
23290 </code></pre>
23291      * @param {String/Date} date The date or valid date string
23292      */
23293     setValue : function(date){
23294         Roo.log('month setValue' + date);
23295         // can only be first of month..
23296         
23297         var val = this.parseDate(date);
23298         
23299         if (this.hiddenField) {
23300             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
23301         }
23302         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
23303         this.value = this.parseDate(date);
23304     },
23305
23306     // private
23307     parseDate : function(value){
23308         if(!value || value instanceof Date){
23309             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
23310             return value;
23311         }
23312         var v = Date.parseDate(value, this.format);
23313         if (!v && this.useIso) {
23314             v = Date.parseDate(value, 'Y-m-d');
23315         }
23316         if (v) {
23317             // 
23318             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
23319         }
23320         
23321         
23322         if(!v && this.altFormats){
23323             if(!this.altFormatsArray){
23324                 this.altFormatsArray = this.altFormats.split("|");
23325             }
23326             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
23327                 v = Date.parseDate(value, this.altFormatsArray[i]);
23328             }
23329         }
23330         return v;
23331     },
23332
23333     // private
23334     formatDate : function(date, fmt){
23335         return (!date || !(date instanceof Date)) ?
23336                date : date.dateFormat(fmt || this.format);
23337     },
23338
23339     // private
23340     menuListeners : {
23341         select: function(m, d){
23342             this.setValue(d);
23343             this.fireEvent('select', this, d);
23344         },
23345         show : function(){ // retain focus styling
23346             this.onFocus();
23347         },
23348         hide : function(){
23349             this.focus.defer(10, this);
23350             var ml = this.menuListeners;
23351             this.menu.un("select", ml.select,  this);
23352             this.menu.un("show", ml.show,  this);
23353             this.menu.un("hide", ml.hide,  this);
23354         }
23355     },
23356     // private
23357     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
23358     onTriggerClick : function(){
23359         if(this.disabled){
23360             return;
23361         }
23362         if(this.menu == null){
23363             this.menu = new Roo.menu.DateMenu();
23364            
23365         }
23366         
23367         Roo.apply(this.menu.picker,  {
23368             
23369             showClear: this.allowBlank,
23370             minDate : this.minValue,
23371             maxDate : this.maxValue,
23372             disabledDatesRE : this.ddMatch,
23373             disabledDatesText : this.disabledDatesText,
23374             
23375             format : this.useIso ? 'Y-m-d' : this.format,
23376             minText : String.format(this.minText, this.formatDate(this.minValue)),
23377             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
23378             
23379         });
23380          this.menu.on(Roo.apply({}, this.menuListeners, {
23381             scope:this
23382         }));
23383        
23384         
23385         var m = this.menu;
23386         var p = m.picker;
23387         
23388         // hide month picker get's called when we called by 'before hide';
23389         
23390         var ignorehide = true;
23391         p.hideMonthPicker  = function(disableAnim){
23392             if (ignorehide) {
23393                 return;
23394             }
23395              if(this.monthPicker){
23396                 Roo.log("hideMonthPicker called");
23397                 if(disableAnim === true){
23398                     this.monthPicker.hide();
23399                 }else{
23400                     this.monthPicker.slideOut('t', {duration:.2});
23401                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
23402                     p.fireEvent("select", this, this.value);
23403                     m.hide();
23404                 }
23405             }
23406         }
23407         
23408         Roo.log('picker set value');
23409         Roo.log(this.getValue());
23410         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
23411         m.show(this.el, 'tl-bl?');
23412         ignorehide  = false;
23413         // this will trigger hideMonthPicker..
23414         
23415         
23416         // hidden the day picker
23417         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
23418         
23419         
23420         
23421       
23422         
23423         p.showMonthPicker.defer(100, p);
23424     
23425         
23426        
23427     },
23428
23429     beforeBlur : function(){
23430         var v = this.parseDate(this.getRawValue());
23431         if(v){
23432             this.setValue(v);
23433         }
23434     }
23435
23436     /** @cfg {Boolean} grow @hide */
23437     /** @cfg {Number} growMin @hide */
23438     /** @cfg {Number} growMax @hide */
23439     /**
23440      * @hide
23441      * @method autoSize
23442      */
23443 });/*
23444  * Based on:
23445  * Ext JS Library 1.1.1
23446  * Copyright(c) 2006-2007, Ext JS, LLC.
23447  *
23448  * Originally Released Under LGPL - original licence link has changed is not relivant.
23449  *
23450  * Fork - LGPL
23451  * <script type="text/javascript">
23452  */
23453  
23454
23455 /**
23456  * @class Roo.form.ComboBox
23457  * @extends Roo.form.TriggerField
23458  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
23459  * @constructor
23460  * Create a new ComboBox.
23461  * @param {Object} config Configuration options
23462  */
23463 Roo.form.ComboBox = function(config){
23464     Roo.form.ComboBox.superclass.constructor.call(this, config);
23465     this.addEvents({
23466         /**
23467          * @event expand
23468          * Fires when the dropdown list is expanded
23469              * @param {Roo.form.ComboBox} combo This combo box
23470              */
23471         'expand' : true,
23472         /**
23473          * @event collapse
23474          * Fires when the dropdown list is collapsed
23475              * @param {Roo.form.ComboBox} combo This combo box
23476              */
23477         'collapse' : true,
23478         /**
23479          * @event beforeselect
23480          * Fires before a list item is selected. Return false to cancel the selection.
23481              * @param {Roo.form.ComboBox} combo This combo box
23482              * @param {Roo.data.Record} record The data record returned from the underlying store
23483              * @param {Number} index The index of the selected item in the dropdown list
23484              */
23485         'beforeselect' : true,
23486         /**
23487          * @event select
23488          * Fires when a list item is selected
23489              * @param {Roo.form.ComboBox} combo This combo box
23490              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
23491              * @param {Number} index The index of the selected item in the dropdown list
23492              */
23493         'select' : true,
23494         /**
23495          * @event beforequery
23496          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
23497          * The event object passed has these properties:
23498              * @param {Roo.form.ComboBox} combo This combo box
23499              * @param {String} query The query
23500              * @param {Boolean} forceAll true to force "all" query
23501              * @param {Boolean} cancel true to cancel the query
23502              * @param {Object} e The query event object
23503              */
23504         'beforequery': true,
23505          /**
23506          * @event add
23507          * Fires when the 'add' icon is pressed (add a listener to enable add button)
23508              * @param {Roo.form.ComboBox} combo This combo box
23509              */
23510         'add' : true,
23511         /**
23512          * @event edit
23513          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
23514              * @param {Roo.form.ComboBox} combo This combo box
23515              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
23516              */
23517         'edit' : true
23518         
23519         
23520     });
23521     if(this.transform){
23522         this.allowDomMove = false;
23523         var s = Roo.getDom(this.transform);
23524         if(!this.hiddenName){
23525             this.hiddenName = s.name;
23526         }
23527         if(!this.store){
23528             this.mode = 'local';
23529             var d = [], opts = s.options;
23530             for(var i = 0, len = opts.length;i < len; i++){
23531                 var o = opts[i];
23532                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
23533                 if(o.selected) {
23534                     this.value = value;
23535                 }
23536                 d.push([value, o.text]);
23537             }
23538             this.store = new Roo.data.SimpleStore({
23539                 'id': 0,
23540                 fields: ['value', 'text'],
23541                 data : d
23542             });
23543             this.valueField = 'value';
23544             this.displayField = 'text';
23545         }
23546         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
23547         if(!this.lazyRender){
23548             this.target = true;
23549             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
23550             s.parentNode.removeChild(s); // remove it
23551             this.render(this.el.parentNode);
23552         }else{
23553             s.parentNode.removeChild(s); // remove it
23554         }
23555
23556     }
23557     if (this.store) {
23558         this.store = Roo.factory(this.store, Roo.data);
23559     }
23560     
23561     this.selectedIndex = -1;
23562     if(this.mode == 'local'){
23563         if(config.queryDelay === undefined){
23564             this.queryDelay = 10;
23565         }
23566         if(config.minChars === undefined){
23567             this.minChars = 0;
23568         }
23569     }
23570 };
23571
23572 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
23573     /**
23574      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
23575      */
23576     /**
23577      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
23578      * rendering into an Roo.Editor, defaults to false)
23579      */
23580     /**
23581      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
23582      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
23583      */
23584     /**
23585      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
23586      */
23587     /**
23588      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
23589      * the dropdown list (defaults to undefined, with no header element)
23590      */
23591
23592      /**
23593      * @cfg {String/Roo.Template} tpl The template to use to render the output
23594      */
23595      
23596     // private
23597     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
23598     /**
23599      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
23600      */
23601     listWidth: undefined,
23602     /**
23603      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
23604      * mode = 'remote' or 'text' if mode = 'local')
23605      */
23606     displayField: undefined,
23607     /**
23608      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
23609      * mode = 'remote' or 'value' if mode = 'local'). 
23610      * Note: use of a valueField requires the user make a selection
23611      * in order for a value to be mapped.
23612      */
23613     valueField: undefined,
23614     
23615     
23616     /**
23617      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
23618      * field's data value (defaults to the underlying DOM element's name)
23619      */
23620     hiddenName: undefined,
23621     /**
23622      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
23623      */
23624     listClass: '',
23625     /**
23626      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
23627      */
23628     selectedClass: 'x-combo-selected',
23629     /**
23630      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
23631      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
23632      * which displays a downward arrow icon).
23633      */
23634     triggerClass : 'x-form-arrow-trigger',
23635     /**
23636      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
23637      */
23638     shadow:'sides',
23639     /**
23640      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
23641      * anchor positions (defaults to 'tl-bl')
23642      */
23643     listAlign: 'tl-bl?',
23644     /**
23645      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
23646      */
23647     maxHeight: 300,
23648     /**
23649      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
23650      * query specified by the allQuery config option (defaults to 'query')
23651      */
23652     triggerAction: 'query',
23653     /**
23654      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
23655      * (defaults to 4, does not apply if editable = false)
23656      */
23657     minChars : 4,
23658     /**
23659      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
23660      * delay (typeAheadDelay) if it matches a known value (defaults to false)
23661      */
23662     typeAhead: false,
23663     /**
23664      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
23665      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
23666      */
23667     queryDelay: 500,
23668     /**
23669      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
23670      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
23671      */
23672     pageSize: 0,
23673     /**
23674      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
23675      * when editable = true (defaults to false)
23676      */
23677     selectOnFocus:false,
23678     /**
23679      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
23680      */
23681     queryParam: 'query',
23682     /**
23683      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
23684      * when mode = 'remote' (defaults to 'Loading...')
23685      */
23686     loadingText: 'Loading...',
23687     /**
23688      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
23689      */
23690     resizable: false,
23691     /**
23692      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
23693      */
23694     handleHeight : 8,
23695     /**
23696      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
23697      * traditional select (defaults to true)
23698      */
23699     editable: true,
23700     /**
23701      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
23702      */
23703     allQuery: '',
23704     /**
23705      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
23706      */
23707     mode: 'remote',
23708     /**
23709      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
23710      * listWidth has a higher value)
23711      */
23712     minListWidth : 70,
23713     /**
23714      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
23715      * allow the user to set arbitrary text into the field (defaults to false)
23716      */
23717     forceSelection:false,
23718     /**
23719      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
23720      * if typeAhead = true (defaults to 250)
23721      */
23722     typeAheadDelay : 250,
23723     /**
23724      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
23725      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
23726      */
23727     valueNotFoundText : undefined,
23728     /**
23729      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
23730      */
23731     blockFocus : false,
23732     
23733     /**
23734      * @cfg {Boolean} disableClear Disable showing of clear button.
23735      */
23736     disableClear : false,
23737     /**
23738      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
23739      */
23740     alwaysQuery : false,
23741     
23742     //private
23743     addicon : false,
23744     editicon: false,
23745     
23746     // element that contains real text value.. (when hidden is used..)
23747      
23748     // private
23749     onRender : function(ct, position){
23750         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
23751         if(this.hiddenName){
23752             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
23753                     'before', true);
23754             this.hiddenField.value =
23755                 this.hiddenValue !== undefined ? this.hiddenValue :
23756                 this.value !== undefined ? this.value : '';
23757
23758             // prevent input submission
23759             this.el.dom.removeAttribute('name');
23760              
23761              
23762         }
23763         if(Roo.isGecko){
23764             this.el.dom.setAttribute('autocomplete', 'off');
23765         }
23766
23767         var cls = 'x-combo-list';
23768
23769         this.list = new Roo.Layer({
23770             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
23771         });
23772
23773         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
23774         this.list.setWidth(lw);
23775         this.list.swallowEvent('mousewheel');
23776         this.assetHeight = 0;
23777
23778         if(this.title){
23779             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
23780             this.assetHeight += this.header.getHeight();
23781         }
23782
23783         this.innerList = this.list.createChild({cls:cls+'-inner'});
23784         this.innerList.on('mouseover', this.onViewOver, this);
23785         this.innerList.on('mousemove', this.onViewMove, this);
23786         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23787         
23788         if(this.allowBlank && !this.pageSize && !this.disableClear){
23789             this.footer = this.list.createChild({cls:cls+'-ft'});
23790             this.pageTb = new Roo.Toolbar(this.footer);
23791            
23792         }
23793         if(this.pageSize){
23794             this.footer = this.list.createChild({cls:cls+'-ft'});
23795             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
23796                     {pageSize: this.pageSize});
23797             
23798         }
23799         
23800         if (this.pageTb && this.allowBlank && !this.disableClear) {
23801             var _this = this;
23802             this.pageTb.add(new Roo.Toolbar.Fill(), {
23803                 cls: 'x-btn-icon x-btn-clear',
23804                 text: '&#160;',
23805                 handler: function()
23806                 {
23807                     _this.collapse();
23808                     _this.clearValue();
23809                     _this.onSelect(false, -1);
23810                 }
23811             });
23812         }
23813         if (this.footer) {
23814             this.assetHeight += this.footer.getHeight();
23815         }
23816         
23817
23818         if(!this.tpl){
23819             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
23820         }
23821
23822         this.view = new Roo.View(this.innerList, this.tpl, {
23823             singleSelect:true, store: this.store, selectedClass: this.selectedClass
23824         });
23825
23826         this.view.on('click', this.onViewClick, this);
23827
23828         this.store.on('beforeload', this.onBeforeLoad, this);
23829         this.store.on('load', this.onLoad, this);
23830         this.store.on('loadexception', this.onLoadException, this);
23831
23832         if(this.resizable){
23833             this.resizer = new Roo.Resizable(this.list,  {
23834                pinned:true, handles:'se'
23835             });
23836             this.resizer.on('resize', function(r, w, h){
23837                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
23838                 this.listWidth = w;
23839                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
23840                 this.restrictHeight();
23841             }, this);
23842             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
23843         }
23844         if(!this.editable){
23845             this.editable = true;
23846             this.setEditable(false);
23847         }  
23848         
23849         
23850         if (typeof(this.events.add.listeners) != 'undefined') {
23851             
23852             this.addicon = this.wrap.createChild(
23853                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
23854        
23855             this.addicon.on('click', function(e) {
23856                 this.fireEvent('add', this);
23857             }, this);
23858         }
23859         if (typeof(this.events.edit.listeners) != 'undefined') {
23860             
23861             this.editicon = this.wrap.createChild(
23862                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
23863             if (this.addicon) {
23864                 this.editicon.setStyle('margin-left', '40px');
23865             }
23866             this.editicon.on('click', function(e) {
23867                 
23868                 // we fire even  if inothing is selected..
23869                 this.fireEvent('edit', this, this.lastData );
23870                 
23871             }, this);
23872         }
23873         
23874         
23875         
23876     },
23877
23878     // private
23879     initEvents : function(){
23880         Roo.form.ComboBox.superclass.initEvents.call(this);
23881
23882         this.keyNav = new Roo.KeyNav(this.el, {
23883             "up" : function(e){
23884                 this.inKeyMode = true;
23885                 this.selectPrev();
23886             },
23887
23888             "down" : function(e){
23889                 if(!this.isExpanded()){
23890                     this.onTriggerClick();
23891                 }else{
23892                     this.inKeyMode = true;
23893                     this.selectNext();
23894                 }
23895             },
23896
23897             "enter" : function(e){
23898                 this.onViewClick();
23899                 //return true;
23900             },
23901
23902             "esc" : function(e){
23903                 this.collapse();
23904             },
23905
23906             "tab" : function(e){
23907                 this.onViewClick(false);
23908                 this.fireEvent("specialkey", this, e);
23909                 return true;
23910             },
23911
23912             scope : this,
23913
23914             doRelay : function(foo, bar, hname){
23915                 if(hname == 'down' || this.scope.isExpanded()){
23916                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
23917                 }
23918                 return true;
23919             },
23920
23921             forceKeyDown: true
23922         });
23923         this.queryDelay = Math.max(this.queryDelay || 10,
23924                 this.mode == 'local' ? 10 : 250);
23925         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
23926         if(this.typeAhead){
23927             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
23928         }
23929         if(this.editable !== false){
23930             this.el.on("keyup", this.onKeyUp, this);
23931         }
23932         if(this.forceSelection){
23933             this.on('blur', this.doForce, this);
23934         }
23935     },
23936
23937     onDestroy : function(){
23938         if(this.view){
23939             this.view.setStore(null);
23940             this.view.el.removeAllListeners();
23941             this.view.el.remove();
23942             this.view.purgeListeners();
23943         }
23944         if(this.list){
23945             this.list.destroy();
23946         }
23947         if(this.store){
23948             this.store.un('beforeload', this.onBeforeLoad, this);
23949             this.store.un('load', this.onLoad, this);
23950             this.store.un('loadexception', this.onLoadException, this);
23951         }
23952         Roo.form.ComboBox.superclass.onDestroy.call(this);
23953     },
23954
23955     // private
23956     fireKey : function(e){
23957         if(e.isNavKeyPress() && !this.list.isVisible()){
23958             this.fireEvent("specialkey", this, e);
23959         }
23960     },
23961
23962     // private
23963     onResize: function(w, h){
23964         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
23965         
23966         if(typeof w != 'number'){
23967             // we do not handle it!?!?
23968             return;
23969         }
23970         var tw = this.trigger.getWidth();
23971         tw += this.addicon ? this.addicon.getWidth() : 0;
23972         tw += this.editicon ? this.editicon.getWidth() : 0;
23973         var x = w - tw;
23974         this.el.setWidth( this.adjustWidth('input', x));
23975             
23976         this.trigger.setStyle('left', x+'px');
23977         
23978         if(this.list && this.listWidth === undefined){
23979             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
23980             this.list.setWidth(lw);
23981             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23982         }
23983         
23984     
23985         
23986     },
23987
23988     /**
23989      * Allow or prevent the user from directly editing the field text.  If false is passed,
23990      * the user will only be able to select from the items defined in the dropdown list.  This method
23991      * is the runtime equivalent of setting the 'editable' config option at config time.
23992      * @param {Boolean} value True to allow the user to directly edit the field text
23993      */
23994     setEditable : function(value){
23995         if(value == this.editable){
23996             return;
23997         }
23998         this.editable = value;
23999         if(!value){
24000             this.el.dom.setAttribute('readOnly', true);
24001             this.el.on('mousedown', this.onTriggerClick,  this);
24002             this.el.addClass('x-combo-noedit');
24003         }else{
24004             this.el.dom.setAttribute('readOnly', false);
24005             this.el.un('mousedown', this.onTriggerClick,  this);
24006             this.el.removeClass('x-combo-noedit');
24007         }
24008     },
24009
24010     // private
24011     onBeforeLoad : function(){
24012         if(!this.hasFocus){
24013             return;
24014         }
24015         this.innerList.update(this.loadingText ?
24016                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
24017         this.restrictHeight();
24018         this.selectedIndex = -1;
24019     },
24020
24021     // private
24022     onLoad : function(){
24023         if(!this.hasFocus){
24024             return;
24025         }
24026         if(this.store.getCount() > 0){
24027             this.expand();
24028             this.restrictHeight();
24029             if(this.lastQuery == this.allQuery){
24030                 if(this.editable){
24031                     this.el.dom.select();
24032                 }
24033                 if(!this.selectByValue(this.value, true)){
24034                     this.select(0, true);
24035                 }
24036             }else{
24037                 this.selectNext();
24038                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
24039                     this.taTask.delay(this.typeAheadDelay);
24040                 }
24041             }
24042         }else{
24043             this.onEmptyResults();
24044         }
24045         //this.el.focus();
24046     },
24047     // private
24048     onLoadException : function()
24049     {
24050         this.collapse();
24051         Roo.log(this.store.reader.jsonData);
24052         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
24053             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
24054         }
24055         
24056         
24057     },
24058     // private
24059     onTypeAhead : function(){
24060         if(this.store.getCount() > 0){
24061             var r = this.store.getAt(0);
24062             var newValue = r.data[this.displayField];
24063             var len = newValue.length;
24064             var selStart = this.getRawValue().length;
24065             if(selStart != len){
24066                 this.setRawValue(newValue);
24067                 this.selectText(selStart, newValue.length);
24068             }
24069         }
24070     },
24071
24072     // private
24073     onSelect : function(record, index){
24074         if(this.fireEvent('beforeselect', this, record, index) !== false){
24075             this.setFromData(index > -1 ? record.data : false);
24076             this.collapse();
24077             this.fireEvent('select', this, record, index);
24078         }
24079     },
24080
24081     /**
24082      * Returns the currently selected field value or empty string if no value is set.
24083      * @return {String} value The selected value
24084      */
24085     getValue : function(){
24086         if(this.valueField){
24087             return typeof this.value != 'undefined' ? this.value : '';
24088         }else{
24089             return Roo.form.ComboBox.superclass.getValue.call(this);
24090         }
24091     },
24092
24093     /**
24094      * Clears any text/value currently set in the field
24095      */
24096     clearValue : function(){
24097         if(this.hiddenField){
24098             this.hiddenField.value = '';
24099         }
24100         this.value = '';
24101         this.setRawValue('');
24102         this.lastSelectionText = '';
24103         
24104     },
24105
24106     /**
24107      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
24108      * will be displayed in the field.  If the value does not match the data value of an existing item,
24109      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
24110      * Otherwise the field will be blank (although the value will still be set).
24111      * @param {String} value The value to match
24112      */
24113     setValue : function(v){
24114         var text = v;
24115         if(this.valueField){
24116             var r = this.findRecord(this.valueField, v);
24117             if(r){
24118                 text = r.data[this.displayField];
24119             }else if(this.valueNotFoundText !== undefined){
24120                 text = this.valueNotFoundText;
24121             }
24122         }
24123         this.lastSelectionText = text;
24124         if(this.hiddenField){
24125             this.hiddenField.value = v;
24126         }
24127         Roo.form.ComboBox.superclass.setValue.call(this, text);
24128         this.value = v;
24129     },
24130     /**
24131      * @property {Object} the last set data for the element
24132      */
24133     
24134     lastData : false,
24135     /**
24136      * Sets the value of the field based on a object which is related to the record format for the store.
24137      * @param {Object} value the value to set as. or false on reset?
24138      */
24139     setFromData : function(o){
24140         var dv = ''; // display value
24141         var vv = ''; // value value..
24142         this.lastData = o;
24143         if (this.displayField) {
24144             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
24145         } else {
24146             // this is an error condition!!!
24147             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
24148         }
24149         
24150         if(this.valueField){
24151             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
24152         }
24153         if(this.hiddenField){
24154             this.hiddenField.value = vv;
24155             
24156             this.lastSelectionText = dv;
24157             Roo.form.ComboBox.superclass.setValue.call(this, dv);
24158             this.value = vv;
24159             return;
24160         }
24161         // no hidden field.. - we store the value in 'value', but still display
24162         // display field!!!!
24163         this.lastSelectionText = dv;
24164         Roo.form.ComboBox.superclass.setValue.call(this, dv);
24165         this.value = vv;
24166         
24167         
24168     },
24169     // private
24170     reset : function(){
24171         // overridden so that last data is reset..
24172         this.setValue(this.originalValue);
24173         this.clearInvalid();
24174         this.lastData = false;
24175         if (this.view) {
24176             this.view.clearSelections();
24177         }
24178     },
24179     // private
24180     findRecord : function(prop, value){
24181         var record;
24182         if(this.store.getCount() > 0){
24183             this.store.each(function(r){
24184                 if(r.data[prop] == value){
24185                     record = r;
24186                     return false;
24187                 }
24188                 return true;
24189             });
24190         }
24191         return record;
24192     },
24193     
24194     getName: function()
24195     {
24196         // returns hidden if it's set..
24197         if (!this.rendered) {return ''};
24198         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
24199         
24200     },
24201     // private
24202     onViewMove : function(e, t){
24203         this.inKeyMode = false;
24204     },
24205
24206     // private
24207     onViewOver : function(e, t){
24208         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
24209             return;
24210         }
24211         var item = this.view.findItemFromChild(t);
24212         if(item){
24213             var index = this.view.indexOf(item);
24214             this.select(index, false);
24215         }
24216     },
24217
24218     // private
24219     onViewClick : function(doFocus)
24220     {
24221         var index = this.view.getSelectedIndexes()[0];
24222         var r = this.store.getAt(index);
24223         if(r){
24224             this.onSelect(r, index);
24225         }
24226         if(doFocus !== false && !this.blockFocus){
24227             this.el.focus();
24228         }
24229     },
24230
24231     // private
24232     restrictHeight : function(){
24233         this.innerList.dom.style.height = '';
24234         var inner = this.innerList.dom;
24235         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
24236         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
24237         this.list.beginUpdate();
24238         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
24239         this.list.alignTo(this.el, this.listAlign);
24240         this.list.endUpdate();
24241     },
24242
24243     // private
24244     onEmptyResults : function(){
24245         this.collapse();
24246     },
24247
24248     /**
24249      * Returns true if the dropdown list is expanded, else false.
24250      */
24251     isExpanded : function(){
24252         return this.list.isVisible();
24253     },
24254
24255     /**
24256      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
24257      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
24258      * @param {String} value The data value of the item to select
24259      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
24260      * selected item if it is not currently in view (defaults to true)
24261      * @return {Boolean} True if the value matched an item in the list, else false
24262      */
24263     selectByValue : function(v, scrollIntoView){
24264         if(v !== undefined && v !== null){
24265             var r = this.findRecord(this.valueField || this.displayField, v);
24266             if(r){
24267                 this.select(this.store.indexOf(r), scrollIntoView);
24268                 return true;
24269             }
24270         }
24271         return false;
24272     },
24273
24274     /**
24275      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
24276      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
24277      * @param {Number} index The zero-based index of the list item to select
24278      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
24279      * selected item if it is not currently in view (defaults to true)
24280      */
24281     select : function(index, scrollIntoView){
24282         this.selectedIndex = index;
24283         this.view.select(index);
24284         if(scrollIntoView !== false){
24285             var el = this.view.getNode(index);
24286             if(el){
24287                 this.innerList.scrollChildIntoView(el, false);
24288             }
24289         }
24290     },
24291
24292     // private
24293     selectNext : function(){
24294         var ct = this.store.getCount();
24295         if(ct > 0){
24296             if(this.selectedIndex == -1){
24297                 this.select(0);
24298             }else if(this.selectedIndex < ct-1){
24299                 this.select(this.selectedIndex+1);
24300             }
24301         }
24302     },
24303
24304     // private
24305     selectPrev : function(){
24306         var ct = this.store.getCount();
24307         if(ct > 0){
24308             if(this.selectedIndex == -1){
24309                 this.select(0);
24310             }else if(this.selectedIndex != 0){
24311                 this.select(this.selectedIndex-1);
24312             }
24313         }
24314     },
24315
24316     // private
24317     onKeyUp : function(e){
24318         if(this.editable !== false && !e.isSpecialKey()){
24319             this.lastKey = e.getKey();
24320             this.dqTask.delay(this.queryDelay);
24321         }
24322     },
24323
24324     // private
24325     validateBlur : function(){
24326         return !this.list || !this.list.isVisible();   
24327     },
24328
24329     // private
24330     initQuery : function(){
24331         this.doQuery(this.getRawValue());
24332     },
24333
24334     // private
24335     doForce : function(){
24336         if(this.el.dom.value.length > 0){
24337             this.el.dom.value =
24338                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
24339              
24340         }
24341     },
24342
24343     /**
24344      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
24345      * query allowing the query action to be canceled if needed.
24346      * @param {String} query The SQL query to execute
24347      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
24348      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
24349      * saved in the current store (defaults to false)
24350      */
24351     doQuery : function(q, forceAll){
24352         if(q === undefined || q === null){
24353             q = '';
24354         }
24355         var qe = {
24356             query: q,
24357             forceAll: forceAll,
24358             combo: this,
24359             cancel:false
24360         };
24361         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
24362             return false;
24363         }
24364         q = qe.query;
24365         forceAll = qe.forceAll;
24366         if(forceAll === true || (q.length >= this.minChars)){
24367             if(this.lastQuery != q || this.alwaysQuery){
24368                 this.lastQuery = q;
24369                 if(this.mode == 'local'){
24370                     this.selectedIndex = -1;
24371                     if(forceAll){
24372                         this.store.clearFilter();
24373                     }else{
24374                         this.store.filter(this.displayField, q);
24375                     }
24376                     this.onLoad();
24377                 }else{
24378                     this.store.baseParams[this.queryParam] = q;
24379                     this.store.load({
24380                         params: this.getParams(q)
24381                     });
24382                     this.expand();
24383                 }
24384             }else{
24385                 this.selectedIndex = -1;
24386                 this.onLoad();   
24387             }
24388         }
24389     },
24390
24391     // private
24392     getParams : function(q){
24393         var p = {};
24394         //p[this.queryParam] = q;
24395         if(this.pageSize){
24396             p.start = 0;
24397             p.limit = this.pageSize;
24398         }
24399         return p;
24400     },
24401
24402     /**
24403      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
24404      */
24405     collapse : function(){
24406         if(!this.isExpanded()){
24407             return;
24408         }
24409         this.list.hide();
24410         Roo.get(document).un('mousedown', this.collapseIf, this);
24411         Roo.get(document).un('mousewheel', this.collapseIf, this);
24412         if (!this.editable) {
24413             Roo.get(document).un('keydown', this.listKeyPress, this);
24414         }
24415         this.fireEvent('collapse', this);
24416     },
24417
24418     // private
24419     collapseIf : function(e){
24420         if(!e.within(this.wrap) && !e.within(this.list)){
24421             this.collapse();
24422         }
24423     },
24424
24425     /**
24426      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
24427      */
24428     expand : function(){
24429         if(this.isExpanded() || !this.hasFocus){
24430             return;
24431         }
24432         this.list.alignTo(this.el, this.listAlign);
24433         this.list.show();
24434         Roo.get(document).on('mousedown', this.collapseIf, this);
24435         Roo.get(document).on('mousewheel', this.collapseIf, this);
24436         if (!this.editable) {
24437             Roo.get(document).on('keydown', this.listKeyPress, this);
24438         }
24439         
24440         this.fireEvent('expand', this);
24441     },
24442
24443     // private
24444     // Implements the default empty TriggerField.onTriggerClick function
24445     onTriggerClick : function(){
24446         if(this.disabled){
24447             return;
24448         }
24449         if(this.isExpanded()){
24450             this.collapse();
24451             if (!this.blockFocus) {
24452                 this.el.focus();
24453             }
24454             
24455         }else {
24456             this.hasFocus = true;
24457             if(this.triggerAction == 'all') {
24458                 this.doQuery(this.allQuery, true);
24459             } else {
24460                 this.doQuery(this.getRawValue());
24461             }
24462             if (!this.blockFocus) {
24463                 this.el.focus();
24464             }
24465         }
24466     },
24467     listKeyPress : function(e)
24468     {
24469         //Roo.log('listkeypress');
24470         // scroll to first matching element based on key pres..
24471         if (e.isSpecialKey()) {
24472             return false;
24473         }
24474         var k = String.fromCharCode(e.getKey()).toUpperCase();
24475         //Roo.log(k);
24476         var match  = false;
24477         var csel = this.view.getSelectedNodes();
24478         var cselitem = false;
24479         if (csel.length) {
24480             var ix = this.view.indexOf(csel[0]);
24481             cselitem  = this.store.getAt(ix);
24482             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
24483                 cselitem = false;
24484             }
24485             
24486         }
24487         
24488         this.store.each(function(v) { 
24489             if (cselitem) {
24490                 // start at existing selection.
24491                 if (cselitem.id == v.id) {
24492                     cselitem = false;
24493                 }
24494                 return;
24495             }
24496                 
24497             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
24498                 match = this.store.indexOf(v);
24499                 return false;
24500             }
24501         }, this);
24502         
24503         if (match === false) {
24504             return true; // no more action?
24505         }
24506         // scroll to?
24507         this.view.select(match);
24508         var sn = Roo.get(this.view.getSelectedNodes()[0])
24509         sn.scrollIntoView(sn.dom.parentNode, false);
24510     }
24511
24512     /** 
24513     * @cfg {Boolean} grow 
24514     * @hide 
24515     */
24516     /** 
24517     * @cfg {Number} growMin 
24518     * @hide 
24519     */
24520     /** 
24521     * @cfg {Number} growMax 
24522     * @hide 
24523     */
24524     /**
24525      * @hide
24526      * @method autoSize
24527      */
24528 });/*
24529  * Copyright(c) 2010-2012, Roo J Solutions Limited
24530  *
24531  * Licence LGPL
24532  *
24533  */
24534
24535 /**
24536  * @class Roo.form.ComboBoxArray
24537  * @extends Roo.form.TextField
24538  * A facebook style adder... for lists of email / people / countries  etc...
24539  * pick multiple items from a combo box, and shows each one.
24540  *
24541  *  Fred [x]  Brian [x]  [Pick another |v]
24542  *
24543  *
24544  *  For this to work: it needs various extra information
24545  *    - normal combo problay has
24546  *      name, hiddenName
24547  *    + displayField, valueField
24548  *
24549  *    For our purpose...
24550  *
24551  *
24552  *   If we change from 'extends' to wrapping...
24553  *   
24554  *  
24555  *
24556  
24557  
24558  * @constructor
24559  * Create a new ComboBoxArray.
24560  * @param {Object} config Configuration options
24561  */
24562  
24563
24564 Roo.form.ComboBoxArray = function(config)
24565 {
24566     
24567     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
24568     
24569     this.items = new Roo.util.MixedCollection(false);
24570     
24571     // construct the child combo...
24572     
24573     
24574     
24575     
24576    
24577     
24578 }
24579
24580  
24581 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
24582
24583     /**
24584      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
24585      */
24586     
24587     lastData : false,
24588     
24589     // behavies liek a hiddne field
24590     inputType:      'hidden',
24591     /**
24592      * @cfg {Number} width The width of the box that displays the selected element
24593      */ 
24594     width:          300,
24595
24596     
24597     
24598     /**
24599      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
24600      */
24601     name : false,
24602     /**
24603      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
24604      */
24605     hiddenName : false,
24606     
24607     
24608     // private the array of items that are displayed..
24609     items  : false,
24610     // private - the hidden field el.
24611     hiddenEl : false,
24612     // private - the filed el..
24613     el : false,
24614     
24615     //validateValue : function() { return true; }, // all values are ok!
24616     //onAddClick: function() { },
24617     
24618     onRender : function(ct, position) 
24619     {
24620         
24621         // create the standard hidden element
24622         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
24623         
24624         
24625         // give fake names to child combo;
24626         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
24627         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
24628         
24629         this.combo = Roo.factory(this.combo, Roo.form);
24630         this.combo.onRender(ct, position);
24631         if (typeof(this.combo.width) != 'undefined') {
24632             this.combo.onResize(this.combo.width,0);
24633         }
24634         
24635         this.combo.initEvents();
24636         
24637         // assigned so form know we need to do this..
24638         this.store          = this.combo.store;
24639         this.valueField     = this.combo.valueField;
24640         this.displayField   = this.combo.displayField ;
24641         
24642         
24643         this.combo.wrap.addClass('x-cbarray-grp');
24644         
24645         var cbwrap = this.combo.wrap.createChild(
24646             {tag: 'div', cls: 'x-cbarray-cb'},
24647             this.combo.el.dom
24648         );
24649         
24650              
24651         this.hiddenEl = this.combo.wrap.createChild({
24652             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
24653         });
24654         this.el = this.combo.wrap.createChild({
24655             tag: 'input',  type:'hidden' , name: this.name, value : ''
24656         });
24657          //   this.el.dom.removeAttribute("name");
24658         
24659         
24660         this.outerWrap = this.combo.wrap;
24661         this.wrap = cbwrap;
24662         
24663         this.outerWrap.setWidth(this.width);
24664         this.outerWrap.dom.removeChild(this.el.dom);
24665         
24666         this.wrap.dom.appendChild(this.el.dom);
24667         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
24668         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
24669         
24670         this.combo.trigger.setStyle('position','relative');
24671         this.combo.trigger.setStyle('left', '0px');
24672         this.combo.trigger.setStyle('top', '2px');
24673         
24674         this.combo.el.setStyle('vertical-align', 'text-bottom');
24675         
24676         //this.trigger.setStyle('vertical-align', 'top');
24677         
24678         // this should use the code from combo really... on('add' ....)
24679         if (this.adder) {
24680             
24681         
24682             this.adder = this.outerWrap.createChild(
24683                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
24684             var _t = this;
24685             this.adder.on('click', function(e) {
24686                 _t.fireEvent('adderclick', this, e);
24687             }, _t);
24688         }
24689         //var _t = this;
24690         //this.adder.on('click', this.onAddClick, _t);
24691         
24692         
24693         this.combo.on('select', function(cb, rec, ix) {
24694             this.addItem(rec.data);
24695             
24696             cb.setValue('');
24697             cb.el.dom.value = '';
24698             //cb.lastData = rec.data;
24699             // add to list
24700             
24701         }, this);
24702         
24703         
24704     },
24705     
24706     
24707     getName: function()
24708     {
24709         // returns hidden if it's set..
24710         if (!this.rendered) {return ''};
24711         return  this.hiddenName ? this.hiddenName : this.name;
24712         
24713     },
24714     
24715     
24716     onResize: function(w, h){
24717         
24718         return;
24719         // not sure if this is needed..
24720         //this.combo.onResize(w,h);
24721         
24722         if(typeof w != 'number'){
24723             // we do not handle it!?!?
24724             return;
24725         }
24726         var tw = this.combo.trigger.getWidth();
24727         tw += this.addicon ? this.addicon.getWidth() : 0;
24728         tw += this.editicon ? this.editicon.getWidth() : 0;
24729         var x = w - tw;
24730         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
24731             
24732         this.combo.trigger.setStyle('left', '0px');
24733         
24734         if(this.list && this.listWidth === undefined){
24735             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
24736             this.list.setWidth(lw);
24737             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
24738         }
24739         
24740     
24741         
24742     },
24743     
24744     addItem: function(rec)
24745     {
24746         var valueField = this.combo.valueField;
24747         var displayField = this.combo.displayField;
24748         if (this.items.indexOfKey(rec[valueField]) > -1) {
24749             //console.log("GOT " + rec.data.id);
24750             return;
24751         }
24752         
24753         var x = new Roo.form.ComboBoxArray.Item({
24754             //id : rec[this.idField],
24755             data : rec,
24756             displayField : displayField ,
24757             tipField : displayField ,
24758             cb : this
24759         });
24760         // use the 
24761         this.items.add(rec[valueField],x);
24762         // add it before the element..
24763         this.updateHiddenEl();
24764         x.render(this.outerWrap, this.wrap.dom);
24765         // add the image handler..
24766     },
24767     
24768     updateHiddenEl : function()
24769     {
24770         this.validate();
24771         if (!this.hiddenEl) {
24772             return;
24773         }
24774         var ar = [];
24775         var idField = this.combo.valueField;
24776         
24777         this.items.each(function(f) {
24778             ar.push(f.data[idField]);
24779            
24780         });
24781         this.hiddenEl.dom.value = ar.join(',');
24782         this.validate();
24783     },
24784     
24785     reset : function()
24786     {
24787         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
24788         this.items.each(function(f) {
24789            f.remove(); 
24790         });
24791         this.el.dom.value = '';
24792         if (this.hiddenEl) {
24793             this.hiddenEl.dom.value = '';
24794         }
24795         
24796     },
24797     getValue: function()
24798     {
24799         return this.hiddenEl ? this.hiddenEl.dom.value : '';
24800     },
24801     setValue: function(v) // not a valid action - must use addItems..
24802     {
24803          
24804         this.reset();
24805         
24806         
24807         
24808         if (this.store.isLocal && (typeof(v) == 'string')) {
24809             // then we can use the store to find the values..
24810             // comma seperated at present.. this needs to allow JSON based encoding..
24811             this.hiddenEl.value  = v;
24812             var v_ar = [];
24813             Roo.each(v.split(','), function(k) {
24814                 Roo.log("CHECK " + this.valueField + ',' + k);
24815                 var li = this.store.query(this.valueField, k);
24816                 if (!li.length) {
24817                     return;
24818                 }
24819                 var add = {};
24820                 add[this.valueField] = k;
24821                 add[this.displayField] = li.item(0).data[this.displayField];
24822                 
24823                 this.addItem(add);
24824             }, this) 
24825              
24826         }
24827         if (typeof(v) == 'object') {
24828             // then let's assume it's an array of objects..
24829             Roo.each(v, function(l) {
24830                 this.addItem(l);
24831             }, this);
24832              
24833         }
24834         
24835         
24836     },
24837     setFromData: function(v)
24838     {
24839         // this recieves an object, if setValues is called.
24840         this.reset();
24841         this.el.dom.value = v[this.displayField];
24842         this.hiddenEl.dom.value = v[this.valueField];
24843         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
24844             return;
24845         }
24846         var kv = v[this.valueField];
24847         var dv = v[this.displayField];
24848         kv = typeof(kv) != 'string' ? '' : kv;
24849         dv = typeof(dv) != 'string' ? '' : dv;
24850         
24851         
24852         var keys = kv.split(',');
24853         var display = dv.split(',');
24854         for (var i = 0 ; i < keys.length; i++) {
24855             
24856             add = {};
24857             add[this.valueField] = keys[i];
24858             add[this.displayField] = display[i];
24859             this.addItem(add);
24860         }
24861       
24862         
24863     },
24864     
24865     
24866     validateValue : function(value){
24867         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
24868         
24869     }
24870     
24871 });
24872
24873
24874
24875 /**
24876  * @class Roo.form.ComboBoxArray.Item
24877  * @extends Roo.BoxComponent
24878  * A selected item in the list
24879  *  Fred [x]  Brian [x]  [Pick another |v]
24880  * 
24881  * @constructor
24882  * Create a new item.
24883  * @param {Object} config Configuration options
24884  */
24885  
24886 Roo.form.ComboBoxArray.Item = function(config) {
24887     config.id = Roo.id();
24888     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
24889 }
24890
24891 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
24892     data : {},
24893     cb: false,
24894     displayField : false,
24895     tipField : false,
24896     
24897     
24898     defaultAutoCreate : {
24899         tag: 'div',
24900         cls: 'x-cbarray-item',
24901         cn : [ 
24902             { tag: 'div' },
24903             {
24904                 tag: 'img',
24905                 width:16,
24906                 height : 16,
24907                 src : Roo.BLANK_IMAGE_URL ,
24908                 align: 'center'
24909             }
24910         ]
24911         
24912     },
24913     
24914  
24915     onRender : function(ct, position)
24916     {
24917         Roo.form.Field.superclass.onRender.call(this, ct, position);
24918         
24919         if(!this.el){
24920             var cfg = this.getAutoCreate();
24921             this.el = ct.createChild(cfg, position);
24922         }
24923         
24924         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
24925         
24926         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
24927             this.cb.renderer(this.data) :
24928             String.format('{0}',this.data[this.displayField]);
24929         
24930             
24931         this.el.child('div').dom.setAttribute('qtip',
24932                         String.format('{0}',this.data[this.tipField])
24933         );
24934         
24935         this.el.child('img').on('click', this.remove, this);
24936         
24937     },
24938    
24939     remove : function()
24940     {
24941         
24942         this.cb.items.remove(this);
24943         this.el.child('img').un('click', this.remove, this);
24944         this.el.remove();
24945         this.cb.updateHiddenEl();
24946     }
24947     
24948     
24949 });/*
24950  * Based on:
24951  * Ext JS Library 1.1.1
24952  * Copyright(c) 2006-2007, Ext JS, LLC.
24953  *
24954  * Originally Released Under LGPL - original licence link has changed is not relivant.
24955  *
24956  * Fork - LGPL
24957  * <script type="text/javascript">
24958  */
24959 /**
24960  * @class Roo.form.Checkbox
24961  * @extends Roo.form.Field
24962  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
24963  * @constructor
24964  * Creates a new Checkbox
24965  * @param {Object} config Configuration options
24966  */
24967 Roo.form.Checkbox = function(config){
24968     Roo.form.Checkbox.superclass.constructor.call(this, config);
24969     this.addEvents({
24970         /**
24971          * @event check
24972          * Fires when the checkbox is checked or unchecked.
24973              * @param {Roo.form.Checkbox} this This checkbox
24974              * @param {Boolean} checked The new checked value
24975              */
24976         check : true
24977     });
24978 };
24979
24980 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
24981     /**
24982      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
24983      */
24984     focusClass : undefined,
24985     /**
24986      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
24987      */
24988     fieldClass: "x-form-field",
24989     /**
24990      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
24991      */
24992     checked: false,
24993     /**
24994      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
24995      * {tag: "input", type: "checkbox", autocomplete: "off"})
24996      */
24997     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
24998     /**
24999      * @cfg {String} boxLabel The text that appears beside the checkbox
25000      */
25001     boxLabel : "",
25002     /**
25003      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
25004      */  
25005     inputValue : '1',
25006     /**
25007      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
25008      */
25009      valueOff: '0', // value when not checked..
25010
25011     actionMode : 'viewEl', 
25012     //
25013     // private
25014     itemCls : 'x-menu-check-item x-form-item',
25015     groupClass : 'x-menu-group-item',
25016     inputType : 'hidden',
25017     
25018     
25019     inSetChecked: false, // check that we are not calling self...
25020     
25021     inputElement: false, // real input element?
25022     basedOn: false, // ????
25023     
25024     isFormField: true, // not sure where this is needed!!!!
25025
25026     onResize : function(){
25027         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
25028         if(!this.boxLabel){
25029             this.el.alignTo(this.wrap, 'c-c');
25030         }
25031     },
25032
25033     initEvents : function(){
25034         Roo.form.Checkbox.superclass.initEvents.call(this);
25035         this.el.on("click", this.onClick,  this);
25036         this.el.on("change", this.onClick,  this);
25037     },
25038
25039
25040     getResizeEl : function(){
25041         return this.wrap;
25042     },
25043
25044     getPositionEl : function(){
25045         return this.wrap;
25046     },
25047
25048     // private
25049     onRender : function(ct, position){
25050         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
25051         /*
25052         if(this.inputValue !== undefined){
25053             this.el.dom.value = this.inputValue;
25054         }
25055         */
25056         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
25057         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
25058         var viewEl = this.wrap.createChild({ 
25059             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
25060         this.viewEl = viewEl;   
25061         this.wrap.on('click', this.onClick,  this); 
25062         
25063         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
25064         this.el.on('propertychange', this.setFromHidden,  this);  //ie
25065         
25066         
25067         
25068         if(this.boxLabel){
25069             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
25070         //    viewEl.on('click', this.onClick,  this); 
25071         }
25072         //if(this.checked){
25073             this.setChecked(this.checked);
25074         //}else{
25075             //this.checked = this.el.dom;
25076         //}
25077
25078     },
25079
25080     // private
25081     initValue : Roo.emptyFn,
25082
25083     /**
25084      * Returns the checked state of the checkbox.
25085      * @return {Boolean} True if checked, else false
25086      */
25087     getValue : function(){
25088         if(this.el){
25089             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
25090         }
25091         return this.valueOff;
25092         
25093     },
25094
25095         // private
25096     onClick : function(){ 
25097         this.setChecked(!this.checked);
25098
25099         //if(this.el.dom.checked != this.checked){
25100         //    this.setValue(this.el.dom.checked);
25101        // }
25102     },
25103
25104     /**
25105      * Sets the checked state of the checkbox.
25106      * On is always based on a string comparison between inputValue and the param.
25107      * @param {Boolean/String} value - the value to set 
25108      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
25109      */
25110     setValue : function(v,suppressEvent){
25111         
25112         
25113         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
25114         //if(this.el && this.el.dom){
25115         //    this.el.dom.checked = this.checked;
25116         //    this.el.dom.defaultChecked = this.checked;
25117         //}
25118         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
25119         //this.fireEvent("check", this, this.checked);
25120     },
25121     // private..
25122     setChecked : function(state,suppressEvent)
25123     {
25124         if (this.inSetChecked) {
25125             this.checked = state;
25126             return;
25127         }
25128         
25129     
25130         if(this.wrap){
25131             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
25132         }
25133         this.checked = state;
25134         if(suppressEvent !== true){
25135             this.fireEvent('check', this, state);
25136         }
25137         this.inSetChecked = true;
25138         this.el.dom.value = state ? this.inputValue : this.valueOff;
25139         this.inSetChecked = false;
25140         
25141     },
25142     // handle setting of hidden value by some other method!!?!?
25143     setFromHidden: function()
25144     {
25145         if(!this.el){
25146             return;
25147         }
25148         //console.log("SET FROM HIDDEN");
25149         //alert('setFrom hidden');
25150         this.setValue(this.el.dom.value);
25151     },
25152     
25153     onDestroy : function()
25154     {
25155         if(this.viewEl){
25156             Roo.get(this.viewEl).remove();
25157         }
25158          
25159         Roo.form.Checkbox.superclass.onDestroy.call(this);
25160     }
25161
25162 });/*
25163  * Based on:
25164  * Ext JS Library 1.1.1
25165  * Copyright(c) 2006-2007, Ext JS, LLC.
25166  *
25167  * Originally Released Under LGPL - original licence link has changed is not relivant.
25168  *
25169  * Fork - LGPL
25170  * <script type="text/javascript">
25171  */
25172  
25173 /**
25174  * @class Roo.form.Radio
25175  * @extends Roo.form.Checkbox
25176  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
25177  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
25178  * @constructor
25179  * Creates a new Radio
25180  * @param {Object} config Configuration options
25181  */
25182 Roo.form.Radio = function(){
25183     Roo.form.Radio.superclass.constructor.apply(this, arguments);
25184 };
25185 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
25186     inputType: 'radio',
25187
25188     /**
25189      * If this radio is part of a group, it will return the selected value
25190      * @return {String}
25191      */
25192     getGroupValue : function(){
25193         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
25194     },
25195     
25196     
25197     onRender : function(ct, position){
25198         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
25199         
25200         if(this.inputValue !== undefined){
25201             this.el.dom.value = this.inputValue;
25202         }
25203          
25204         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
25205         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
25206         //var viewEl = this.wrap.createChild({ 
25207         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
25208         //this.viewEl = viewEl;   
25209         //this.wrap.on('click', this.onClick,  this); 
25210         
25211         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
25212         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
25213         
25214         
25215         
25216         if(this.boxLabel){
25217             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
25218         //    viewEl.on('click', this.onClick,  this); 
25219         }
25220          if(this.checked){
25221             this.el.dom.checked =   'checked' ;
25222         }
25223          
25224     } 
25225     
25226     
25227 });//<script type="text/javascript">
25228
25229 /*
25230  * Ext JS Library 1.1.1
25231  * Copyright(c) 2006-2007, Ext JS, LLC.
25232  * licensing@extjs.com
25233  * 
25234  * http://www.extjs.com/license
25235  */
25236  
25237  /*
25238   * 
25239   * Known bugs:
25240   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
25241   * - IE ? - no idea how much works there.
25242   * 
25243   * 
25244   * 
25245   */
25246  
25247
25248 /**
25249  * @class Ext.form.HtmlEditor
25250  * @extends Ext.form.Field
25251  * Provides a lightweight HTML Editor component.
25252  *
25253  * This has been tested on Fireforx / Chrome.. IE may not be so great..
25254  * 
25255  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
25256  * supported by this editor.</b><br/><br/>
25257  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
25258  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25259  */
25260 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
25261       /**
25262      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25263      */
25264     toolbars : false,
25265     /**
25266      * @cfg {String} createLinkText The default text for the create link prompt
25267      */
25268     createLinkText : 'Please enter the URL for the link:',
25269     /**
25270      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
25271      */
25272     defaultLinkValue : 'http:/'+'/',
25273    
25274      /**
25275      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25276      *                        Roo.resizable.
25277      */
25278     resizable : false,
25279      /**
25280      * @cfg {Number} height (in pixels)
25281      */   
25282     height: 300,
25283    /**
25284      * @cfg {Number} width (in pixels)
25285      */   
25286     width: 500,
25287     
25288     /**
25289      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25290      * 
25291      */
25292     stylesheets: false,
25293     
25294     // id of frame..
25295     frameId: false,
25296     
25297     // private properties
25298     validationEvent : false,
25299     deferHeight: true,
25300     initialized : false,
25301     activated : false,
25302     sourceEditMode : false,
25303     onFocus : Roo.emptyFn,
25304     iframePad:3,
25305     hideMode:'offsets',
25306     
25307     defaultAutoCreate : { // modified by initCompnoent..
25308         tag: "textarea",
25309         style:"width:500px;height:300px;",
25310         autocomplete: "off"
25311     },
25312
25313     // private
25314     initComponent : function(){
25315         this.addEvents({
25316             /**
25317              * @event initialize
25318              * Fires when the editor is fully initialized (including the iframe)
25319              * @param {HtmlEditor} this
25320              */
25321             initialize: true,
25322             /**
25323              * @event activate
25324              * Fires when the editor is first receives the focus. Any insertion must wait
25325              * until after this event.
25326              * @param {HtmlEditor} this
25327              */
25328             activate: true,
25329              /**
25330              * @event beforesync
25331              * Fires before the textarea is updated with content from the editor iframe. Return false
25332              * to cancel the sync.
25333              * @param {HtmlEditor} this
25334              * @param {String} html
25335              */
25336             beforesync: true,
25337              /**
25338              * @event beforepush
25339              * Fires before the iframe editor is updated with content from the textarea. Return false
25340              * to cancel the push.
25341              * @param {HtmlEditor} this
25342              * @param {String} html
25343              */
25344             beforepush: true,
25345              /**
25346              * @event sync
25347              * Fires when the textarea is updated with content from the editor iframe.
25348              * @param {HtmlEditor} this
25349              * @param {String} html
25350              */
25351             sync: true,
25352              /**
25353              * @event push
25354              * Fires when the iframe editor is updated with content from the textarea.
25355              * @param {HtmlEditor} this
25356              * @param {String} html
25357              */
25358             push: true,
25359              /**
25360              * @event editmodechange
25361              * Fires when the editor switches edit modes
25362              * @param {HtmlEditor} this
25363              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25364              */
25365             editmodechange: true,
25366             /**
25367              * @event editorevent
25368              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25369              * @param {HtmlEditor} this
25370              */
25371             editorevent: true
25372         });
25373         this.defaultAutoCreate =  {
25374             tag: "textarea",
25375             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
25376             autocomplete: "off"
25377         };
25378     },
25379
25380     /**
25381      * Protected method that will not generally be called directly. It
25382      * is called when the editor creates its toolbar. Override this method if you need to
25383      * add custom toolbar buttons.
25384      * @param {HtmlEditor} editor
25385      */
25386     createToolbar : function(editor){
25387         if (!editor.toolbars || !editor.toolbars.length) {
25388             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
25389         }
25390         
25391         for (var i =0 ; i < editor.toolbars.length;i++) {
25392             editor.toolbars[i] = Roo.factory(
25393                     typeof(editor.toolbars[i]) == 'string' ?
25394                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
25395                 Roo.form.HtmlEditor);
25396             editor.toolbars[i].init(editor);
25397         }
25398          
25399         
25400     },
25401
25402     /**
25403      * Protected method that will not generally be called directly. It
25404      * is called when the editor initializes the iframe with HTML contents. Override this method if you
25405      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25406      */
25407     getDocMarkup : function(){
25408         // body styles..
25409         var st = '';
25410         if (this.stylesheets === false) {
25411             
25412             Roo.get(document.head).select('style').each(function(node) {
25413                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25414             });
25415             
25416             Roo.get(document.head).select('link').each(function(node) { 
25417                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25418             });
25419             
25420         } else if (!this.stylesheets.length) {
25421                 // simple..
25422                 st = '<style type="text/css">' +
25423                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25424                    '</style>';
25425         } else {
25426             Roo.each(this.stylesheets, function(s) {
25427                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
25428             });
25429             
25430         }
25431         
25432         st +=  '<style type="text/css">' +
25433             'IMG { cursor: pointer } ' +
25434         '</style>';
25435
25436         
25437         return '<html><head>' + st  +
25438             //<style type="text/css">' +
25439             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25440             //'</style>' +
25441             ' </head><body class="roo-htmleditor-body"></body></html>';
25442     },
25443
25444     // private
25445     onRender : function(ct, position)
25446     {
25447         var _t = this;
25448         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
25449         this.el.dom.style.border = '0 none';
25450         this.el.dom.setAttribute('tabIndex', -1);
25451         this.el.addClass('x-hidden');
25452         if(Roo.isIE){ // fix IE 1px bogus margin
25453             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25454         }
25455         this.wrap = this.el.wrap({
25456             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25457         });
25458         
25459         if (this.resizable) {
25460             this.resizeEl = new Roo.Resizable(this.wrap, {
25461                 pinned : true,
25462                 wrap: true,
25463                 dynamic : true,
25464                 minHeight : this.height,
25465                 height: this.height,
25466                 handles : this.resizable,
25467                 width: this.width,
25468                 listeners : {
25469                     resize : function(r, w, h) {
25470                         _t.onResize(w,h); // -something
25471                     }
25472                 }
25473             });
25474             
25475         }
25476
25477         this.frameId = Roo.id();
25478         
25479         this.createToolbar(this);
25480         
25481       
25482         
25483         var iframe = this.wrap.createChild({
25484             tag: 'iframe',
25485             id: this.frameId,
25486             name: this.frameId,
25487             frameBorder : 'no',
25488             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
25489         }, this.el
25490         );
25491         
25492        // console.log(iframe);
25493         //this.wrap.dom.appendChild(iframe);
25494
25495         this.iframe = iframe.dom;
25496
25497          this.assignDocWin();
25498         
25499         this.doc.designMode = 'on';
25500        
25501         this.doc.open();
25502         this.doc.write(this.getDocMarkup());
25503         this.doc.close();
25504
25505         
25506         var task = { // must defer to wait for browser to be ready
25507             run : function(){
25508                 //console.log("run task?" + this.doc.readyState);
25509                 this.assignDocWin();
25510                 if(this.doc.body || this.doc.readyState == 'complete'){
25511                     try {
25512                         this.doc.designMode="on";
25513                     } catch (e) {
25514                         return;
25515                     }
25516                     Roo.TaskMgr.stop(task);
25517                     this.initEditor.defer(10, this);
25518                 }
25519             },
25520             interval : 10,
25521             duration:10000,
25522             scope: this
25523         };
25524         Roo.TaskMgr.start(task);
25525
25526         if(!this.width){
25527             this.setSize(this.wrap.getSize());
25528         }
25529         if (this.resizeEl) {
25530             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
25531             // should trigger onReize..
25532         }
25533     },
25534
25535     // private
25536     onResize : function(w, h)
25537     {
25538         //Roo.log('resize: ' +w + ',' + h );
25539         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
25540         if(this.el && this.iframe){
25541             if(typeof w == 'number'){
25542                 var aw = w - this.wrap.getFrameWidth('lr');
25543                 this.el.setWidth(this.adjustWidth('textarea', aw));
25544                 this.iframe.style.width = aw + 'px';
25545             }
25546             if(typeof h == 'number'){
25547                 var tbh = 0;
25548                 for (var i =0; i < this.toolbars.length;i++) {
25549                     // fixme - ask toolbars for heights?
25550                     tbh += this.toolbars[i].tb.el.getHeight();
25551                     if (this.toolbars[i].footer) {
25552                         tbh += this.toolbars[i].footer.el.getHeight();
25553                     }
25554                 }
25555                 
25556                 
25557                 
25558                 
25559                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
25560                 ah -= 5; // knock a few pixes off for look..
25561                 this.el.setHeight(this.adjustWidth('textarea', ah));
25562                 this.iframe.style.height = ah + 'px';
25563                 if(this.doc){
25564                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
25565                 }
25566             }
25567         }
25568     },
25569
25570     /**
25571      * Toggles the editor between standard and source edit mode.
25572      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25573      */
25574     toggleSourceEdit : function(sourceEditMode){
25575         
25576         this.sourceEditMode = sourceEditMode === true;
25577         
25578         if(this.sourceEditMode){
25579 //            Roo.log('in');
25580 //            Roo.log(this.syncValue());
25581             this.syncValue();
25582             this.iframe.className = 'x-hidden';
25583             this.el.removeClass('x-hidden');
25584             this.el.dom.removeAttribute('tabIndex');
25585             this.el.focus();
25586         }else{
25587 //            Roo.log('out')
25588 //            Roo.log(this.pushValue()); 
25589             this.pushValue();
25590             this.iframe.className = '';
25591             this.el.addClass('x-hidden');
25592             this.el.dom.setAttribute('tabIndex', -1);
25593             this.deferFocus();
25594         }
25595         this.setSize(this.wrap.getSize());
25596         this.fireEvent('editmodechange', this, this.sourceEditMode);
25597     },
25598
25599     // private used internally
25600     createLink : function(){
25601         var url = prompt(this.createLinkText, this.defaultLinkValue);
25602         if(url && url != 'http:/'+'/'){
25603             this.relayCmd('createlink', url);
25604         }
25605     },
25606
25607     // private (for BoxComponent)
25608     adjustSize : Roo.BoxComponent.prototype.adjustSize,
25609
25610     // private (for BoxComponent)
25611     getResizeEl : function(){
25612         return this.wrap;
25613     },
25614
25615     // private (for BoxComponent)
25616     getPositionEl : function(){
25617         return this.wrap;
25618     },
25619
25620     // private
25621     initEvents : function(){
25622         this.originalValue = this.getValue();
25623     },
25624
25625     /**
25626      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25627      * @method
25628      */
25629     markInvalid : Roo.emptyFn,
25630     /**
25631      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25632      * @method
25633      */
25634     clearInvalid : Roo.emptyFn,
25635
25636     setValue : function(v){
25637         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
25638         this.pushValue();
25639     },
25640
25641     /**
25642      * Protected method that will not generally be called directly. If you need/want
25643      * custom HTML cleanup, this is the method you should override.
25644      * @param {String} html The HTML to be cleaned
25645      * return {String} The cleaned HTML
25646      */
25647     cleanHtml : function(html){
25648         html = String(html);
25649         if(html.length > 5){
25650             if(Roo.isSafari){ // strip safari nonsense
25651                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25652             }
25653         }
25654         if(html == '&nbsp;'){
25655             html = '';
25656         }
25657         return html;
25658     },
25659
25660     /**
25661      * Protected method that will not generally be called directly. Syncs the contents
25662      * of the editor iframe with the textarea.
25663      */
25664     syncValue : function(){
25665         if(this.initialized){
25666             var bd = (this.doc.body || this.doc.documentElement);
25667             //this.cleanUpPaste(); -- this is done else where and causes havoc..
25668             var html = bd.innerHTML;
25669             if(Roo.isSafari){
25670                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25671                 var m = bs.match(/text-align:(.*?);/i);
25672                 if(m && m[1]){
25673                     html = '<div style="'+m[0]+'">' + html + '</div>';
25674                 }
25675             }
25676             html = this.cleanHtml(html);
25677             // fix up the special chars.. normaly like back quotes in word...
25678             // however we do not want to do this with chinese..
25679             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
25680                 var cc = b.charCodeAt();
25681                 if (
25682                     (cc >= 0x4E00 && cc < 0xA000 ) ||
25683                     (cc >= 0x3400 && cc < 0x4E00 ) ||
25684                     (cc >= 0xf900 && cc < 0xfb00 )
25685                 ) {
25686                         return b;
25687                 }
25688                 return "&#"+cc+";" 
25689             });
25690             if(this.fireEvent('beforesync', this, html) !== false){
25691                 this.el.dom.value = html;
25692                 this.fireEvent('sync', this, html);
25693             }
25694         }
25695     },
25696
25697     /**
25698      * Protected method that will not generally be called directly. Pushes the value of the textarea
25699      * into the iframe editor.
25700      */
25701     pushValue : function(){
25702         if(this.initialized){
25703             var v = this.el.dom.value;
25704             
25705             if(v.length < 1){
25706                 v = '&#160;';
25707             }
25708             
25709             if(this.fireEvent('beforepush', this, v) !== false){
25710                 var d = (this.doc.body || this.doc.documentElement);
25711                 d.innerHTML = v;
25712                 this.cleanUpPaste();
25713                 this.el.dom.value = d.innerHTML;
25714                 this.fireEvent('push', this, v);
25715             }
25716         }
25717     },
25718
25719     // private
25720     deferFocus : function(){
25721         this.focus.defer(10, this);
25722     },
25723
25724     // doc'ed in Field
25725     focus : function(){
25726         if(this.win && !this.sourceEditMode){
25727             this.win.focus();
25728         }else{
25729             this.el.focus();
25730         }
25731     },
25732     
25733     assignDocWin: function()
25734     {
25735         var iframe = this.iframe;
25736         
25737          if(Roo.isIE){
25738             this.doc = iframe.contentWindow.document;
25739             this.win = iframe.contentWindow;
25740         } else {
25741             if (!Roo.get(this.frameId)) {
25742                 return;
25743             }
25744             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25745             this.win = Roo.get(this.frameId).dom.contentWindow;
25746         }
25747     },
25748     
25749     // private
25750     initEditor : function(){
25751         //console.log("INIT EDITOR");
25752         this.assignDocWin();
25753         
25754         
25755         
25756         this.doc.designMode="on";
25757         this.doc.open();
25758         this.doc.write(this.getDocMarkup());
25759         this.doc.close();
25760         
25761         var dbody = (this.doc.body || this.doc.documentElement);
25762         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25763         // this copies styles from the containing element into thsi one..
25764         // not sure why we need all of this..
25765         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25766         ss['background-attachment'] = 'fixed'; // w3c
25767         dbody.bgProperties = 'fixed'; // ie
25768         Roo.DomHelper.applyStyles(dbody, ss);
25769         Roo.EventManager.on(this.doc, {
25770             //'mousedown': this.onEditorEvent,
25771             'mouseup': this.onEditorEvent,
25772             'dblclick': this.onEditorEvent,
25773             'click': this.onEditorEvent,
25774             'keyup': this.onEditorEvent,
25775             buffer:100,
25776             scope: this
25777         });
25778         if(Roo.isGecko){
25779             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25780         }
25781         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25782             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25783         }
25784         this.initialized = true;
25785
25786         this.fireEvent('initialize', this);
25787         this.pushValue();
25788     },
25789
25790     // private
25791     onDestroy : function(){
25792         
25793         
25794         
25795         if(this.rendered){
25796             
25797             for (var i =0; i < this.toolbars.length;i++) {
25798                 // fixme - ask toolbars for heights?
25799                 this.toolbars[i].onDestroy();
25800             }
25801             
25802             this.wrap.dom.innerHTML = '';
25803             this.wrap.remove();
25804         }
25805     },
25806
25807     // private
25808     onFirstFocus : function(){
25809         
25810         this.assignDocWin();
25811         
25812         
25813         this.activated = true;
25814         for (var i =0; i < this.toolbars.length;i++) {
25815             this.toolbars[i].onFirstFocus();
25816         }
25817        
25818         if(Roo.isGecko){ // prevent silly gecko errors
25819             this.win.focus();
25820             var s = this.win.getSelection();
25821             if(!s.focusNode || s.focusNode.nodeType != 3){
25822                 var r = s.getRangeAt(0);
25823                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
25824                 r.collapse(true);
25825                 this.deferFocus();
25826             }
25827             try{
25828                 this.execCmd('useCSS', true);
25829                 this.execCmd('styleWithCSS', false);
25830             }catch(e){}
25831         }
25832         this.fireEvent('activate', this);
25833     },
25834
25835     // private
25836     adjustFont: function(btn){
25837         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
25838         //if(Roo.isSafari){ // safari
25839         //    adjust *= 2;
25840        // }
25841         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
25842         if(Roo.isSafari){ // safari
25843             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
25844             v =  (v < 10) ? 10 : v;
25845             v =  (v > 48) ? 48 : v;
25846             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
25847             
25848         }
25849         
25850         
25851         v = Math.max(1, v+adjust);
25852         
25853         this.execCmd('FontSize', v  );
25854     },
25855
25856     onEditorEvent : function(e){
25857         this.fireEvent('editorevent', this, e);
25858       //  this.updateToolbar();
25859         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
25860     },
25861
25862     insertTag : function(tg)
25863     {
25864         // could be a bit smarter... -> wrap the current selected tRoo..
25865         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
25866             
25867             range = this.createRange(this.getSelection());
25868             var wrappingNode = this.doc.createElement(tg.toLowerCase());
25869             wrappingNode.appendChild(range.extractContents());
25870             range.insertNode(wrappingNode);
25871
25872             return;
25873             
25874             
25875             
25876         }
25877         this.execCmd("formatblock",   tg);
25878         
25879     },
25880     
25881     insertText : function(txt)
25882     {
25883         
25884         
25885         var range = this.createRange();
25886         range.deleteContents();
25887                //alert(Sender.getAttribute('label'));
25888                
25889         range.insertNode(this.doc.createTextNode(txt));
25890     } ,
25891     
25892     // private
25893     relayBtnCmd : function(btn){
25894         this.relayCmd(btn.cmd);
25895     },
25896
25897     /**
25898      * Executes a Midas editor command on the editor document and performs necessary focus and
25899      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25900      * @param {String} cmd The Midas command
25901      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25902      */
25903     relayCmd : function(cmd, value){
25904         this.win.focus();
25905         this.execCmd(cmd, value);
25906         this.fireEvent('editorevent', this);
25907         //this.updateToolbar();
25908         this.deferFocus();
25909     },
25910
25911     /**
25912      * Executes a Midas editor command directly on the editor document.
25913      * For visual commands, you should use {@link #relayCmd} instead.
25914      * <b>This should only be called after the editor is initialized.</b>
25915      * @param {String} cmd The Midas command
25916      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25917      */
25918     execCmd : function(cmd, value){
25919         this.doc.execCommand(cmd, false, value === undefined ? null : value);
25920         this.syncValue();
25921     },
25922  
25923  
25924    
25925     /**
25926      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25927      * to insert tRoo.
25928      * @param {String} text | dom node.. 
25929      */
25930     insertAtCursor : function(text)
25931     {
25932         
25933         
25934         
25935         if(!this.activated){
25936             return;
25937         }
25938         /*
25939         if(Roo.isIE){
25940             this.win.focus();
25941             var r = this.doc.selection.createRange();
25942             if(r){
25943                 r.collapse(true);
25944                 r.pasteHTML(text);
25945                 this.syncValue();
25946                 this.deferFocus();
25947             
25948             }
25949             return;
25950         }
25951         */
25952         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25953             this.win.focus();
25954             
25955             
25956             // from jquery ui (MIT licenced)
25957             var range, node;
25958             var win = this.win;
25959             
25960             if (win.getSelection && win.getSelection().getRangeAt) {
25961                 range = win.getSelection().getRangeAt(0);
25962                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25963                 range.insertNode(node);
25964             } else if (win.document.selection && win.document.selection.createRange) {
25965                 // no firefox support
25966                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25967                 win.document.selection.createRange().pasteHTML(txt);
25968             } else {
25969                 // no firefox support
25970                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25971                 this.execCmd('InsertHTML', txt);
25972             } 
25973             
25974             this.syncValue();
25975             
25976             this.deferFocus();
25977         }
25978     },
25979  // private
25980     mozKeyPress : function(e){
25981         if(e.ctrlKey){
25982             var c = e.getCharCode(), cmd;
25983           
25984             if(c > 0){
25985                 c = String.fromCharCode(c).toLowerCase();
25986                 switch(c){
25987                     case 'b':
25988                         cmd = 'bold';
25989                         break;
25990                     case 'i':
25991                         cmd = 'italic';
25992                         break;
25993                     
25994                     case 'u':
25995                         cmd = 'underline';
25996                         break;
25997                     
25998                     case 'v':
25999                         this.cleanUpPaste.defer(100, this);
26000                         return;
26001                         
26002                 }
26003                 if(cmd){
26004                     this.win.focus();
26005                     this.execCmd(cmd);
26006                     this.deferFocus();
26007                     e.preventDefault();
26008                 }
26009                 
26010             }
26011         }
26012     },
26013
26014     // private
26015     fixKeys : function(){ // load time branching for fastest keydown performance
26016         if(Roo.isIE){
26017             return function(e){
26018                 var k = e.getKey(), r;
26019                 if(k == e.TAB){
26020                     e.stopEvent();
26021                     r = this.doc.selection.createRange();
26022                     if(r){
26023                         r.collapse(true);
26024                         r.pasteHTML('&#160;&#160;&#160;&#160;');
26025                         this.deferFocus();
26026                     }
26027                     return;
26028                 }
26029                 
26030                 if(k == e.ENTER){
26031                     r = this.doc.selection.createRange();
26032                     if(r){
26033                         var target = r.parentElement();
26034                         if(!target || target.tagName.toLowerCase() != 'li'){
26035                             e.stopEvent();
26036                             r.pasteHTML('<br />');
26037                             r.collapse(false);
26038                             r.select();
26039                         }
26040                     }
26041                 }
26042                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26043                     this.cleanUpPaste.defer(100, this);
26044                     return;
26045                 }
26046                 
26047                 
26048             };
26049         }else if(Roo.isOpera){
26050             return function(e){
26051                 var k = e.getKey();
26052                 if(k == e.TAB){
26053                     e.stopEvent();
26054                     this.win.focus();
26055                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
26056                     this.deferFocus();
26057                 }
26058                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26059                     this.cleanUpPaste.defer(100, this);
26060                     return;
26061                 }
26062                 
26063             };
26064         }else if(Roo.isSafari){
26065             return function(e){
26066                 var k = e.getKey();
26067                 
26068                 if(k == e.TAB){
26069                     e.stopEvent();
26070                     this.execCmd('InsertText','\t');
26071                     this.deferFocus();
26072                     return;
26073                 }
26074                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26075                     this.cleanUpPaste.defer(100, this);
26076                     return;
26077                 }
26078                 
26079              };
26080         }
26081     }(),
26082     
26083     getAllAncestors: function()
26084     {
26085         var p = this.getSelectedNode();
26086         var a = [];
26087         if (!p) {
26088             a.push(p); // push blank onto stack..
26089             p = this.getParentElement();
26090         }
26091         
26092         
26093         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26094             a.push(p);
26095             p = p.parentNode;
26096         }
26097         a.push(this.doc.body);
26098         return a;
26099     },
26100     lastSel : false,
26101     lastSelNode : false,
26102     
26103     
26104     getSelection : function() 
26105     {
26106         this.assignDocWin();
26107         return Roo.isIE ? this.doc.selection : this.win.getSelection();
26108     },
26109     
26110     getSelectedNode: function() 
26111     {
26112         // this may only work on Gecko!!!
26113         
26114         // should we cache this!!!!
26115         
26116         
26117         
26118          
26119         var range = this.createRange(this.getSelection()).cloneRange();
26120         
26121         if (Roo.isIE) {
26122             var parent = range.parentElement();
26123             while (true) {
26124                 var testRange = range.duplicate();
26125                 testRange.moveToElementText(parent);
26126                 if (testRange.inRange(range)) {
26127                     break;
26128                 }
26129                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26130                     break;
26131                 }
26132                 parent = parent.parentElement;
26133             }
26134             return parent;
26135         }
26136         
26137         // is ancestor a text element.
26138         var ac =  range.commonAncestorContainer;
26139         if (ac.nodeType == 3) {
26140             ac = ac.parentNode;
26141         }
26142         
26143         var ar = ac.childNodes;
26144          
26145         var nodes = [];
26146         var other_nodes = [];
26147         var has_other_nodes = false;
26148         for (var i=0;i<ar.length;i++) {
26149             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
26150                 continue;
26151             }
26152             // fullly contained node.
26153             
26154             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26155                 nodes.push(ar[i]);
26156                 continue;
26157             }
26158             
26159             // probably selected..
26160             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26161                 other_nodes.push(ar[i]);
26162                 continue;
26163             }
26164             // outer..
26165             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
26166                 continue;
26167             }
26168             
26169             
26170             has_other_nodes = true;
26171         }
26172         if (!nodes.length && other_nodes.length) {
26173             nodes= other_nodes;
26174         }
26175         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26176             return false;
26177         }
26178         
26179         return nodes[0];
26180     },
26181     createRange: function(sel)
26182     {
26183         // this has strange effects when using with 
26184         // top toolbar - not sure if it's a great idea.
26185         //this.editor.contentWindow.focus();
26186         if (typeof sel != "undefined") {
26187             try {
26188                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26189             } catch(e) {
26190                 return this.doc.createRange();
26191             }
26192         } else {
26193             return this.doc.createRange();
26194         }
26195     },
26196     getParentElement: function()
26197     {
26198         
26199         this.assignDocWin();
26200         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26201         
26202         var range = this.createRange(sel);
26203          
26204         try {
26205             var p = range.commonAncestorContainer;
26206             while (p.nodeType == 3) { // text node
26207                 p = p.parentNode;
26208             }
26209             return p;
26210         } catch (e) {
26211             return null;
26212         }
26213     
26214     },
26215     /***
26216      *
26217      * Range intersection.. the hard stuff...
26218      *  '-1' = before
26219      *  '0' = hits..
26220      *  '1' = after.
26221      *         [ -- selected range --- ]
26222      *   [fail]                        [fail]
26223      *
26224      *    basically..
26225      *      if end is before start or  hits it. fail.
26226      *      if start is after end or hits it fail.
26227      *
26228      *   if either hits (but other is outside. - then it's not 
26229      *   
26230      *    
26231      **/
26232     
26233     
26234     // @see http://www.thismuchiknow.co.uk/?p=64.
26235     rangeIntersectsNode : function(range, node)
26236     {
26237         var nodeRange = node.ownerDocument.createRange();
26238         try {
26239             nodeRange.selectNode(node);
26240         } catch (e) {
26241             nodeRange.selectNodeContents(node);
26242         }
26243     
26244         var rangeStartRange = range.cloneRange();
26245         rangeStartRange.collapse(true);
26246     
26247         var rangeEndRange = range.cloneRange();
26248         rangeEndRange.collapse(false);
26249     
26250         var nodeStartRange = nodeRange.cloneRange();
26251         nodeStartRange.collapse(true);
26252     
26253         var nodeEndRange = nodeRange.cloneRange();
26254         nodeEndRange.collapse(false);
26255     
26256         return rangeStartRange.compareBoundaryPoints(
26257                  Range.START_TO_START, nodeEndRange) == -1 &&
26258                rangeEndRange.compareBoundaryPoints(
26259                  Range.START_TO_START, nodeStartRange) == 1;
26260         
26261          
26262     },
26263     rangeCompareNode : function(range, node)
26264     {
26265         var nodeRange = node.ownerDocument.createRange();
26266         try {
26267             nodeRange.selectNode(node);
26268         } catch (e) {
26269             nodeRange.selectNodeContents(node);
26270         }
26271         
26272         
26273         range.collapse(true);
26274     
26275         nodeRange.collapse(true);
26276      
26277         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26278         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
26279          
26280         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26281         
26282         var nodeIsBefore   =  ss == 1;
26283         var nodeIsAfter    = ee == -1;
26284         
26285         if (nodeIsBefore && nodeIsAfter)
26286             return 0; // outer
26287         if (!nodeIsBefore && nodeIsAfter)
26288             return 1; //right trailed.
26289         
26290         if (nodeIsBefore && !nodeIsAfter)
26291             return 2;  // left trailed.
26292         // fully contined.
26293         return 3;
26294     },
26295
26296     // private? - in a new class?
26297     cleanUpPaste :  function()
26298     {
26299         // cleans up the whole document..
26300          Roo.log('cleanuppaste');
26301         this.cleanUpChildren(this.doc.body);
26302         var clean = this.cleanWordChars(this.doc.body.innerHTML);
26303         if (clean != this.doc.body.innerHTML) {
26304             this.doc.body.innerHTML = clean;
26305         }
26306         
26307     },
26308     
26309     cleanWordChars : function(input) {// change the chars to hex code
26310         var he = Roo.form.HtmlEditor;
26311         
26312         var output = input;
26313         Roo.each(he.swapCodes, function(sw) { 
26314             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26315             
26316             output = output.replace(swapper, sw[1]);
26317         });
26318         
26319         return output;
26320     },
26321     
26322     
26323     cleanUpChildren : function (n)
26324     {
26325         if (!n.childNodes.length) {
26326             return;
26327         }
26328         for (var i = n.childNodes.length-1; i > -1 ; i--) {
26329            this.cleanUpChild(n.childNodes[i]);
26330         }
26331     },
26332     
26333     
26334         
26335     
26336     cleanUpChild : function (node)
26337     {
26338         var ed = this;
26339         //console.log(node);
26340         if (node.nodeName == "#text") {
26341             // clean up silly Windows -- stuff?
26342             return; 
26343         }
26344         if (node.nodeName == "#comment") {
26345             node.parentNode.removeChild(node);
26346             // clean up silly Windows -- stuff?
26347             return; 
26348         }
26349         
26350         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
26351             // remove node.
26352             node.parentNode.removeChild(node);
26353             return;
26354             
26355         }
26356         
26357         var remove_keep_children= Roo.form.HtmlEditor.remove.indexOf(node.tagName.toLowerCase()) > -1;
26358         
26359         // remove <a name=....> as rendering on yahoo mailer is borked with this.
26360         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
26361         
26362         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
26363         //    remove_keep_children = true;
26364         //}
26365         
26366         if (remove_keep_children) {
26367             this.cleanUpChildren(node);
26368             // inserts everything just before this node...
26369             while (node.childNodes.length) {
26370                 var cn = node.childNodes[0];
26371                 node.removeChild(cn);
26372                 node.parentNode.insertBefore(cn, node);
26373             }
26374             node.parentNode.removeChild(node);
26375             return;
26376         }
26377         
26378         if (!node.attributes || !node.attributes.length) {
26379             this.cleanUpChildren(node);
26380             return;
26381         }
26382         
26383         function cleanAttr(n,v)
26384         {
26385             
26386             if (v.match(/^\./) || v.match(/^\//)) {
26387                 return;
26388             }
26389             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
26390                 return;
26391             }
26392             if (v.match(/^#/)) {
26393                 return;
26394             }
26395 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26396             node.removeAttribute(n);
26397             
26398         }
26399         
26400         function cleanStyle(n,v)
26401         {
26402             if (v.match(/expression/)) { //XSS?? should we even bother..
26403                 node.removeAttribute(n);
26404                 return;
26405             }
26406             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.form.HtmlEditor.cwhite : ed.cwhite;
26407             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.form.HtmlEditor.cblack : ed.cblack;
26408             
26409             
26410             var parts = v.split(/;/);
26411             var clean = [];
26412             
26413             Roo.each(parts, function(p) {
26414                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26415                 if (!p.length) {
26416                     return true;
26417                 }
26418                 var l = p.split(':').shift().replace(/\s+/g,'');
26419                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26420                 
26421                 
26422                 if ( cblack.indexOf(l) > -1) {
26423 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26424                     //node.removeAttribute(n);
26425                     return true;
26426                 }
26427                 //Roo.log()
26428                 // only allow 'c whitelisted system attributes'
26429                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
26430 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26431                     //node.removeAttribute(n);
26432                     return true;
26433                 }
26434                 
26435                 
26436                  
26437                 
26438                 clean.push(p);
26439                 return true;
26440             });
26441             if (clean.length) { 
26442                 node.setAttribute(n, clean.join(';'));
26443             } else {
26444                 node.removeAttribute(n);
26445             }
26446             
26447         }
26448         
26449         
26450         for (var i = node.attributes.length-1; i > -1 ; i--) {
26451             var a = node.attributes[i];
26452             //console.log(a);
26453             
26454             if (a.name.toLowerCase().substr(0,2)=='on')  {
26455                 node.removeAttribute(a.name);
26456                 continue;
26457             }
26458             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
26459                 node.removeAttribute(a.name);
26460                 continue;
26461             }
26462             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
26463                 cleanAttr(a.name,a.value); // fixme..
26464                 continue;
26465             }
26466             if (a.name == 'style') {
26467                 cleanStyle(a.name,a.value);
26468                 continue;
26469             }
26470             /// clean up MS crap..
26471             // tecnically this should be a list of valid class'es..
26472             
26473             
26474             if (a.name == 'class') {
26475                 if (a.value.match(/^Mso/)) {
26476                     node.className = '';
26477                 }
26478                 
26479                 if (a.value.match(/body/)) {
26480                     node.className = '';
26481                 }
26482                 continue;
26483             }
26484             
26485             // style cleanup!?
26486             // class cleanup?
26487             
26488         }
26489         
26490         
26491         this.cleanUpChildren(node);
26492         
26493         
26494     }
26495     
26496     
26497     // hide stuff that is not compatible
26498     /**
26499      * @event blur
26500      * @hide
26501      */
26502     /**
26503      * @event change
26504      * @hide
26505      */
26506     /**
26507      * @event focus
26508      * @hide
26509      */
26510     /**
26511      * @event specialkey
26512      * @hide
26513      */
26514     /**
26515      * @cfg {String} fieldClass @hide
26516      */
26517     /**
26518      * @cfg {String} focusClass @hide
26519      */
26520     /**
26521      * @cfg {String} autoCreate @hide
26522      */
26523     /**
26524      * @cfg {String} inputType @hide
26525      */
26526     /**
26527      * @cfg {String} invalidClass @hide
26528      */
26529     /**
26530      * @cfg {String} invalidText @hide
26531      */
26532     /**
26533      * @cfg {String} msgFx @hide
26534      */
26535     /**
26536      * @cfg {String} validateOnBlur @hide
26537      */
26538 });
26539
26540 Roo.form.HtmlEditor.white = [
26541         'area', 'br', 'img', 'input', 'hr', 'wbr',
26542         
26543        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
26544        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
26545        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
26546        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
26547        'table',   'ul',         'xmp', 
26548        
26549        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
26550       'thead',   'tr', 
26551      
26552       'dir', 'menu', 'ol', 'ul', 'dl',
26553        
26554       'embed',  'object'
26555 ];
26556
26557
26558 Roo.form.HtmlEditor.black = [
26559     //    'embed',  'object', // enable - backend responsiblity to clean thiese
26560         'applet', // 
26561         'base',   'basefont', 'bgsound', 'blink',  'body', 
26562         'frame',  'frameset', 'head',    'html',   'ilayer', 
26563         'iframe', 'layer',  'link',     'meta',    'object',   
26564         'script', 'style' ,'title',  'xml' // clean later..
26565 ];
26566 Roo.form.HtmlEditor.clean = [
26567     'script', 'style', 'title', 'xml'
26568 ];
26569 Roo.form.HtmlEditor.remove = [
26570     'font'
26571 ];
26572 // attributes..
26573
26574 Roo.form.HtmlEditor.ablack = [
26575     'on'
26576 ];
26577     
26578 Roo.form.HtmlEditor.aclean = [ 
26579     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
26580 ];
26581
26582 // protocols..
26583 Roo.form.HtmlEditor.pwhite= [
26584         'http',  'https',  'mailto'
26585 ];
26586
26587 // white listed style attributes.
26588 Roo.form.HtmlEditor.cwhite= [
26589       //  'text-align', /// default is to allow most things..
26590       
26591          
26592 //        'font-size'//??
26593 ];
26594
26595 // black listed style attributes.
26596 Roo.form.HtmlEditor.cblack= [
26597       //  'font-size' -- this can be set by the project 
26598 ];
26599
26600
26601 Roo.form.HtmlEditor.swapCodes   =[ 
26602     [    8211, "--" ], 
26603     [    8212, "--" ], 
26604     [    8216,  "'" ],  
26605     [    8217, "'" ],  
26606     [    8220, '"' ],  
26607     [    8221, '"' ],  
26608     [    8226, "*" ],  
26609     [    8230, "..." ]
26610 ]; 
26611
26612     // <script type="text/javascript">
26613 /*
26614  * Based on
26615  * Ext JS Library 1.1.1
26616  * Copyright(c) 2006-2007, Ext JS, LLC.
26617  *  
26618  
26619  */
26620
26621 /**
26622  * @class Roo.form.HtmlEditorToolbar1
26623  * Basic Toolbar
26624  * 
26625  * Usage:
26626  *
26627  new Roo.form.HtmlEditor({
26628     ....
26629     toolbars : [
26630         new Roo.form.HtmlEditorToolbar1({
26631             disable : { fonts: 1 , format: 1, ..., ... , ...],
26632             btns : [ .... ]
26633         })
26634     }
26635      
26636  * 
26637  * @cfg {Object} disable List of elements to disable..
26638  * @cfg {Array} btns List of additional buttons.
26639  * 
26640  * 
26641  * NEEDS Extra CSS? 
26642  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26643  */
26644  
26645 Roo.form.HtmlEditor.ToolbarStandard = function(config)
26646 {
26647     
26648     Roo.apply(this, config);
26649     
26650     // default disabled, based on 'good practice'..
26651     this.disable = this.disable || {};
26652     Roo.applyIf(this.disable, {
26653         fontSize : true,
26654         colors : true,
26655         specialElements : true
26656     });
26657     
26658     
26659     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26660     // dont call parent... till later.
26661 }
26662
26663 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
26664     
26665     tb: false,
26666     
26667     rendered: false,
26668     
26669     editor : false,
26670     /**
26671      * @cfg {Object} disable  List of toolbar elements to disable
26672          
26673      */
26674     disable : false,
26675       /**
26676      * @cfg {Array} fontFamilies An array of available font families
26677      */
26678     fontFamilies : [
26679         'Arial',
26680         'Courier New',
26681         'Tahoma',
26682         'Times New Roman',
26683         'Verdana'
26684     ],
26685     
26686     specialChars : [
26687            "&#169;",
26688           "&#174;",     
26689           "&#8482;",    
26690           "&#163;" ,    
26691          // "&#8212;",    
26692           "&#8230;",    
26693           "&#247;" ,    
26694         //  "&#225;" ,     ?? a acute?
26695            "&#8364;"    , //Euro
26696        //   "&#8220;"    ,
26697         //  "&#8221;"    ,
26698         //  "&#8226;"    ,
26699           "&#176;"  //   , // degrees
26700
26701          // "&#233;"     , // e ecute
26702          // "&#250;"     , // u ecute?
26703     ],
26704     
26705     specialElements : [
26706         {
26707             text: "Insert Table",
26708             xtype: 'MenuItem',
26709             xns : Roo.Menu,
26710             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
26711                 
26712         },
26713         {    
26714             text: "Insert Image",
26715             xtype: 'MenuItem',
26716             xns : Roo.Menu,
26717             ihtml : '<img src="about:blank"/>'
26718             
26719         }
26720         
26721          
26722     ],
26723     
26724     
26725     inputElements : [ 
26726             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
26727             "input:submit", "input:button", "select", "textarea", "label" ],
26728     formats : [
26729         ["p"] ,  
26730         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
26731         ["pre"],[ "code"], 
26732         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
26733         ['div'],['span']
26734     ],
26735      /**
26736      * @cfg {String} defaultFont default font to use.
26737      */
26738     defaultFont: 'tahoma',
26739    
26740     fontSelect : false,
26741     
26742     
26743     formatCombo : false,
26744     
26745     init : function(editor)
26746     {
26747         this.editor = editor;
26748         
26749         
26750         var fid = editor.frameId;
26751         var etb = this;
26752         function btn(id, toggle, handler){
26753             var xid = fid + '-'+ id ;
26754             return {
26755                 id : xid,
26756                 cmd : id,
26757                 cls : 'x-btn-icon x-edit-'+id,
26758                 enableToggle:toggle !== false,
26759                 scope: editor, // was editor...
26760                 handler:handler||editor.relayBtnCmd,
26761                 clickEvent:'mousedown',
26762                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
26763                 tabIndex:-1
26764             };
26765         }
26766         
26767         
26768         
26769         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
26770         this.tb = tb;
26771          // stop form submits
26772         tb.el.on('click', function(e){
26773             e.preventDefault(); // what does this do?
26774         });
26775
26776         if(!this.disable.font) { // && !Roo.isSafari){
26777             /* why no safari for fonts 
26778             editor.fontSelect = tb.el.createChild({
26779                 tag:'select',
26780                 tabIndex: -1,
26781                 cls:'x-font-select',
26782                 html: this.createFontOptions()
26783             });
26784             
26785             editor.fontSelect.on('change', function(){
26786                 var font = editor.fontSelect.dom.value;
26787                 editor.relayCmd('fontname', font);
26788                 editor.deferFocus();
26789             }, editor);
26790             
26791             tb.add(
26792                 editor.fontSelect.dom,
26793                 '-'
26794             );
26795             */
26796             
26797         };
26798         if(!this.disable.formats){
26799             this.formatCombo = new Roo.form.ComboBox({
26800                 store: new Roo.data.SimpleStore({
26801                     id : 'tag',
26802                     fields: ['tag'],
26803                     data : this.formats // from states.js
26804                 }),
26805                 blockFocus : true,
26806                 name : '',
26807                 //autoCreate : {tag: "div",  size: "20"},
26808                 displayField:'tag',
26809                 typeAhead: false,
26810                 mode: 'local',
26811                 editable : false,
26812                 triggerAction: 'all',
26813                 emptyText:'Add tag',
26814                 selectOnFocus:true,
26815                 width:135,
26816                 listeners : {
26817                     'select': function(c, r, i) {
26818                         editor.insertTag(r.get('tag'));
26819                         editor.focus();
26820                     }
26821                 }
26822
26823             });
26824             tb.addField(this.formatCombo);
26825             
26826         }
26827         
26828         if(!this.disable.format){
26829             tb.add(
26830                 btn('bold'),
26831                 btn('italic'),
26832                 btn('underline')
26833             );
26834         };
26835         if(!this.disable.fontSize){
26836             tb.add(
26837                 '-',
26838                 
26839                 
26840                 btn('increasefontsize', false, editor.adjustFont),
26841                 btn('decreasefontsize', false, editor.adjustFont)
26842             );
26843         };
26844         
26845         
26846         if(!this.disable.colors){
26847             tb.add(
26848                 '-', {
26849                     id:editor.frameId +'-forecolor',
26850                     cls:'x-btn-icon x-edit-forecolor',
26851                     clickEvent:'mousedown',
26852                     tooltip: this.buttonTips['forecolor'] || undefined,
26853                     tabIndex:-1,
26854                     menu : new Roo.menu.ColorMenu({
26855                         allowReselect: true,
26856                         focus: Roo.emptyFn,
26857                         value:'000000',
26858                         plain:true,
26859                         selectHandler: function(cp, color){
26860                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
26861                             editor.deferFocus();
26862                         },
26863                         scope: editor,
26864                         clickEvent:'mousedown'
26865                     })
26866                 }, {
26867                     id:editor.frameId +'backcolor',
26868                     cls:'x-btn-icon x-edit-backcolor',
26869                     clickEvent:'mousedown',
26870                     tooltip: this.buttonTips['backcolor'] || undefined,
26871                     tabIndex:-1,
26872                     menu : new Roo.menu.ColorMenu({
26873                         focus: Roo.emptyFn,
26874                         value:'FFFFFF',
26875                         plain:true,
26876                         allowReselect: true,
26877                         selectHandler: function(cp, color){
26878                             if(Roo.isGecko){
26879                                 editor.execCmd('useCSS', false);
26880                                 editor.execCmd('hilitecolor', color);
26881                                 editor.execCmd('useCSS', true);
26882                                 editor.deferFocus();
26883                             }else{
26884                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
26885                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
26886                                 editor.deferFocus();
26887                             }
26888                         },
26889                         scope:editor,
26890                         clickEvent:'mousedown'
26891                     })
26892                 }
26893             );
26894         };
26895         // now add all the items...
26896         
26897
26898         if(!this.disable.alignments){
26899             tb.add(
26900                 '-',
26901                 btn('justifyleft'),
26902                 btn('justifycenter'),
26903                 btn('justifyright')
26904             );
26905         };
26906
26907         //if(!Roo.isSafari){
26908             if(!this.disable.links){
26909                 tb.add(
26910                     '-',
26911                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
26912                 );
26913             };
26914
26915             if(!this.disable.lists){
26916                 tb.add(
26917                     '-',
26918                     btn('insertorderedlist'),
26919                     btn('insertunorderedlist')
26920                 );
26921             }
26922             if(!this.disable.sourceEdit){
26923                 tb.add(
26924                     '-',
26925                     btn('sourceedit', true, function(btn){
26926                         this.toggleSourceEdit(btn.pressed);
26927                     })
26928                 );
26929             }
26930         //}
26931         
26932         var smenu = { };
26933         // special menu.. - needs to be tidied up..
26934         if (!this.disable.special) {
26935             smenu = {
26936                 text: "&#169;",
26937                 cls: 'x-edit-none',
26938                 
26939                 menu : {
26940                     items : []
26941                 }
26942             };
26943             for (var i =0; i < this.specialChars.length; i++) {
26944                 smenu.menu.items.push({
26945                     
26946                     html: this.specialChars[i],
26947                     handler: function(a,b) {
26948                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
26949                         //editor.insertAtCursor(a.html);
26950                         
26951                     },
26952                     tabIndex:-1
26953                 });
26954             }
26955             
26956             
26957             tb.add(smenu);
26958             
26959             
26960         }
26961          
26962         if (!this.disable.specialElements) {
26963             var semenu = {
26964                 text: "Other;",
26965                 cls: 'x-edit-none',
26966                 menu : {
26967                     items : []
26968                 }
26969             };
26970             for (var i =0; i < this.specialElements.length; i++) {
26971                 semenu.menu.items.push(
26972                     Roo.apply({ 
26973                         handler: function(a,b) {
26974                             editor.insertAtCursor(this.ihtml);
26975                         }
26976                     }, this.specialElements[i])
26977                 );
26978                     
26979             }
26980             
26981             tb.add(semenu);
26982             
26983             
26984         }
26985          
26986         
26987         if (this.btns) {
26988             for(var i =0; i< this.btns.length;i++) {
26989                 var b = Roo.factory(this.btns[i],Roo.form);
26990                 b.cls =  'x-edit-none';
26991                 b.scope = editor;
26992                 tb.add(b);
26993             }
26994         
26995         }
26996         
26997         
26998         
26999         // disable everything...
27000         
27001         this.tb.items.each(function(item){
27002            if(item.id != editor.frameId+ '-sourceedit'){
27003                 item.disable();
27004             }
27005         });
27006         this.rendered = true;
27007         
27008         // the all the btns;
27009         editor.on('editorevent', this.updateToolbar, this);
27010         // other toolbars need to implement this..
27011         //editor.on('editmodechange', this.updateToolbar, this);
27012     },
27013     
27014     
27015     
27016     /**
27017      * Protected method that will not generally be called directly. It triggers
27018      * a toolbar update by reading the markup state of the current selection in the editor.
27019      */
27020     updateToolbar: function(){
27021
27022         if(!this.editor.activated){
27023             this.editor.onFirstFocus();
27024             return;
27025         }
27026
27027         var btns = this.tb.items.map, 
27028             doc = this.editor.doc,
27029             frameId = this.editor.frameId;
27030
27031         if(!this.disable.font && !Roo.isSafari){
27032             /*
27033             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
27034             if(name != this.fontSelect.dom.value){
27035                 this.fontSelect.dom.value = name;
27036             }
27037             */
27038         }
27039         if(!this.disable.format){
27040             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
27041             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
27042             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
27043         }
27044         if(!this.disable.alignments){
27045             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
27046             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
27047             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
27048         }
27049         if(!Roo.isSafari && !this.disable.lists){
27050             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
27051             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
27052         }
27053         
27054         var ans = this.editor.getAllAncestors();
27055         if (this.formatCombo) {
27056             
27057             
27058             var store = this.formatCombo.store;
27059             this.formatCombo.setValue("");
27060             for (var i =0; i < ans.length;i++) {
27061                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27062                     // select it..
27063                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27064                     break;
27065                 }
27066             }
27067         }
27068         
27069         
27070         
27071         // hides menus... - so this cant be on a menu...
27072         Roo.menu.MenuMgr.hideAll();
27073
27074         //this.editorsyncValue();
27075     },
27076    
27077     
27078     createFontOptions : function(){
27079         var buf = [], fs = this.fontFamilies, ff, lc;
27080         
27081         
27082         
27083         for(var i = 0, len = fs.length; i< len; i++){
27084             ff = fs[i];
27085             lc = ff.toLowerCase();
27086             buf.push(
27087                 '<option value="',lc,'" style="font-family:',ff,';"',
27088                     (this.defaultFont == lc ? ' selected="true">' : '>'),
27089                     ff,
27090                 '</option>'
27091             );
27092         }
27093         return buf.join('');
27094     },
27095     
27096     toggleSourceEdit : function(sourceEditMode){
27097         if(sourceEditMode === undefined){
27098             sourceEditMode = !this.sourceEditMode;
27099         }
27100         this.sourceEditMode = sourceEditMode === true;
27101         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
27102         // just toggle the button?
27103         if(btn.pressed !== this.editor.sourceEditMode){
27104             btn.toggle(this.editor.sourceEditMode);
27105             return;
27106         }
27107         
27108         if(this.sourceEditMode){
27109             this.tb.items.each(function(item){
27110                 if(item.cmd != 'sourceedit'){
27111                     item.disable();
27112                 }
27113             });
27114           
27115         }else{
27116             if(this.initialized){
27117                 this.tb.items.each(function(item){
27118                     item.enable();
27119                 });
27120             }
27121             
27122         }
27123         // tell the editor that it's been pressed..
27124         this.editor.toggleSourceEdit(sourceEditMode);
27125        
27126     },
27127      /**
27128      * Object collection of toolbar tooltips for the buttons in the editor. The key
27129      * is the command id associated with that button and the value is a valid QuickTips object.
27130      * For example:
27131 <pre><code>
27132 {
27133     bold : {
27134         title: 'Bold (Ctrl+B)',
27135         text: 'Make the selected text bold.',
27136         cls: 'x-html-editor-tip'
27137     },
27138     italic : {
27139         title: 'Italic (Ctrl+I)',
27140         text: 'Make the selected text italic.',
27141         cls: 'x-html-editor-tip'
27142     },
27143     ...
27144 </code></pre>
27145     * @type Object
27146      */
27147     buttonTips : {
27148         bold : {
27149             title: 'Bold (Ctrl+B)',
27150             text: 'Make the selected text bold.',
27151             cls: 'x-html-editor-tip'
27152         },
27153         italic : {
27154             title: 'Italic (Ctrl+I)',
27155             text: 'Make the selected text italic.',
27156             cls: 'x-html-editor-tip'
27157         },
27158         underline : {
27159             title: 'Underline (Ctrl+U)',
27160             text: 'Underline the selected text.',
27161             cls: 'x-html-editor-tip'
27162         },
27163         increasefontsize : {
27164             title: 'Grow Text',
27165             text: 'Increase the font size.',
27166             cls: 'x-html-editor-tip'
27167         },
27168         decreasefontsize : {
27169             title: 'Shrink Text',
27170             text: 'Decrease the font size.',
27171             cls: 'x-html-editor-tip'
27172         },
27173         backcolor : {
27174             title: 'Text Highlight Color',
27175             text: 'Change the background color of the selected text.',
27176             cls: 'x-html-editor-tip'
27177         },
27178         forecolor : {
27179             title: 'Font Color',
27180             text: 'Change the color of the selected text.',
27181             cls: 'x-html-editor-tip'
27182         },
27183         justifyleft : {
27184             title: 'Align Text Left',
27185             text: 'Align text to the left.',
27186             cls: 'x-html-editor-tip'
27187         },
27188         justifycenter : {
27189             title: 'Center Text',
27190             text: 'Center text in the editor.',
27191             cls: 'x-html-editor-tip'
27192         },
27193         justifyright : {
27194             title: 'Align Text Right',
27195             text: 'Align text to the right.',
27196             cls: 'x-html-editor-tip'
27197         },
27198         insertunorderedlist : {
27199             title: 'Bullet List',
27200             text: 'Start a bulleted list.',
27201             cls: 'x-html-editor-tip'
27202         },
27203         insertorderedlist : {
27204             title: 'Numbered List',
27205             text: 'Start a numbered list.',
27206             cls: 'x-html-editor-tip'
27207         },
27208         createlink : {
27209             title: 'Hyperlink',
27210             text: 'Make the selected text a hyperlink.',
27211             cls: 'x-html-editor-tip'
27212         },
27213         sourceedit : {
27214             title: 'Source Edit',
27215             text: 'Switch to source editing mode.',
27216             cls: 'x-html-editor-tip'
27217         }
27218     },
27219     // private
27220     onDestroy : function(){
27221         if(this.rendered){
27222             
27223             this.tb.items.each(function(item){
27224                 if(item.menu){
27225                     item.menu.removeAll();
27226                     if(item.menu.el){
27227                         item.menu.el.destroy();
27228                     }
27229                 }
27230                 item.destroy();
27231             });
27232              
27233         }
27234     },
27235     onFirstFocus: function() {
27236         this.tb.items.each(function(item){
27237            item.enable();
27238         });
27239     }
27240 });
27241
27242
27243
27244
27245 // <script type="text/javascript">
27246 /*
27247  * Based on
27248  * Ext JS Library 1.1.1
27249  * Copyright(c) 2006-2007, Ext JS, LLC.
27250  *  
27251  
27252  */
27253
27254  
27255 /**
27256  * @class Roo.form.HtmlEditor.ToolbarContext
27257  * Context Toolbar
27258  * 
27259  * Usage:
27260  *
27261  new Roo.form.HtmlEditor({
27262     ....
27263     toolbars : [
27264         { xtype: 'ToolbarStandard', styles : {} }
27265         { xtype: 'ToolbarContext', disable : {} }
27266     ]
27267 })
27268
27269      
27270  * 
27271  * @config : {Object} disable List of elements to disable.. (not done yet.)
27272  * @config : {Object} styles  Map of styles available.
27273  * 
27274  */
27275
27276 Roo.form.HtmlEditor.ToolbarContext = function(config)
27277 {
27278     
27279     Roo.apply(this, config);
27280     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27281     // dont call parent... till later.
27282     this.styles = this.styles || {};
27283 }
27284
27285  
27286
27287 Roo.form.HtmlEditor.ToolbarContext.types = {
27288     'IMG' : {
27289         width : {
27290             title: "Width",
27291             width: 40
27292         },
27293         height:  {
27294             title: "Height",
27295             width: 40
27296         },
27297         align: {
27298             title: "Align",
27299             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
27300             width : 80
27301             
27302         },
27303         border: {
27304             title: "Border",
27305             width: 40
27306         },
27307         alt: {
27308             title: "Alt",
27309             width: 120
27310         },
27311         src : {
27312             title: "Src",
27313             width: 220
27314         }
27315         
27316     },
27317     'A' : {
27318         name : {
27319             title: "Name",
27320             width: 50
27321         },
27322         href:  {
27323             title: "Href",
27324             width: 220
27325         } // border?
27326         
27327     },
27328     'TABLE' : {
27329         rows : {
27330             title: "Rows",
27331             width: 20
27332         },
27333         cols : {
27334             title: "Cols",
27335             width: 20
27336         },
27337         width : {
27338             title: "Width",
27339             width: 40
27340         },
27341         height : {
27342             title: "Height",
27343             width: 40
27344         },
27345         border : {
27346             title: "Border",
27347             width: 20
27348         }
27349     },
27350     'TD' : {
27351         width : {
27352             title: "Width",
27353             width: 40
27354         },
27355         height : {
27356             title: "Height",
27357             width: 40
27358         },   
27359         align: {
27360             title: "Align",
27361             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
27362             width: 80
27363         },
27364         valign: {
27365             title: "Valign",
27366             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
27367             width: 80
27368         },
27369         colspan: {
27370             title: "Colspan",
27371             width: 20
27372             
27373         },
27374          'font-family'  : {
27375             title : "Font",
27376             style : 'fontFamily',
27377             displayField: 'display',
27378             optname : 'font-family',
27379             width: 140
27380         }
27381     },
27382     'INPUT' : {
27383         name : {
27384             title: "name",
27385             width: 120
27386         },
27387         value : {
27388             title: "Value",
27389             width: 120
27390         },
27391         width : {
27392             title: "Width",
27393             width: 40
27394         }
27395     },
27396     'LABEL' : {
27397         'for' : {
27398             title: "For",
27399             width: 120
27400         }
27401     },
27402     'TEXTAREA' : {
27403           name : {
27404             title: "name",
27405             width: 120
27406         },
27407         rows : {
27408             title: "Rows",
27409             width: 20
27410         },
27411         cols : {
27412             title: "Cols",
27413             width: 20
27414         }
27415     },
27416     'SELECT' : {
27417         name : {
27418             title: "name",
27419             width: 120
27420         },
27421         selectoptions : {
27422             title: "Options",
27423             width: 200
27424         }
27425     },
27426     
27427     // should we really allow this??
27428     // should this just be 
27429     'BODY' : {
27430         title : {
27431             title: "Title",
27432             width: 200,
27433             disabled : true
27434         }
27435     },
27436     'SPAN' : {
27437         'font-family'  : {
27438             title : "Font",
27439             style : 'fontFamily',
27440             displayField: 'display',
27441             optname : 'font-family',
27442             width: 140
27443         }
27444     },
27445     'DIV' : {
27446         'font-family'  : {
27447             title : "Font",
27448             style : 'fontFamily',
27449             displayField: 'display',
27450             optname : 'font-family',
27451             width: 140
27452         }
27453     },
27454      'P' : {
27455         'font-family'  : {
27456             title : "Font",
27457             style : 'fontFamily',
27458             displayField: 'display',
27459             optname : 'font-family',
27460             width: 140
27461         }
27462     },
27463     
27464     '*' : {
27465         // empty..
27466     }
27467
27468 };
27469
27470 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
27471 Roo.form.HtmlEditor.ToolbarContext.stores = false;
27472
27473 Roo.form.HtmlEditor.ToolbarContext.options = {
27474         'font-family'  : [ 
27475                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
27476                 [ 'Courier New', 'Courier New'],
27477                 [ 'Tahoma', 'Tahoma'],
27478                 [ 'Times New Roman,serif', 'Times'],
27479                 [ 'Verdana','Verdana' ]
27480         ]
27481 };
27482
27483 // fixme - these need to be configurable..
27484  
27485
27486 Roo.form.HtmlEditor.ToolbarContext.types
27487
27488
27489 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
27490     
27491     tb: false,
27492     
27493     rendered: false,
27494     
27495     editor : false,
27496     /**
27497      * @cfg {Object} disable  List of toolbar elements to disable
27498          
27499      */
27500     disable : false,
27501     /**
27502      * @cfg {Object} styles List of styles 
27503      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
27504      *
27505      * These must be defined in the page, so they get rendered correctly..
27506      * .headline { }
27507      * TD.underline { }
27508      * 
27509      */
27510     styles : false,
27511     
27512     options: false,
27513     
27514     toolbars : false,
27515     
27516     init : function(editor)
27517     {
27518         this.editor = editor;
27519         
27520         
27521         var fid = editor.frameId;
27522         var etb = this;
27523         function btn(id, toggle, handler){
27524             var xid = fid + '-'+ id ;
27525             return {
27526                 id : xid,
27527                 cmd : id,
27528                 cls : 'x-btn-icon x-edit-'+id,
27529                 enableToggle:toggle !== false,
27530                 scope: editor, // was editor...
27531                 handler:handler||editor.relayBtnCmd,
27532                 clickEvent:'mousedown',
27533                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27534                 tabIndex:-1
27535             };
27536         }
27537         // create a new element.
27538         var wdiv = editor.wrap.createChild({
27539                 tag: 'div'
27540             }, editor.wrap.dom.firstChild.nextSibling, true);
27541         
27542         // can we do this more than once??
27543         
27544          // stop form submits
27545       
27546  
27547         // disable everything...
27548         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
27549         this.toolbars = {};
27550            
27551         for (var i in  ty) {
27552           
27553             this.toolbars[i] = this.buildToolbar(ty[i],i);
27554         }
27555         this.tb = this.toolbars.BODY;
27556         this.tb.el.show();
27557         this.buildFooter();
27558         this.footer.show();
27559         editor.on('hide', function( ) { this.footer.hide() }, this);
27560         editor.on('show', function( ) { this.footer.show() }, this);
27561         
27562          
27563         this.rendered = true;
27564         
27565         // the all the btns;
27566         editor.on('editorevent', this.updateToolbar, this);
27567         // other toolbars need to implement this..
27568         //editor.on('editmodechange', this.updateToolbar, this);
27569     },
27570     
27571     
27572     
27573     /**
27574      * Protected method that will not generally be called directly. It triggers
27575      * a toolbar update by reading the markup state of the current selection in the editor.
27576      */
27577     updateToolbar: function(editor,ev,sel){
27578
27579         //Roo.log(ev);
27580         // capture mouse up - this is handy for selecting images..
27581         // perhaps should go somewhere else...
27582         if(!this.editor.activated){
27583              this.editor.onFirstFocus();
27584             return;
27585         }
27586         
27587         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
27588         // selectNode - might want to handle IE?
27589         if (ev &&
27590             (ev.type == 'mouseup' || ev.type == 'click' ) &&
27591             ev.target && ev.target.tagName == 'IMG') {
27592             // they have click on an image...
27593             // let's see if we can change the selection...
27594             sel = ev.target;
27595          
27596               var nodeRange = sel.ownerDocument.createRange();
27597             try {
27598                 nodeRange.selectNode(sel);
27599             } catch (e) {
27600                 nodeRange.selectNodeContents(sel);
27601             }
27602             //nodeRange.collapse(true);
27603             var s = editor.win.getSelection();
27604             s.removeAllRanges();
27605             s.addRange(nodeRange);
27606         }  
27607         
27608       
27609         var updateFooter = sel ? false : true;
27610         
27611         
27612         var ans = this.editor.getAllAncestors();
27613         
27614         // pick
27615         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
27616         
27617         if (!sel) { 
27618             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
27619             sel = sel ? sel : this.editor.doc.body;
27620             sel = sel.tagName.length ? sel : this.editor.doc.body;
27621             
27622         }
27623         // pick a menu that exists..
27624         var tn = sel.tagName.toUpperCase();
27625         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
27626         
27627         tn = sel.tagName.toUpperCase();
27628         
27629         var lastSel = this.tb.selectedNode
27630         
27631         this.tb.selectedNode = sel;
27632         
27633         // if current menu does not match..
27634         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
27635                 
27636             this.tb.el.hide();
27637             ///console.log("show: " + tn);
27638             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
27639             this.tb.el.show();
27640             // update name
27641             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
27642             
27643             
27644             // update attributes
27645             if (this.tb.fields) {
27646                 this.tb.fields.each(function(e) {
27647                     if (e.stylename) {
27648                         e.setValue(sel.style[e.stylename]);
27649                         return;
27650                     } 
27651                    e.setValue(sel.getAttribute(e.attrname));
27652                 });
27653             }
27654             
27655             var hasStyles = false;
27656             for(var i in this.styles) {
27657                 hasStyles = true;
27658                 break;
27659             }
27660             
27661             // update styles
27662             if (hasStyles) { 
27663                 var st = this.tb.fields.item(0);
27664                 
27665                 st.store.removeAll();
27666                
27667                 
27668                 var cn = sel.className.split(/\s+/);
27669                 
27670                 var avs = [];
27671                 if (this.styles['*']) {
27672                     
27673                     Roo.each(this.styles['*'], function(v) {
27674                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
27675                     });
27676                 }
27677                 if (this.styles[tn]) { 
27678                     Roo.each(this.styles[tn], function(v) {
27679                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
27680                     });
27681                 }
27682                 
27683                 st.store.loadData(avs);
27684                 st.collapse();
27685                 st.setValue(cn);
27686             }
27687             // flag our selected Node.
27688             this.tb.selectedNode = sel;
27689            
27690            
27691             Roo.menu.MenuMgr.hideAll();
27692
27693         }
27694         
27695         if (!updateFooter) {
27696             //this.footDisp.dom.innerHTML = ''; 
27697             return;
27698         }
27699         // update the footer
27700         //
27701         var html = '';
27702         
27703         this.footerEls = ans.reverse();
27704         Roo.each(this.footerEls, function(a,i) {
27705             if (!a) { return; }
27706             html += html.length ? ' &gt; '  :  '';
27707             
27708             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
27709             
27710         });
27711        
27712         // 
27713         var sz = this.footDisp.up('td').getSize();
27714         this.footDisp.dom.style.width = (sz.width -10) + 'px';
27715         this.footDisp.dom.style.marginLeft = '5px';
27716         
27717         this.footDisp.dom.style.overflow = 'hidden';
27718         
27719         this.footDisp.dom.innerHTML = html;
27720             
27721         //this.editorsyncValue();
27722     },
27723      
27724     
27725    
27726        
27727     // private
27728     onDestroy : function(){
27729         if(this.rendered){
27730             
27731             this.tb.items.each(function(item){
27732                 if(item.menu){
27733                     item.menu.removeAll();
27734                     if(item.menu.el){
27735                         item.menu.el.destroy();
27736                     }
27737                 }
27738                 item.destroy();
27739             });
27740              
27741         }
27742     },
27743     onFirstFocus: function() {
27744         // need to do this for all the toolbars..
27745         this.tb.items.each(function(item){
27746            item.enable();
27747         });
27748     },
27749     buildToolbar: function(tlist, nm)
27750     {
27751         var editor = this.editor;
27752          // create a new element.
27753         var wdiv = editor.wrap.createChild({
27754                 tag: 'div'
27755             }, editor.wrap.dom.firstChild.nextSibling, true);
27756         
27757        
27758         var tb = new Roo.Toolbar(wdiv);
27759         // add the name..
27760         
27761         tb.add(nm+ ":&nbsp;");
27762         
27763         var styles = [];
27764         for(var i in this.styles) {
27765             styles.push(i);
27766         }
27767         
27768         // styles...
27769         if (styles && styles.length) {
27770             
27771             // this needs a multi-select checkbox...
27772             tb.addField( new Roo.form.ComboBox({
27773                 store: new Roo.data.SimpleStore({
27774                     id : 'val',
27775                     fields: ['val', 'selected'],
27776                     data : [] 
27777                 }),
27778                 name : '-roo-edit-className',
27779                 attrname : 'className',
27780                 displayField: 'val',
27781                 typeAhead: false,
27782                 mode: 'local',
27783                 editable : false,
27784                 triggerAction: 'all',
27785                 emptyText:'Select Style',
27786                 selectOnFocus:true,
27787                 width: 130,
27788                 listeners : {
27789                     'select': function(c, r, i) {
27790                         // initial support only for on class per el..
27791                         tb.selectedNode.className =  r ? r.get('val') : '';
27792                         editor.syncValue();
27793                     }
27794                 }
27795     
27796             }));
27797         }
27798         
27799         var tbc = Roo.form.HtmlEditor.ToolbarContext;
27800         var tbops = tbc.options;
27801         
27802         for (var i in tlist) {
27803             
27804             var item = tlist[i];
27805             tb.add(item.title + ":&nbsp;");
27806             
27807             
27808             //optname == used so you can configure the options available..
27809             var opts = item.opts ? item.opts : false;
27810             if (item.optname) {
27811                 opts = tbops[item.optname];
27812            
27813             }
27814             
27815             if (opts) {
27816                 // opts == pulldown..
27817                 tb.addField( new Roo.form.ComboBox({
27818                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
27819                         id : 'val',
27820                         fields: ['val', 'display'],
27821                         data : opts  
27822                     }),
27823                     name : '-roo-edit-' + i,
27824                     attrname : i,
27825                     stylename : item.style ? item.style : false,
27826                     displayField: item.displayField ? item.displayField : 'val',
27827                     valueField :  'val',
27828                     typeAhead: false,
27829                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
27830                     editable : false,
27831                     triggerAction: 'all',
27832                     emptyText:'Select',
27833                     selectOnFocus:true,
27834                     width: item.width ? item.width  : 130,
27835                     listeners : {
27836                         'select': function(c, r, i) {
27837                             if (c.stylename) {
27838                                 tb.selectedNode.style[c.stylename] =  r.get('val');
27839                                 return;
27840                             }
27841                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
27842                         }
27843                     }
27844
27845                 }));
27846                 continue;
27847                     
27848                  
27849                 
27850                 tb.addField( new Roo.form.TextField({
27851                     name: i,
27852                     width: 100,
27853                     //allowBlank:false,
27854                     value: ''
27855                 }));
27856                 continue;
27857             }
27858             tb.addField( new Roo.form.TextField({
27859                 name: '-roo-edit-' + i,
27860                 attrname : i,
27861                 
27862                 width: item.width,
27863                 //allowBlank:true,
27864                 value: '',
27865                 listeners: {
27866                     'change' : function(f, nv, ov) {
27867                         tb.selectedNode.setAttribute(f.attrname, nv);
27868                     }
27869                 }
27870             }));
27871              
27872         }
27873         tb.addFill();
27874         var _this = this;
27875         tb.addButton( {
27876             text: 'Remove Tag',
27877     
27878             listeners : {
27879                 click : function ()
27880                 {
27881                     // remove
27882                     // undo does not work.
27883                      
27884                     var sn = tb.selectedNode;
27885                     
27886                     var pn = sn.parentNode;
27887                     
27888                     var stn =  sn.childNodes[0];
27889                     var en = sn.childNodes[sn.childNodes.length - 1 ];
27890                     while (sn.childNodes.length) {
27891                         var node = sn.childNodes[0];
27892                         sn.removeChild(node);
27893                         //Roo.log(node);
27894                         pn.insertBefore(node, sn);
27895                         
27896                     }
27897                     pn.removeChild(sn);
27898                     var range = editor.createRange();
27899         
27900                     range.setStart(stn,0);
27901                     range.setEnd(en,0); //????
27902                     //range.selectNode(sel);
27903                     
27904                     
27905                     var selection = editor.getSelection();
27906                     selection.removeAllRanges();
27907                     selection.addRange(range);
27908                     
27909                     
27910                     
27911                     //_this.updateToolbar(null, null, pn);
27912                     _this.updateToolbar(null, null, null);
27913                     _this.footDisp.dom.innerHTML = ''; 
27914                 }
27915             }
27916             
27917                     
27918                 
27919             
27920         });
27921         
27922         
27923         tb.el.on('click', function(e){
27924             e.preventDefault(); // what does this do?
27925         });
27926         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
27927         tb.el.hide();
27928         tb.name = nm;
27929         // dont need to disable them... as they will get hidden
27930         return tb;
27931          
27932         
27933     },
27934     buildFooter : function()
27935     {
27936         
27937         var fel = this.editor.wrap.createChild();
27938         this.footer = new Roo.Toolbar(fel);
27939         // toolbar has scrolly on left / right?
27940         var footDisp= new Roo.Toolbar.Fill();
27941         var _t = this;
27942         this.footer.add(
27943             {
27944                 text : '&lt;',
27945                 xtype: 'Button',
27946                 handler : function() {
27947                     _t.footDisp.scrollTo('left',0,true)
27948                 }
27949             }
27950         );
27951         this.footer.add( footDisp );
27952         this.footer.add( 
27953             {
27954                 text : '&gt;',
27955                 xtype: 'Button',
27956                 handler : function() {
27957                     // no animation..
27958                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
27959                 }
27960             }
27961         );
27962         var fel = Roo.get(footDisp.el);
27963         fel.addClass('x-editor-context');
27964         this.footDispWrap = fel; 
27965         this.footDispWrap.overflow  = 'hidden';
27966         
27967         this.footDisp = fel.createChild();
27968         this.footDispWrap.on('click', this.onContextClick, this)
27969         
27970         
27971     },
27972     onContextClick : function (ev,dom)
27973     {
27974         ev.preventDefault();
27975         var  cn = dom.className;
27976         //Roo.log(cn);
27977         if (!cn.match(/x-ed-loc-/)) {
27978             return;
27979         }
27980         var n = cn.split('-').pop();
27981         var ans = this.footerEls;
27982         var sel = ans[n];
27983         
27984          // pick
27985         var range = this.editor.createRange();
27986         
27987         range.selectNodeContents(sel);
27988         //range.selectNode(sel);
27989         
27990         
27991         var selection = this.editor.getSelection();
27992         selection.removeAllRanges();
27993         selection.addRange(range);
27994         
27995         
27996         
27997         this.updateToolbar(null, null, sel);
27998         
27999         
28000     }
28001     
28002     
28003     
28004     
28005     
28006 });
28007
28008
28009
28010
28011
28012 /*
28013  * Based on:
28014  * Ext JS Library 1.1.1
28015  * Copyright(c) 2006-2007, Ext JS, LLC.
28016  *
28017  * Originally Released Under LGPL - original licence link has changed is not relivant.
28018  *
28019  * Fork - LGPL
28020  * <script type="text/javascript">
28021  */
28022  
28023 /**
28024  * @class Roo.form.BasicForm
28025  * @extends Roo.util.Observable
28026  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
28027  * @constructor
28028  * @param {String/HTMLElement/Roo.Element} el The form element or its id
28029  * @param {Object} config Configuration options
28030  */
28031 Roo.form.BasicForm = function(el, config){
28032     this.allItems = [];
28033     this.childForms = [];
28034     Roo.apply(this, config);
28035     /*
28036      * The Roo.form.Field items in this form.
28037      * @type MixedCollection
28038      */
28039      
28040      
28041     this.items = new Roo.util.MixedCollection(false, function(o){
28042         return o.id || (o.id = Roo.id());
28043     });
28044     this.addEvents({
28045         /**
28046          * @event beforeaction
28047          * Fires before any action is performed. Return false to cancel the action.
28048          * @param {Form} this
28049          * @param {Action} action The action to be performed
28050          */
28051         beforeaction: true,
28052         /**
28053          * @event actionfailed
28054          * Fires when an action fails.
28055          * @param {Form} this
28056          * @param {Action} action The action that failed
28057          */
28058         actionfailed : true,
28059         /**
28060          * @event actioncomplete
28061          * Fires when an action is completed.
28062          * @param {Form} this
28063          * @param {Action} action The action that completed
28064          */
28065         actioncomplete : true
28066     });
28067     if(el){
28068         this.initEl(el);
28069     }
28070     Roo.form.BasicForm.superclass.constructor.call(this);
28071 };
28072
28073 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
28074     /**
28075      * @cfg {String} method
28076      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
28077      */
28078     /**
28079      * @cfg {DataReader} reader
28080      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
28081      * This is optional as there is built-in support for processing JSON.
28082      */
28083     /**
28084      * @cfg {DataReader} errorReader
28085      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
28086      * This is completely optional as there is built-in support for processing JSON.
28087      */
28088     /**
28089      * @cfg {String} url
28090      * The URL to use for form actions if one isn't supplied in the action options.
28091      */
28092     /**
28093      * @cfg {Boolean} fileUpload
28094      * Set to true if this form is a file upload.
28095      */
28096      
28097     /**
28098      * @cfg {Object} baseParams
28099      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
28100      */
28101      /**
28102      
28103     /**
28104      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
28105      */
28106     timeout: 30,
28107
28108     // private
28109     activeAction : null,
28110
28111     /**
28112      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
28113      * or setValues() data instead of when the form was first created.
28114      */
28115     trackResetOnLoad : false,
28116     
28117     
28118     /**
28119      * childForms - used for multi-tab forms
28120      * @type {Array}
28121      */
28122     childForms : false,
28123     
28124     /**
28125      * allItems - full list of fields.
28126      * @type {Array}
28127      */
28128     allItems : false,
28129     
28130     /**
28131      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
28132      * element by passing it or its id or mask the form itself by passing in true.
28133      * @type Mixed
28134      */
28135     waitMsgTarget : false,
28136
28137     // private
28138     initEl : function(el){
28139         this.el = Roo.get(el);
28140         this.id = this.el.id || Roo.id();
28141         this.el.on('submit', this.onSubmit, this);
28142         this.el.addClass('x-form');
28143     },
28144
28145     // private
28146     onSubmit : function(e){
28147         e.stopEvent();
28148     },
28149
28150     /**
28151      * Returns true if client-side validation on the form is successful.
28152      * @return Boolean
28153      */
28154     isValid : function(){
28155         var valid = true;
28156         this.items.each(function(f){
28157            if(!f.validate()){
28158                valid = false;
28159            }
28160         });
28161         return valid;
28162     },
28163
28164     /**
28165      * Returns true if any fields in this form have changed since their original load.
28166      * @return Boolean
28167      */
28168     isDirty : function(){
28169         var dirty = false;
28170         this.items.each(function(f){
28171            if(f.isDirty()){
28172                dirty = true;
28173                return false;
28174            }
28175         });
28176         return dirty;
28177     },
28178
28179     /**
28180      * Performs a predefined action (submit or load) or custom actions you define on this form.
28181      * @param {String} actionName The name of the action type
28182      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
28183      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
28184      * accept other config options):
28185      * <pre>
28186 Property          Type             Description
28187 ----------------  ---------------  ----------------------------------------------------------------------------------
28188 url               String           The url for the action (defaults to the form's url)
28189 method            String           The form method to use (defaults to the form's method, or POST if not defined)
28190 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
28191 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
28192                                    validate the form on the client (defaults to false)
28193      * </pre>
28194      * @return {BasicForm} this
28195      */
28196     doAction : function(action, options){
28197         if(typeof action == 'string'){
28198             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
28199         }
28200         if(this.fireEvent('beforeaction', this, action) !== false){
28201             this.beforeAction(action);
28202             action.run.defer(100, action);
28203         }
28204         return this;
28205     },
28206
28207     /**
28208      * Shortcut to do a submit action.
28209      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
28210      * @return {BasicForm} this
28211      */
28212     submit : function(options){
28213         this.doAction('submit', options);
28214         return this;
28215     },
28216
28217     /**
28218      * Shortcut to do a load action.
28219      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
28220      * @return {BasicForm} this
28221      */
28222     load : function(options){
28223         this.doAction('load', options);
28224         return this;
28225     },
28226
28227     /**
28228      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
28229      * @param {Record} record The record to edit
28230      * @return {BasicForm} this
28231      */
28232     updateRecord : function(record){
28233         record.beginEdit();
28234         var fs = record.fields;
28235         fs.each(function(f){
28236             var field = this.findField(f.name);
28237             if(field){
28238                 record.set(f.name, field.getValue());
28239             }
28240         }, this);
28241         record.endEdit();
28242         return this;
28243     },
28244
28245     /**
28246      * Loads an Roo.data.Record into this form.
28247      * @param {Record} record The record to load
28248      * @return {BasicForm} this
28249      */
28250     loadRecord : function(record){
28251         this.setValues(record.data);
28252         return this;
28253     },
28254
28255     // private
28256     beforeAction : function(action){
28257         var o = action.options;
28258         
28259        
28260         if(this.waitMsgTarget === true){
28261             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
28262         }else if(this.waitMsgTarget){
28263             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
28264             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
28265         }else {
28266             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
28267         }
28268          
28269     },
28270
28271     // private
28272     afterAction : function(action, success){
28273         this.activeAction = null;
28274         var o = action.options;
28275         
28276         if(this.waitMsgTarget === true){
28277             this.el.unmask();
28278         }else if(this.waitMsgTarget){
28279             this.waitMsgTarget.unmask();
28280         }else{
28281             Roo.MessageBox.updateProgress(1);
28282             Roo.MessageBox.hide();
28283         }
28284          
28285         if(success){
28286             if(o.reset){
28287                 this.reset();
28288             }
28289             Roo.callback(o.success, o.scope, [this, action]);
28290             this.fireEvent('actioncomplete', this, action);
28291             
28292         }else{
28293             
28294             // failure condition..
28295             // we have a scenario where updates need confirming.
28296             // eg. if a locking scenario exists..
28297             // we look for { errors : { needs_confirm : true }} in the response.
28298             if (
28299                 (typeof(action.result) != 'undefined')  &&
28300                 (typeof(action.result.errors) != 'undefined')  &&
28301                 (typeof(action.result.errors.needs_confirm) != 'undefined')
28302            ){
28303                 var _t = this;
28304                 Roo.MessageBox.confirm(
28305                     "Change requires confirmation",
28306                     action.result.errorMsg,
28307                     function(r) {
28308                         if (r != 'yes') {
28309                             return;
28310                         }
28311                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
28312                     }
28313                     
28314                 );
28315                 
28316                 
28317                 
28318                 return;
28319             }
28320             
28321             Roo.callback(o.failure, o.scope, [this, action]);
28322             // show an error message if no failed handler is set..
28323             if (!this.hasListener('actionfailed')) {
28324                 Roo.MessageBox.alert("Error",
28325                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
28326                         action.result.errorMsg :
28327                         "Saving Failed, please check your entries or try again"
28328                 );
28329             }
28330             
28331             this.fireEvent('actionfailed', this, action);
28332         }
28333         
28334     },
28335
28336     /**
28337      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
28338      * @param {String} id The value to search for
28339      * @return Field
28340      */
28341     findField : function(id){
28342         var field = this.items.get(id);
28343         if(!field){
28344             this.items.each(function(f){
28345                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
28346                     field = f;
28347                     return false;
28348                 }
28349             });
28350         }
28351         return field || null;
28352     },
28353
28354     /**
28355      * Add a secondary form to this one, 
28356      * Used to provide tabbed forms. One form is primary, with hidden values 
28357      * which mirror the elements from the other forms.
28358      * 
28359      * @param {Roo.form.Form} form to add.
28360      * 
28361      */
28362     addForm : function(form)
28363     {
28364        
28365         if (this.childForms.indexOf(form) > -1) {
28366             // already added..
28367             return;
28368         }
28369         this.childForms.push(form);
28370         var n = '';
28371         Roo.each(form.allItems, function (fe) {
28372             
28373             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
28374             if (this.findField(n)) { // already added..
28375                 return;
28376             }
28377             var add = new Roo.form.Hidden({
28378                 name : n
28379             });
28380             add.render(this.el);
28381             
28382             this.add( add );
28383         }, this);
28384         
28385     },
28386     /**
28387      * Mark fields in this form invalid in bulk.
28388      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
28389      * @return {BasicForm} this
28390      */
28391     markInvalid : function(errors){
28392         if(errors instanceof Array){
28393             for(var i = 0, len = errors.length; i < len; i++){
28394                 var fieldError = errors[i];
28395                 var f = this.findField(fieldError.id);
28396                 if(f){
28397                     f.markInvalid(fieldError.msg);
28398                 }
28399             }
28400         }else{
28401             var field, id;
28402             for(id in errors){
28403                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
28404                     field.markInvalid(errors[id]);
28405                 }
28406             }
28407         }
28408         Roo.each(this.childForms || [], function (f) {
28409             f.markInvalid(errors);
28410         });
28411         
28412         return this;
28413     },
28414
28415     /**
28416      * Set values for fields in this form in bulk.
28417      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
28418      * @return {BasicForm} this
28419      */
28420     setValues : function(values){
28421         if(values instanceof Array){ // array of objects
28422             for(var i = 0, len = values.length; i < len; i++){
28423                 var v = values[i];
28424                 var f = this.findField(v.id);
28425                 if(f){
28426                     f.setValue(v.value);
28427                     if(this.trackResetOnLoad){
28428                         f.originalValue = f.getValue();
28429                     }
28430                 }
28431             }
28432         }else{ // object hash
28433             var field, id;
28434             for(id in values){
28435                 if(typeof values[id] != 'function' && (field = this.findField(id))){
28436                     
28437                     if (field.setFromData && 
28438                         field.valueField && 
28439                         field.displayField &&
28440                         // combos' with local stores can 
28441                         // be queried via setValue()
28442                         // to set their value..
28443                         (field.store && !field.store.isLocal)
28444                         ) {
28445                         // it's a combo
28446                         var sd = { };
28447                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
28448                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
28449                         field.setFromData(sd);
28450                         
28451                     } else {
28452                         field.setValue(values[id]);
28453                     }
28454                     
28455                     
28456                     if(this.trackResetOnLoad){
28457                         field.originalValue = field.getValue();
28458                     }
28459                 }
28460             }
28461         }
28462          
28463         Roo.each(this.childForms || [], function (f) {
28464             f.setValues(values);
28465         });
28466                 
28467         return this;
28468     },
28469
28470     /**
28471      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
28472      * they are returned as an array.
28473      * @param {Boolean} asString
28474      * @return {Object}
28475      */
28476     getValues : function(asString){
28477         if (this.childForms) {
28478             // copy values from the child forms
28479             Roo.each(this.childForms, function (f) {
28480                 this.setValues(f.getValues());
28481             }, this);
28482         }
28483         
28484         
28485         
28486         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
28487         if(asString === true){
28488             return fs;
28489         }
28490         return Roo.urlDecode(fs);
28491     },
28492     
28493     /**
28494      * Returns the fields in this form as an object with key/value pairs. 
28495      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
28496      * @return {Object}
28497      */
28498     getFieldValues : function(with_hidden)
28499     {
28500         if (this.childForms) {
28501             // copy values from the child forms
28502             // should this call getFieldValues - probably not as we do not currently copy
28503             // hidden fields when we generate..
28504             Roo.each(this.childForms, function (f) {
28505                 this.setValues(f.getValues());
28506             }, this);
28507         }
28508         
28509         var ret = {};
28510         this.items.each(function(f){
28511             if (!f.getName()) {
28512                 return;
28513             }
28514             var v = f.getValue();
28515             if (f.inputType =='radio') {
28516                 if (typeof(ret[f.getName()]) == 'undefined') {
28517                     ret[f.getName()] = ''; // empty..
28518                 }
28519                 
28520                 if (!f.el.dom.checked) {
28521                     return;
28522                     
28523                 }
28524                 v = f.el.dom.value;
28525                 
28526             }
28527             
28528             // not sure if this supported any more..
28529             if ((typeof(v) == 'object') && f.getRawValue) {
28530                 v = f.getRawValue() ; // dates..
28531             }
28532             // combo boxes where name != hiddenName...
28533             if (f.name != f.getName()) {
28534                 ret[f.name] = f.getRawValue();
28535             }
28536             ret[f.getName()] = v;
28537         });
28538         
28539         return ret;
28540     },
28541
28542     /**
28543      * Clears all invalid messages in this form.
28544      * @return {BasicForm} this
28545      */
28546     clearInvalid : function(){
28547         this.items.each(function(f){
28548            f.clearInvalid();
28549         });
28550         
28551         Roo.each(this.childForms || [], function (f) {
28552             f.clearInvalid();
28553         });
28554         
28555         
28556         return this;
28557     },
28558
28559     /**
28560      * Resets this form.
28561      * @return {BasicForm} this
28562      */
28563     reset : function(){
28564         this.items.each(function(f){
28565             f.reset();
28566         });
28567         
28568         Roo.each(this.childForms || [], function (f) {
28569             f.reset();
28570         });
28571        
28572         
28573         return this;
28574     },
28575
28576     /**
28577      * Add Roo.form components to this form.
28578      * @param {Field} field1
28579      * @param {Field} field2 (optional)
28580      * @param {Field} etc (optional)
28581      * @return {BasicForm} this
28582      */
28583     add : function(){
28584         this.items.addAll(Array.prototype.slice.call(arguments, 0));
28585         return this;
28586     },
28587
28588
28589     /**
28590      * Removes a field from the items collection (does NOT remove its markup).
28591      * @param {Field} field
28592      * @return {BasicForm} this
28593      */
28594     remove : function(field){
28595         this.items.remove(field);
28596         return this;
28597     },
28598
28599     /**
28600      * Looks at the fields in this form, checks them for an id attribute,
28601      * and calls applyTo on the existing dom element with that id.
28602      * @return {BasicForm} this
28603      */
28604     render : function(){
28605         this.items.each(function(f){
28606             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
28607                 f.applyTo(f.id);
28608             }
28609         });
28610         return this;
28611     },
28612
28613     /**
28614      * Calls {@link Ext#apply} for all fields in this form with the passed object.
28615      * @param {Object} values
28616      * @return {BasicForm} this
28617      */
28618     applyToFields : function(o){
28619         this.items.each(function(f){
28620            Roo.apply(f, o);
28621         });
28622         return this;
28623     },
28624
28625     /**
28626      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
28627      * @param {Object} values
28628      * @return {BasicForm} this
28629      */
28630     applyIfToFields : function(o){
28631         this.items.each(function(f){
28632            Roo.applyIf(f, o);
28633         });
28634         return this;
28635     }
28636 });
28637
28638 // back compat
28639 Roo.BasicForm = Roo.form.BasicForm;/*
28640  * Based on:
28641  * Ext JS Library 1.1.1
28642  * Copyright(c) 2006-2007, Ext JS, LLC.
28643  *
28644  * Originally Released Under LGPL - original licence link has changed is not relivant.
28645  *
28646  * Fork - LGPL
28647  * <script type="text/javascript">
28648  */
28649
28650 /**
28651  * @class Roo.form.Form
28652  * @extends Roo.form.BasicForm
28653  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
28654  * @constructor
28655  * @param {Object} config Configuration options
28656  */
28657 Roo.form.Form = function(config){
28658     var xitems =  [];
28659     if (config.items) {
28660         xitems = config.items;
28661         delete config.items;
28662     }
28663    
28664     
28665     Roo.form.Form.superclass.constructor.call(this, null, config);
28666     this.url = this.url || this.action;
28667     if(!this.root){
28668         this.root = new Roo.form.Layout(Roo.applyIf({
28669             id: Roo.id()
28670         }, config));
28671     }
28672     this.active = this.root;
28673     /**
28674      * Array of all the buttons that have been added to this form via {@link addButton}
28675      * @type Array
28676      */
28677     this.buttons = [];
28678     this.allItems = [];
28679     this.addEvents({
28680         /**
28681          * @event clientvalidation
28682          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
28683          * @param {Form} this
28684          * @param {Boolean} valid true if the form has passed client-side validation
28685          */
28686         clientvalidation: true,
28687         /**
28688          * @event rendered
28689          * Fires when the form is rendered
28690          * @param {Roo.form.Form} form
28691          */
28692         rendered : true
28693     });
28694     
28695     if (this.progressUrl) {
28696             // push a hidden field onto the list of fields..
28697             this.addxtype( {
28698                     xns: Roo.form, 
28699                     xtype : 'Hidden', 
28700                     name : 'UPLOAD_IDENTIFIER' 
28701             });
28702         }
28703         
28704     
28705     Roo.each(xitems, this.addxtype, this);
28706     
28707     
28708     
28709 };
28710
28711 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
28712     /**
28713      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
28714      */
28715     /**
28716      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
28717      */
28718     /**
28719      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
28720      */
28721     buttonAlign:'center',
28722
28723     /**
28724      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
28725      */
28726     minButtonWidth:75,
28727
28728     /**
28729      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
28730      * This property cascades to child containers if not set.
28731      */
28732     labelAlign:'left',
28733
28734     /**
28735      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
28736      * fires a looping event with that state. This is required to bind buttons to the valid
28737      * state using the config value formBind:true on the button.
28738      */
28739     monitorValid : false,
28740
28741     /**
28742      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
28743      */
28744     monitorPoll : 200,
28745     
28746     /**
28747      * @cfg {String} progressUrl - Url to return progress data 
28748      */
28749     
28750     progressUrl : false,
28751   
28752     /**
28753      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
28754      * fields are added and the column is closed. If no fields are passed the column remains open
28755      * until end() is called.
28756      * @param {Object} config The config to pass to the column
28757      * @param {Field} field1 (optional)
28758      * @param {Field} field2 (optional)
28759      * @param {Field} etc (optional)
28760      * @return Column The column container object
28761      */
28762     column : function(c){
28763         var col = new Roo.form.Column(c);
28764         this.start(col);
28765         if(arguments.length > 1){ // duplicate code required because of Opera
28766             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28767             this.end();
28768         }
28769         return col;
28770     },
28771
28772     /**
28773      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
28774      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
28775      * until end() is called.
28776      * @param {Object} config The config to pass to the fieldset
28777      * @param {Field} field1 (optional)
28778      * @param {Field} field2 (optional)
28779      * @param {Field} etc (optional)
28780      * @return FieldSet The fieldset container object
28781      */
28782     fieldset : function(c){
28783         var fs = new Roo.form.FieldSet(c);
28784         this.start(fs);
28785         if(arguments.length > 1){ // duplicate code required because of Opera
28786             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28787             this.end();
28788         }
28789         return fs;
28790     },
28791
28792     /**
28793      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
28794      * fields are added and the container is closed. If no fields are passed the container remains open
28795      * until end() is called.
28796      * @param {Object} config The config to pass to the Layout
28797      * @param {Field} field1 (optional)
28798      * @param {Field} field2 (optional)
28799      * @param {Field} etc (optional)
28800      * @return Layout The container object
28801      */
28802     container : function(c){
28803         var l = new Roo.form.Layout(c);
28804         this.start(l);
28805         if(arguments.length > 1){ // duplicate code required because of Opera
28806             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28807             this.end();
28808         }
28809         return l;
28810     },
28811
28812     /**
28813      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
28814      * @param {Object} container A Roo.form.Layout or subclass of Layout
28815      * @return {Form} this
28816      */
28817     start : function(c){
28818         // cascade label info
28819         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
28820         this.active.stack.push(c);
28821         c.ownerCt = this.active;
28822         this.active = c;
28823         return this;
28824     },
28825
28826     /**
28827      * Closes the current open container
28828      * @return {Form} this
28829      */
28830     end : function(){
28831         if(this.active == this.root){
28832             return this;
28833         }
28834         this.active = this.active.ownerCt;
28835         return this;
28836     },
28837
28838     /**
28839      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
28840      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
28841      * as the label of the field.
28842      * @param {Field} field1
28843      * @param {Field} field2 (optional)
28844      * @param {Field} etc. (optional)
28845      * @return {Form} this
28846      */
28847     add : function(){
28848         this.active.stack.push.apply(this.active.stack, arguments);
28849         this.allItems.push.apply(this.allItems,arguments);
28850         var r = [];
28851         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
28852             if(a[i].isFormField){
28853                 r.push(a[i]);
28854             }
28855         }
28856         if(r.length > 0){
28857             Roo.form.Form.superclass.add.apply(this, r);
28858         }
28859         return this;
28860     },
28861     
28862
28863     
28864     
28865     
28866      /**
28867      * Find any element that has been added to a form, using it's ID or name
28868      * This can include framesets, columns etc. along with regular fields..
28869      * @param {String} id - id or name to find.
28870      
28871      * @return {Element} e - or false if nothing found.
28872      */
28873     findbyId : function(id)
28874     {
28875         var ret = false;
28876         if (!id) {
28877             return ret;
28878         }
28879         Roo.each(this.allItems, function(f){
28880             if (f.id == id || f.name == id ){
28881                 ret = f;
28882                 return false;
28883             }
28884         });
28885         return ret;
28886     },
28887
28888     
28889     
28890     /**
28891      * Render this form into the passed container. This should only be called once!
28892      * @param {String/HTMLElement/Element} container The element this component should be rendered into
28893      * @return {Form} this
28894      */
28895     render : function(ct)
28896     {
28897         
28898         
28899         
28900         ct = Roo.get(ct);
28901         var o = this.autoCreate || {
28902             tag: 'form',
28903             method : this.method || 'POST',
28904             id : this.id || Roo.id()
28905         };
28906         this.initEl(ct.createChild(o));
28907
28908         this.root.render(this.el);
28909         
28910        
28911              
28912         this.items.each(function(f){
28913             f.render('x-form-el-'+f.id);
28914         });
28915
28916         if(this.buttons.length > 0){
28917             // tables are required to maintain order and for correct IE layout
28918             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
28919                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
28920                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
28921             }}, null, true);
28922             var tr = tb.getElementsByTagName('tr')[0];
28923             for(var i = 0, len = this.buttons.length; i < len; i++) {
28924                 var b = this.buttons[i];
28925                 var td = document.createElement('td');
28926                 td.className = 'x-form-btn-td';
28927                 b.render(tr.appendChild(td));
28928             }
28929         }
28930         if(this.monitorValid){ // initialize after render
28931             this.startMonitoring();
28932         }
28933         this.fireEvent('rendered', this);
28934         return this;
28935     },
28936
28937     /**
28938      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
28939      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
28940      * object or a valid Roo.DomHelper element config
28941      * @param {Function} handler The function called when the button is clicked
28942      * @param {Object} scope (optional) The scope of the handler function
28943      * @return {Roo.Button}
28944      */
28945     addButton : function(config, handler, scope){
28946         var bc = {
28947             handler: handler,
28948             scope: scope,
28949             minWidth: this.minButtonWidth,
28950             hideParent:true
28951         };
28952         if(typeof config == "string"){
28953             bc.text = config;
28954         }else{
28955             Roo.apply(bc, config);
28956         }
28957         var btn = new Roo.Button(null, bc);
28958         this.buttons.push(btn);
28959         return btn;
28960     },
28961
28962      /**
28963      * Adds a series of form elements (using the xtype property as the factory method.
28964      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
28965      * @param {Object} config 
28966      */
28967     
28968     addxtype : function()
28969     {
28970         var ar = Array.prototype.slice.call(arguments, 0);
28971         var ret = false;
28972         for(var i = 0; i < ar.length; i++) {
28973             if (!ar[i]) {
28974                 continue; // skip -- if this happends something invalid got sent, we 
28975                 // should ignore it, as basically that interface element will not show up
28976                 // and that should be pretty obvious!!
28977             }
28978             
28979             if (Roo.form[ar[i].xtype]) {
28980                 ar[i].form = this;
28981                 var fe = Roo.factory(ar[i], Roo.form);
28982                 if (!ret) {
28983                     ret = fe;
28984                 }
28985                 fe.form = this;
28986                 if (fe.store) {
28987                     fe.store.form = this;
28988                 }
28989                 if (fe.isLayout) {  
28990                          
28991                     this.start(fe);
28992                     this.allItems.push(fe);
28993                     if (fe.items && fe.addxtype) {
28994                         fe.addxtype.apply(fe, fe.items);
28995                         delete fe.items;
28996                     }
28997                      this.end();
28998                     continue;
28999                 }
29000                 
29001                 
29002                  
29003                 this.add(fe);
29004               //  console.log('adding ' + ar[i].xtype);
29005             }
29006             if (ar[i].xtype == 'Button') {  
29007                 //console.log('adding button');
29008                 //console.log(ar[i]);
29009                 this.addButton(ar[i]);
29010                 this.allItems.push(fe);
29011                 continue;
29012             }
29013             
29014             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
29015                 alert('end is not supported on xtype any more, use items');
29016             //    this.end();
29017             //    //console.log('adding end');
29018             }
29019             
29020         }
29021         return ret;
29022     },
29023     
29024     /**
29025      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
29026      * option "monitorValid"
29027      */
29028     startMonitoring : function(){
29029         if(!this.bound){
29030             this.bound = true;
29031             Roo.TaskMgr.start({
29032                 run : this.bindHandler,
29033                 interval : this.monitorPoll || 200,
29034                 scope: this
29035             });
29036         }
29037     },
29038
29039     /**
29040      * Stops monitoring of the valid state of this form
29041      */
29042     stopMonitoring : function(){
29043         this.bound = false;
29044     },
29045
29046     // private
29047     bindHandler : function(){
29048         if(!this.bound){
29049             return false; // stops binding
29050         }
29051         var valid = true;
29052         this.items.each(function(f){
29053             if(!f.isValid(true)){
29054                 valid = false;
29055                 return false;
29056             }
29057         });
29058         for(var i = 0, len = this.buttons.length; i < len; i++){
29059             var btn = this.buttons[i];
29060             if(btn.formBind === true && btn.disabled === valid){
29061                 btn.setDisabled(!valid);
29062             }
29063         }
29064         this.fireEvent('clientvalidation', this, valid);
29065     }
29066     
29067     
29068     
29069     
29070     
29071     
29072     
29073     
29074 });
29075
29076
29077 // back compat
29078 Roo.Form = Roo.form.Form;
29079 /*
29080  * Based on:
29081  * Ext JS Library 1.1.1
29082  * Copyright(c) 2006-2007, Ext JS, LLC.
29083  *
29084  * Originally Released Under LGPL - original licence link has changed is not relivant.
29085  *
29086  * Fork - LGPL
29087  * <script type="text/javascript">
29088  */
29089  
29090  /**
29091  * @class Roo.form.Action
29092  * Internal Class used to handle form actions
29093  * @constructor
29094  * @param {Roo.form.BasicForm} el The form element or its id
29095  * @param {Object} config Configuration options
29096  */
29097  
29098  
29099 // define the action interface
29100 Roo.form.Action = function(form, options){
29101     this.form = form;
29102     this.options = options || {};
29103 };
29104 /**
29105  * Client Validation Failed
29106  * @const 
29107  */
29108 Roo.form.Action.CLIENT_INVALID = 'client';
29109 /**
29110  * Server Validation Failed
29111  * @const 
29112  */
29113  Roo.form.Action.SERVER_INVALID = 'server';
29114  /**
29115  * Connect to Server Failed
29116  * @const 
29117  */
29118 Roo.form.Action.CONNECT_FAILURE = 'connect';
29119 /**
29120  * Reading Data from Server Failed
29121  * @const 
29122  */
29123 Roo.form.Action.LOAD_FAILURE = 'load';
29124
29125 Roo.form.Action.prototype = {
29126     type : 'default',
29127     failureType : undefined,
29128     response : undefined,
29129     result : undefined,
29130
29131     // interface method
29132     run : function(options){
29133
29134     },
29135
29136     // interface method
29137     success : function(response){
29138
29139     },
29140
29141     // interface method
29142     handleResponse : function(response){
29143
29144     },
29145
29146     // default connection failure
29147     failure : function(response){
29148         
29149         this.response = response;
29150         this.failureType = Roo.form.Action.CONNECT_FAILURE;
29151         this.form.afterAction(this, false);
29152     },
29153
29154     processResponse : function(response){
29155         this.response = response;
29156         if(!response.responseText){
29157             return true;
29158         }
29159         this.result = this.handleResponse(response);
29160         return this.result;
29161     },
29162
29163     // utility functions used internally
29164     getUrl : function(appendParams){
29165         var url = this.options.url || this.form.url || this.form.el.dom.action;
29166         if(appendParams){
29167             var p = this.getParams();
29168             if(p){
29169                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
29170             }
29171         }
29172         return url;
29173     },
29174
29175     getMethod : function(){
29176         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
29177     },
29178
29179     getParams : function(){
29180         var bp = this.form.baseParams;
29181         var p = this.options.params;
29182         if(p){
29183             if(typeof p == "object"){
29184                 p = Roo.urlEncode(Roo.applyIf(p, bp));
29185             }else if(typeof p == 'string' && bp){
29186                 p += '&' + Roo.urlEncode(bp);
29187             }
29188         }else if(bp){
29189             p = Roo.urlEncode(bp);
29190         }
29191         return p;
29192     },
29193
29194     createCallback : function(){
29195         return {
29196             success: this.success,
29197             failure: this.failure,
29198             scope: this,
29199             timeout: (this.form.timeout*1000),
29200             upload: this.form.fileUpload ? this.success : undefined
29201         };
29202     }
29203 };
29204
29205 Roo.form.Action.Submit = function(form, options){
29206     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
29207 };
29208
29209 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
29210     type : 'submit',
29211
29212     haveProgress : false,
29213     uploadComplete : false,
29214     
29215     // uploadProgress indicator.
29216     uploadProgress : function()
29217     {
29218         if (!this.form.progressUrl) {
29219             return;
29220         }
29221         
29222         if (!this.haveProgress) {
29223             Roo.MessageBox.progress("Uploading", "Uploading");
29224         }
29225         if (this.uploadComplete) {
29226            Roo.MessageBox.hide();
29227            return;
29228         }
29229         
29230         this.haveProgress = true;
29231    
29232         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
29233         
29234         var c = new Roo.data.Connection();
29235         c.request({
29236             url : this.form.progressUrl,
29237             params: {
29238                 id : uid
29239             },
29240             method: 'GET',
29241             success : function(req){
29242                //console.log(data);
29243                 var rdata = false;
29244                 var edata;
29245                 try  {
29246                    rdata = Roo.decode(req.responseText)
29247                 } catch (e) {
29248                     Roo.log("Invalid data from server..");
29249                     Roo.log(edata);
29250                     return;
29251                 }
29252                 if (!rdata || !rdata.success) {
29253                     Roo.log(rdata);
29254                     Roo.MessageBox.alert(Roo.encode(rdata));
29255                     return;
29256                 }
29257                 var data = rdata.data;
29258                 
29259                 if (this.uploadComplete) {
29260                    Roo.MessageBox.hide();
29261                    return;
29262                 }
29263                    
29264                 if (data){
29265                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
29266                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
29267                     );
29268                 }
29269                 this.uploadProgress.defer(2000,this);
29270             },
29271        
29272             failure: function(data) {
29273                 Roo.log('progress url failed ');
29274                 Roo.log(data);
29275             },
29276             scope : this
29277         });
29278            
29279     },
29280     
29281     
29282     run : function()
29283     {
29284         // run get Values on the form, so it syncs any secondary forms.
29285         this.form.getValues();
29286         
29287         var o = this.options;
29288         var method = this.getMethod();
29289         var isPost = method == 'POST';
29290         if(o.clientValidation === false || this.form.isValid()){
29291             
29292             if (this.form.progressUrl) {
29293                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
29294                     (new Date() * 1) + '' + Math.random());
29295                     
29296             } 
29297             
29298             
29299             Roo.Ajax.request(Roo.apply(this.createCallback(), {
29300                 form:this.form.el.dom,
29301                 url:this.getUrl(!isPost),
29302                 method: method,
29303                 params:isPost ? this.getParams() : null,
29304                 isUpload: this.form.fileUpload
29305             }));
29306             
29307             this.uploadProgress();
29308
29309         }else if (o.clientValidation !== false){ // client validation failed
29310             this.failureType = Roo.form.Action.CLIENT_INVALID;
29311             this.form.afterAction(this, false);
29312         }
29313     },
29314
29315     success : function(response)
29316     {
29317         this.uploadComplete= true;
29318         if (this.haveProgress) {
29319             Roo.MessageBox.hide();
29320         }
29321         
29322         
29323         var result = this.processResponse(response);
29324         if(result === true || result.success){
29325             this.form.afterAction(this, true);
29326             return;
29327         }
29328         if(result.errors){
29329             this.form.markInvalid(result.errors);
29330             this.failureType = Roo.form.Action.SERVER_INVALID;
29331         }
29332         this.form.afterAction(this, false);
29333     },
29334     failure : function(response)
29335     {
29336         this.uploadComplete= true;
29337         if (this.haveProgress) {
29338             Roo.MessageBox.hide();
29339         }
29340         
29341         this.response = response;
29342         this.failureType = Roo.form.Action.CONNECT_FAILURE;
29343         this.form.afterAction(this, false);
29344     },
29345     
29346     handleResponse : function(response){
29347         if(this.form.errorReader){
29348             var rs = this.form.errorReader.read(response);
29349             var errors = [];
29350             if(rs.records){
29351                 for(var i = 0, len = rs.records.length; i < len; i++) {
29352                     var r = rs.records[i];
29353                     errors[i] = r.data;
29354                 }
29355             }
29356             if(errors.length < 1){
29357                 errors = null;
29358             }
29359             return {
29360                 success : rs.success,
29361                 errors : errors
29362             };
29363         }
29364         var ret = false;
29365         try {
29366             ret = Roo.decode(response.responseText);
29367         } catch (e) {
29368             ret = {
29369                 success: false,
29370                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
29371                 errors : []
29372             };
29373         }
29374         return ret;
29375         
29376     }
29377 });
29378
29379
29380 Roo.form.Action.Load = function(form, options){
29381     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
29382     this.reader = this.form.reader;
29383 };
29384
29385 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
29386     type : 'load',
29387
29388     run : function(){
29389         
29390         Roo.Ajax.request(Roo.apply(
29391                 this.createCallback(), {
29392                     method:this.getMethod(),
29393                     url:this.getUrl(false),
29394                     params:this.getParams()
29395         }));
29396     },
29397
29398     success : function(response){
29399         
29400         var result = this.processResponse(response);
29401         if(result === true || !result.success || !result.data){
29402             this.failureType = Roo.form.Action.LOAD_FAILURE;
29403             this.form.afterAction(this, false);
29404             return;
29405         }
29406         this.form.clearInvalid();
29407         this.form.setValues(result.data);
29408         this.form.afterAction(this, true);
29409     },
29410
29411     handleResponse : function(response){
29412         if(this.form.reader){
29413             var rs = this.form.reader.read(response);
29414             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
29415             return {
29416                 success : rs.success,
29417                 data : data
29418             };
29419         }
29420         return Roo.decode(response.responseText);
29421     }
29422 });
29423
29424 Roo.form.Action.ACTION_TYPES = {
29425     'load' : Roo.form.Action.Load,
29426     'submit' : Roo.form.Action.Submit
29427 };/*
29428  * Based on:
29429  * Ext JS Library 1.1.1
29430  * Copyright(c) 2006-2007, Ext JS, LLC.
29431  *
29432  * Originally Released Under LGPL - original licence link has changed is not relivant.
29433  *
29434  * Fork - LGPL
29435  * <script type="text/javascript">
29436  */
29437  
29438 /**
29439  * @class Roo.form.Layout
29440  * @extends Roo.Component
29441  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
29442  * @constructor
29443  * @param {Object} config Configuration options
29444  */
29445 Roo.form.Layout = function(config){
29446     var xitems = [];
29447     if (config.items) {
29448         xitems = config.items;
29449         delete config.items;
29450     }
29451     Roo.form.Layout.superclass.constructor.call(this, config);
29452     this.stack = [];
29453     Roo.each(xitems, this.addxtype, this);
29454      
29455 };
29456
29457 Roo.extend(Roo.form.Layout, Roo.Component, {
29458     /**
29459      * @cfg {String/Object} autoCreate
29460      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
29461      */
29462     /**
29463      * @cfg {String/Object/Function} style
29464      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
29465      * a function which returns such a specification.
29466      */
29467     /**
29468      * @cfg {String} labelAlign
29469      * Valid values are "left," "top" and "right" (defaults to "left")
29470      */
29471     /**
29472      * @cfg {Number} labelWidth
29473      * Fixed width in pixels of all field labels (defaults to undefined)
29474      */
29475     /**
29476      * @cfg {Boolean} clear
29477      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
29478      */
29479     clear : true,
29480     /**
29481      * @cfg {String} labelSeparator
29482      * The separator to use after field labels (defaults to ':')
29483      */
29484     labelSeparator : ':',
29485     /**
29486      * @cfg {Boolean} hideLabels
29487      * True to suppress the display of field labels in this layout (defaults to false)
29488      */
29489     hideLabels : false,
29490
29491     // private
29492     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
29493     
29494     isLayout : true,
29495     
29496     // private
29497     onRender : function(ct, position){
29498         if(this.el){ // from markup
29499             this.el = Roo.get(this.el);
29500         }else {  // generate
29501             var cfg = this.getAutoCreate();
29502             this.el = ct.createChild(cfg, position);
29503         }
29504         if(this.style){
29505             this.el.applyStyles(this.style);
29506         }
29507         if(this.labelAlign){
29508             this.el.addClass('x-form-label-'+this.labelAlign);
29509         }
29510         if(this.hideLabels){
29511             this.labelStyle = "display:none";
29512             this.elementStyle = "padding-left:0;";
29513         }else{
29514             if(typeof this.labelWidth == 'number'){
29515                 this.labelStyle = "width:"+this.labelWidth+"px;";
29516                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
29517             }
29518             if(this.labelAlign == 'top'){
29519                 this.labelStyle = "width:auto;";
29520                 this.elementStyle = "padding-left:0;";
29521             }
29522         }
29523         var stack = this.stack;
29524         var slen = stack.length;
29525         if(slen > 0){
29526             if(!this.fieldTpl){
29527                 var t = new Roo.Template(
29528                     '<div class="x-form-item {5}">',
29529                         '<label for="{0}" style="{2}">{1}{4}</label>',
29530                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
29531                         '</div>',
29532                     '</div><div class="x-form-clear-left"></div>'
29533                 );
29534                 t.disableFormats = true;
29535                 t.compile();
29536                 Roo.form.Layout.prototype.fieldTpl = t;
29537             }
29538             for(var i = 0; i < slen; i++) {
29539                 if(stack[i].isFormField){
29540                     this.renderField(stack[i]);
29541                 }else{
29542                     this.renderComponent(stack[i]);
29543                 }
29544             }
29545         }
29546         if(this.clear){
29547             this.el.createChild({cls:'x-form-clear'});
29548         }
29549     },
29550
29551     // private
29552     renderField : function(f){
29553         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
29554                f.id, //0
29555                f.fieldLabel, //1
29556                f.labelStyle||this.labelStyle||'', //2
29557                this.elementStyle||'', //3
29558                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
29559                f.itemCls||this.itemCls||''  //5
29560        ], true).getPrevSibling());
29561     },
29562
29563     // private
29564     renderComponent : function(c){
29565         c.render(c.isLayout ? this.el : this.el.createChild());    
29566     },
29567     /**
29568      * Adds a object form elements (using the xtype property as the factory method.)
29569      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
29570      * @param {Object} config 
29571      */
29572     addxtype : function(o)
29573     {
29574         // create the lement.
29575         o.form = this.form;
29576         var fe = Roo.factory(o, Roo.form);
29577         this.form.allItems.push(fe);
29578         this.stack.push(fe);
29579         
29580         if (fe.isFormField) {
29581             this.form.items.add(fe);
29582         }
29583          
29584         return fe;
29585     }
29586 });
29587
29588 /**
29589  * @class Roo.form.Column
29590  * @extends Roo.form.Layout
29591  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
29592  * @constructor
29593  * @param {Object} config Configuration options
29594  */
29595 Roo.form.Column = function(config){
29596     Roo.form.Column.superclass.constructor.call(this, config);
29597 };
29598
29599 Roo.extend(Roo.form.Column, Roo.form.Layout, {
29600     /**
29601      * @cfg {Number/String} width
29602      * The fixed width of the column in pixels or CSS value (defaults to "auto")
29603      */
29604     /**
29605      * @cfg {String/Object} autoCreate
29606      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
29607      */
29608
29609     // private
29610     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
29611
29612     // private
29613     onRender : function(ct, position){
29614         Roo.form.Column.superclass.onRender.call(this, ct, position);
29615         if(this.width){
29616             this.el.setWidth(this.width);
29617         }
29618     }
29619 });
29620
29621
29622 /**
29623  * @class Roo.form.Row
29624  * @extends Roo.form.Layout
29625  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
29626  * @constructor
29627  * @param {Object} config Configuration options
29628  */
29629
29630  
29631 Roo.form.Row = function(config){
29632     Roo.form.Row.superclass.constructor.call(this, config);
29633 };
29634  
29635 Roo.extend(Roo.form.Row, Roo.form.Layout, {
29636       /**
29637      * @cfg {Number/String} width
29638      * The fixed width of the column in pixels or CSS value (defaults to "auto")
29639      */
29640     /**
29641      * @cfg {Number/String} height
29642      * The fixed height of the column in pixels or CSS value (defaults to "auto")
29643      */
29644     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
29645     
29646     padWidth : 20,
29647     // private
29648     onRender : function(ct, position){
29649         //console.log('row render');
29650         if(!this.rowTpl){
29651             var t = new Roo.Template(
29652                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
29653                     '<label for="{0}" style="{2}">{1}{4}</label>',
29654                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
29655                     '</div>',
29656                 '</div>'
29657             );
29658             t.disableFormats = true;
29659             t.compile();
29660             Roo.form.Layout.prototype.rowTpl = t;
29661         }
29662         this.fieldTpl = this.rowTpl;
29663         
29664         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
29665         var labelWidth = 100;
29666         
29667         if ((this.labelAlign != 'top')) {
29668             if (typeof this.labelWidth == 'number') {
29669                 labelWidth = this.labelWidth
29670             }
29671             this.padWidth =  20 + labelWidth;
29672             
29673         }
29674         
29675         Roo.form.Column.superclass.onRender.call(this, ct, position);
29676         if(this.width){
29677             this.el.setWidth(this.width);
29678         }
29679         if(this.height){
29680             this.el.setHeight(this.height);
29681         }
29682     },
29683     
29684     // private
29685     renderField : function(f){
29686         f.fieldEl = this.fieldTpl.append(this.el, [
29687                f.id, f.fieldLabel,
29688                f.labelStyle||this.labelStyle||'',
29689                this.elementStyle||'',
29690                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
29691                f.itemCls||this.itemCls||'',
29692                f.width ? f.width + this.padWidth : 160 + this.padWidth
29693        ],true);
29694     }
29695 });
29696  
29697
29698 /**
29699  * @class Roo.form.FieldSet
29700  * @extends Roo.form.Layout
29701  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
29702  * @constructor
29703  * @param {Object} config Configuration options
29704  */
29705 Roo.form.FieldSet = function(config){
29706     Roo.form.FieldSet.superclass.constructor.call(this, config);
29707 };
29708
29709 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
29710     /**
29711      * @cfg {String} legend
29712      * The text to display as the legend for the FieldSet (defaults to '')
29713      */
29714     /**
29715      * @cfg {String/Object} autoCreate
29716      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
29717      */
29718
29719     // private
29720     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
29721
29722     // private
29723     onRender : function(ct, position){
29724         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
29725         if(this.legend){
29726             this.setLegend(this.legend);
29727         }
29728     },
29729
29730     // private
29731     setLegend : function(text){
29732         if(this.rendered){
29733             this.el.child('legend').update(text);
29734         }
29735     }
29736 });/*
29737  * Based on:
29738  * Ext JS Library 1.1.1
29739  * Copyright(c) 2006-2007, Ext JS, LLC.
29740  *
29741  * Originally Released Under LGPL - original licence link has changed is not relivant.
29742  *
29743  * Fork - LGPL
29744  * <script type="text/javascript">
29745  */
29746 /**
29747  * @class Roo.form.VTypes
29748  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
29749  * @singleton
29750  */
29751 Roo.form.VTypes = function(){
29752     // closure these in so they are only created once.
29753     var alpha = /^[a-zA-Z_]+$/;
29754     var alphanum = /^[a-zA-Z0-9_]+$/;
29755     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
29756     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
29757
29758     // All these messages and functions are configurable
29759     return {
29760         /**
29761          * The function used to validate email addresses
29762          * @param {String} value The email address
29763          */
29764         'email' : function(v){
29765             return email.test(v);
29766         },
29767         /**
29768          * The error text to display when the email validation function returns false
29769          * @type String
29770          */
29771         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
29772         /**
29773          * The keystroke filter mask to be applied on email input
29774          * @type RegExp
29775          */
29776         'emailMask' : /[a-z0-9_\.\-@]/i,
29777
29778         /**
29779          * The function used to validate URLs
29780          * @param {String} value The URL
29781          */
29782         'url' : function(v){
29783             return url.test(v);
29784         },
29785         /**
29786          * The error text to display when the url validation function returns false
29787          * @type String
29788          */
29789         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
29790         
29791         /**
29792          * The function used to validate alpha values
29793          * @param {String} value The value
29794          */
29795         'alpha' : function(v){
29796             return alpha.test(v);
29797         },
29798         /**
29799          * The error text to display when the alpha validation function returns false
29800          * @type String
29801          */
29802         'alphaText' : 'This field should only contain letters and _',
29803         /**
29804          * The keystroke filter mask to be applied on alpha input
29805          * @type RegExp
29806          */
29807         'alphaMask' : /[a-z_]/i,
29808
29809         /**
29810          * The function used to validate alphanumeric values
29811          * @param {String} value The value
29812          */
29813         'alphanum' : function(v){
29814             return alphanum.test(v);
29815         },
29816         /**
29817          * The error text to display when the alphanumeric validation function returns false
29818          * @type String
29819          */
29820         'alphanumText' : 'This field should only contain letters, numbers and _',
29821         /**
29822          * The keystroke filter mask to be applied on alphanumeric input
29823          * @type RegExp
29824          */
29825         'alphanumMask' : /[a-z0-9_]/i
29826     };
29827 }();//<script type="text/javascript">
29828
29829 /**
29830  * @class Roo.form.FCKeditor
29831  * @extends Roo.form.TextArea
29832  * Wrapper around the FCKEditor http://www.fckeditor.net
29833  * @constructor
29834  * Creates a new FCKeditor
29835  * @param {Object} config Configuration options
29836  */
29837 Roo.form.FCKeditor = function(config){
29838     Roo.form.FCKeditor.superclass.constructor.call(this, config);
29839     this.addEvents({
29840          /**
29841          * @event editorinit
29842          * Fired when the editor is initialized - you can add extra handlers here..
29843          * @param {FCKeditor} this
29844          * @param {Object} the FCK object.
29845          */
29846         editorinit : true
29847     });
29848     
29849     
29850 };
29851 Roo.form.FCKeditor.editors = { };
29852 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
29853 {
29854     //defaultAutoCreate : {
29855     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
29856     //},
29857     // private
29858     /**
29859      * @cfg {Object} fck options - see fck manual for details.
29860      */
29861     fckconfig : false,
29862     
29863     /**
29864      * @cfg {Object} fck toolbar set (Basic or Default)
29865      */
29866     toolbarSet : 'Basic',
29867     /**
29868      * @cfg {Object} fck BasePath
29869      */ 
29870     basePath : '/fckeditor/',
29871     
29872     
29873     frame : false,
29874     
29875     value : '',
29876     
29877    
29878     onRender : function(ct, position)
29879     {
29880         if(!this.el){
29881             this.defaultAutoCreate = {
29882                 tag: "textarea",
29883                 style:"width:300px;height:60px;",
29884                 autocomplete: "off"
29885             };
29886         }
29887         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
29888         /*
29889         if(this.grow){
29890             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
29891             if(this.preventScrollbars){
29892                 this.el.setStyle("overflow", "hidden");
29893             }
29894             this.el.setHeight(this.growMin);
29895         }
29896         */
29897         //console.log('onrender' + this.getId() );
29898         Roo.form.FCKeditor.editors[this.getId()] = this;
29899          
29900
29901         this.replaceTextarea() ;
29902         
29903     },
29904     
29905     getEditor : function() {
29906         return this.fckEditor;
29907     },
29908     /**
29909      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
29910      * @param {Mixed} value The value to set
29911      */
29912     
29913     
29914     setValue : function(value)
29915     {
29916         //console.log('setValue: ' + value);
29917         
29918         if(typeof(value) == 'undefined') { // not sure why this is happending...
29919             return;
29920         }
29921         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
29922         
29923         //if(!this.el || !this.getEditor()) {
29924         //    this.value = value;
29925             //this.setValue.defer(100,this,[value]);    
29926         //    return;
29927         //} 
29928         
29929         if(!this.getEditor()) {
29930             return;
29931         }
29932         
29933         this.getEditor().SetData(value);
29934         
29935         //
29936
29937     },
29938
29939     /**
29940      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
29941      * @return {Mixed} value The field value
29942      */
29943     getValue : function()
29944     {
29945         
29946         if (this.frame && this.frame.dom.style.display == 'none') {
29947             return Roo.form.FCKeditor.superclass.getValue.call(this);
29948         }
29949         
29950         if(!this.el || !this.getEditor()) {
29951            
29952            // this.getValue.defer(100,this); 
29953             return this.value;
29954         }
29955        
29956         
29957         var value=this.getEditor().GetData();
29958         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
29959         return Roo.form.FCKeditor.superclass.getValue.call(this);
29960         
29961
29962     },
29963
29964     /**
29965      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
29966      * @return {Mixed} value The field value
29967      */
29968     getRawValue : function()
29969     {
29970         if (this.frame && this.frame.dom.style.display == 'none') {
29971             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
29972         }
29973         
29974         if(!this.el || !this.getEditor()) {
29975             //this.getRawValue.defer(100,this); 
29976             return this.value;
29977             return;
29978         }
29979         
29980         
29981         
29982         var value=this.getEditor().GetData();
29983         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
29984         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
29985          
29986     },
29987     
29988     setSize : function(w,h) {
29989         
29990         
29991         
29992         //if (this.frame && this.frame.dom.style.display == 'none') {
29993         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
29994         //    return;
29995         //}
29996         //if(!this.el || !this.getEditor()) {
29997         //    this.setSize.defer(100,this, [w,h]); 
29998         //    return;
29999         //}
30000         
30001         
30002         
30003         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
30004         
30005         this.frame.dom.setAttribute('width', w);
30006         this.frame.dom.setAttribute('height', h);
30007         this.frame.setSize(w,h);
30008         
30009     },
30010     
30011     toggleSourceEdit : function(value) {
30012         
30013       
30014          
30015         this.el.dom.style.display = value ? '' : 'none';
30016         this.frame.dom.style.display = value ?  'none' : '';
30017         
30018     },
30019     
30020     
30021     focus: function(tag)
30022     {
30023         if (this.frame.dom.style.display == 'none') {
30024             return Roo.form.FCKeditor.superclass.focus.call(this);
30025         }
30026         if(!this.el || !this.getEditor()) {
30027             this.focus.defer(100,this, [tag]); 
30028             return;
30029         }
30030         
30031         
30032         
30033         
30034         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
30035         this.getEditor().Focus();
30036         if (tgs.length) {
30037             if (!this.getEditor().Selection.GetSelection()) {
30038                 this.focus.defer(100,this, [tag]); 
30039                 return;
30040             }
30041             
30042             
30043             var r = this.getEditor().EditorDocument.createRange();
30044             r.setStart(tgs[0],0);
30045             r.setEnd(tgs[0],0);
30046             this.getEditor().Selection.GetSelection().removeAllRanges();
30047             this.getEditor().Selection.GetSelection().addRange(r);
30048             this.getEditor().Focus();
30049         }
30050         
30051     },
30052     
30053     
30054     
30055     replaceTextarea : function()
30056     {
30057         if ( document.getElementById( this.getId() + '___Frame' ) )
30058             return ;
30059         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
30060         //{
30061             // We must check the elements firstly using the Id and then the name.
30062         var oTextarea = document.getElementById( this.getId() );
30063         
30064         var colElementsByName = document.getElementsByName( this.getId() ) ;
30065          
30066         oTextarea.style.display = 'none' ;
30067
30068         if ( oTextarea.tabIndex ) {            
30069             this.TabIndex = oTextarea.tabIndex ;
30070         }
30071         
30072         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
30073         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
30074         this.frame = Roo.get(this.getId() + '___Frame')
30075     },
30076     
30077     _getConfigHtml : function()
30078     {
30079         var sConfig = '' ;
30080
30081         for ( var o in this.fckconfig ) {
30082             sConfig += sConfig.length > 0  ? '&amp;' : '';
30083             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
30084         }
30085
30086         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
30087     },
30088     
30089     
30090     _getIFrameHtml : function()
30091     {
30092         var sFile = 'fckeditor.html' ;
30093         /* no idea what this is about..
30094         try
30095         {
30096             if ( (/fcksource=true/i).test( window.top.location.search ) )
30097                 sFile = 'fckeditor.original.html' ;
30098         }
30099         catch (e) { 
30100         */
30101
30102         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
30103         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
30104         
30105         
30106         var html = '<iframe id="' + this.getId() +
30107             '___Frame" src="' + sLink +
30108             '" width="' + this.width +
30109             '" height="' + this.height + '"' +
30110             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
30111             ' frameborder="0" scrolling="no"></iframe>' ;
30112
30113         return html ;
30114     },
30115     
30116     _insertHtmlBefore : function( html, element )
30117     {
30118         if ( element.insertAdjacentHTML )       {
30119             // IE
30120             element.insertAdjacentHTML( 'beforeBegin', html ) ;
30121         } else { // Gecko
30122             var oRange = document.createRange() ;
30123             oRange.setStartBefore( element ) ;
30124             var oFragment = oRange.createContextualFragment( html );
30125             element.parentNode.insertBefore( oFragment, element ) ;
30126         }
30127     }
30128     
30129     
30130   
30131     
30132     
30133     
30134     
30135
30136 });
30137
30138 //Roo.reg('fckeditor', Roo.form.FCKeditor);
30139
30140 function FCKeditor_OnComplete(editorInstance){
30141     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
30142     f.fckEditor = editorInstance;
30143     //console.log("loaded");
30144     f.fireEvent('editorinit', f, editorInstance);
30145
30146   
30147
30148  
30149
30150
30151
30152
30153
30154
30155
30156
30157
30158
30159
30160
30161
30162
30163
30164 //<script type="text/javascript">
30165 /**
30166  * @class Roo.form.GridField
30167  * @extends Roo.form.Field
30168  * Embed a grid (or editable grid into a form)
30169  * STATUS ALPHA
30170  * 
30171  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
30172  * it needs 
30173  * xgrid.store = Roo.data.Store
30174  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
30175  * xgrid.store.reader = Roo.data.JsonReader 
30176  * 
30177  * 
30178  * @constructor
30179  * Creates a new GridField
30180  * @param {Object} config Configuration options
30181  */
30182 Roo.form.GridField = function(config){
30183     Roo.form.GridField.superclass.constructor.call(this, config);
30184      
30185 };
30186
30187 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
30188     /**
30189      * @cfg {Number} width  - used to restrict width of grid..
30190      */
30191     width : 100,
30192     /**
30193      * @cfg {Number} height - used to restrict height of grid..
30194      */
30195     height : 50,
30196      /**
30197      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
30198          * 
30199          *}
30200      */
30201     xgrid : false, 
30202     /**
30203      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30204      * {tag: "input", type: "checkbox", autocomplete: "off"})
30205      */
30206    // defaultAutoCreate : { tag: 'div' },
30207     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
30208     /**
30209      * @cfg {String} addTitle Text to include for adding a title.
30210      */
30211     addTitle : false,
30212     //
30213     onResize : function(){
30214         Roo.form.Field.superclass.onResize.apply(this, arguments);
30215     },
30216
30217     initEvents : function(){
30218         // Roo.form.Checkbox.superclass.initEvents.call(this);
30219         // has no events...
30220        
30221     },
30222
30223
30224     getResizeEl : function(){
30225         return this.wrap;
30226     },
30227
30228     getPositionEl : function(){
30229         return this.wrap;
30230     },
30231
30232     // private
30233     onRender : function(ct, position){
30234         
30235         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
30236         var style = this.style;
30237         delete this.style;
30238         
30239         Roo.form.GridField.superclass.onRender.call(this, ct, position);
30240         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
30241         this.viewEl = this.wrap.createChild({ tag: 'div' });
30242         if (style) {
30243             this.viewEl.applyStyles(style);
30244         }
30245         if (this.width) {
30246             this.viewEl.setWidth(this.width);
30247         }
30248         if (this.height) {
30249             this.viewEl.setHeight(this.height);
30250         }
30251         //if(this.inputValue !== undefined){
30252         //this.setValue(this.value);
30253         
30254         
30255         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
30256         
30257         
30258         this.grid.render();
30259         this.grid.getDataSource().on('remove', this.refreshValue, this);
30260         this.grid.getDataSource().on('update', this.refreshValue, this);
30261         this.grid.on('afteredit', this.refreshValue, this);
30262  
30263     },
30264      
30265     
30266     /**
30267      * Sets the value of the item. 
30268      * @param {String} either an object  or a string..
30269      */
30270     setValue : function(v){
30271         //this.value = v;
30272         v = v || []; // empty set..
30273         // this does not seem smart - it really only affects memoryproxy grids..
30274         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
30275             var ds = this.grid.getDataSource();
30276             // assumes a json reader..
30277             var data = {}
30278             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
30279             ds.loadData( data);
30280         }
30281         // clear selection so it does not get stale.
30282         if (this.grid.sm) { 
30283             this.grid.sm.clearSelections();
30284         }
30285         
30286         Roo.form.GridField.superclass.setValue.call(this, v);
30287         this.refreshValue();
30288         // should load data in the grid really....
30289     },
30290     
30291     // private
30292     refreshValue: function() {
30293          var val = [];
30294         this.grid.getDataSource().each(function(r) {
30295             val.push(r.data);
30296         });
30297         this.el.dom.value = Roo.encode(val);
30298     }
30299     
30300      
30301     
30302     
30303 });/*
30304  * Based on:
30305  * Ext JS Library 1.1.1
30306  * Copyright(c) 2006-2007, Ext JS, LLC.
30307  *
30308  * Originally Released Under LGPL - original licence link has changed is not relivant.
30309  *
30310  * Fork - LGPL
30311  * <script type="text/javascript">
30312  */
30313 /**
30314  * @class Roo.form.DisplayField
30315  * @extends Roo.form.Field
30316  * A generic Field to display non-editable data.
30317  * @constructor
30318  * Creates a new Display Field item.
30319  * @param {Object} config Configuration options
30320  */
30321 Roo.form.DisplayField = function(config){
30322     Roo.form.DisplayField.superclass.constructor.call(this, config);
30323     
30324 };
30325
30326 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
30327     inputType:      'hidden',
30328     allowBlank:     true,
30329     readOnly:         true,
30330     
30331  
30332     /**
30333      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
30334      */
30335     focusClass : undefined,
30336     /**
30337      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
30338      */
30339     fieldClass: 'x-form-field',
30340     
30341      /**
30342      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
30343      */
30344     valueRenderer: undefined,
30345     
30346     width: 100,
30347     /**
30348      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30349      * {tag: "input", type: "checkbox", autocomplete: "off"})
30350      */
30351      
30352  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
30353
30354     onResize : function(){
30355         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
30356         
30357     },
30358
30359     initEvents : function(){
30360         // Roo.form.Checkbox.superclass.initEvents.call(this);
30361         // has no events...
30362        
30363     },
30364
30365
30366     getResizeEl : function(){
30367         return this.wrap;
30368     },
30369
30370     getPositionEl : function(){
30371         return this.wrap;
30372     },
30373
30374     // private
30375     onRender : function(ct, position){
30376         
30377         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
30378         //if(this.inputValue !== undefined){
30379         this.wrap = this.el.wrap();
30380         
30381         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
30382         
30383         if (this.bodyStyle) {
30384             this.viewEl.applyStyles(this.bodyStyle);
30385         }
30386         //this.viewEl.setStyle('padding', '2px');
30387         
30388         this.setValue(this.value);
30389         
30390     },
30391 /*
30392     // private
30393     initValue : Roo.emptyFn,
30394
30395   */
30396
30397         // private
30398     onClick : function(){
30399         
30400     },
30401
30402     /**
30403      * Sets the checked state of the checkbox.
30404      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
30405      */
30406     setValue : function(v){
30407         this.value = v;
30408         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
30409         // this might be called before we have a dom element..
30410         if (!this.viewEl) {
30411             return;
30412         }
30413         this.viewEl.dom.innerHTML = html;
30414         Roo.form.DisplayField.superclass.setValue.call(this, v);
30415
30416     }
30417 });/*
30418  * 
30419  * Licence- LGPL
30420  * 
30421  */
30422
30423 /**
30424  * @class Roo.form.DayPicker
30425  * @extends Roo.form.Field
30426  * A Day picker show [M] [T] [W] ....
30427  * @constructor
30428  * Creates a new Day Picker
30429  * @param {Object} config Configuration options
30430  */
30431 Roo.form.DayPicker= function(config){
30432     Roo.form.DayPicker.superclass.constructor.call(this, config);
30433      
30434 };
30435
30436 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
30437     /**
30438      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
30439      */
30440     focusClass : undefined,
30441     /**
30442      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
30443      */
30444     fieldClass: "x-form-field",
30445    
30446     /**
30447      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30448      * {tag: "input", type: "checkbox", autocomplete: "off"})
30449      */
30450     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
30451     
30452    
30453     actionMode : 'viewEl', 
30454     //
30455     // private
30456  
30457     inputType : 'hidden',
30458     
30459      
30460     inputElement: false, // real input element?
30461     basedOn: false, // ????
30462     
30463     isFormField: true, // not sure where this is needed!!!!
30464
30465     onResize : function(){
30466         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
30467         if(!this.boxLabel){
30468             this.el.alignTo(this.wrap, 'c-c');
30469         }
30470     },
30471
30472     initEvents : function(){
30473         Roo.form.Checkbox.superclass.initEvents.call(this);
30474         this.el.on("click", this.onClick,  this);
30475         this.el.on("change", this.onClick,  this);
30476     },
30477
30478
30479     getResizeEl : function(){
30480         return this.wrap;
30481     },
30482
30483     getPositionEl : function(){
30484         return this.wrap;
30485     },
30486
30487     
30488     // private
30489     onRender : function(ct, position){
30490         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
30491        
30492         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
30493         
30494         var r1 = '<table><tr>';
30495         var r2 = '<tr class="x-form-daypick-icons">';
30496         for (var i=0; i < 7; i++) {
30497             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
30498             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
30499         }
30500         
30501         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
30502         viewEl.select('img').on('click', this.onClick, this);
30503         this.viewEl = viewEl;   
30504         
30505         
30506         // this will not work on Chrome!!!
30507         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
30508         this.el.on('propertychange', this.setFromHidden,  this);  //ie
30509         
30510         
30511           
30512
30513     },
30514
30515     // private
30516     initValue : Roo.emptyFn,
30517
30518     /**
30519      * Returns the checked state of the checkbox.
30520      * @return {Boolean} True if checked, else false
30521      */
30522     getValue : function(){
30523         return this.el.dom.value;
30524         
30525     },
30526
30527         // private
30528     onClick : function(e){ 
30529         //this.setChecked(!this.checked);
30530         Roo.get(e.target).toggleClass('x-menu-item-checked');
30531         this.refreshValue();
30532         //if(this.el.dom.checked != this.checked){
30533         //    this.setValue(this.el.dom.checked);
30534        // }
30535     },
30536     
30537     // private
30538     refreshValue : function()
30539     {
30540         var val = '';
30541         this.viewEl.select('img',true).each(function(e,i,n)  {
30542             val += e.is(".x-menu-item-checked") ? String(n) : '';
30543         });
30544         this.setValue(val, true);
30545     },
30546
30547     /**
30548      * Sets the checked state of the checkbox.
30549      * On is always based on a string comparison between inputValue and the param.
30550      * @param {Boolean/String} value - the value to set 
30551      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
30552      */
30553     setValue : function(v,suppressEvent){
30554         if (!this.el.dom) {
30555             return;
30556         }
30557         var old = this.el.dom.value ;
30558         this.el.dom.value = v;
30559         if (suppressEvent) {
30560             return ;
30561         }
30562          
30563         // update display..
30564         this.viewEl.select('img',true).each(function(e,i,n)  {
30565             
30566             var on = e.is(".x-menu-item-checked");
30567             var newv = v.indexOf(String(n)) > -1;
30568             if (on != newv) {
30569                 e.toggleClass('x-menu-item-checked');
30570             }
30571             
30572         });
30573         
30574         
30575         this.fireEvent('change', this, v, old);
30576         
30577         
30578     },
30579    
30580     // handle setting of hidden value by some other method!!?!?
30581     setFromHidden: function()
30582     {
30583         if(!this.el){
30584             return;
30585         }
30586         //console.log("SET FROM HIDDEN");
30587         //alert('setFrom hidden');
30588         this.setValue(this.el.dom.value);
30589     },
30590     
30591     onDestroy : function()
30592     {
30593         if(this.viewEl){
30594             Roo.get(this.viewEl).remove();
30595         }
30596          
30597         Roo.form.DayPicker.superclass.onDestroy.call(this);
30598     }
30599
30600 });/*
30601  * RooJS Library 1.1.1
30602  * Copyright(c) 2008-2011  Alan Knowles
30603  *
30604  * License - LGPL
30605  */
30606  
30607
30608 /**
30609  * @class Roo.form.ComboCheck
30610  * @extends Roo.form.ComboBox
30611  * A combobox for multiple select items.
30612  *
30613  * FIXME - could do with a reset button..
30614  * 
30615  * @constructor
30616  * Create a new ComboCheck
30617  * @param {Object} config Configuration options
30618  */
30619 Roo.form.ComboCheck = function(config){
30620     Roo.form.ComboCheck.superclass.constructor.call(this, config);
30621     // should verify some data...
30622     // like
30623     // hiddenName = required..
30624     // displayField = required
30625     // valudField == required
30626     var req= [ 'hiddenName', 'displayField', 'valueField' ];
30627     var _t = this;
30628     Roo.each(req, function(e) {
30629         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
30630             throw "Roo.form.ComboCheck : missing value for: " + e;
30631         }
30632     });
30633     
30634     
30635 };
30636
30637 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
30638      
30639      
30640     editable : false,
30641      
30642     selectedClass: 'x-menu-item-checked', 
30643     
30644     // private
30645     onRender : function(ct, position){
30646         var _t = this;
30647         
30648         
30649         
30650         if(!this.tpl){
30651             var cls = 'x-combo-list';
30652
30653             
30654             this.tpl =  new Roo.Template({
30655                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
30656                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
30657                    '<span>{' + this.displayField + '}</span>' +
30658                     '</div>' 
30659                 
30660             });
30661         }
30662  
30663         
30664         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
30665         this.view.singleSelect = false;
30666         this.view.multiSelect = true;
30667         this.view.toggleSelect = true;
30668         this.pageTb.add(new Roo.Toolbar.Fill(), {
30669             
30670             text: 'Done',
30671             handler: function()
30672             {
30673                 _t.collapse();
30674             }
30675         });
30676     },
30677     
30678     onViewOver : function(e, t){
30679         // do nothing...
30680         return;
30681         
30682     },
30683     
30684     onViewClick : function(doFocus,index){
30685         return;
30686         
30687     },
30688     select: function () {
30689         //Roo.log("SELECT CALLED");
30690     },
30691      
30692     selectByValue : function(xv, scrollIntoView){
30693         var ar = this.getValueArray();
30694         var sels = [];
30695         
30696         Roo.each(ar, function(v) {
30697             if(v === undefined || v === null){
30698                 return;
30699             }
30700             var r = this.findRecord(this.valueField, v);
30701             if(r){
30702                 sels.push(this.store.indexOf(r))
30703                 
30704             }
30705         },this);
30706         this.view.select(sels);
30707         return false;
30708     },
30709     
30710     
30711     
30712     onSelect : function(record, index){
30713        // Roo.log("onselect Called");
30714        // this is only called by the clear button now..
30715         this.view.clearSelections();
30716         this.setValue('[]');
30717         if (this.value != this.valueBefore) {
30718             this.fireEvent('change', this, this.value, this.valueBefore);
30719             this.valueBefore = this.value;
30720         }
30721     },
30722     getValueArray : function()
30723     {
30724         var ar = [] ;
30725         
30726         try {
30727             //Roo.log(this.value);
30728             if (typeof(this.value) == 'undefined') {
30729                 return [];
30730             }
30731             var ar = Roo.decode(this.value);
30732             return  ar instanceof Array ? ar : []; //?? valid?
30733             
30734         } catch(e) {
30735             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
30736             return [];
30737         }
30738          
30739     },
30740     expand : function ()
30741     {
30742         
30743         Roo.form.ComboCheck.superclass.expand.call(this);
30744         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
30745         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
30746         
30747
30748     },
30749     
30750     collapse : function(){
30751         Roo.form.ComboCheck.superclass.collapse.call(this);
30752         var sl = this.view.getSelectedIndexes();
30753         var st = this.store;
30754         var nv = [];
30755         var tv = [];
30756         var r;
30757         Roo.each(sl, function(i) {
30758             r = st.getAt(i);
30759             nv.push(r.get(this.valueField));
30760         },this);
30761         this.setValue(Roo.encode(nv));
30762         if (this.value != this.valueBefore) {
30763
30764             this.fireEvent('change', this, this.value, this.valueBefore);
30765             this.valueBefore = this.value;
30766         }
30767         
30768     },
30769     
30770     setValue : function(v){
30771         // Roo.log(v);
30772         this.value = v;
30773         
30774         var vals = this.getValueArray();
30775         var tv = [];
30776         Roo.each(vals, function(k) {
30777             var r = this.findRecord(this.valueField, k);
30778             if(r){
30779                 tv.push(r.data[this.displayField]);
30780             }else if(this.valueNotFoundText !== undefined){
30781                 tv.push( this.valueNotFoundText );
30782             }
30783         },this);
30784        // Roo.log(tv);
30785         
30786         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
30787         this.hiddenField.value = v;
30788         this.value = v;
30789     }
30790     
30791 });/*
30792  * Based on:
30793  * Ext JS Library 1.1.1
30794  * Copyright(c) 2006-2007, Ext JS, LLC.
30795  *
30796  * Originally Released Under LGPL - original licence link has changed is not relivant.
30797  *
30798  * Fork - LGPL
30799  * <script type="text/javascript">
30800  */
30801  
30802 /**
30803  * @class Roo.form.Signature
30804  * @extends Roo.form.Field
30805  * Signature field.  
30806  * @constructor
30807  * 
30808  * @param {Object} config Configuration options
30809  */
30810
30811 Roo.form.Signature = function(config){
30812     Roo.form.Signature.superclass.constructor.call(this, config);
30813     
30814     this.addEvents({// not in used??
30815          /**
30816          * @event confirm
30817          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
30818              * @param {Roo.form.Signature} combo This combo box
30819              */
30820         'confirm' : true,
30821         /**
30822          * @event reset
30823          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
30824              * @param {Roo.form.ComboBox} combo This combo box
30825              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
30826              */
30827         'reset' : true
30828     });
30829 };
30830
30831 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
30832     /**
30833      * @cfg {Object} labels Label to use when rendering a form.
30834      * defaults to 
30835      * labels : { 
30836      *      clear : "Clear",
30837      *      confirm : "Confirm"
30838      *  }
30839      */
30840     labels : { 
30841         clear : "Clear",
30842         confirm : "Confirm"
30843     },
30844     /**
30845      * @cfg {Number} width The signature panel width (defaults to 300)
30846      */
30847     width: 300,
30848     /**
30849      * @cfg {Number} height The signature panel height (defaults to 100)
30850      */
30851     height : 100,
30852     /**
30853      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
30854      */
30855     allowBlank : false,
30856     
30857     //private
30858     // {Object} signPanel The signature SVG panel element (defaults to {})
30859     signPanel : {},
30860     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
30861     isMouseDown : false,
30862     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
30863     isConfirmed : false,
30864     // {String} signatureTmp SVG mapping string (defaults to empty string)
30865     signatureTmp : '',
30866     
30867     
30868     defaultAutoCreate : { // modified by initCompnoent..
30869         tag: "input",
30870         type:"hidden"
30871     },
30872
30873     // private
30874     onRender : function(ct, position){
30875         
30876         Roo.form.Signature.superclass.onRender.call(this, ct, position);
30877         
30878         this.wrap = this.el.wrap({
30879             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
30880         });
30881         
30882         this.createToolbar(this);
30883         this.signPanel = this.wrap.createChild({
30884                 tag: 'div',
30885                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
30886             }, this.el
30887         );
30888             
30889         this.svgID = Roo.id();
30890         this.svgEl = this.signPanel.createChild({
30891               xmlns : 'http://www.w3.org/2000/svg',
30892               tag : 'svg',
30893               id : this.svgID + "-svg",
30894               width: this.width,
30895               height: this.height,
30896               viewBox: '0 0 '+this.width+' '+this.height,
30897               cn : [
30898                 {
30899                     tag: "rect",
30900                     id: this.svgID + "-svg-r",
30901                     width: this.width,
30902                     height: this.height,
30903                     fill: "#ffa"
30904                 },
30905                 {
30906                     tag: "line",
30907                     id: this.svgID + "-svg-l",
30908                     x1: "0", // start
30909                     y1: (this.height*0.8), // start set the line in 80% of height
30910                     x2: this.width, // end
30911                     y2: (this.height*0.8), // end set the line in 80% of height
30912                     'stroke': "#666",
30913                     'stroke-width': "1",
30914                     'stroke-dasharray': "3",
30915                     'shape-rendering': "crispEdges",
30916                     'pointer-events': "none"
30917                 },
30918                 {
30919                     tag: "path",
30920                     id: this.svgID + "-svg-p",
30921                     'stroke': "navy",
30922                     'stroke-width': "3",
30923                     'fill': "none",
30924                     'pointer-events': 'none'
30925                 }
30926               ]
30927         });
30928         this.createSVG();
30929         this.svgBox = this.svgEl.dom.getScreenCTM();
30930     },
30931     createSVG : function(){ 
30932         var svg = this.signPanel;
30933         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
30934         var t = this;
30935
30936         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
30937         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
30938         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
30939         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
30940         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
30941         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
30942         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
30943         
30944     },
30945     isTouchEvent : function(e){
30946         return e.type.match(/^touch/);
30947     },
30948     getCoords : function (e) {
30949         var pt    = this.svgEl.dom.createSVGPoint();
30950         pt.x = e.clientX; 
30951         pt.y = e.clientY;
30952         if (this.isTouchEvent(e)) {
30953             pt.x =  e.targetTouches[0].clientX 
30954             pt.y = e.targetTouches[0].clientY;
30955         }
30956         var a = this.svgEl.dom.getScreenCTM();
30957         var b = a.inverse();
30958         var mx = pt.matrixTransform(b);
30959         return mx.x + ',' + mx.y;
30960     },
30961     //mouse event headler 
30962     down : function (e) {
30963         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
30964         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
30965         
30966         this.isMouseDown = true;
30967         
30968         e.preventDefault();
30969     },
30970     move : function (e) {
30971         if (this.isMouseDown) {
30972             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
30973             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
30974         }
30975         
30976         e.preventDefault();
30977     },
30978     up : function (e) {
30979         this.isMouseDown = false;
30980         var sp = this.signatureTmp.split(' ');
30981         
30982         if(sp.length > 1){
30983             if(!sp[sp.length-2].match(/^L/)){
30984                 sp.pop();
30985                 sp.pop();
30986                 sp.push("");
30987                 this.signatureTmp = sp.join(" ");
30988             }
30989         }
30990         if(this.getValue() != this.signatureTmp){
30991             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
30992             this.isConfirmed = false;
30993         }
30994         e.preventDefault();
30995     },
30996     
30997     /**
30998      * Protected method that will not generally be called directly. It
30999      * is called when the editor creates its toolbar. Override this method if you need to
31000      * add custom toolbar buttons.
31001      * @param {HtmlEditor} editor
31002      */
31003     createToolbar : function(editor){
31004          function btn(id, toggle, handler){
31005             var xid = fid + '-'+ id ;
31006             return {
31007                 id : xid,
31008                 cmd : id,
31009                 cls : 'x-btn-icon x-edit-'+id,
31010                 enableToggle:toggle !== false,
31011                 scope: editor, // was editor...
31012                 handler:handler||editor.relayBtnCmd,
31013                 clickEvent:'mousedown',
31014                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
31015                 tabIndex:-1
31016             };
31017         }
31018         
31019         
31020         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
31021         this.tb = tb;
31022         this.tb.add(
31023            {
31024                 cls : ' x-signature-btn x-signature-'+id,
31025                 scope: editor, // was editor...
31026                 handler: this.reset,
31027                 clickEvent:'mousedown',
31028                 text: this.labels.clear
31029             },
31030             {
31031                  xtype : 'Fill',
31032                  xns: Roo.Toolbar
31033             }, 
31034             {
31035                 cls : '  x-signature-btn x-signature-'+id,
31036                 scope: editor, // was editor...
31037                 handler: this.confirmHandler,
31038                 clickEvent:'mousedown',
31039                 text: this.labels.confirm
31040             }
31041         );
31042     
31043     },
31044     //public
31045     /**
31046      * when user is clicked confirm then show this image.....
31047      * 
31048      * @return {String} Image Data URI
31049      */
31050     getImageDataURI : function(){
31051         var svg = this.svgEl.dom.parentNode.innerHTML;
31052         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
31053         return src; 
31054     },
31055     /**
31056      * 
31057      * @return {Boolean} this.isConfirmed
31058      */
31059     getConfirmed : function(){
31060         return this.isConfirmed;
31061     },
31062     /**
31063      * 
31064      * @return {Number} this.width
31065      */
31066     getWidth : function(){
31067         return this.width;
31068     },
31069     /**
31070      * 
31071      * @return {Number} this.height
31072      */
31073     getHeight : function(){
31074         return this.height;
31075     },
31076     // private
31077     getSignature : function(){
31078         return this.signatureTmp;
31079     },
31080     // private
31081     reset : function(){
31082         this.signatureTmp = '';
31083         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31084         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
31085         this.isConfirmed = false;
31086         Roo.form.Signature.superclass.reset.call(this);
31087     },
31088     setSignature : function(s){
31089         this.signatureTmp = s;
31090         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31091         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
31092         this.setValue(s);
31093         this.isConfirmed = false;
31094         Roo.form.Signature.superclass.reset.call(this);
31095     }, 
31096     test : function(){
31097 //        Roo.log(this.signPanel.dom.contentWindow.up())
31098     },
31099     //private
31100     setConfirmed : function(){
31101         
31102         
31103         
31104 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
31105     },
31106     // private
31107     confirmHandler : function(){
31108         if(!this.getSignature()){
31109             return;
31110         }
31111         
31112         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
31113         this.setValue(this.getSignature());
31114         this.isConfirmed = true;
31115         
31116         this.fireEvent('confirm', this);
31117     },
31118     // private
31119     // Subclasses should provide the validation implementation by overriding this
31120     validateValue : function(value){
31121         if(this.allowBlank){
31122             return true;
31123         }
31124         
31125         if(this.isConfirmed){
31126             return true;
31127         }
31128         return false;
31129     }
31130 });/*
31131  * Based on:
31132  * Ext JS Library 1.1.1
31133  * Copyright(c) 2006-2007, Ext JS, LLC.
31134  *
31135  * Originally Released Under LGPL - original licence link has changed is not relivant.
31136  *
31137  * Fork - LGPL
31138  * <script type="text/javascript">
31139  */
31140  
31141
31142 /**
31143  * @class Roo.form.ComboBox
31144  * @extends Roo.form.TriggerField
31145  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
31146  * @constructor
31147  * Create a new ComboBox.
31148  * @param {Object} config Configuration options
31149  */
31150 Roo.form.Select = function(config){
31151     Roo.form.Select.superclass.constructor.call(this, config);
31152      
31153 };
31154
31155 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
31156     /**
31157      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
31158      */
31159     /**
31160      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
31161      * rendering into an Roo.Editor, defaults to false)
31162      */
31163     /**
31164      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
31165      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
31166      */
31167     /**
31168      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
31169      */
31170     /**
31171      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
31172      * the dropdown list (defaults to undefined, with no header element)
31173      */
31174
31175      /**
31176      * @cfg {String/Roo.Template} tpl The template to use to render the output
31177      */
31178      
31179     // private
31180     defaultAutoCreate : {tag: "select"  },
31181     /**
31182      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
31183      */
31184     listWidth: undefined,
31185     /**
31186      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
31187      * mode = 'remote' or 'text' if mode = 'local')
31188      */
31189     displayField: undefined,
31190     /**
31191      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
31192      * mode = 'remote' or 'value' if mode = 'local'). 
31193      * Note: use of a valueField requires the user make a selection
31194      * in order for a value to be mapped.
31195      */
31196     valueField: undefined,
31197     
31198     
31199     /**
31200      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
31201      * field's data value (defaults to the underlying DOM element's name)
31202      */
31203     hiddenName: undefined,
31204     /**
31205      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
31206      */
31207     listClass: '',
31208     /**
31209      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
31210      */
31211     selectedClass: 'x-combo-selected',
31212     /**
31213      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
31214      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
31215      * which displays a downward arrow icon).
31216      */
31217     triggerClass : 'x-form-arrow-trigger',
31218     /**
31219      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
31220      */
31221     shadow:'sides',
31222     /**
31223      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
31224      * anchor positions (defaults to 'tl-bl')
31225      */
31226     listAlign: 'tl-bl?',
31227     /**
31228      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
31229      */
31230     maxHeight: 300,
31231     /**
31232      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
31233      * query specified by the allQuery config option (defaults to 'query')
31234      */
31235     triggerAction: 'query',
31236     /**
31237      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
31238      * (defaults to 4, does not apply if editable = false)
31239      */
31240     minChars : 4,
31241     /**
31242      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
31243      * delay (typeAheadDelay) if it matches a known value (defaults to false)
31244      */
31245     typeAhead: false,
31246     /**
31247      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
31248      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
31249      */
31250     queryDelay: 500,
31251     /**
31252      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
31253      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
31254      */
31255     pageSize: 0,
31256     /**
31257      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
31258      * when editable = true (defaults to false)
31259      */
31260     selectOnFocus:false,
31261     /**
31262      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
31263      */
31264     queryParam: 'query',
31265     /**
31266      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
31267      * when mode = 'remote' (defaults to 'Loading...')
31268      */
31269     loadingText: 'Loading...',
31270     /**
31271      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
31272      */
31273     resizable: false,
31274     /**
31275      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
31276      */
31277     handleHeight : 8,
31278     /**
31279      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
31280      * traditional select (defaults to true)
31281      */
31282     editable: true,
31283     /**
31284      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
31285      */
31286     allQuery: '',
31287     /**
31288      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
31289      */
31290     mode: 'remote',
31291     /**
31292      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
31293      * listWidth has a higher value)
31294      */
31295     minListWidth : 70,
31296     /**
31297      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
31298      * allow the user to set arbitrary text into the field (defaults to false)
31299      */
31300     forceSelection:false,
31301     /**
31302      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
31303      * if typeAhead = true (defaults to 250)
31304      */
31305     typeAheadDelay : 250,
31306     /**
31307      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
31308      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
31309      */
31310     valueNotFoundText : undefined,
31311     
31312     /**
31313      * @cfg {String} defaultValue The value displayed after loading the store.
31314      */
31315     defaultValue: '',
31316     
31317     /**
31318      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
31319      */
31320     blockFocus : false,
31321     
31322     /**
31323      * @cfg {Boolean} disableClear Disable showing of clear button.
31324      */
31325     disableClear : false,
31326     /**
31327      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
31328      */
31329     alwaysQuery : false,
31330     
31331     //private
31332     addicon : false,
31333     editicon: false,
31334     
31335     // element that contains real text value.. (when hidden is used..)
31336      
31337     // private
31338     onRender : function(ct, position){
31339         Roo.form.Field.prototype.onRender.call(this, ct, position);
31340         
31341         if(this.store){
31342             this.store.on('beforeload', this.onBeforeLoad, this);
31343             this.store.on('load', this.onLoad, this);
31344             this.store.on('loadexception', this.onLoadException, this);
31345             this.store.load({});
31346         }
31347         
31348         
31349         
31350     },
31351
31352     // private
31353     initEvents : function(){
31354         //Roo.form.ComboBox.superclass.initEvents.call(this);
31355  
31356     },
31357
31358     onDestroy : function(){
31359        
31360         if(this.store){
31361             this.store.un('beforeload', this.onBeforeLoad, this);
31362             this.store.un('load', this.onLoad, this);
31363             this.store.un('loadexception', this.onLoadException, this);
31364         }
31365         //Roo.form.ComboBox.superclass.onDestroy.call(this);
31366     },
31367
31368     // private
31369     fireKey : function(e){
31370         if(e.isNavKeyPress() && !this.list.isVisible()){
31371             this.fireEvent("specialkey", this, e);
31372         }
31373     },
31374
31375     // private
31376     onResize: function(w, h){
31377         
31378         return; 
31379     
31380         
31381     },
31382
31383     /**
31384      * Allow or prevent the user from directly editing the field text.  If false is passed,
31385      * the user will only be able to select from the items defined in the dropdown list.  This method
31386      * is the runtime equivalent of setting the 'editable' config option at config time.
31387      * @param {Boolean} value True to allow the user to directly edit the field text
31388      */
31389     setEditable : function(value){
31390          
31391     },
31392
31393     // private
31394     onBeforeLoad : function(){
31395         
31396         Roo.log("Select before load");
31397         return;
31398     
31399         this.innerList.update(this.loadingText ?
31400                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
31401         //this.restrictHeight();
31402         this.selectedIndex = -1;
31403     },
31404
31405     // private
31406     onLoad : function(){
31407
31408     
31409         var dom = this.el.dom;
31410         dom.innerHTML = '';
31411          var od = dom.ownerDocument;
31412          
31413         if (this.emptyText) {
31414             var op = od.createElement('option');
31415             op.setAttribute('value', '');
31416             op.innerHTML = String.format('{0}', this.emptyText);
31417             dom.appendChild(op);
31418         }
31419         if(this.store.getCount() > 0){
31420            
31421             var vf = this.valueField;
31422             var df = this.displayField;
31423             this.store.data.each(function(r) {
31424                 // which colmsn to use... testing - cdoe / title..
31425                 var op = od.createElement('option');
31426                 op.setAttribute('value', r.data[vf]);
31427                 op.innerHTML = String.format('{0}', r.data[df]);
31428                 dom.appendChild(op);
31429             });
31430             if (typeof(this.defaultValue != 'undefined')) {
31431                 this.setValue(this.defaultValue);
31432             }
31433             
31434              
31435         }else{
31436             //this.onEmptyResults();
31437         }
31438         //this.el.focus();
31439     },
31440     // private
31441     onLoadException : function()
31442     {
31443         dom.innerHTML = '';
31444             
31445         Roo.log("Select on load exception");
31446         return;
31447     
31448         this.collapse();
31449         Roo.log(this.store.reader.jsonData);
31450         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
31451             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
31452         }
31453         
31454         
31455     },
31456     // private
31457     onTypeAhead : function(){
31458          
31459     },
31460
31461     // private
31462     onSelect : function(record, index){
31463         Roo.log('on select?');
31464         return;
31465         if(this.fireEvent('beforeselect', this, record, index) !== false){
31466             this.setFromData(index > -1 ? record.data : false);
31467             this.collapse();
31468             this.fireEvent('select', this, record, index);
31469         }
31470     },
31471
31472     /**
31473      * Returns the currently selected field value or empty string if no value is set.
31474      * @return {String} value The selected value
31475      */
31476     getValue : function(){
31477         var dom = this.el.dom;
31478         this.value = dom.options[dom.selectedIndex].value;
31479         return this.value;
31480         
31481     },
31482
31483     /**
31484      * Clears any text/value currently set in the field
31485      */
31486     clearValue : function(){
31487         this.value = '';
31488         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
31489         
31490     },
31491
31492     /**
31493      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
31494      * will be displayed in the field.  If the value does not match the data value of an existing item,
31495      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
31496      * Otherwise the field will be blank (although the value will still be set).
31497      * @param {String} value The value to match
31498      */
31499     setValue : function(v){
31500         var d = this.el.dom;
31501         for (var i =0; i < d.options.length;i++) {
31502             if (v == d.options[i].value) {
31503                 d.selectedIndex = i;
31504                 this.value = v;
31505                 return;
31506             }
31507         }
31508         this.clearValue();
31509     },
31510     /**
31511      * @property {Object} the last set data for the element
31512      */
31513     
31514     lastData : false,
31515     /**
31516      * Sets the value of the field based on a object which is related to the record format for the store.
31517      * @param {Object} value the value to set as. or false on reset?
31518      */
31519     setFromData : function(o){
31520         Roo.log('setfrom data?');
31521          
31522         
31523         
31524     },
31525     // private
31526     reset : function(){
31527         this.clearValue();
31528     },
31529     // private
31530     findRecord : function(prop, value){
31531         
31532         return false;
31533     
31534         var record;
31535         if(this.store.getCount() > 0){
31536             this.store.each(function(r){
31537                 if(r.data[prop] == value){
31538                     record = r;
31539                     return false;
31540                 }
31541                 return true;
31542             });
31543         }
31544         return record;
31545     },
31546     
31547     getName: function()
31548     {
31549         // returns hidden if it's set..
31550         if (!this.rendered) {return ''};
31551         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
31552         
31553     },
31554      
31555
31556     
31557
31558     // private
31559     onEmptyResults : function(){
31560         Roo.log('empty results');
31561         //this.collapse();
31562     },
31563
31564     /**
31565      * Returns true if the dropdown list is expanded, else false.
31566      */
31567     isExpanded : function(){
31568         return false;
31569     },
31570
31571     /**
31572      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
31573      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
31574      * @param {String} value The data value of the item to select
31575      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
31576      * selected item if it is not currently in view (defaults to true)
31577      * @return {Boolean} True if the value matched an item in the list, else false
31578      */
31579     selectByValue : function(v, scrollIntoView){
31580         Roo.log('select By Value');
31581         return false;
31582     
31583         if(v !== undefined && v !== null){
31584             var r = this.findRecord(this.valueField || this.displayField, v);
31585             if(r){
31586                 this.select(this.store.indexOf(r), scrollIntoView);
31587                 return true;
31588             }
31589         }
31590         return false;
31591     },
31592
31593     /**
31594      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
31595      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
31596      * @param {Number} index The zero-based index of the list item to select
31597      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
31598      * selected item if it is not currently in view (defaults to true)
31599      */
31600     select : function(index, scrollIntoView){
31601         Roo.log('select ');
31602         return  ;
31603         
31604         this.selectedIndex = index;
31605         this.view.select(index);
31606         if(scrollIntoView !== false){
31607             var el = this.view.getNode(index);
31608             if(el){
31609                 this.innerList.scrollChildIntoView(el, false);
31610             }
31611         }
31612     },
31613
31614       
31615
31616     // private
31617     validateBlur : function(){
31618         
31619         return;
31620         
31621     },
31622
31623     // private
31624     initQuery : function(){
31625         this.doQuery(this.getRawValue());
31626     },
31627
31628     // private
31629     doForce : function(){
31630         if(this.el.dom.value.length > 0){
31631             this.el.dom.value =
31632                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
31633              
31634         }
31635     },
31636
31637     /**
31638      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
31639      * query allowing the query action to be canceled if needed.
31640      * @param {String} query The SQL query to execute
31641      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
31642      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
31643      * saved in the current store (defaults to false)
31644      */
31645     doQuery : function(q, forceAll){
31646         
31647         Roo.log('doQuery?');
31648         if(q === undefined || q === null){
31649             q = '';
31650         }
31651         var qe = {
31652             query: q,
31653             forceAll: forceAll,
31654             combo: this,
31655             cancel:false
31656         };
31657         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
31658             return false;
31659         }
31660         q = qe.query;
31661         forceAll = qe.forceAll;
31662         if(forceAll === true || (q.length >= this.minChars)){
31663             if(this.lastQuery != q || this.alwaysQuery){
31664                 this.lastQuery = q;
31665                 if(this.mode == 'local'){
31666                     this.selectedIndex = -1;
31667                     if(forceAll){
31668                         this.store.clearFilter();
31669                     }else{
31670                         this.store.filter(this.displayField, q);
31671                     }
31672                     this.onLoad();
31673                 }else{
31674                     this.store.baseParams[this.queryParam] = q;
31675                     this.store.load({
31676                         params: this.getParams(q)
31677                     });
31678                     this.expand();
31679                 }
31680             }else{
31681                 this.selectedIndex = -1;
31682                 this.onLoad();   
31683             }
31684         }
31685     },
31686
31687     // private
31688     getParams : function(q){
31689         var p = {};
31690         //p[this.queryParam] = q;
31691         if(this.pageSize){
31692             p.start = 0;
31693             p.limit = this.pageSize;
31694         }
31695         return p;
31696     },
31697
31698     /**
31699      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
31700      */
31701     collapse : function(){
31702         
31703     },
31704
31705     // private
31706     collapseIf : function(e){
31707         
31708     },
31709
31710     /**
31711      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
31712      */
31713     expand : function(){
31714         
31715     } ,
31716
31717     // private
31718      
31719
31720     /** 
31721     * @cfg {Boolean} grow 
31722     * @hide 
31723     */
31724     /** 
31725     * @cfg {Number} growMin 
31726     * @hide 
31727     */
31728     /** 
31729     * @cfg {Number} growMax 
31730     * @hide 
31731     */
31732     /**
31733      * @hide
31734      * @method autoSize
31735      */
31736     
31737     setWidth : function()
31738     {
31739         
31740     },
31741     getResizeEl : function(){
31742         return this.el;
31743     }
31744 });//<script type="text/javasscript">
31745  
31746
31747 /**
31748  * @class Roo.DDView
31749  * A DnD enabled version of Roo.View.
31750  * @param {Element/String} container The Element in which to create the View.
31751  * @param {String} tpl The template string used to create the markup for each element of the View
31752  * @param {Object} config The configuration properties. These include all the config options of
31753  * {@link Roo.View} plus some specific to this class.<br>
31754  * <p>
31755  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
31756  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
31757  * <p>
31758  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
31759 .x-view-drag-insert-above {
31760         border-top:1px dotted #3366cc;
31761 }
31762 .x-view-drag-insert-below {
31763         border-bottom:1px dotted #3366cc;
31764 }
31765 </code></pre>
31766  * 
31767  */
31768  
31769 Roo.DDView = function(container, tpl, config) {
31770     Roo.DDView.superclass.constructor.apply(this, arguments);
31771     this.getEl().setStyle("outline", "0px none");
31772     this.getEl().unselectable();
31773     if (this.dragGroup) {
31774                 this.setDraggable(this.dragGroup.split(","));
31775     }
31776     if (this.dropGroup) {
31777                 this.setDroppable(this.dropGroup.split(","));
31778     }
31779     if (this.deletable) {
31780         this.setDeletable();
31781     }
31782     this.isDirtyFlag = false;
31783         this.addEvents({
31784                 "drop" : true
31785         });
31786 };
31787
31788 Roo.extend(Roo.DDView, Roo.View, {
31789 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
31790 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
31791 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
31792 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
31793
31794         isFormField: true,
31795
31796         reset: Roo.emptyFn,
31797         
31798         clearInvalid: Roo.form.Field.prototype.clearInvalid,
31799
31800         validate: function() {
31801                 return true;
31802         },
31803         
31804         destroy: function() {
31805                 this.purgeListeners();
31806                 this.getEl.removeAllListeners();
31807                 this.getEl().remove();
31808                 if (this.dragZone) {
31809                         if (this.dragZone.destroy) {
31810                                 this.dragZone.destroy();
31811                         }
31812                 }
31813                 if (this.dropZone) {
31814                         if (this.dropZone.destroy) {
31815                                 this.dropZone.destroy();
31816                         }
31817                 }
31818         },
31819
31820 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
31821         getName: function() {
31822                 return this.name;
31823         },
31824
31825 /**     Loads the View from a JSON string representing the Records to put into the Store. */
31826         setValue: function(v) {
31827                 if (!this.store) {
31828                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
31829                 }
31830                 var data = {};
31831                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
31832                 this.store.proxy = new Roo.data.MemoryProxy(data);
31833                 this.store.load();
31834         },
31835
31836 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
31837         getValue: function() {
31838                 var result = '(';
31839                 this.store.each(function(rec) {
31840                         result += rec.id + ',';
31841                 });
31842                 return result.substr(0, result.length - 1) + ')';
31843         },
31844         
31845         getIds: function() {
31846                 var i = 0, result = new Array(this.store.getCount());
31847                 this.store.each(function(rec) {
31848                         result[i++] = rec.id;
31849                 });
31850                 return result;
31851         },
31852         
31853         isDirty: function() {
31854                 return this.isDirtyFlag;
31855         },
31856
31857 /**
31858  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
31859  *      whole Element becomes the target, and this causes the drop gesture to append.
31860  */
31861     getTargetFromEvent : function(e) {
31862                 var target = e.getTarget();
31863                 while ((target !== null) && (target.parentNode != this.el.dom)) {
31864                 target = target.parentNode;
31865                 }
31866                 if (!target) {
31867                         target = this.el.dom.lastChild || this.el.dom;
31868                 }
31869                 return target;
31870     },
31871
31872 /**
31873  *      Create the drag data which consists of an object which has the property "ddel" as
31874  *      the drag proxy element. 
31875  */
31876     getDragData : function(e) {
31877         var target = this.findItemFromChild(e.getTarget());
31878                 if(target) {
31879                         this.handleSelection(e);
31880                         var selNodes = this.getSelectedNodes();
31881             var dragData = {
31882                 source: this,
31883                 copy: this.copy || (this.allowCopy && e.ctrlKey),
31884                 nodes: selNodes,
31885                 records: []
31886                         };
31887                         var selectedIndices = this.getSelectedIndexes();
31888                         for (var i = 0; i < selectedIndices.length; i++) {
31889                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
31890                         }
31891                         if (selNodes.length == 1) {
31892                                 dragData.ddel = target.cloneNode(true); // the div element
31893                         } else {
31894                                 var div = document.createElement('div'); // create the multi element drag "ghost"
31895                                 div.className = 'multi-proxy';
31896                                 for (var i = 0, len = selNodes.length; i < len; i++) {
31897                                         div.appendChild(selNodes[i].cloneNode(true));
31898                                 }
31899                                 dragData.ddel = div;
31900                         }
31901             //console.log(dragData)
31902             //console.log(dragData.ddel.innerHTML)
31903                         return dragData;
31904                 }
31905         //console.log('nodragData')
31906                 return false;
31907     },
31908     
31909 /**     Specify to which ddGroup items in this DDView may be dragged. */
31910     setDraggable: function(ddGroup) {
31911         if (ddGroup instanceof Array) {
31912                 Roo.each(ddGroup, this.setDraggable, this);
31913                 return;
31914         }
31915         if (this.dragZone) {
31916                 this.dragZone.addToGroup(ddGroup);
31917         } else {
31918                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
31919                                 containerScroll: true,
31920                                 ddGroup: ddGroup 
31921
31922                         });
31923 //                      Draggability implies selection. DragZone's mousedown selects the element.
31924                         if (!this.multiSelect) { this.singleSelect = true; }
31925
31926 //                      Wire the DragZone's handlers up to methods in *this*
31927                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
31928                 }
31929     },
31930
31931 /**     Specify from which ddGroup this DDView accepts drops. */
31932     setDroppable: function(ddGroup) {
31933         if (ddGroup instanceof Array) {
31934                 Roo.each(ddGroup, this.setDroppable, this);
31935                 return;
31936         }
31937         if (this.dropZone) {
31938                 this.dropZone.addToGroup(ddGroup);
31939         } else {
31940                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
31941                                 containerScroll: true,
31942                                 ddGroup: ddGroup
31943                         });
31944
31945 //                      Wire the DropZone's handlers up to methods in *this*
31946                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
31947                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
31948                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
31949                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
31950                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
31951                 }
31952     },
31953
31954 /**     Decide whether to drop above or below a View node. */
31955     getDropPoint : function(e, n, dd){
31956         if (n == this.el.dom) { return "above"; }
31957                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
31958                 var c = t + (b - t) / 2;
31959                 var y = Roo.lib.Event.getPageY(e);
31960                 if(y <= c) {
31961                         return "above";
31962                 }else{
31963                         return "below";
31964                 }
31965     },
31966
31967     onNodeEnter : function(n, dd, e, data){
31968                 return false;
31969     },
31970     
31971     onNodeOver : function(n, dd, e, data){
31972                 var pt = this.getDropPoint(e, n, dd);
31973                 // set the insert point style on the target node
31974                 var dragElClass = this.dropNotAllowed;
31975                 if (pt) {
31976                         var targetElClass;
31977                         if (pt == "above"){
31978                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
31979                                 targetElClass = "x-view-drag-insert-above";
31980                         } else {
31981                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
31982                                 targetElClass = "x-view-drag-insert-below";
31983                         }
31984                         if (this.lastInsertClass != targetElClass){
31985                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
31986                                 this.lastInsertClass = targetElClass;
31987                         }
31988                 }
31989                 return dragElClass;
31990         },
31991
31992     onNodeOut : function(n, dd, e, data){
31993                 this.removeDropIndicators(n);
31994     },
31995
31996     onNodeDrop : function(n, dd, e, data){
31997         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
31998                 return false;
31999         }
32000         var pt = this.getDropPoint(e, n, dd);
32001                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
32002                 if (pt == "below") { insertAt++; }
32003                 for (var i = 0; i < data.records.length; i++) {
32004                         var r = data.records[i];
32005                         var dup = this.store.getById(r.id);
32006                         if (dup && (dd != this.dragZone)) {
32007                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
32008                         } else {
32009                                 if (data.copy) {
32010                                         this.store.insert(insertAt++, r.copy());
32011                                 } else {
32012                                         data.source.isDirtyFlag = true;
32013                                         r.store.remove(r);
32014                                         this.store.insert(insertAt++, r);
32015                                 }
32016                                 this.isDirtyFlag = true;
32017                         }
32018                 }
32019                 this.dragZone.cachedTarget = null;
32020                 return true;
32021     },
32022
32023     removeDropIndicators : function(n){
32024                 if(n){
32025                         Roo.fly(n).removeClass([
32026                                 "x-view-drag-insert-above",
32027                                 "x-view-drag-insert-below"]);
32028                         this.lastInsertClass = "_noclass";
32029                 }
32030     },
32031
32032 /**
32033  *      Utility method. Add a delete option to the DDView's context menu.
32034  *      @param {String} imageUrl The URL of the "delete" icon image.
32035  */
32036         setDeletable: function(imageUrl) {
32037                 if (!this.singleSelect && !this.multiSelect) {
32038                         this.singleSelect = true;
32039                 }
32040                 var c = this.getContextMenu();
32041                 this.contextMenu.on("itemclick", function(item) {
32042                         switch (item.id) {
32043                                 case "delete":
32044                                         this.remove(this.getSelectedIndexes());
32045                                         break;
32046                         }
32047                 }, this);
32048                 this.contextMenu.add({
32049                         icon: imageUrl,
32050                         id: "delete",
32051                         text: 'Delete'
32052                 });
32053         },
32054         
32055 /**     Return the context menu for this DDView. */
32056         getContextMenu: function() {
32057                 if (!this.contextMenu) {
32058 //                      Create the View's context menu
32059                         this.contextMenu = new Roo.menu.Menu({
32060                                 id: this.id + "-contextmenu"
32061                         });
32062                         this.el.on("contextmenu", this.showContextMenu, this);
32063                 }
32064                 return this.contextMenu;
32065         },
32066         
32067         disableContextMenu: function() {
32068                 if (this.contextMenu) {
32069                         this.el.un("contextmenu", this.showContextMenu, this);
32070                 }
32071         },
32072
32073         showContextMenu: function(e, item) {
32074         item = this.findItemFromChild(e.getTarget());
32075                 if (item) {
32076                         e.stopEvent();
32077                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
32078                         this.contextMenu.showAt(e.getXY());
32079             }
32080     },
32081
32082 /**
32083  *      Remove {@link Roo.data.Record}s at the specified indices.
32084  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
32085  */
32086     remove: function(selectedIndices) {
32087                 selectedIndices = [].concat(selectedIndices);
32088                 for (var i = 0; i < selectedIndices.length; i++) {
32089                         var rec = this.store.getAt(selectedIndices[i]);
32090                         this.store.remove(rec);
32091                 }
32092     },
32093
32094 /**
32095  *      Double click fires the event, but also, if this is draggable, and there is only one other
32096  *      related DropZone, it transfers the selected node.
32097  */
32098     onDblClick : function(e){
32099         var item = this.findItemFromChild(e.getTarget());
32100         if(item){
32101             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
32102                 return false;
32103             }
32104             if (this.dragGroup) {
32105                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
32106                     while (targets.indexOf(this.dropZone) > -1) {
32107                             targets.remove(this.dropZone);
32108                                 }
32109                     if (targets.length == 1) {
32110                                         this.dragZone.cachedTarget = null;
32111                         var el = Roo.get(targets[0].getEl());
32112                         var box = el.getBox(true);
32113                         targets[0].onNodeDrop(el.dom, {
32114                                 target: el.dom,
32115                                 xy: [box.x, box.y + box.height - 1]
32116                         }, null, this.getDragData(e));
32117                     }
32118                 }
32119         }
32120     },
32121     
32122     handleSelection: function(e) {
32123                 this.dragZone.cachedTarget = null;
32124         var item = this.findItemFromChild(e.getTarget());
32125         if (!item) {
32126                 this.clearSelections(true);
32127                 return;
32128         }
32129                 if (item && (this.multiSelect || this.singleSelect)){
32130                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
32131                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
32132                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
32133                                 this.unselect(item);
32134                         } else {
32135                                 this.select(item, this.multiSelect && e.ctrlKey);
32136                                 this.lastSelection = item;
32137                         }
32138                 }
32139     },
32140
32141     onItemClick : function(item, index, e){
32142                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
32143                         return false;
32144                 }
32145                 return true;
32146     },
32147
32148     unselect : function(nodeInfo, suppressEvent){
32149                 var node = this.getNode(nodeInfo);
32150                 if(node && this.isSelected(node)){
32151                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
32152                                 Roo.fly(node).removeClass(this.selectedClass);
32153                                 this.selections.remove(node);
32154                                 if(!suppressEvent){
32155                                         this.fireEvent("selectionchange", this, this.selections);
32156                                 }
32157                         }
32158                 }
32159     }
32160 });
32161 /*
32162  * Based on:
32163  * Ext JS Library 1.1.1
32164  * Copyright(c) 2006-2007, Ext JS, LLC.
32165  *
32166  * Originally Released Under LGPL - original licence link has changed is not relivant.
32167  *
32168  * Fork - LGPL
32169  * <script type="text/javascript">
32170  */
32171  
32172 /**
32173  * @class Roo.LayoutManager
32174  * @extends Roo.util.Observable
32175  * Base class for layout managers.
32176  */
32177 Roo.LayoutManager = function(container, config){
32178     Roo.LayoutManager.superclass.constructor.call(this);
32179     this.el = Roo.get(container);
32180     // ie scrollbar fix
32181     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
32182         document.body.scroll = "no";
32183     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
32184         this.el.position('relative');
32185     }
32186     this.id = this.el.id;
32187     this.el.addClass("x-layout-container");
32188     /** false to disable window resize monitoring @type Boolean */
32189     this.monitorWindowResize = true;
32190     this.regions = {};
32191     this.addEvents({
32192         /**
32193          * @event layout
32194          * Fires when a layout is performed. 
32195          * @param {Roo.LayoutManager} this
32196          */
32197         "layout" : true,
32198         /**
32199          * @event regionresized
32200          * Fires when the user resizes a region. 
32201          * @param {Roo.LayoutRegion} region The resized region
32202          * @param {Number} newSize The new size (width for east/west, height for north/south)
32203          */
32204         "regionresized" : true,
32205         /**
32206          * @event regioncollapsed
32207          * Fires when a region is collapsed. 
32208          * @param {Roo.LayoutRegion} region The collapsed region
32209          */
32210         "regioncollapsed" : true,
32211         /**
32212          * @event regionexpanded
32213          * Fires when a region is expanded.  
32214          * @param {Roo.LayoutRegion} region The expanded region
32215          */
32216         "regionexpanded" : true
32217     });
32218     this.updating = false;
32219     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
32220 };
32221
32222 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
32223     /**
32224      * Returns true if this layout is currently being updated
32225      * @return {Boolean}
32226      */
32227     isUpdating : function(){
32228         return this.updating; 
32229     },
32230     
32231     /**
32232      * Suspend the LayoutManager from doing auto-layouts while
32233      * making multiple add or remove calls
32234      */
32235     beginUpdate : function(){
32236         this.updating = true;    
32237     },
32238     
32239     /**
32240      * Restore auto-layouts and optionally disable the manager from performing a layout
32241      * @param {Boolean} noLayout true to disable a layout update 
32242      */
32243     endUpdate : function(noLayout){
32244         this.updating = false;
32245         if(!noLayout){
32246             this.layout();
32247         }    
32248     },
32249     
32250     layout: function(){
32251         
32252     },
32253     
32254     onRegionResized : function(region, newSize){
32255         this.fireEvent("regionresized", region, newSize);
32256         this.layout();
32257     },
32258     
32259     onRegionCollapsed : function(region){
32260         this.fireEvent("regioncollapsed", region);
32261     },
32262     
32263     onRegionExpanded : function(region){
32264         this.fireEvent("regionexpanded", region);
32265     },
32266         
32267     /**
32268      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
32269      * performs box-model adjustments.
32270      * @return {Object} The size as an object {width: (the width), height: (the height)}
32271      */
32272     getViewSize : function(){
32273         var size;
32274         if(this.el.dom != document.body){
32275             size = this.el.getSize();
32276         }else{
32277             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
32278         }
32279         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
32280         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
32281         return size;
32282     },
32283     
32284     /**
32285      * Returns the Element this layout is bound to.
32286      * @return {Roo.Element}
32287      */
32288     getEl : function(){
32289         return this.el;
32290     },
32291     
32292     /**
32293      * Returns the specified region.
32294      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
32295      * @return {Roo.LayoutRegion}
32296      */
32297     getRegion : function(target){
32298         return this.regions[target.toLowerCase()];
32299     },
32300     
32301     onWindowResize : function(){
32302         if(this.monitorWindowResize){
32303             this.layout();
32304         }
32305     }
32306 });/*
32307  * Based on:
32308  * Ext JS Library 1.1.1
32309  * Copyright(c) 2006-2007, Ext JS, LLC.
32310  *
32311  * Originally Released Under LGPL - original licence link has changed is not relivant.
32312  *
32313  * Fork - LGPL
32314  * <script type="text/javascript">
32315  */
32316 /**
32317  * @class Roo.BorderLayout
32318  * @extends Roo.LayoutManager
32319  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
32320  * please see: <br><br>
32321  * <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>
32322  * <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>
32323  * Example:
32324  <pre><code>
32325  var layout = new Roo.BorderLayout(document.body, {
32326     north: {
32327         initialSize: 25,
32328         titlebar: false
32329     },
32330     west: {
32331         split:true,
32332         initialSize: 200,
32333         minSize: 175,
32334         maxSize: 400,
32335         titlebar: true,
32336         collapsible: true
32337     },
32338     east: {
32339         split:true,
32340         initialSize: 202,
32341         minSize: 175,
32342         maxSize: 400,
32343         titlebar: true,
32344         collapsible: true
32345     },
32346     south: {
32347         split:true,
32348         initialSize: 100,
32349         minSize: 100,
32350         maxSize: 200,
32351         titlebar: true,
32352         collapsible: true
32353     },
32354     center: {
32355         titlebar: true,
32356         autoScroll:true,
32357         resizeTabs: true,
32358         minTabWidth: 50,
32359         preferredTabWidth: 150
32360     }
32361 });
32362
32363 // shorthand
32364 var CP = Roo.ContentPanel;
32365
32366 layout.beginUpdate();
32367 layout.add("north", new CP("north", "North"));
32368 layout.add("south", new CP("south", {title: "South", closable: true}));
32369 layout.add("west", new CP("west", {title: "West"}));
32370 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
32371 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
32372 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
32373 layout.getRegion("center").showPanel("center1");
32374 layout.endUpdate();
32375 </code></pre>
32376
32377 <b>The container the layout is rendered into can be either the body element or any other element.
32378 If it is not the body element, the container needs to either be an absolute positioned element,
32379 or you will need to add "position:relative" to the css of the container.  You will also need to specify
32380 the container size if it is not the body element.</b>
32381
32382 * @constructor
32383 * Create a new BorderLayout
32384 * @param {String/HTMLElement/Element} container The container this layout is bound to
32385 * @param {Object} config Configuration options
32386  */
32387 Roo.BorderLayout = function(container, config){
32388     config = config || {};
32389     Roo.BorderLayout.superclass.constructor.call(this, container, config);
32390     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
32391     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
32392         var target = this.factory.validRegions[i];
32393         if(config[target]){
32394             this.addRegion(target, config[target]);
32395         }
32396     }
32397 };
32398
32399 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
32400     /**
32401      * Creates and adds a new region if it doesn't already exist.
32402      * @param {String} target The target region key (north, south, east, west or center).
32403      * @param {Object} config The regions config object
32404      * @return {BorderLayoutRegion} The new region
32405      */
32406     addRegion : function(target, config){
32407         if(!this.regions[target]){
32408             var r = this.factory.create(target, this, config);
32409             this.bindRegion(target, r);
32410         }
32411         return this.regions[target];
32412     },
32413
32414     // private (kinda)
32415     bindRegion : function(name, r){
32416         this.regions[name] = r;
32417         r.on("visibilitychange", this.layout, this);
32418         r.on("paneladded", this.layout, this);
32419         r.on("panelremoved", this.layout, this);
32420         r.on("invalidated", this.layout, this);
32421         r.on("resized", this.onRegionResized, this);
32422         r.on("collapsed", this.onRegionCollapsed, this);
32423         r.on("expanded", this.onRegionExpanded, this);
32424     },
32425
32426     /**
32427      * Performs a layout update.
32428      */
32429     layout : function(){
32430         if(this.updating) return;
32431         var size = this.getViewSize();
32432         var w = size.width;
32433         var h = size.height;
32434         var centerW = w;
32435         var centerH = h;
32436         var centerY = 0;
32437         var centerX = 0;
32438         //var x = 0, y = 0;
32439
32440         var rs = this.regions;
32441         var north = rs["north"];
32442         var south = rs["south"]; 
32443         var west = rs["west"];
32444         var east = rs["east"];
32445         var center = rs["center"];
32446         //if(this.hideOnLayout){ // not supported anymore
32447             //c.el.setStyle("display", "none");
32448         //}
32449         if(north && north.isVisible()){
32450             var b = north.getBox();
32451             var m = north.getMargins();
32452             b.width = w - (m.left+m.right);
32453             b.x = m.left;
32454             b.y = m.top;
32455             centerY = b.height + b.y + m.bottom;
32456             centerH -= centerY;
32457             north.updateBox(this.safeBox(b));
32458         }
32459         if(south && south.isVisible()){
32460             var b = south.getBox();
32461             var m = south.getMargins();
32462             b.width = w - (m.left+m.right);
32463             b.x = m.left;
32464             var totalHeight = (b.height + m.top + m.bottom);
32465             b.y = h - totalHeight + m.top;
32466             centerH -= totalHeight;
32467             south.updateBox(this.safeBox(b));
32468         }
32469         if(west && west.isVisible()){
32470             var b = west.getBox();
32471             var m = west.getMargins();
32472             b.height = centerH - (m.top+m.bottom);
32473             b.x = m.left;
32474             b.y = centerY + m.top;
32475             var totalWidth = (b.width + m.left + m.right);
32476             centerX += totalWidth;
32477             centerW -= totalWidth;
32478             west.updateBox(this.safeBox(b));
32479         }
32480         if(east && east.isVisible()){
32481             var b = east.getBox();
32482             var m = east.getMargins();
32483             b.height = centerH - (m.top+m.bottom);
32484             var totalWidth = (b.width + m.left + m.right);
32485             b.x = w - totalWidth + m.left;
32486             b.y = centerY + m.top;
32487             centerW -= totalWidth;
32488             east.updateBox(this.safeBox(b));
32489         }
32490         if(center){
32491             var m = center.getMargins();
32492             var centerBox = {
32493                 x: centerX + m.left,
32494                 y: centerY + m.top,
32495                 width: centerW - (m.left+m.right),
32496                 height: centerH - (m.top+m.bottom)
32497             };
32498             //if(this.hideOnLayout){
32499                 //center.el.setStyle("display", "block");
32500             //}
32501             center.updateBox(this.safeBox(centerBox));
32502         }
32503         this.el.repaint();
32504         this.fireEvent("layout", this);
32505     },
32506
32507     // private
32508     safeBox : function(box){
32509         box.width = Math.max(0, box.width);
32510         box.height = Math.max(0, box.height);
32511         return box;
32512     },
32513
32514     /**
32515      * Adds a ContentPanel (or subclass) to this layout.
32516      * @param {String} target The target region key (north, south, east, west or center).
32517      * @param {Roo.ContentPanel} panel The panel to add
32518      * @return {Roo.ContentPanel} The added panel
32519      */
32520     add : function(target, panel){
32521          
32522         target = target.toLowerCase();
32523         return this.regions[target].add(panel);
32524     },
32525
32526     /**
32527      * Remove a ContentPanel (or subclass) to this layout.
32528      * @param {String} target The target region key (north, south, east, west or center).
32529      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
32530      * @return {Roo.ContentPanel} The removed panel
32531      */
32532     remove : function(target, panel){
32533         target = target.toLowerCase();
32534         return this.regions[target].remove(panel);
32535     },
32536
32537     /**
32538      * Searches all regions for a panel with the specified id
32539      * @param {String} panelId
32540      * @return {Roo.ContentPanel} The panel or null if it wasn't found
32541      */
32542     findPanel : function(panelId){
32543         var rs = this.regions;
32544         for(var target in rs){
32545             if(typeof rs[target] != "function"){
32546                 var p = rs[target].getPanel(panelId);
32547                 if(p){
32548                     return p;
32549                 }
32550             }
32551         }
32552         return null;
32553     },
32554
32555     /**
32556      * Searches all regions for a panel with the specified id and activates (shows) it.
32557      * @param {String/ContentPanel} panelId The panels id or the panel itself
32558      * @return {Roo.ContentPanel} The shown panel or null
32559      */
32560     showPanel : function(panelId) {
32561       var rs = this.regions;
32562       for(var target in rs){
32563          var r = rs[target];
32564          if(typeof r != "function"){
32565             if(r.hasPanel(panelId)){
32566                return r.showPanel(panelId);
32567             }
32568          }
32569       }
32570       return null;
32571    },
32572
32573    /**
32574      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
32575      * @param {Roo.state.Provider} provider (optional) An alternate state provider
32576      */
32577     restoreState : function(provider){
32578         if(!provider){
32579             provider = Roo.state.Manager;
32580         }
32581         var sm = new Roo.LayoutStateManager();
32582         sm.init(this, provider);
32583     },
32584
32585     /**
32586      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
32587      * object should contain properties for each region to add ContentPanels to, and each property's value should be
32588      * a valid ContentPanel config object.  Example:
32589      * <pre><code>
32590 // Create the main layout
32591 var layout = new Roo.BorderLayout('main-ct', {
32592     west: {
32593         split:true,
32594         minSize: 175,
32595         titlebar: true
32596     },
32597     center: {
32598         title:'Components'
32599     }
32600 }, 'main-ct');
32601
32602 // Create and add multiple ContentPanels at once via configs
32603 layout.batchAdd({
32604    west: {
32605        id: 'source-files',
32606        autoCreate:true,
32607        title:'Ext Source Files',
32608        autoScroll:true,
32609        fitToFrame:true
32610    },
32611    center : {
32612        el: cview,
32613        autoScroll:true,
32614        fitToFrame:true,
32615        toolbar: tb,
32616        resizeEl:'cbody'
32617    }
32618 });
32619 </code></pre>
32620      * @param {Object} regions An object containing ContentPanel configs by region name
32621      */
32622     batchAdd : function(regions){
32623         this.beginUpdate();
32624         for(var rname in regions){
32625             var lr = this.regions[rname];
32626             if(lr){
32627                 this.addTypedPanels(lr, regions[rname]);
32628             }
32629         }
32630         this.endUpdate();
32631     },
32632
32633     // private
32634     addTypedPanels : function(lr, ps){
32635         if(typeof ps == 'string'){
32636             lr.add(new Roo.ContentPanel(ps));
32637         }
32638         else if(ps instanceof Array){
32639             for(var i =0, len = ps.length; i < len; i++){
32640                 this.addTypedPanels(lr, ps[i]);
32641             }
32642         }
32643         else if(!ps.events){ // raw config?
32644             var el = ps.el;
32645             delete ps.el; // prevent conflict
32646             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
32647         }
32648         else {  // panel object assumed!
32649             lr.add(ps);
32650         }
32651     },
32652     /**
32653      * Adds a xtype elements to the layout.
32654      * <pre><code>
32655
32656 layout.addxtype({
32657        xtype : 'ContentPanel',
32658        region: 'west',
32659        items: [ .... ]
32660    }
32661 );
32662
32663 layout.addxtype({
32664         xtype : 'NestedLayoutPanel',
32665         region: 'west',
32666         layout: {
32667            center: { },
32668            west: { }   
32669         },
32670         items : [ ... list of content panels or nested layout panels.. ]
32671    }
32672 );
32673 </code></pre>
32674      * @param {Object} cfg Xtype definition of item to add.
32675      */
32676     addxtype : function(cfg)
32677     {
32678         // basically accepts a pannel...
32679         // can accept a layout region..!?!?
32680         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
32681         
32682         if (!cfg.xtype.match(/Panel$/)) {
32683             return false;
32684         }
32685         var ret = false;
32686         
32687         if (typeof(cfg.region) == 'undefined') {
32688             Roo.log("Failed to add Panel, region was not set");
32689             Roo.log(cfg);
32690             return false;
32691         }
32692         var region = cfg.region;
32693         delete cfg.region;
32694         
32695           
32696         var xitems = [];
32697         if (cfg.items) {
32698             xitems = cfg.items;
32699             delete cfg.items;
32700         }
32701         var nb = false;
32702         
32703         switch(cfg.xtype) 
32704         {
32705             case 'ContentPanel':  // ContentPanel (el, cfg)
32706             case 'ScrollPanel':  // ContentPanel (el, cfg)
32707             case 'ViewPanel': 
32708                 if(cfg.autoCreate) {
32709                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32710                 } else {
32711                     var el = this.el.createChild();
32712                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
32713                 }
32714                 
32715                 this.add(region, ret);
32716                 break;
32717             
32718             
32719             case 'TreePanel': // our new panel!
32720                 cfg.el = this.el.createChild();
32721                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32722                 this.add(region, ret);
32723                 break;
32724             
32725             case 'NestedLayoutPanel': 
32726                 // create a new Layout (which is  a Border Layout...
32727                 var el = this.el.createChild();
32728                 var clayout = cfg.layout;
32729                 delete cfg.layout;
32730                 clayout.items   = clayout.items  || [];
32731                 // replace this exitems with the clayout ones..
32732                 xitems = clayout.items;
32733                  
32734                 
32735                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
32736                     cfg.background = false;
32737                 }
32738                 var layout = new Roo.BorderLayout(el, clayout);
32739                 
32740                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
32741                 //console.log('adding nested layout panel '  + cfg.toSource());
32742                 this.add(region, ret);
32743                 nb = {}; /// find first...
32744                 break;
32745                 
32746             case 'GridPanel': 
32747             
32748                 // needs grid and region
32749                 
32750                 //var el = this.getRegion(region).el.createChild();
32751                 var el = this.el.createChild();
32752                 // create the grid first...
32753                 
32754                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
32755                 delete cfg.grid;
32756                 if (region == 'center' && this.active ) {
32757                     cfg.background = false;
32758                 }
32759                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
32760                 
32761                 this.add(region, ret);
32762                 if (cfg.background) {
32763                     ret.on('activate', function(gp) {
32764                         if (!gp.grid.rendered) {
32765                             gp.grid.render();
32766                         }
32767                     });
32768                 } else {
32769                     grid.render();
32770                 }
32771                 break;
32772            
32773            
32774            
32775                 
32776                 
32777                 
32778             default: 
32779                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
32780                 return null;
32781              // GridPanel (grid, cfg)
32782             
32783         }
32784         this.beginUpdate();
32785         // add children..
32786         var region = '';
32787         var abn = {};
32788         Roo.each(xitems, function(i)  {
32789             region = nb && i.region ? i.region : false;
32790             
32791             var add = ret.addxtype(i);
32792            
32793             if (region) {
32794                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
32795                 if (!i.background) {
32796                     abn[region] = nb[region] ;
32797                 }
32798             }
32799             
32800         });
32801         this.endUpdate();
32802
32803         // make the last non-background panel active..
32804         //if (nb) { Roo.log(abn); }
32805         if (nb) {
32806             
32807             for(var r in abn) {
32808                 region = this.getRegion(r);
32809                 if (region) {
32810                     // tried using nb[r], but it does not work..
32811                      
32812                     region.showPanel(abn[r]);
32813                    
32814                 }
32815             }
32816         }
32817         return ret;
32818         
32819     }
32820 });
32821
32822 /**
32823  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
32824  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
32825  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
32826  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
32827  * <pre><code>
32828 // shorthand
32829 var CP = Roo.ContentPanel;
32830
32831 var layout = Roo.BorderLayout.create({
32832     north: {
32833         initialSize: 25,
32834         titlebar: false,
32835         panels: [new CP("north", "North")]
32836     },
32837     west: {
32838         split:true,
32839         initialSize: 200,
32840         minSize: 175,
32841         maxSize: 400,
32842         titlebar: true,
32843         collapsible: true,
32844         panels: [new CP("west", {title: "West"})]
32845     },
32846     east: {
32847         split:true,
32848         initialSize: 202,
32849         minSize: 175,
32850         maxSize: 400,
32851         titlebar: true,
32852         collapsible: true,
32853         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
32854     },
32855     south: {
32856         split:true,
32857         initialSize: 100,
32858         minSize: 100,
32859         maxSize: 200,
32860         titlebar: true,
32861         collapsible: true,
32862         panels: [new CP("south", {title: "South", closable: true})]
32863     },
32864     center: {
32865         titlebar: true,
32866         autoScroll:true,
32867         resizeTabs: true,
32868         minTabWidth: 50,
32869         preferredTabWidth: 150,
32870         panels: [
32871             new CP("center1", {title: "Close Me", closable: true}),
32872             new CP("center2", {title: "Center Panel", closable: false})
32873         ]
32874     }
32875 }, document.body);
32876
32877 layout.getRegion("center").showPanel("center1");
32878 </code></pre>
32879  * @param config
32880  * @param targetEl
32881  */
32882 Roo.BorderLayout.create = function(config, targetEl){
32883     var layout = new Roo.BorderLayout(targetEl || document.body, config);
32884     layout.beginUpdate();
32885     var regions = Roo.BorderLayout.RegionFactory.validRegions;
32886     for(var j = 0, jlen = regions.length; j < jlen; j++){
32887         var lr = regions[j];
32888         if(layout.regions[lr] && config[lr].panels){
32889             var r = layout.regions[lr];
32890             var ps = config[lr].panels;
32891             layout.addTypedPanels(r, ps);
32892         }
32893     }
32894     layout.endUpdate();
32895     return layout;
32896 };
32897
32898 // private
32899 Roo.BorderLayout.RegionFactory = {
32900     // private
32901     validRegions : ["north","south","east","west","center"],
32902
32903     // private
32904     create : function(target, mgr, config){
32905         target = target.toLowerCase();
32906         if(config.lightweight || config.basic){
32907             return new Roo.BasicLayoutRegion(mgr, config, target);
32908         }
32909         switch(target){
32910             case "north":
32911                 return new Roo.NorthLayoutRegion(mgr, config);
32912             case "south":
32913                 return new Roo.SouthLayoutRegion(mgr, config);
32914             case "east":
32915                 return new Roo.EastLayoutRegion(mgr, config);
32916             case "west":
32917                 return new Roo.WestLayoutRegion(mgr, config);
32918             case "center":
32919                 return new Roo.CenterLayoutRegion(mgr, config);
32920         }
32921         throw 'Layout region "'+target+'" not supported.';
32922     }
32923 };/*
32924  * Based on:
32925  * Ext JS Library 1.1.1
32926  * Copyright(c) 2006-2007, Ext JS, LLC.
32927  *
32928  * Originally Released Under LGPL - original licence link has changed is not relivant.
32929  *
32930  * Fork - LGPL
32931  * <script type="text/javascript">
32932  */
32933  
32934 /**
32935  * @class Roo.BasicLayoutRegion
32936  * @extends Roo.util.Observable
32937  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
32938  * and does not have a titlebar, tabs or any other features. All it does is size and position 
32939  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
32940  */
32941 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
32942     this.mgr = mgr;
32943     this.position  = pos;
32944     this.events = {
32945         /**
32946          * @scope Roo.BasicLayoutRegion
32947          */
32948         
32949         /**
32950          * @event beforeremove
32951          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
32952          * @param {Roo.LayoutRegion} this
32953          * @param {Roo.ContentPanel} panel The panel
32954          * @param {Object} e The cancel event object
32955          */
32956         "beforeremove" : true,
32957         /**
32958          * @event invalidated
32959          * Fires when the layout for this region is changed.
32960          * @param {Roo.LayoutRegion} this
32961          */
32962         "invalidated" : true,
32963         /**
32964          * @event visibilitychange
32965          * Fires when this region is shown or hidden 
32966          * @param {Roo.LayoutRegion} this
32967          * @param {Boolean} visibility true or false
32968          */
32969         "visibilitychange" : true,
32970         /**
32971          * @event paneladded
32972          * Fires when a panel is added. 
32973          * @param {Roo.LayoutRegion} this
32974          * @param {Roo.ContentPanel} panel The panel
32975          */
32976         "paneladded" : true,
32977         /**
32978          * @event panelremoved
32979          * Fires when a panel is removed. 
32980          * @param {Roo.LayoutRegion} this
32981          * @param {Roo.ContentPanel} panel The panel
32982          */
32983         "panelremoved" : true,
32984         /**
32985          * @event collapsed
32986          * Fires when this region is collapsed.
32987          * @param {Roo.LayoutRegion} this
32988          */
32989         "collapsed" : true,
32990         /**
32991          * @event expanded
32992          * Fires when this region is expanded.
32993          * @param {Roo.LayoutRegion} this
32994          */
32995         "expanded" : true,
32996         /**
32997          * @event slideshow
32998          * Fires when this region is slid into view.
32999          * @param {Roo.LayoutRegion} this
33000          */
33001         "slideshow" : true,
33002         /**
33003          * @event slidehide
33004          * Fires when this region slides out of view. 
33005          * @param {Roo.LayoutRegion} this
33006          */
33007         "slidehide" : true,
33008         /**
33009          * @event panelactivated
33010          * Fires when a panel is activated. 
33011          * @param {Roo.LayoutRegion} this
33012          * @param {Roo.ContentPanel} panel The activated panel
33013          */
33014         "panelactivated" : true,
33015         /**
33016          * @event resized
33017          * Fires when the user resizes this region. 
33018          * @param {Roo.LayoutRegion} this
33019          * @param {Number} newSize The new size (width for east/west, height for north/south)
33020          */
33021         "resized" : true
33022     };
33023     /** A collection of panels in this region. @type Roo.util.MixedCollection */
33024     this.panels = new Roo.util.MixedCollection();
33025     this.panels.getKey = this.getPanelId.createDelegate(this);
33026     this.box = null;
33027     this.activePanel = null;
33028     // ensure listeners are added...
33029     
33030     if (config.listeners || config.events) {
33031         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
33032             listeners : config.listeners || {},
33033             events : config.events || {}
33034         });
33035     }
33036     
33037     if(skipConfig !== true){
33038         this.applyConfig(config);
33039     }
33040 };
33041
33042 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
33043     getPanelId : function(p){
33044         return p.getId();
33045     },
33046     
33047     applyConfig : function(config){
33048         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33049         this.config = config;
33050         
33051     },
33052     
33053     /**
33054      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
33055      * the width, for horizontal (north, south) the height.
33056      * @param {Number} newSize The new width or height
33057      */
33058     resizeTo : function(newSize){
33059         var el = this.el ? this.el :
33060                  (this.activePanel ? this.activePanel.getEl() : null);
33061         if(el){
33062             switch(this.position){
33063                 case "east":
33064                 case "west":
33065                     el.setWidth(newSize);
33066                     this.fireEvent("resized", this, newSize);
33067                 break;
33068                 case "north":
33069                 case "south":
33070                     el.setHeight(newSize);
33071                     this.fireEvent("resized", this, newSize);
33072                 break;                
33073             }
33074         }
33075     },
33076     
33077     getBox : function(){
33078         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
33079     },
33080     
33081     getMargins : function(){
33082         return this.margins;
33083     },
33084     
33085     updateBox : function(box){
33086         this.box = box;
33087         var el = this.activePanel.getEl();
33088         el.dom.style.left = box.x + "px";
33089         el.dom.style.top = box.y + "px";
33090         this.activePanel.setSize(box.width, box.height);
33091     },
33092     
33093     /**
33094      * Returns the container element for this region.
33095      * @return {Roo.Element}
33096      */
33097     getEl : function(){
33098         return this.activePanel;
33099     },
33100     
33101     /**
33102      * Returns true if this region is currently visible.
33103      * @return {Boolean}
33104      */
33105     isVisible : function(){
33106         return this.activePanel ? true : false;
33107     },
33108     
33109     setActivePanel : function(panel){
33110         panel = this.getPanel(panel);
33111         if(this.activePanel && this.activePanel != panel){
33112             this.activePanel.setActiveState(false);
33113             this.activePanel.getEl().setLeftTop(-10000,-10000);
33114         }
33115         this.activePanel = panel;
33116         panel.setActiveState(true);
33117         if(this.box){
33118             panel.setSize(this.box.width, this.box.height);
33119         }
33120         this.fireEvent("panelactivated", this, panel);
33121         this.fireEvent("invalidated");
33122     },
33123     
33124     /**
33125      * Show the specified panel.
33126      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
33127      * @return {Roo.ContentPanel} The shown panel or null
33128      */
33129     showPanel : function(panel){
33130         if(panel = this.getPanel(panel)){
33131             this.setActivePanel(panel);
33132         }
33133         return panel;
33134     },
33135     
33136     /**
33137      * Get the active panel for this region.
33138      * @return {Roo.ContentPanel} The active panel or null
33139      */
33140     getActivePanel : function(){
33141         return this.activePanel;
33142     },
33143     
33144     /**
33145      * Add the passed ContentPanel(s)
33146      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33147      * @return {Roo.ContentPanel} The panel added (if only one was added)
33148      */
33149     add : function(panel){
33150         if(arguments.length > 1){
33151             for(var i = 0, len = arguments.length; i < len; i++) {
33152                 this.add(arguments[i]);
33153             }
33154             return null;
33155         }
33156         if(this.hasPanel(panel)){
33157             this.showPanel(panel);
33158             return panel;
33159         }
33160         var el = panel.getEl();
33161         if(el.dom.parentNode != this.mgr.el.dom){
33162             this.mgr.el.dom.appendChild(el.dom);
33163         }
33164         if(panel.setRegion){
33165             panel.setRegion(this);
33166         }
33167         this.panels.add(panel);
33168         el.setStyle("position", "absolute");
33169         if(!panel.background){
33170             this.setActivePanel(panel);
33171             if(this.config.initialSize && this.panels.getCount()==1){
33172                 this.resizeTo(this.config.initialSize);
33173             }
33174         }
33175         this.fireEvent("paneladded", this, panel);
33176         return panel;
33177     },
33178     
33179     /**
33180      * Returns true if the panel is in this region.
33181      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33182      * @return {Boolean}
33183      */
33184     hasPanel : function(panel){
33185         if(typeof panel == "object"){ // must be panel obj
33186             panel = panel.getId();
33187         }
33188         return this.getPanel(panel) ? true : false;
33189     },
33190     
33191     /**
33192      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33193      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33194      * @param {Boolean} preservePanel Overrides the config preservePanel option
33195      * @return {Roo.ContentPanel} The panel that was removed
33196      */
33197     remove : function(panel, preservePanel){
33198         panel = this.getPanel(panel);
33199         if(!panel){
33200             return null;
33201         }
33202         var e = {};
33203         this.fireEvent("beforeremove", this, panel, e);
33204         if(e.cancel === true){
33205             return null;
33206         }
33207         var panelId = panel.getId();
33208         this.panels.removeKey(panelId);
33209         return panel;
33210     },
33211     
33212     /**
33213      * Returns the panel specified or null if it's not in this region.
33214      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33215      * @return {Roo.ContentPanel}
33216      */
33217     getPanel : function(id){
33218         if(typeof id == "object"){ // must be panel obj
33219             return id;
33220         }
33221         return this.panels.get(id);
33222     },
33223     
33224     /**
33225      * Returns this regions position (north/south/east/west/center).
33226      * @return {String} 
33227      */
33228     getPosition: function(){
33229         return this.position;    
33230     }
33231 });/*
33232  * Based on:
33233  * Ext JS Library 1.1.1
33234  * Copyright(c) 2006-2007, Ext JS, LLC.
33235  *
33236  * Originally Released Under LGPL - original licence link has changed is not relivant.
33237  *
33238  * Fork - LGPL
33239  * <script type="text/javascript">
33240  */
33241  
33242 /**
33243  * @class Roo.LayoutRegion
33244  * @extends Roo.BasicLayoutRegion
33245  * This class represents a region in a layout manager.
33246  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
33247  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
33248  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
33249  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
33250  * @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})
33251  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
33252  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
33253  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
33254  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
33255  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
33256  * @cfg {String}    title           The title for the region (overrides panel titles)
33257  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
33258  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
33259  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
33260  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
33261  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
33262  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
33263  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
33264  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
33265  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
33266  * @cfg {Boolean}   showPin         True to show a pin button
33267  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
33268  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
33269  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
33270  * @cfg {Number}    width           For East/West panels
33271  * @cfg {Number}    height          For North/South panels
33272  * @cfg {Boolean}   split           To show the splitter
33273  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
33274  */
33275 Roo.LayoutRegion = function(mgr, config, pos){
33276     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
33277     var dh = Roo.DomHelper;
33278     /** This region's container element 
33279     * @type Roo.Element */
33280     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
33281     /** This region's title element 
33282     * @type Roo.Element */
33283
33284     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
33285         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
33286         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
33287     ]}, true);
33288     this.titleEl.enableDisplayMode();
33289     /** This region's title text element 
33290     * @type HTMLElement */
33291     this.titleTextEl = this.titleEl.dom.firstChild;
33292     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
33293     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
33294     this.closeBtn.enableDisplayMode();
33295     this.closeBtn.on("click", this.closeClicked, this);
33296     this.closeBtn.hide();
33297
33298     this.createBody(config);
33299     this.visible = true;
33300     this.collapsed = false;
33301
33302     if(config.hideWhenEmpty){
33303         this.hide();
33304         this.on("paneladded", this.validateVisibility, this);
33305         this.on("panelremoved", this.validateVisibility, this);
33306     }
33307     this.applyConfig(config);
33308 };
33309
33310 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
33311
33312     createBody : function(){
33313         /** This region's body element 
33314         * @type Roo.Element */
33315         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
33316     },
33317
33318     applyConfig : function(c){
33319         if(c.collapsible && this.position != "center" && !this.collapsedEl){
33320             var dh = Roo.DomHelper;
33321             if(c.titlebar !== false){
33322                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
33323                 this.collapseBtn.on("click", this.collapse, this);
33324                 this.collapseBtn.enableDisplayMode();
33325
33326                 if(c.showPin === true || this.showPin){
33327                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
33328                     this.stickBtn.enableDisplayMode();
33329                     this.stickBtn.on("click", this.expand, this);
33330                     this.stickBtn.hide();
33331                 }
33332             }
33333             /** This region's collapsed element
33334             * @type Roo.Element */
33335             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
33336                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
33337             ]}, true);
33338             if(c.floatable !== false){
33339                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
33340                this.collapsedEl.on("click", this.collapseClick, this);
33341             }
33342
33343             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
33344                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
33345                    id: "message", unselectable: "on", style:{"float":"left"}});
33346                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
33347              }
33348             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
33349             this.expandBtn.on("click", this.expand, this);
33350         }
33351         if(this.collapseBtn){
33352             this.collapseBtn.setVisible(c.collapsible == true);
33353         }
33354         this.cmargins = c.cmargins || this.cmargins ||
33355                          (this.position == "west" || this.position == "east" ?
33356                              {top: 0, left: 2, right:2, bottom: 0} :
33357                              {top: 2, left: 0, right:0, bottom: 2});
33358         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33359         this.bottomTabs = c.tabPosition != "top";
33360         this.autoScroll = c.autoScroll || false;
33361         if(this.autoScroll){
33362             this.bodyEl.setStyle("overflow", "auto");
33363         }else{
33364             this.bodyEl.setStyle("overflow", "hidden");
33365         }
33366         //if(c.titlebar !== false){
33367             if((!c.titlebar && !c.title) || c.titlebar === false){
33368                 this.titleEl.hide();
33369             }else{
33370                 this.titleEl.show();
33371                 if(c.title){
33372                     this.titleTextEl.innerHTML = c.title;
33373                 }
33374             }
33375         //}
33376         this.duration = c.duration || .30;
33377         this.slideDuration = c.slideDuration || .45;
33378         this.config = c;
33379         if(c.collapsed){
33380             this.collapse(true);
33381         }
33382         if(c.hidden){
33383             this.hide();
33384         }
33385     },
33386     /**
33387      * Returns true if this region is currently visible.
33388      * @return {Boolean}
33389      */
33390     isVisible : function(){
33391         return this.visible;
33392     },
33393
33394     /**
33395      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
33396      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
33397      */
33398     setCollapsedTitle : function(title){
33399         title = title || "&#160;";
33400         if(this.collapsedTitleTextEl){
33401             this.collapsedTitleTextEl.innerHTML = title;
33402         }
33403     },
33404
33405     getBox : function(){
33406         var b;
33407         if(!this.collapsed){
33408             b = this.el.getBox(false, true);
33409         }else{
33410             b = this.collapsedEl.getBox(false, true);
33411         }
33412         return b;
33413     },
33414
33415     getMargins : function(){
33416         return this.collapsed ? this.cmargins : this.margins;
33417     },
33418
33419     highlight : function(){
33420         this.el.addClass("x-layout-panel-dragover");
33421     },
33422
33423     unhighlight : function(){
33424         this.el.removeClass("x-layout-panel-dragover");
33425     },
33426
33427     updateBox : function(box){
33428         this.box = box;
33429         if(!this.collapsed){
33430             this.el.dom.style.left = box.x + "px";
33431             this.el.dom.style.top = box.y + "px";
33432             this.updateBody(box.width, box.height);
33433         }else{
33434             this.collapsedEl.dom.style.left = box.x + "px";
33435             this.collapsedEl.dom.style.top = box.y + "px";
33436             this.collapsedEl.setSize(box.width, box.height);
33437         }
33438         if(this.tabs){
33439             this.tabs.autoSizeTabs();
33440         }
33441     },
33442
33443     updateBody : function(w, h){
33444         if(w !== null){
33445             this.el.setWidth(w);
33446             w -= this.el.getBorderWidth("rl");
33447             if(this.config.adjustments){
33448                 w += this.config.adjustments[0];
33449             }
33450         }
33451         if(h !== null){
33452             this.el.setHeight(h);
33453             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
33454             h -= this.el.getBorderWidth("tb");
33455             if(this.config.adjustments){
33456                 h += this.config.adjustments[1];
33457             }
33458             this.bodyEl.setHeight(h);
33459             if(this.tabs){
33460                 h = this.tabs.syncHeight(h);
33461             }
33462         }
33463         if(this.panelSize){
33464             w = w !== null ? w : this.panelSize.width;
33465             h = h !== null ? h : this.panelSize.height;
33466         }
33467         if(this.activePanel){
33468             var el = this.activePanel.getEl();
33469             w = w !== null ? w : el.getWidth();
33470             h = h !== null ? h : el.getHeight();
33471             this.panelSize = {width: w, height: h};
33472             this.activePanel.setSize(w, h);
33473         }
33474         if(Roo.isIE && this.tabs){
33475             this.tabs.el.repaint();
33476         }
33477     },
33478
33479     /**
33480      * Returns the container element for this region.
33481      * @return {Roo.Element}
33482      */
33483     getEl : function(){
33484         return this.el;
33485     },
33486
33487     /**
33488      * Hides this region.
33489      */
33490     hide : function(){
33491         if(!this.collapsed){
33492             this.el.dom.style.left = "-2000px";
33493             this.el.hide();
33494         }else{
33495             this.collapsedEl.dom.style.left = "-2000px";
33496             this.collapsedEl.hide();
33497         }
33498         this.visible = false;
33499         this.fireEvent("visibilitychange", this, false);
33500     },
33501
33502     /**
33503      * Shows this region if it was previously hidden.
33504      */
33505     show : function(){
33506         if(!this.collapsed){
33507             this.el.show();
33508         }else{
33509             this.collapsedEl.show();
33510         }
33511         this.visible = true;
33512         this.fireEvent("visibilitychange", this, true);
33513     },
33514
33515     closeClicked : function(){
33516         if(this.activePanel){
33517             this.remove(this.activePanel);
33518         }
33519     },
33520
33521     collapseClick : function(e){
33522         if(this.isSlid){
33523            e.stopPropagation();
33524            this.slideIn();
33525         }else{
33526            e.stopPropagation();
33527            this.slideOut();
33528         }
33529     },
33530
33531     /**
33532      * Collapses this region.
33533      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
33534      */
33535     collapse : function(skipAnim){
33536         if(this.collapsed) return;
33537         this.collapsed = true;
33538         if(this.split){
33539             this.split.el.hide();
33540         }
33541         if(this.config.animate && skipAnim !== true){
33542             this.fireEvent("invalidated", this);
33543             this.animateCollapse();
33544         }else{
33545             this.el.setLocation(-20000,-20000);
33546             this.el.hide();
33547             this.collapsedEl.show();
33548             this.fireEvent("collapsed", this);
33549             this.fireEvent("invalidated", this);
33550         }
33551     },
33552
33553     animateCollapse : function(){
33554         // overridden
33555     },
33556
33557     /**
33558      * Expands this region if it was previously collapsed.
33559      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
33560      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
33561      */
33562     expand : function(e, skipAnim){
33563         if(e) e.stopPropagation();
33564         if(!this.collapsed || this.el.hasActiveFx()) return;
33565         if(this.isSlid){
33566             this.afterSlideIn();
33567             skipAnim = true;
33568         }
33569         this.collapsed = false;
33570         if(this.config.animate && skipAnim !== true){
33571             this.animateExpand();
33572         }else{
33573             this.el.show();
33574             if(this.split){
33575                 this.split.el.show();
33576             }
33577             this.collapsedEl.setLocation(-2000,-2000);
33578             this.collapsedEl.hide();
33579             this.fireEvent("invalidated", this);
33580             this.fireEvent("expanded", this);
33581         }
33582     },
33583
33584     animateExpand : function(){
33585         // overridden
33586     },
33587
33588     initTabs : function()
33589     {
33590         this.bodyEl.setStyle("overflow", "hidden");
33591         var ts = new Roo.TabPanel(
33592                 this.bodyEl.dom,
33593                 {
33594                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
33595                     disableTooltips: this.config.disableTabTips,
33596                     toolbar : this.config.toolbar
33597                 }
33598         );
33599         if(this.config.hideTabs){
33600             ts.stripWrap.setDisplayed(false);
33601         }
33602         this.tabs = ts;
33603         ts.resizeTabs = this.config.resizeTabs === true;
33604         ts.minTabWidth = this.config.minTabWidth || 40;
33605         ts.maxTabWidth = this.config.maxTabWidth || 250;
33606         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
33607         ts.monitorResize = false;
33608         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
33609         ts.bodyEl.addClass('x-layout-tabs-body');
33610         this.panels.each(this.initPanelAsTab, this);
33611     },
33612
33613     initPanelAsTab : function(panel){
33614         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
33615                     this.config.closeOnTab && panel.isClosable());
33616         if(panel.tabTip !== undefined){
33617             ti.setTooltip(panel.tabTip);
33618         }
33619         ti.on("activate", function(){
33620               this.setActivePanel(panel);
33621         }, this);
33622         if(this.config.closeOnTab){
33623             ti.on("beforeclose", function(t, e){
33624                 e.cancel = true;
33625                 this.remove(panel);
33626             }, this);
33627         }
33628         return ti;
33629     },
33630
33631     updatePanelTitle : function(panel, title){
33632         if(this.activePanel == panel){
33633             this.updateTitle(title);
33634         }
33635         if(this.tabs){
33636             var ti = this.tabs.getTab(panel.getEl().id);
33637             ti.setText(title);
33638             if(panel.tabTip !== undefined){
33639                 ti.setTooltip(panel.tabTip);
33640             }
33641         }
33642     },
33643
33644     updateTitle : function(title){
33645         if(this.titleTextEl && !this.config.title){
33646             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
33647         }
33648     },
33649
33650     setActivePanel : function(panel){
33651         panel = this.getPanel(panel);
33652         if(this.activePanel && this.activePanel != panel){
33653             this.activePanel.setActiveState(false);
33654         }
33655         this.activePanel = panel;
33656         panel.setActiveState(true);
33657         if(this.panelSize){
33658             panel.setSize(this.panelSize.width, this.panelSize.height);
33659         }
33660         if(this.closeBtn){
33661             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
33662         }
33663         this.updateTitle(panel.getTitle());
33664         if(this.tabs){
33665             this.fireEvent("invalidated", this);
33666         }
33667         this.fireEvent("panelactivated", this, panel);
33668     },
33669
33670     /**
33671      * Shows the specified panel.
33672      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
33673      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
33674      */
33675     showPanel : function(panel){
33676         if(panel = this.getPanel(panel)){
33677             if(this.tabs){
33678                 var tab = this.tabs.getTab(panel.getEl().id);
33679                 if(tab.isHidden()){
33680                     this.tabs.unhideTab(tab.id);
33681                 }
33682                 tab.activate();
33683             }else{
33684                 this.setActivePanel(panel);
33685             }
33686         }
33687         return panel;
33688     },
33689
33690     /**
33691      * Get the active panel for this region.
33692      * @return {Roo.ContentPanel} The active panel or null
33693      */
33694     getActivePanel : function(){
33695         return this.activePanel;
33696     },
33697
33698     validateVisibility : function(){
33699         if(this.panels.getCount() < 1){
33700             this.updateTitle("&#160;");
33701             this.closeBtn.hide();
33702             this.hide();
33703         }else{
33704             if(!this.isVisible()){
33705                 this.show();
33706             }
33707         }
33708     },
33709
33710     /**
33711      * Adds the passed ContentPanel(s) to this region.
33712      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33713      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
33714      */
33715     add : function(panel){
33716         if(arguments.length > 1){
33717             for(var i = 0, len = arguments.length; i < len; i++) {
33718                 this.add(arguments[i]);
33719             }
33720             return null;
33721         }
33722         if(this.hasPanel(panel)){
33723             this.showPanel(panel);
33724             return panel;
33725         }
33726         panel.setRegion(this);
33727         this.panels.add(panel);
33728         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
33729             this.bodyEl.dom.appendChild(panel.getEl().dom);
33730             if(panel.background !== true){
33731                 this.setActivePanel(panel);
33732             }
33733             this.fireEvent("paneladded", this, panel);
33734             return panel;
33735         }
33736         if(!this.tabs){
33737             this.initTabs();
33738         }else{
33739             this.initPanelAsTab(panel);
33740         }
33741         if(panel.background !== true){
33742             this.tabs.activate(panel.getEl().id);
33743         }
33744         this.fireEvent("paneladded", this, panel);
33745         return panel;
33746     },
33747
33748     /**
33749      * Hides the tab for the specified panel.
33750      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33751      */
33752     hidePanel : function(panel){
33753         if(this.tabs && (panel = this.getPanel(panel))){
33754             this.tabs.hideTab(panel.getEl().id);
33755         }
33756     },
33757
33758     /**
33759      * Unhides the tab for a previously hidden panel.
33760      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33761      */
33762     unhidePanel : function(panel){
33763         if(this.tabs && (panel = this.getPanel(panel))){
33764             this.tabs.unhideTab(panel.getEl().id);
33765         }
33766     },
33767
33768     clearPanels : function(){
33769         while(this.panels.getCount() > 0){
33770              this.remove(this.panels.first());
33771         }
33772     },
33773
33774     /**
33775      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33776      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33777      * @param {Boolean} preservePanel Overrides the config preservePanel option
33778      * @return {Roo.ContentPanel} The panel that was removed
33779      */
33780     remove : function(panel, preservePanel){
33781         panel = this.getPanel(panel);
33782         if(!panel){
33783             return null;
33784         }
33785         var e = {};
33786         this.fireEvent("beforeremove", this, panel, e);
33787         if(e.cancel === true){
33788             return null;
33789         }
33790         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
33791         var panelId = panel.getId();
33792         this.panels.removeKey(panelId);
33793         if(preservePanel){
33794             document.body.appendChild(panel.getEl().dom);
33795         }
33796         if(this.tabs){
33797             this.tabs.removeTab(panel.getEl().id);
33798         }else if (!preservePanel){
33799             this.bodyEl.dom.removeChild(panel.getEl().dom);
33800         }
33801         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
33802             var p = this.panels.first();
33803             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
33804             tempEl.appendChild(p.getEl().dom);
33805             this.bodyEl.update("");
33806             this.bodyEl.dom.appendChild(p.getEl().dom);
33807             tempEl = null;
33808             this.updateTitle(p.getTitle());
33809             this.tabs = null;
33810             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
33811             this.setActivePanel(p);
33812         }
33813         panel.setRegion(null);
33814         if(this.activePanel == panel){
33815             this.activePanel = null;
33816         }
33817         if(this.config.autoDestroy !== false && preservePanel !== true){
33818             try{panel.destroy();}catch(e){}
33819         }
33820         this.fireEvent("panelremoved", this, panel);
33821         return panel;
33822     },
33823
33824     /**
33825      * Returns the TabPanel component used by this region
33826      * @return {Roo.TabPanel}
33827      */
33828     getTabs : function(){
33829         return this.tabs;
33830     },
33831
33832     createTool : function(parentEl, className){
33833         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
33834             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
33835         btn.addClassOnOver("x-layout-tools-button-over");
33836         return btn;
33837     }
33838 });/*
33839  * Based on:
33840  * Ext JS Library 1.1.1
33841  * Copyright(c) 2006-2007, Ext JS, LLC.
33842  *
33843  * Originally Released Under LGPL - original licence link has changed is not relivant.
33844  *
33845  * Fork - LGPL
33846  * <script type="text/javascript">
33847  */
33848  
33849
33850
33851 /**
33852  * @class Roo.SplitLayoutRegion
33853  * @extends Roo.LayoutRegion
33854  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
33855  */
33856 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
33857     this.cursor = cursor;
33858     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
33859 };
33860
33861 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
33862     splitTip : "Drag to resize.",
33863     collapsibleSplitTip : "Drag to resize. Double click to hide.",
33864     useSplitTips : false,
33865
33866     applyConfig : function(config){
33867         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
33868         if(config.split){
33869             if(!this.split){
33870                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
33871                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
33872                 /** The SplitBar for this region 
33873                 * @type Roo.SplitBar */
33874                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
33875                 this.split.on("moved", this.onSplitMove, this);
33876                 this.split.useShim = config.useShim === true;
33877                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
33878                 if(this.useSplitTips){
33879                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
33880                 }
33881                 if(config.collapsible){
33882                     this.split.el.on("dblclick", this.collapse,  this);
33883                 }
33884             }
33885             if(typeof config.minSize != "undefined"){
33886                 this.split.minSize = config.minSize;
33887             }
33888             if(typeof config.maxSize != "undefined"){
33889                 this.split.maxSize = config.maxSize;
33890             }
33891             if(config.hideWhenEmpty || config.hidden || config.collapsed){
33892                 this.hideSplitter();
33893             }
33894         }
33895     },
33896
33897     getHMaxSize : function(){
33898          var cmax = this.config.maxSize || 10000;
33899          var center = this.mgr.getRegion("center");
33900          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
33901     },
33902
33903     getVMaxSize : function(){
33904          var cmax = this.config.maxSize || 10000;
33905          var center = this.mgr.getRegion("center");
33906          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
33907     },
33908
33909     onSplitMove : function(split, newSize){
33910         this.fireEvent("resized", this, newSize);
33911     },
33912     
33913     /** 
33914      * Returns the {@link Roo.SplitBar} for this region.
33915      * @return {Roo.SplitBar}
33916      */
33917     getSplitBar : function(){
33918         return this.split;
33919     },
33920     
33921     hide : function(){
33922         this.hideSplitter();
33923         Roo.SplitLayoutRegion.superclass.hide.call(this);
33924     },
33925
33926     hideSplitter : function(){
33927         if(this.split){
33928             this.split.el.setLocation(-2000,-2000);
33929             this.split.el.hide();
33930         }
33931     },
33932
33933     show : function(){
33934         if(this.split){
33935             this.split.el.show();
33936         }
33937         Roo.SplitLayoutRegion.superclass.show.call(this);
33938     },
33939     
33940     beforeSlide: function(){
33941         if(Roo.isGecko){// firefox overflow auto bug workaround
33942             this.bodyEl.clip();
33943             if(this.tabs) this.tabs.bodyEl.clip();
33944             if(this.activePanel){
33945                 this.activePanel.getEl().clip();
33946                 
33947                 if(this.activePanel.beforeSlide){
33948                     this.activePanel.beforeSlide();
33949                 }
33950             }
33951         }
33952     },
33953     
33954     afterSlide : function(){
33955         if(Roo.isGecko){// firefox overflow auto bug workaround
33956             this.bodyEl.unclip();
33957             if(this.tabs) this.tabs.bodyEl.unclip();
33958             if(this.activePanel){
33959                 this.activePanel.getEl().unclip();
33960                 if(this.activePanel.afterSlide){
33961                     this.activePanel.afterSlide();
33962                 }
33963             }
33964         }
33965     },
33966
33967     initAutoHide : function(){
33968         if(this.autoHide !== false){
33969             if(!this.autoHideHd){
33970                 var st = new Roo.util.DelayedTask(this.slideIn, this);
33971                 this.autoHideHd = {
33972                     "mouseout": function(e){
33973                         if(!e.within(this.el, true)){
33974                             st.delay(500);
33975                         }
33976                     },
33977                     "mouseover" : function(e){
33978                         st.cancel();
33979                     },
33980                     scope : this
33981                 };
33982             }
33983             this.el.on(this.autoHideHd);
33984         }
33985     },
33986
33987     clearAutoHide : function(){
33988         if(this.autoHide !== false){
33989             this.el.un("mouseout", this.autoHideHd.mouseout);
33990             this.el.un("mouseover", this.autoHideHd.mouseover);
33991         }
33992     },
33993
33994     clearMonitor : function(){
33995         Roo.get(document).un("click", this.slideInIf, this);
33996     },
33997
33998     // these names are backwards but not changed for compat
33999     slideOut : function(){
34000         if(this.isSlid || this.el.hasActiveFx()){
34001             return;
34002         }
34003         this.isSlid = true;
34004         if(this.collapseBtn){
34005             this.collapseBtn.hide();
34006         }
34007         this.closeBtnState = this.closeBtn.getStyle('display');
34008         this.closeBtn.hide();
34009         if(this.stickBtn){
34010             this.stickBtn.show();
34011         }
34012         this.el.show();
34013         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
34014         this.beforeSlide();
34015         this.el.setStyle("z-index", 10001);
34016         this.el.slideIn(this.getSlideAnchor(), {
34017             callback: function(){
34018                 this.afterSlide();
34019                 this.initAutoHide();
34020                 Roo.get(document).on("click", this.slideInIf, this);
34021                 this.fireEvent("slideshow", this);
34022             },
34023             scope: this,
34024             block: true
34025         });
34026     },
34027
34028     afterSlideIn : function(){
34029         this.clearAutoHide();
34030         this.isSlid = false;
34031         this.clearMonitor();
34032         this.el.setStyle("z-index", "");
34033         if(this.collapseBtn){
34034             this.collapseBtn.show();
34035         }
34036         this.closeBtn.setStyle('display', this.closeBtnState);
34037         if(this.stickBtn){
34038             this.stickBtn.hide();
34039         }
34040         this.fireEvent("slidehide", this);
34041     },
34042
34043     slideIn : function(cb){
34044         if(!this.isSlid || this.el.hasActiveFx()){
34045             Roo.callback(cb);
34046             return;
34047         }
34048         this.isSlid = false;
34049         this.beforeSlide();
34050         this.el.slideOut(this.getSlideAnchor(), {
34051             callback: function(){
34052                 this.el.setLeftTop(-10000, -10000);
34053                 this.afterSlide();
34054                 this.afterSlideIn();
34055                 Roo.callback(cb);
34056             },
34057             scope: this,
34058             block: true
34059         });
34060     },
34061     
34062     slideInIf : function(e){
34063         if(!e.within(this.el)){
34064             this.slideIn();
34065         }
34066     },
34067
34068     animateCollapse : function(){
34069         this.beforeSlide();
34070         this.el.setStyle("z-index", 20000);
34071         var anchor = this.getSlideAnchor();
34072         this.el.slideOut(anchor, {
34073             callback : function(){
34074                 this.el.setStyle("z-index", "");
34075                 this.collapsedEl.slideIn(anchor, {duration:.3});
34076                 this.afterSlide();
34077                 this.el.setLocation(-10000,-10000);
34078                 this.el.hide();
34079                 this.fireEvent("collapsed", this);
34080             },
34081             scope: this,
34082             block: true
34083         });
34084     },
34085
34086     animateExpand : function(){
34087         this.beforeSlide();
34088         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
34089         this.el.setStyle("z-index", 20000);
34090         this.collapsedEl.hide({
34091             duration:.1
34092         });
34093         this.el.slideIn(this.getSlideAnchor(), {
34094             callback : function(){
34095                 this.el.setStyle("z-index", "");
34096                 this.afterSlide();
34097                 if(this.split){
34098                     this.split.el.show();
34099                 }
34100                 this.fireEvent("invalidated", this);
34101                 this.fireEvent("expanded", this);
34102             },
34103             scope: this,
34104             block: true
34105         });
34106     },
34107
34108     anchors : {
34109         "west" : "left",
34110         "east" : "right",
34111         "north" : "top",
34112         "south" : "bottom"
34113     },
34114
34115     sanchors : {
34116         "west" : "l",
34117         "east" : "r",
34118         "north" : "t",
34119         "south" : "b"
34120     },
34121
34122     canchors : {
34123         "west" : "tl-tr",
34124         "east" : "tr-tl",
34125         "north" : "tl-bl",
34126         "south" : "bl-tl"
34127     },
34128
34129     getAnchor : function(){
34130         return this.anchors[this.position];
34131     },
34132
34133     getCollapseAnchor : function(){
34134         return this.canchors[this.position];
34135     },
34136
34137     getSlideAnchor : function(){
34138         return this.sanchors[this.position];
34139     },
34140
34141     getAlignAdj : function(){
34142         var cm = this.cmargins;
34143         switch(this.position){
34144             case "west":
34145                 return [0, 0];
34146             break;
34147             case "east":
34148                 return [0, 0];
34149             break;
34150             case "north":
34151                 return [0, 0];
34152             break;
34153             case "south":
34154                 return [0, 0];
34155             break;
34156         }
34157     },
34158
34159     getExpandAdj : function(){
34160         var c = this.collapsedEl, cm = this.cmargins;
34161         switch(this.position){
34162             case "west":
34163                 return [-(cm.right+c.getWidth()+cm.left), 0];
34164             break;
34165             case "east":
34166                 return [cm.right+c.getWidth()+cm.left, 0];
34167             break;
34168             case "north":
34169                 return [0, -(cm.top+cm.bottom+c.getHeight())];
34170             break;
34171             case "south":
34172                 return [0, cm.top+cm.bottom+c.getHeight()];
34173             break;
34174         }
34175     }
34176 });/*
34177  * Based on:
34178  * Ext JS Library 1.1.1
34179  * Copyright(c) 2006-2007, Ext JS, LLC.
34180  *
34181  * Originally Released Under LGPL - original licence link has changed is not relivant.
34182  *
34183  * Fork - LGPL
34184  * <script type="text/javascript">
34185  */
34186 /*
34187  * These classes are private internal classes
34188  */
34189 Roo.CenterLayoutRegion = function(mgr, config){
34190     Roo.LayoutRegion.call(this, mgr, config, "center");
34191     this.visible = true;
34192     this.minWidth = config.minWidth || 20;
34193     this.minHeight = config.minHeight || 20;
34194 };
34195
34196 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
34197     hide : function(){
34198         // center panel can't be hidden
34199     },
34200     
34201     show : function(){
34202         // center panel can't be hidden
34203     },
34204     
34205     getMinWidth: function(){
34206         return this.minWidth;
34207     },
34208     
34209     getMinHeight: function(){
34210         return this.minHeight;
34211     }
34212 });
34213
34214
34215 Roo.NorthLayoutRegion = function(mgr, config){
34216     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
34217     if(this.split){
34218         this.split.placement = Roo.SplitBar.TOP;
34219         this.split.orientation = Roo.SplitBar.VERTICAL;
34220         this.split.el.addClass("x-layout-split-v");
34221     }
34222     var size = config.initialSize || config.height;
34223     if(typeof size != "undefined"){
34224         this.el.setHeight(size);
34225     }
34226 };
34227 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
34228     orientation: Roo.SplitBar.VERTICAL,
34229     getBox : function(){
34230         if(this.collapsed){
34231             return this.collapsedEl.getBox();
34232         }
34233         var box = this.el.getBox();
34234         if(this.split){
34235             box.height += this.split.el.getHeight();
34236         }
34237         return box;
34238     },
34239     
34240     updateBox : function(box){
34241         if(this.split && !this.collapsed){
34242             box.height -= this.split.el.getHeight();
34243             this.split.el.setLeft(box.x);
34244             this.split.el.setTop(box.y+box.height);
34245             this.split.el.setWidth(box.width);
34246         }
34247         if(this.collapsed){
34248             this.updateBody(box.width, null);
34249         }
34250         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34251     }
34252 });
34253
34254 Roo.SouthLayoutRegion = function(mgr, config){
34255     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
34256     if(this.split){
34257         this.split.placement = Roo.SplitBar.BOTTOM;
34258         this.split.orientation = Roo.SplitBar.VERTICAL;
34259         this.split.el.addClass("x-layout-split-v");
34260     }
34261     var size = config.initialSize || config.height;
34262     if(typeof size != "undefined"){
34263         this.el.setHeight(size);
34264     }
34265 };
34266 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
34267     orientation: Roo.SplitBar.VERTICAL,
34268     getBox : function(){
34269         if(this.collapsed){
34270             return this.collapsedEl.getBox();
34271         }
34272         var box = this.el.getBox();
34273         if(this.split){
34274             var sh = this.split.el.getHeight();
34275             box.height += sh;
34276             box.y -= sh;
34277         }
34278         return box;
34279     },
34280     
34281     updateBox : function(box){
34282         if(this.split && !this.collapsed){
34283             var sh = this.split.el.getHeight();
34284             box.height -= sh;
34285             box.y += sh;
34286             this.split.el.setLeft(box.x);
34287             this.split.el.setTop(box.y-sh);
34288             this.split.el.setWidth(box.width);
34289         }
34290         if(this.collapsed){
34291             this.updateBody(box.width, null);
34292         }
34293         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34294     }
34295 });
34296
34297 Roo.EastLayoutRegion = function(mgr, config){
34298     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
34299     if(this.split){
34300         this.split.placement = Roo.SplitBar.RIGHT;
34301         this.split.orientation = Roo.SplitBar.HORIZONTAL;
34302         this.split.el.addClass("x-layout-split-h");
34303     }
34304     var size = config.initialSize || config.width;
34305     if(typeof size != "undefined"){
34306         this.el.setWidth(size);
34307     }
34308 };
34309 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
34310     orientation: Roo.SplitBar.HORIZONTAL,
34311     getBox : function(){
34312         if(this.collapsed){
34313             return this.collapsedEl.getBox();
34314         }
34315         var box = this.el.getBox();
34316         if(this.split){
34317             var sw = this.split.el.getWidth();
34318             box.width += sw;
34319             box.x -= sw;
34320         }
34321         return box;
34322     },
34323
34324     updateBox : function(box){
34325         if(this.split && !this.collapsed){
34326             var sw = this.split.el.getWidth();
34327             box.width -= sw;
34328             this.split.el.setLeft(box.x);
34329             this.split.el.setTop(box.y);
34330             this.split.el.setHeight(box.height);
34331             box.x += sw;
34332         }
34333         if(this.collapsed){
34334             this.updateBody(null, box.height);
34335         }
34336         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34337     }
34338 });
34339
34340 Roo.WestLayoutRegion = function(mgr, config){
34341     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
34342     if(this.split){
34343         this.split.placement = Roo.SplitBar.LEFT;
34344         this.split.orientation = Roo.SplitBar.HORIZONTAL;
34345         this.split.el.addClass("x-layout-split-h");
34346     }
34347     var size = config.initialSize || config.width;
34348     if(typeof size != "undefined"){
34349         this.el.setWidth(size);
34350     }
34351 };
34352 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
34353     orientation: Roo.SplitBar.HORIZONTAL,
34354     getBox : function(){
34355         if(this.collapsed){
34356             return this.collapsedEl.getBox();
34357         }
34358         var box = this.el.getBox();
34359         if(this.split){
34360             box.width += this.split.el.getWidth();
34361         }
34362         return box;
34363     },
34364     
34365     updateBox : function(box){
34366         if(this.split && !this.collapsed){
34367             var sw = this.split.el.getWidth();
34368             box.width -= sw;
34369             this.split.el.setLeft(box.x+box.width);
34370             this.split.el.setTop(box.y);
34371             this.split.el.setHeight(box.height);
34372         }
34373         if(this.collapsed){
34374             this.updateBody(null, box.height);
34375         }
34376         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34377     }
34378 });
34379 /*
34380  * Based on:
34381  * Ext JS Library 1.1.1
34382  * Copyright(c) 2006-2007, Ext JS, LLC.
34383  *
34384  * Originally Released Under LGPL - original licence link has changed is not relivant.
34385  *
34386  * Fork - LGPL
34387  * <script type="text/javascript">
34388  */
34389  
34390  
34391 /*
34392  * Private internal class for reading and applying state
34393  */
34394 Roo.LayoutStateManager = function(layout){
34395      // default empty state
34396      this.state = {
34397         north: {},
34398         south: {},
34399         east: {},
34400         west: {}       
34401     };
34402 };
34403
34404 Roo.LayoutStateManager.prototype = {
34405     init : function(layout, provider){
34406         this.provider = provider;
34407         var state = provider.get(layout.id+"-layout-state");
34408         if(state){
34409             var wasUpdating = layout.isUpdating();
34410             if(!wasUpdating){
34411                 layout.beginUpdate();
34412             }
34413             for(var key in state){
34414                 if(typeof state[key] != "function"){
34415                     var rstate = state[key];
34416                     var r = layout.getRegion(key);
34417                     if(r && rstate){
34418                         if(rstate.size){
34419                             r.resizeTo(rstate.size);
34420                         }
34421                         if(rstate.collapsed == true){
34422                             r.collapse(true);
34423                         }else{
34424                             r.expand(null, true);
34425                         }
34426                     }
34427                 }
34428             }
34429             if(!wasUpdating){
34430                 layout.endUpdate();
34431             }
34432             this.state = state; 
34433         }
34434         this.layout = layout;
34435         layout.on("regionresized", this.onRegionResized, this);
34436         layout.on("regioncollapsed", this.onRegionCollapsed, this);
34437         layout.on("regionexpanded", this.onRegionExpanded, this);
34438     },
34439     
34440     storeState : function(){
34441         this.provider.set(this.layout.id+"-layout-state", this.state);
34442     },
34443     
34444     onRegionResized : function(region, newSize){
34445         this.state[region.getPosition()].size = newSize;
34446         this.storeState();
34447     },
34448     
34449     onRegionCollapsed : function(region){
34450         this.state[region.getPosition()].collapsed = true;
34451         this.storeState();
34452     },
34453     
34454     onRegionExpanded : function(region){
34455         this.state[region.getPosition()].collapsed = false;
34456         this.storeState();
34457     }
34458 };/*
34459  * Based on:
34460  * Ext JS Library 1.1.1
34461  * Copyright(c) 2006-2007, Ext JS, LLC.
34462  *
34463  * Originally Released Under LGPL - original licence link has changed is not relivant.
34464  *
34465  * Fork - LGPL
34466  * <script type="text/javascript">
34467  */
34468 /**
34469  * @class Roo.ContentPanel
34470  * @extends Roo.util.Observable
34471  * A basic ContentPanel element.
34472  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
34473  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
34474  * @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
34475  * @cfg {Boolean}   closable      True if the panel can be closed/removed
34476  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
34477  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
34478  * @cfg {Toolbar}   toolbar       A toolbar for this panel
34479  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
34480  * @cfg {String} title          The title for this panel
34481  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
34482  * @cfg {String} url            Calls {@link #setUrl} with this value
34483  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
34484  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
34485  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
34486  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
34487
34488  * @constructor
34489  * Create a new ContentPanel.
34490  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
34491  * @param {String/Object} config A string to set only the title or a config object
34492  * @param {String} content (optional) Set the HTML content for this panel
34493  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
34494  */
34495 Roo.ContentPanel = function(el, config, content){
34496     
34497      
34498     /*
34499     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
34500         config = el;
34501         el = Roo.id();
34502     }
34503     if (config && config.parentLayout) { 
34504         el = config.parentLayout.el.createChild(); 
34505     }
34506     */
34507     if(el.autoCreate){ // xtype is available if this is called from factory
34508         config = el;
34509         el = Roo.id();
34510     }
34511     this.el = Roo.get(el);
34512     if(!this.el && config && config.autoCreate){
34513         if(typeof config.autoCreate == "object"){
34514             if(!config.autoCreate.id){
34515                 config.autoCreate.id = config.id||el;
34516             }
34517             this.el = Roo.DomHelper.append(document.body,
34518                         config.autoCreate, true);
34519         }else{
34520             this.el = Roo.DomHelper.append(document.body,
34521                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
34522         }
34523     }
34524     this.closable = false;
34525     this.loaded = false;
34526     this.active = false;
34527     if(typeof config == "string"){
34528         this.title = config;
34529     }else{
34530         Roo.apply(this, config);
34531     }
34532     
34533     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
34534         this.wrapEl = this.el.wrap();
34535         this.toolbar.container = this.el.insertSibling(false, 'before');
34536         this.toolbar = new Roo.Toolbar(this.toolbar);
34537     }
34538     
34539     // xtype created footer. - not sure if will work as we normally have to render first..
34540     if (this.footer && !this.footer.el && this.footer.xtype) {
34541         if (!this.wrapEl) {
34542             this.wrapEl = this.el.wrap();
34543         }
34544     
34545         this.footer.container = this.wrapEl.createChild();
34546          
34547         this.footer = Roo.factory(this.footer, Roo);
34548         
34549     }
34550     
34551     if(this.resizeEl){
34552         this.resizeEl = Roo.get(this.resizeEl, true);
34553     }else{
34554         this.resizeEl = this.el;
34555     }
34556     // handle view.xtype
34557     
34558  
34559     
34560     
34561     this.addEvents({
34562         /**
34563          * @event activate
34564          * Fires when this panel is activated. 
34565          * @param {Roo.ContentPanel} this
34566          */
34567         "activate" : true,
34568         /**
34569          * @event deactivate
34570          * Fires when this panel is activated. 
34571          * @param {Roo.ContentPanel} this
34572          */
34573         "deactivate" : true,
34574
34575         /**
34576          * @event resize
34577          * Fires when this panel is resized if fitToFrame is true.
34578          * @param {Roo.ContentPanel} this
34579          * @param {Number} width The width after any component adjustments
34580          * @param {Number} height The height after any component adjustments
34581          */
34582         "resize" : true,
34583         
34584          /**
34585          * @event render
34586          * Fires when this tab is created
34587          * @param {Roo.ContentPanel} this
34588          */
34589         "render" : true
34590         
34591         
34592         
34593     });
34594     
34595
34596     
34597     
34598     if(this.autoScroll){
34599         this.resizeEl.setStyle("overflow", "auto");
34600     } else {
34601         // fix randome scrolling
34602         this.el.on('scroll', function() {
34603             Roo.log('fix random scolling');
34604             this.scrollTo('top',0); 
34605         });
34606     }
34607     content = content || this.content;
34608     if(content){
34609         this.setContent(content);
34610     }
34611     if(config && config.url){
34612         this.setUrl(this.url, this.params, this.loadOnce);
34613     }
34614     
34615     
34616     
34617     Roo.ContentPanel.superclass.constructor.call(this);
34618     
34619     if (this.view && typeof(this.view.xtype) != 'undefined') {
34620         this.view.el = this.el.appendChild(document.createElement("div"));
34621         this.view = Roo.factory(this.view); 
34622         this.view.render  &&  this.view.render(false, '');  
34623     }
34624     
34625     
34626     this.fireEvent('render', this);
34627 };
34628
34629 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
34630     tabTip:'',
34631     setRegion : function(region){
34632         this.region = region;
34633         if(region){
34634            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
34635         }else{
34636            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
34637         } 
34638     },
34639     
34640     /**
34641      * Returns the toolbar for this Panel if one was configured. 
34642      * @return {Roo.Toolbar} 
34643      */
34644     getToolbar : function(){
34645         return this.toolbar;
34646     },
34647     
34648     setActiveState : function(active){
34649         this.active = active;
34650         if(!active){
34651             this.fireEvent("deactivate", this);
34652         }else{
34653             this.fireEvent("activate", this);
34654         }
34655     },
34656     /**
34657      * Updates this panel's element
34658      * @param {String} content The new content
34659      * @param {Boolean} loadScripts (optional) true to look for and process scripts
34660     */
34661     setContent : function(content, loadScripts){
34662         this.el.update(content, loadScripts);
34663     },
34664
34665     ignoreResize : function(w, h){
34666         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
34667             return true;
34668         }else{
34669             this.lastSize = {width: w, height: h};
34670             return false;
34671         }
34672     },
34673     /**
34674      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
34675      * @return {Roo.UpdateManager} The UpdateManager
34676      */
34677     getUpdateManager : function(){
34678         return this.el.getUpdateManager();
34679     },
34680      /**
34681      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
34682      * @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:
34683 <pre><code>
34684 panel.load({
34685     url: "your-url.php",
34686     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
34687     callback: yourFunction,
34688     scope: yourObject, //(optional scope)
34689     discardUrl: false,
34690     nocache: false,
34691     text: "Loading...",
34692     timeout: 30,
34693     scripts: false
34694 });
34695 </code></pre>
34696      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
34697      * 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.
34698      * @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}
34699      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
34700      * @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.
34701      * @return {Roo.ContentPanel} this
34702      */
34703     load : function(){
34704         var um = this.el.getUpdateManager();
34705         um.update.apply(um, arguments);
34706         return this;
34707     },
34708
34709
34710     /**
34711      * 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.
34712      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
34713      * @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)
34714      * @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)
34715      * @return {Roo.UpdateManager} The UpdateManager
34716      */
34717     setUrl : function(url, params, loadOnce){
34718         if(this.refreshDelegate){
34719             this.removeListener("activate", this.refreshDelegate);
34720         }
34721         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
34722         this.on("activate", this.refreshDelegate);
34723         return this.el.getUpdateManager();
34724     },
34725     
34726     _handleRefresh : function(url, params, loadOnce){
34727         if(!loadOnce || !this.loaded){
34728             var updater = this.el.getUpdateManager();
34729             updater.update(url, params, this._setLoaded.createDelegate(this));
34730         }
34731     },
34732     
34733     _setLoaded : function(){
34734         this.loaded = true;
34735     }, 
34736     
34737     /**
34738      * Returns this panel's id
34739      * @return {String} 
34740      */
34741     getId : function(){
34742         return this.el.id;
34743     },
34744     
34745     /** 
34746      * Returns this panel's element - used by regiosn to add.
34747      * @return {Roo.Element} 
34748      */
34749     getEl : function(){
34750         return this.wrapEl || this.el;
34751     },
34752     
34753     adjustForComponents : function(width, height)
34754     {
34755         //Roo.log('adjustForComponents ');
34756         if(this.resizeEl != this.el){
34757             width -= this.el.getFrameWidth('lr');
34758             height -= this.el.getFrameWidth('tb');
34759         }
34760         if(this.toolbar){
34761             var te = this.toolbar.getEl();
34762             height -= te.getHeight();
34763             te.setWidth(width);
34764         }
34765         if(this.footer){
34766             var te = this.footer.getEl();
34767             Roo.log("footer:" + te.getHeight());
34768             
34769             height -= te.getHeight();
34770             te.setWidth(width);
34771         }
34772         
34773         
34774         if(this.adjustments){
34775             width += this.adjustments[0];
34776             height += this.adjustments[1];
34777         }
34778         return {"width": width, "height": height};
34779     },
34780     
34781     setSize : function(width, height){
34782         if(this.fitToFrame && !this.ignoreResize(width, height)){
34783             if(this.fitContainer && this.resizeEl != this.el){
34784                 this.el.setSize(width, height);
34785             }
34786             var size = this.adjustForComponents(width, height);
34787             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
34788             this.fireEvent('resize', this, size.width, size.height);
34789         }
34790     },
34791     
34792     /**
34793      * Returns this panel's title
34794      * @return {String} 
34795      */
34796     getTitle : function(){
34797         return this.title;
34798     },
34799     
34800     /**
34801      * Set this panel's title
34802      * @param {String} title
34803      */
34804     setTitle : function(title){
34805         this.title = title;
34806         if(this.region){
34807             this.region.updatePanelTitle(this, title);
34808         }
34809     },
34810     
34811     /**
34812      * Returns true is this panel was configured to be closable
34813      * @return {Boolean} 
34814      */
34815     isClosable : function(){
34816         return this.closable;
34817     },
34818     
34819     beforeSlide : function(){
34820         this.el.clip();
34821         this.resizeEl.clip();
34822     },
34823     
34824     afterSlide : function(){
34825         this.el.unclip();
34826         this.resizeEl.unclip();
34827     },
34828     
34829     /**
34830      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
34831      *   Will fail silently if the {@link #setUrl} method has not been called.
34832      *   This does not activate the panel, just updates its content.
34833      */
34834     refresh : function(){
34835         if(this.refreshDelegate){
34836            this.loaded = false;
34837            this.refreshDelegate();
34838         }
34839     },
34840     
34841     /**
34842      * Destroys this panel
34843      */
34844     destroy : function(){
34845         this.el.removeAllListeners();
34846         var tempEl = document.createElement("span");
34847         tempEl.appendChild(this.el.dom);
34848         tempEl.innerHTML = "";
34849         this.el.remove();
34850         this.el = null;
34851     },
34852     
34853     /**
34854      * form - if the content panel contains a form - this is a reference to it.
34855      * @type {Roo.form.Form}
34856      */
34857     form : false,
34858     /**
34859      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
34860      *    This contains a reference to it.
34861      * @type {Roo.View}
34862      */
34863     view : false,
34864     
34865       /**
34866      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
34867      * <pre><code>
34868
34869 layout.addxtype({
34870        xtype : 'Form',
34871        items: [ .... ]
34872    }
34873 );
34874
34875 </code></pre>
34876      * @param {Object} cfg Xtype definition of item to add.
34877      */
34878     
34879     addxtype : function(cfg) {
34880         // add form..
34881         if (cfg.xtype.match(/^Form$/)) {
34882             
34883             var el;
34884             //if (this.footer) {
34885             //    el = this.footer.container.insertSibling(false, 'before');
34886             //} else {
34887                 el = this.el.createChild();
34888             //}
34889
34890             this.form = new  Roo.form.Form(cfg);
34891             
34892             
34893             if ( this.form.allItems.length) this.form.render(el.dom);
34894             return this.form;
34895         }
34896         // should only have one of theses..
34897         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
34898             // views.. should not be just added - used named prop 'view''
34899             
34900             cfg.el = this.el.appendChild(document.createElement("div"));
34901             // factory?
34902             
34903             var ret = new Roo.factory(cfg);
34904              
34905              ret.render && ret.render(false, ''); // render blank..
34906             this.view = ret;
34907             return ret;
34908         }
34909         return false;
34910     }
34911 });
34912
34913 /**
34914  * @class Roo.GridPanel
34915  * @extends Roo.ContentPanel
34916  * @constructor
34917  * Create a new GridPanel.
34918  * @param {Roo.grid.Grid} grid The grid for this panel
34919  * @param {String/Object} config A string to set only the panel's title, or a config object
34920  */
34921 Roo.GridPanel = function(grid, config){
34922     
34923   
34924     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
34925         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
34926         
34927     this.wrapper.dom.appendChild(grid.getGridEl().dom);
34928     
34929     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
34930     
34931     if(this.toolbar){
34932         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
34933     }
34934     // xtype created footer. - not sure if will work as we normally have to render first..
34935     if (this.footer && !this.footer.el && this.footer.xtype) {
34936         
34937         this.footer.container = this.grid.getView().getFooterPanel(true);
34938         this.footer.dataSource = this.grid.dataSource;
34939         this.footer = Roo.factory(this.footer, Roo);
34940         
34941     }
34942     
34943     grid.monitorWindowResize = false; // turn off autosizing
34944     grid.autoHeight = false;
34945     grid.autoWidth = false;
34946     this.grid = grid;
34947     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
34948 };
34949
34950 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
34951     getId : function(){
34952         return this.grid.id;
34953     },
34954     
34955     /**
34956      * Returns the grid for this panel
34957      * @return {Roo.grid.Grid} 
34958      */
34959     getGrid : function(){
34960         return this.grid;    
34961     },
34962     
34963     setSize : function(width, height){
34964         if(!this.ignoreResize(width, height)){
34965             var grid = this.grid;
34966             var size = this.adjustForComponents(width, height);
34967             grid.getGridEl().setSize(size.width, size.height);
34968             grid.autoSize();
34969         }
34970     },
34971     
34972     beforeSlide : function(){
34973         this.grid.getView().scroller.clip();
34974     },
34975     
34976     afterSlide : function(){
34977         this.grid.getView().scroller.unclip();
34978     },
34979     
34980     destroy : function(){
34981         this.grid.destroy();
34982         delete this.grid;
34983         Roo.GridPanel.superclass.destroy.call(this); 
34984     }
34985 });
34986
34987
34988 /**
34989  * @class Roo.NestedLayoutPanel
34990  * @extends Roo.ContentPanel
34991  * @constructor
34992  * Create a new NestedLayoutPanel.
34993  * 
34994  * 
34995  * @param {Roo.BorderLayout} layout The layout for this panel
34996  * @param {String/Object} config A string to set only the title or a config object
34997  */
34998 Roo.NestedLayoutPanel = function(layout, config)
34999 {
35000     // construct with only one argument..
35001     /* FIXME - implement nicer consturctors
35002     if (layout.layout) {
35003         config = layout;
35004         layout = config.layout;
35005         delete config.layout;
35006     }
35007     if (layout.xtype && !layout.getEl) {
35008         // then layout needs constructing..
35009         layout = Roo.factory(layout, Roo);
35010     }
35011     */
35012     
35013     
35014     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
35015     
35016     layout.monitorWindowResize = false; // turn off autosizing
35017     this.layout = layout;
35018     this.layout.getEl().addClass("x-layout-nested-layout");
35019     
35020     
35021     
35022     
35023 };
35024
35025 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
35026
35027     setSize : function(width, height){
35028         if(!this.ignoreResize(width, height)){
35029             var size = this.adjustForComponents(width, height);
35030             var el = this.layout.getEl();
35031             el.setSize(size.width, size.height);
35032             var touch = el.dom.offsetWidth;
35033             this.layout.layout();
35034             // ie requires a double layout on the first pass
35035             if(Roo.isIE && !this.initialized){
35036                 this.initialized = true;
35037                 this.layout.layout();
35038             }
35039         }
35040     },
35041     
35042     // activate all subpanels if not currently active..
35043     
35044     setActiveState : function(active){
35045         this.active = active;
35046         if(!active){
35047             this.fireEvent("deactivate", this);
35048             return;
35049         }
35050         
35051         this.fireEvent("activate", this);
35052         // not sure if this should happen before or after..
35053         if (!this.layout) {
35054             return; // should not happen..
35055         }
35056         var reg = false;
35057         for (var r in this.layout.regions) {
35058             reg = this.layout.getRegion(r);
35059             if (reg.getActivePanel()) {
35060                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
35061                 reg.setActivePanel(reg.getActivePanel());
35062                 continue;
35063             }
35064             if (!reg.panels.length) {
35065                 continue;
35066             }
35067             reg.showPanel(reg.getPanel(0));
35068         }
35069         
35070         
35071         
35072         
35073     },
35074     
35075     /**
35076      * Returns the nested BorderLayout for this panel
35077      * @return {Roo.BorderLayout} 
35078      */
35079     getLayout : function(){
35080         return this.layout;
35081     },
35082     
35083      /**
35084      * Adds a xtype elements to the layout of the nested panel
35085      * <pre><code>
35086
35087 panel.addxtype({
35088        xtype : 'ContentPanel',
35089        region: 'west',
35090        items: [ .... ]
35091    }
35092 );
35093
35094 panel.addxtype({
35095         xtype : 'NestedLayoutPanel',
35096         region: 'west',
35097         layout: {
35098            center: { },
35099            west: { }   
35100         },
35101         items : [ ... list of content panels or nested layout panels.. ]
35102    }
35103 );
35104 </code></pre>
35105      * @param {Object} cfg Xtype definition of item to add.
35106      */
35107     addxtype : function(cfg) {
35108         return this.layout.addxtype(cfg);
35109     
35110     }
35111 });
35112
35113 Roo.ScrollPanel = function(el, config, content){
35114     config = config || {};
35115     config.fitToFrame = true;
35116     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
35117     
35118     this.el.dom.style.overflow = "hidden";
35119     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
35120     this.el.removeClass("x-layout-inactive-content");
35121     this.el.on("mousewheel", this.onWheel, this);
35122
35123     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
35124     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
35125     up.unselectable(); down.unselectable();
35126     up.on("click", this.scrollUp, this);
35127     down.on("click", this.scrollDown, this);
35128     up.addClassOnOver("x-scroller-btn-over");
35129     down.addClassOnOver("x-scroller-btn-over");
35130     up.addClassOnClick("x-scroller-btn-click");
35131     down.addClassOnClick("x-scroller-btn-click");
35132     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
35133
35134     this.resizeEl = this.el;
35135     this.el = wrap; this.up = up; this.down = down;
35136 };
35137
35138 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
35139     increment : 100,
35140     wheelIncrement : 5,
35141     scrollUp : function(){
35142         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
35143     },
35144
35145     scrollDown : function(){
35146         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
35147     },
35148
35149     afterScroll : function(){
35150         var el = this.resizeEl;
35151         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
35152         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
35153         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
35154     },
35155
35156     setSize : function(){
35157         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
35158         this.afterScroll();
35159     },
35160
35161     onWheel : function(e){
35162         var d = e.getWheelDelta();
35163         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
35164         this.afterScroll();
35165         e.stopEvent();
35166     },
35167
35168     setContent : function(content, loadScripts){
35169         this.resizeEl.update(content, loadScripts);
35170     }
35171
35172 });
35173
35174
35175
35176
35177
35178
35179
35180
35181
35182 /**
35183  * @class Roo.TreePanel
35184  * @extends Roo.ContentPanel
35185  * @constructor
35186  * Create a new TreePanel. - defaults to fit/scoll contents.
35187  * @param {String/Object} config A string to set only the panel's title, or a config object
35188  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
35189  */
35190 Roo.TreePanel = function(config){
35191     var el = config.el;
35192     var tree = config.tree;
35193     delete config.tree; 
35194     delete config.el; // hopefull!
35195     
35196     // wrapper for IE7 strict & safari scroll issue
35197     
35198     var treeEl = el.createChild();
35199     config.resizeEl = treeEl;
35200     
35201     
35202     
35203     Roo.TreePanel.superclass.constructor.call(this, el, config);
35204  
35205  
35206     this.tree = new Roo.tree.TreePanel(treeEl , tree);
35207     //console.log(tree);
35208     this.on('activate', function()
35209     {
35210         if (this.tree.rendered) {
35211             return;
35212         }
35213         //console.log('render tree');
35214         this.tree.render();
35215     });
35216     // this should not be needed.. - it's actually the 'el' that resizes?
35217     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
35218     
35219     //this.on('resize',  function (cp, w, h) {
35220     //        this.tree.innerCt.setWidth(w);
35221     //        this.tree.innerCt.setHeight(h);
35222     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
35223     //});
35224
35225         
35226     
35227 };
35228
35229 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
35230     fitToFrame : true,
35231     autoScroll : true
35232 });
35233
35234
35235
35236
35237
35238
35239
35240
35241
35242
35243
35244 /*
35245  * Based on:
35246  * Ext JS Library 1.1.1
35247  * Copyright(c) 2006-2007, Ext JS, LLC.
35248  *
35249  * Originally Released Under LGPL - original licence link has changed is not relivant.
35250  *
35251  * Fork - LGPL
35252  * <script type="text/javascript">
35253  */
35254  
35255
35256 /**
35257  * @class Roo.ReaderLayout
35258  * @extends Roo.BorderLayout
35259  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
35260  * center region containing two nested regions (a top one for a list view and one for item preview below),
35261  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
35262  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
35263  * expedites the setup of the overall layout and regions for this common application style.
35264  * Example:
35265  <pre><code>
35266 var reader = new Roo.ReaderLayout();
35267 var CP = Roo.ContentPanel;  // shortcut for adding
35268
35269 reader.beginUpdate();
35270 reader.add("north", new CP("north", "North"));
35271 reader.add("west", new CP("west", {title: "West"}));
35272 reader.add("east", new CP("east", {title: "East"}));
35273
35274 reader.regions.listView.add(new CP("listView", "List"));
35275 reader.regions.preview.add(new CP("preview", "Preview"));
35276 reader.endUpdate();
35277 </code></pre>
35278 * @constructor
35279 * Create a new ReaderLayout
35280 * @param {Object} config Configuration options
35281 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
35282 * document.body if omitted)
35283 */
35284 Roo.ReaderLayout = function(config, renderTo){
35285     var c = config || {size:{}};
35286     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
35287         north: c.north !== false ? Roo.apply({
35288             split:false,
35289             initialSize: 32,
35290             titlebar: false
35291         }, c.north) : false,
35292         west: c.west !== false ? Roo.apply({
35293             split:true,
35294             initialSize: 200,
35295             minSize: 175,
35296             maxSize: 400,
35297             titlebar: true,
35298             collapsible: true,
35299             animate: true,
35300             margins:{left:5,right:0,bottom:5,top:5},
35301             cmargins:{left:5,right:5,bottom:5,top:5}
35302         }, c.west) : false,
35303         east: c.east !== false ? Roo.apply({
35304             split:true,
35305             initialSize: 200,
35306             minSize: 175,
35307             maxSize: 400,
35308             titlebar: true,
35309             collapsible: true,
35310             animate: true,
35311             margins:{left:0,right:5,bottom:5,top:5},
35312             cmargins:{left:5,right:5,bottom:5,top:5}
35313         }, c.east) : false,
35314         center: Roo.apply({
35315             tabPosition: 'top',
35316             autoScroll:false,
35317             closeOnTab: true,
35318             titlebar:false,
35319             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
35320         }, c.center)
35321     });
35322
35323     this.el.addClass('x-reader');
35324
35325     this.beginUpdate();
35326
35327     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
35328         south: c.preview !== false ? Roo.apply({
35329             split:true,
35330             initialSize: 200,
35331             minSize: 100,
35332             autoScroll:true,
35333             collapsible:true,
35334             titlebar: true,
35335             cmargins:{top:5,left:0, right:0, bottom:0}
35336         }, c.preview) : false,
35337         center: Roo.apply({
35338             autoScroll:false,
35339             titlebar:false,
35340             minHeight:200
35341         }, c.listView)
35342     });
35343     this.add('center', new Roo.NestedLayoutPanel(inner,
35344             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
35345
35346     this.endUpdate();
35347
35348     this.regions.preview = inner.getRegion('south');
35349     this.regions.listView = inner.getRegion('center');
35350 };
35351
35352 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
35353  * Based on:
35354  * Ext JS Library 1.1.1
35355  * Copyright(c) 2006-2007, Ext JS, LLC.
35356  *
35357  * Originally Released Under LGPL - original licence link has changed is not relivant.
35358  *
35359  * Fork - LGPL
35360  * <script type="text/javascript">
35361  */
35362  
35363 /**
35364  * @class Roo.grid.Grid
35365  * @extends Roo.util.Observable
35366  * This class represents the primary interface of a component based grid control.
35367  * <br><br>Usage:<pre><code>
35368  var grid = new Roo.grid.Grid("my-container-id", {
35369      ds: myDataStore,
35370      cm: myColModel,
35371      selModel: mySelectionModel,
35372      autoSizeColumns: true,
35373      monitorWindowResize: false,
35374      trackMouseOver: true
35375  });
35376  // set any options
35377  grid.render();
35378  * </code></pre>
35379  * <b>Common Problems:</b><br/>
35380  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
35381  * element will correct this<br/>
35382  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
35383  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
35384  * are unpredictable.<br/>
35385  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
35386  * grid to calculate dimensions/offsets.<br/>
35387   * @constructor
35388  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
35389  * The container MUST have some type of size defined for the grid to fill. The container will be
35390  * automatically set to position relative if it isn't already.
35391  * @param {Object} config A config object that sets properties on this grid.
35392  */
35393 Roo.grid.Grid = function(container, config){
35394         // initialize the container
35395         this.container = Roo.get(container);
35396         this.container.update("");
35397         this.container.setStyle("overflow", "hidden");
35398     this.container.addClass('x-grid-container');
35399
35400     this.id = this.container.id;
35401
35402     Roo.apply(this, config);
35403     // check and correct shorthanded configs
35404     if(this.ds){
35405         this.dataSource = this.ds;
35406         delete this.ds;
35407     }
35408     if(this.cm){
35409         this.colModel = this.cm;
35410         delete this.cm;
35411     }
35412     if(this.sm){
35413         this.selModel = this.sm;
35414         delete this.sm;
35415     }
35416
35417     if (this.selModel) {
35418         this.selModel = Roo.factory(this.selModel, Roo.grid);
35419         this.sm = this.selModel;
35420         this.sm.xmodule = this.xmodule || false;
35421     }
35422     if (typeof(this.colModel.config) == 'undefined') {
35423         this.colModel = new Roo.grid.ColumnModel(this.colModel);
35424         this.cm = this.colModel;
35425         this.cm.xmodule = this.xmodule || false;
35426     }
35427     if (this.dataSource) {
35428         this.dataSource= Roo.factory(this.dataSource, Roo.data);
35429         this.ds = this.dataSource;
35430         this.ds.xmodule = this.xmodule || false;
35431          
35432     }
35433     
35434     
35435     
35436     if(this.width){
35437         this.container.setWidth(this.width);
35438     }
35439
35440     if(this.height){
35441         this.container.setHeight(this.height);
35442     }
35443     /** @private */
35444         this.addEvents({
35445         // raw events
35446         /**
35447          * @event click
35448          * The raw click event for the entire grid.
35449          * @param {Roo.EventObject} e
35450          */
35451         "click" : true,
35452         /**
35453          * @event dblclick
35454          * The raw dblclick event for the entire grid.
35455          * @param {Roo.EventObject} e
35456          */
35457         "dblclick" : true,
35458         /**
35459          * @event contextmenu
35460          * The raw contextmenu event for the entire grid.
35461          * @param {Roo.EventObject} e
35462          */
35463         "contextmenu" : true,
35464         /**
35465          * @event mousedown
35466          * The raw mousedown event for the entire grid.
35467          * @param {Roo.EventObject} e
35468          */
35469         "mousedown" : true,
35470         /**
35471          * @event mouseup
35472          * The raw mouseup event for the entire grid.
35473          * @param {Roo.EventObject} e
35474          */
35475         "mouseup" : true,
35476         /**
35477          * @event mouseover
35478          * The raw mouseover event for the entire grid.
35479          * @param {Roo.EventObject} e
35480          */
35481         "mouseover" : true,
35482         /**
35483          * @event mouseout
35484          * The raw mouseout event for the entire grid.
35485          * @param {Roo.EventObject} e
35486          */
35487         "mouseout" : true,
35488         /**
35489          * @event keypress
35490          * The raw keypress event for the entire grid.
35491          * @param {Roo.EventObject} e
35492          */
35493         "keypress" : true,
35494         /**
35495          * @event keydown
35496          * The raw keydown event for the entire grid.
35497          * @param {Roo.EventObject} e
35498          */
35499         "keydown" : true,
35500
35501         // custom events
35502
35503         /**
35504          * @event cellclick
35505          * Fires when a cell is clicked
35506          * @param {Grid} this
35507          * @param {Number} rowIndex
35508          * @param {Number} columnIndex
35509          * @param {Roo.EventObject} e
35510          */
35511         "cellclick" : true,
35512         /**
35513          * @event celldblclick
35514          * Fires when a cell is double clicked
35515          * @param {Grid} this
35516          * @param {Number} rowIndex
35517          * @param {Number} columnIndex
35518          * @param {Roo.EventObject} e
35519          */
35520         "celldblclick" : true,
35521         /**
35522          * @event rowclick
35523          * Fires when a row is clicked
35524          * @param {Grid} this
35525          * @param {Number} rowIndex
35526          * @param {Roo.EventObject} e
35527          */
35528         "rowclick" : true,
35529         /**
35530          * @event rowdblclick
35531          * Fires when a row is double clicked
35532          * @param {Grid} this
35533          * @param {Number} rowIndex
35534          * @param {Roo.EventObject} e
35535          */
35536         "rowdblclick" : true,
35537         /**
35538          * @event headerclick
35539          * Fires when a header is clicked
35540          * @param {Grid} this
35541          * @param {Number} columnIndex
35542          * @param {Roo.EventObject} e
35543          */
35544         "headerclick" : true,
35545         /**
35546          * @event headerdblclick
35547          * Fires when a header cell is double clicked
35548          * @param {Grid} this
35549          * @param {Number} columnIndex
35550          * @param {Roo.EventObject} e
35551          */
35552         "headerdblclick" : true,
35553         /**
35554          * @event rowcontextmenu
35555          * Fires when a row is right clicked
35556          * @param {Grid} this
35557          * @param {Number} rowIndex
35558          * @param {Roo.EventObject} e
35559          */
35560         "rowcontextmenu" : true,
35561         /**
35562          * @event cellcontextmenu
35563          * Fires when a cell is right clicked
35564          * @param {Grid} this
35565          * @param {Number} rowIndex
35566          * @param {Number} cellIndex
35567          * @param {Roo.EventObject} e
35568          */
35569          "cellcontextmenu" : true,
35570         /**
35571          * @event headercontextmenu
35572          * Fires when a header is right clicked
35573          * @param {Grid} this
35574          * @param {Number} columnIndex
35575          * @param {Roo.EventObject} e
35576          */
35577         "headercontextmenu" : true,
35578         /**
35579          * @event bodyscroll
35580          * Fires when the body element is scrolled
35581          * @param {Number} scrollLeft
35582          * @param {Number} scrollTop
35583          */
35584         "bodyscroll" : true,
35585         /**
35586          * @event columnresize
35587          * Fires when the user resizes a column
35588          * @param {Number} columnIndex
35589          * @param {Number} newSize
35590          */
35591         "columnresize" : true,
35592         /**
35593          * @event columnmove
35594          * Fires when the user moves a column
35595          * @param {Number} oldIndex
35596          * @param {Number} newIndex
35597          */
35598         "columnmove" : true,
35599         /**
35600          * @event startdrag
35601          * Fires when row(s) start being dragged
35602          * @param {Grid} this
35603          * @param {Roo.GridDD} dd The drag drop object
35604          * @param {event} e The raw browser event
35605          */
35606         "startdrag" : true,
35607         /**
35608          * @event enddrag
35609          * Fires when a drag operation is complete
35610          * @param {Grid} this
35611          * @param {Roo.GridDD} dd The drag drop object
35612          * @param {event} e The raw browser event
35613          */
35614         "enddrag" : true,
35615         /**
35616          * @event dragdrop
35617          * Fires when dragged row(s) are dropped on a valid DD target
35618          * @param {Grid} this
35619          * @param {Roo.GridDD} dd The drag drop object
35620          * @param {String} targetId The target drag drop object
35621          * @param {event} e The raw browser event
35622          */
35623         "dragdrop" : true,
35624         /**
35625          * @event dragover
35626          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
35627          * @param {Grid} this
35628          * @param {Roo.GridDD} dd The drag drop object
35629          * @param {String} targetId The target drag drop object
35630          * @param {event} e The raw browser event
35631          */
35632         "dragover" : true,
35633         /**
35634          * @event dragenter
35635          *  Fires when the dragged row(s) first cross another DD target while being dragged
35636          * @param {Grid} this
35637          * @param {Roo.GridDD} dd The drag drop object
35638          * @param {String} targetId The target drag drop object
35639          * @param {event} e The raw browser event
35640          */
35641         "dragenter" : true,
35642         /**
35643          * @event dragout
35644          * Fires when the dragged row(s) leave another DD target while being dragged
35645          * @param {Grid} this
35646          * @param {Roo.GridDD} dd The drag drop object
35647          * @param {String} targetId The target drag drop object
35648          * @param {event} e The raw browser event
35649          */
35650         "dragout" : true,
35651         /**
35652          * @event rowclass
35653          * Fires when a row is rendered, so you can change add a style to it.
35654          * @param {GridView} gridview   The grid view
35655          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
35656          */
35657         'rowclass' : true,
35658
35659         /**
35660          * @event render
35661          * Fires when the grid is rendered
35662          * @param {Grid} grid
35663          */
35664         'render' : true
35665     });
35666
35667     Roo.grid.Grid.superclass.constructor.call(this);
35668 };
35669 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
35670     
35671     /**
35672      * @cfg {String} ddGroup - drag drop group.
35673      */
35674
35675     /**
35676      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
35677      */
35678     minColumnWidth : 25,
35679
35680     /**
35681      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
35682      * <b>on initial render.</b> It is more efficient to explicitly size the columns
35683      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
35684      */
35685     autoSizeColumns : false,
35686
35687     /**
35688      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
35689      */
35690     autoSizeHeaders : true,
35691
35692     /**
35693      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
35694      */
35695     monitorWindowResize : true,
35696
35697     /**
35698      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
35699      * rows measured to get a columns size. Default is 0 (all rows).
35700      */
35701     maxRowsToMeasure : 0,
35702
35703     /**
35704      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
35705      */
35706     trackMouseOver : true,
35707
35708     /**
35709     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
35710     */
35711     
35712     /**
35713     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
35714     */
35715     enableDragDrop : false,
35716     
35717     /**
35718     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
35719     */
35720     enableColumnMove : true,
35721     
35722     /**
35723     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
35724     */
35725     enableColumnHide : true,
35726     
35727     /**
35728     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
35729     */
35730     enableRowHeightSync : false,
35731     
35732     /**
35733     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
35734     */
35735     stripeRows : true,
35736     
35737     /**
35738     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
35739     */
35740     autoHeight : false,
35741
35742     /**
35743      * @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.
35744      */
35745     autoExpandColumn : false,
35746
35747     /**
35748     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
35749     * Default is 50.
35750     */
35751     autoExpandMin : 50,
35752
35753     /**
35754     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
35755     */
35756     autoExpandMax : 1000,
35757
35758     /**
35759     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
35760     */
35761     view : null,
35762
35763     /**
35764     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
35765     */
35766     loadMask : false,
35767     /**
35768     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
35769     */
35770     dropTarget: false,
35771     
35772    
35773     
35774     // private
35775     rendered : false,
35776
35777     /**
35778     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
35779     * of a fixed width. Default is false.
35780     */
35781     /**
35782     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
35783     */
35784     /**
35785      * Called once after all setup has been completed and the grid is ready to be rendered.
35786      * @return {Roo.grid.Grid} this
35787      */
35788     render : function()
35789     {
35790         var c = this.container;
35791         // try to detect autoHeight/width mode
35792         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
35793             this.autoHeight = true;
35794         }
35795         var view = this.getView();
35796         view.init(this);
35797
35798         c.on("click", this.onClick, this);
35799         c.on("dblclick", this.onDblClick, this);
35800         c.on("contextmenu", this.onContextMenu, this);
35801         c.on("keydown", this.onKeyDown, this);
35802         if (Roo.isTouch) {
35803             c.on("touchstart", this.onTouchStart, this);
35804         }
35805
35806         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
35807
35808         this.getSelectionModel().init(this);
35809
35810         view.render();
35811
35812         if(this.loadMask){
35813             this.loadMask = new Roo.LoadMask(this.container,
35814                     Roo.apply({store:this.dataSource}, this.loadMask));
35815         }
35816         
35817         
35818         if (this.toolbar && this.toolbar.xtype) {
35819             this.toolbar.container = this.getView().getHeaderPanel(true);
35820             this.toolbar = new Roo.Toolbar(this.toolbar);
35821         }
35822         if (this.footer && this.footer.xtype) {
35823             this.footer.dataSource = this.getDataSource();
35824             this.footer.container = this.getView().getFooterPanel(true);
35825             this.footer = Roo.factory(this.footer, Roo);
35826         }
35827         if (this.dropTarget && this.dropTarget.xtype) {
35828             delete this.dropTarget.xtype;
35829             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
35830         }
35831         
35832         
35833         this.rendered = true;
35834         this.fireEvent('render', this);
35835         return this;
35836     },
35837
35838         /**
35839          * Reconfigures the grid to use a different Store and Column Model.
35840          * The View will be bound to the new objects and refreshed.
35841          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
35842          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
35843          */
35844     reconfigure : function(dataSource, colModel){
35845         if(this.loadMask){
35846             this.loadMask.destroy();
35847             this.loadMask = new Roo.LoadMask(this.container,
35848                     Roo.apply({store:dataSource}, this.loadMask));
35849         }
35850         this.view.bind(dataSource, colModel);
35851         this.dataSource = dataSource;
35852         this.colModel = colModel;
35853         this.view.refresh(true);
35854     },
35855
35856     // private
35857     onKeyDown : function(e){
35858         this.fireEvent("keydown", e);
35859     },
35860
35861     /**
35862      * Destroy this grid.
35863      * @param {Boolean} removeEl True to remove the element
35864      */
35865     destroy : function(removeEl, keepListeners){
35866         if(this.loadMask){
35867             this.loadMask.destroy();
35868         }
35869         var c = this.container;
35870         c.removeAllListeners();
35871         this.view.destroy();
35872         this.colModel.purgeListeners();
35873         if(!keepListeners){
35874             this.purgeListeners();
35875         }
35876         c.update("");
35877         if(removeEl === true){
35878             c.remove();
35879         }
35880     },
35881
35882     // private
35883     processEvent : function(name, e){
35884         // does this fire select???
35885         Roo.log('grid:processEvent '  + name);
35886         
35887         if (name != 'touchstart' ) {
35888             this.fireEvent(name, e);    
35889         }
35890         
35891         var t = e.getTarget();
35892         var v = this.view;
35893         var header = v.findHeaderIndex(t);
35894         if(header !== false){
35895             this.fireEvent("header" + (name == 'touchstart' ? 'click' : name), this, header, e);
35896         }else{
35897             var row = v.findRowIndex(t);
35898             var cell = v.findCellIndex(t);
35899             if (name == 'touchstart') {
35900                 // first touch is always a click.
35901                 // hopefull this happens after selection is updated.?
35902                 name = false;
35903                 
35904                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
35905                     var cs = this.selModel.getSelectedCell();
35906                     if (row == cs[0] && cell == cs[1]){
35907                         name = 'dblclick';
35908                     }
35909                 }
35910                 if (typeof(this.selModel.getSelections) != 'undefined') {
35911                     var cs = this.selModel.getSelections();
35912                     var ds = this.dataSource;
35913                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
35914                         name = 'dblclick';
35915                     }
35916                 }
35917                 if (!name) {
35918                     return;
35919                 }
35920             }
35921             
35922             
35923             if(row !== false){
35924                 this.fireEvent("row" + name, this, row, e);
35925                 if(cell !== false){
35926                     this.fireEvent("cell" + name, this, row, cell, e);
35927                 }
35928             }
35929         }
35930     },
35931
35932     // private
35933     onClick : function(e){
35934         this.processEvent("click", e);
35935     },
35936    // private
35937     onTouchStart : function(e){
35938         this.processEvent("touchstart", e);
35939     },
35940
35941     // private
35942     onContextMenu : function(e, t){
35943         this.processEvent("contextmenu", e);
35944     },
35945
35946     // private
35947     onDblClick : function(e){
35948         this.processEvent("dblclick", e);
35949     },
35950
35951     // private
35952     walkCells : function(row, col, step, fn, scope){
35953         var cm = this.colModel, clen = cm.getColumnCount();
35954         var ds = this.dataSource, rlen = ds.getCount(), first = true;
35955         if(step < 0){
35956             if(col < 0){
35957                 row--;
35958                 first = false;
35959             }
35960             while(row >= 0){
35961                 if(!first){
35962                     col = clen-1;
35963                 }
35964                 first = false;
35965                 while(col >= 0){
35966                     if(fn.call(scope || this, row, col, cm) === true){
35967                         return [row, col];
35968                     }
35969                     col--;
35970                 }
35971                 row--;
35972             }
35973         } else {
35974             if(col >= clen){
35975                 row++;
35976                 first = false;
35977             }
35978             while(row < rlen){
35979                 if(!first){
35980                     col = 0;
35981                 }
35982                 first = false;
35983                 while(col < clen){
35984                     if(fn.call(scope || this, row, col, cm) === true){
35985                         return [row, col];
35986                     }
35987                     col++;
35988                 }
35989                 row++;
35990             }
35991         }
35992         return null;
35993     },
35994
35995     // private
35996     getSelections : function(){
35997         return this.selModel.getSelections();
35998     },
35999
36000     /**
36001      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
36002      * but if manual update is required this method will initiate it.
36003      */
36004     autoSize : function(){
36005         if(this.rendered){
36006             this.view.layout();
36007             if(this.view.adjustForScroll){
36008                 this.view.adjustForScroll();
36009             }
36010         }
36011     },
36012
36013     /**
36014      * Returns the grid's underlying element.
36015      * @return {Element} The element
36016      */
36017     getGridEl : function(){
36018         return this.container;
36019     },
36020
36021     // private for compatibility, overridden by editor grid
36022     stopEditing : function(){},
36023
36024     /**
36025      * Returns the grid's SelectionModel.
36026      * @return {SelectionModel}
36027      */
36028     getSelectionModel : function(){
36029         if(!this.selModel){
36030             this.selModel = new Roo.grid.RowSelectionModel();
36031         }
36032         return this.selModel;
36033     },
36034
36035     /**
36036      * Returns the grid's DataSource.
36037      * @return {DataSource}
36038      */
36039     getDataSource : function(){
36040         return this.dataSource;
36041     },
36042
36043     /**
36044      * Returns the grid's ColumnModel.
36045      * @return {ColumnModel}
36046      */
36047     getColumnModel : function(){
36048         return this.colModel;
36049     },
36050
36051     /**
36052      * Returns the grid's GridView object.
36053      * @return {GridView}
36054      */
36055     getView : function(){
36056         if(!this.view){
36057             this.view = new Roo.grid.GridView(this.viewConfig);
36058         }
36059         return this.view;
36060     },
36061     /**
36062      * Called to get grid's drag proxy text, by default returns this.ddText.
36063      * @return {String}
36064      */
36065     getDragDropText : function(){
36066         var count = this.selModel.getCount();
36067         return String.format(this.ddText, count, count == 1 ? '' : 's');
36068     }
36069 });
36070 /**
36071  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
36072  * %0 is replaced with the number of selected rows.
36073  * @type String
36074  */
36075 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
36076  * Based on:
36077  * Ext JS Library 1.1.1
36078  * Copyright(c) 2006-2007, Ext JS, LLC.
36079  *
36080  * Originally Released Under LGPL - original licence link has changed is not relivant.
36081  *
36082  * Fork - LGPL
36083  * <script type="text/javascript">
36084  */
36085  
36086 Roo.grid.AbstractGridView = function(){
36087         this.grid = null;
36088         
36089         this.events = {
36090             "beforerowremoved" : true,
36091             "beforerowsinserted" : true,
36092             "beforerefresh" : true,
36093             "rowremoved" : true,
36094             "rowsinserted" : true,
36095             "rowupdated" : true,
36096             "refresh" : true
36097         };
36098     Roo.grid.AbstractGridView.superclass.constructor.call(this);
36099 };
36100
36101 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
36102     rowClass : "x-grid-row",
36103     cellClass : "x-grid-cell",
36104     tdClass : "x-grid-td",
36105     hdClass : "x-grid-hd",
36106     splitClass : "x-grid-hd-split",
36107     
36108         init: function(grid){
36109         this.grid = grid;
36110                 var cid = this.grid.getGridEl().id;
36111         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
36112         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
36113         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
36114         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
36115         },
36116         
36117         getColumnRenderers : function(){
36118         var renderers = [];
36119         var cm = this.grid.colModel;
36120         var colCount = cm.getColumnCount();
36121         for(var i = 0; i < colCount; i++){
36122             renderers[i] = cm.getRenderer(i);
36123         }
36124         return renderers;
36125     },
36126     
36127     getColumnIds : function(){
36128         var ids = [];
36129         var cm = this.grid.colModel;
36130         var colCount = cm.getColumnCount();
36131         for(var i = 0; i < colCount; i++){
36132             ids[i] = cm.getColumnId(i);
36133         }
36134         return ids;
36135     },
36136     
36137     getDataIndexes : function(){
36138         if(!this.indexMap){
36139             this.indexMap = this.buildIndexMap();
36140         }
36141         return this.indexMap.colToData;
36142     },
36143     
36144     getColumnIndexByDataIndex : function(dataIndex){
36145         if(!this.indexMap){
36146             this.indexMap = this.buildIndexMap();
36147         }
36148         return this.indexMap.dataToCol[dataIndex];
36149     },
36150     
36151     /**
36152      * Set a css style for a column dynamically. 
36153      * @param {Number} colIndex The index of the column
36154      * @param {String} name The css property name
36155      * @param {String} value The css value
36156      */
36157     setCSSStyle : function(colIndex, name, value){
36158         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
36159         Roo.util.CSS.updateRule(selector, name, value);
36160     },
36161     
36162     generateRules : function(cm){
36163         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
36164         Roo.util.CSS.removeStyleSheet(rulesId);
36165         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
36166             var cid = cm.getColumnId(i);
36167             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
36168                          this.tdSelector, cid, " {\n}\n",
36169                          this.hdSelector, cid, " {\n}\n",
36170                          this.splitSelector, cid, " {\n}\n");
36171         }
36172         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
36173     }
36174 });/*
36175  * Based on:
36176  * Ext JS Library 1.1.1
36177  * Copyright(c) 2006-2007, Ext JS, LLC.
36178  *
36179  * Originally Released Under LGPL - original licence link has changed is not relivant.
36180  *
36181  * Fork - LGPL
36182  * <script type="text/javascript">
36183  */
36184
36185 // private
36186 // This is a support class used internally by the Grid components
36187 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
36188     this.grid = grid;
36189     this.view = grid.getView();
36190     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
36191     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
36192     if(hd2){
36193         this.setHandleElId(Roo.id(hd));
36194         this.setOuterHandleElId(Roo.id(hd2));
36195     }
36196     this.scroll = false;
36197 };
36198 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
36199     maxDragWidth: 120,
36200     getDragData : function(e){
36201         var t = Roo.lib.Event.getTarget(e);
36202         var h = this.view.findHeaderCell(t);
36203         if(h){
36204             return {ddel: h.firstChild, header:h};
36205         }
36206         return false;
36207     },
36208
36209     onInitDrag : function(e){
36210         this.view.headersDisabled = true;
36211         var clone = this.dragData.ddel.cloneNode(true);
36212         clone.id = Roo.id();
36213         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
36214         this.proxy.update(clone);
36215         return true;
36216     },
36217
36218     afterValidDrop : function(){
36219         var v = this.view;
36220         setTimeout(function(){
36221             v.headersDisabled = false;
36222         }, 50);
36223     },
36224
36225     afterInvalidDrop : function(){
36226         var v = this.view;
36227         setTimeout(function(){
36228             v.headersDisabled = false;
36229         }, 50);
36230     }
36231 });
36232 /*
36233  * Based on:
36234  * Ext JS Library 1.1.1
36235  * Copyright(c) 2006-2007, Ext JS, LLC.
36236  *
36237  * Originally Released Under LGPL - original licence link has changed is not relivant.
36238  *
36239  * Fork - LGPL
36240  * <script type="text/javascript">
36241  */
36242 // private
36243 // This is a support class used internally by the Grid components
36244 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
36245     this.grid = grid;
36246     this.view = grid.getView();
36247     // split the proxies so they don't interfere with mouse events
36248     this.proxyTop = Roo.DomHelper.append(document.body, {
36249         cls:"col-move-top", html:"&#160;"
36250     }, true);
36251     this.proxyBottom = Roo.DomHelper.append(document.body, {
36252         cls:"col-move-bottom", html:"&#160;"
36253     }, true);
36254     this.proxyTop.hide = this.proxyBottom.hide = function(){
36255         this.setLeftTop(-100,-100);
36256         this.setStyle("visibility", "hidden");
36257     };
36258     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
36259     // temporarily disabled
36260     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
36261     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
36262 };
36263 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
36264     proxyOffsets : [-4, -9],
36265     fly: Roo.Element.fly,
36266
36267     getTargetFromEvent : function(e){
36268         var t = Roo.lib.Event.getTarget(e);
36269         var cindex = this.view.findCellIndex(t);
36270         if(cindex !== false){
36271             return this.view.getHeaderCell(cindex);
36272         }
36273         return null;
36274     },
36275
36276     nextVisible : function(h){
36277         var v = this.view, cm = this.grid.colModel;
36278         h = h.nextSibling;
36279         while(h){
36280             if(!cm.isHidden(v.getCellIndex(h))){
36281                 return h;
36282             }
36283             h = h.nextSibling;
36284         }
36285         return null;
36286     },
36287
36288     prevVisible : function(h){
36289         var v = this.view, cm = this.grid.colModel;
36290         h = h.prevSibling;
36291         while(h){
36292             if(!cm.isHidden(v.getCellIndex(h))){
36293                 return h;
36294             }
36295             h = h.prevSibling;
36296         }
36297         return null;
36298     },
36299
36300     positionIndicator : function(h, n, e){
36301         var x = Roo.lib.Event.getPageX(e);
36302         var r = Roo.lib.Dom.getRegion(n.firstChild);
36303         var px, pt, py = r.top + this.proxyOffsets[1];
36304         if((r.right - x) <= (r.right-r.left)/2){
36305             px = r.right+this.view.borderWidth;
36306             pt = "after";
36307         }else{
36308             px = r.left;
36309             pt = "before";
36310         }
36311         var oldIndex = this.view.getCellIndex(h);
36312         var newIndex = this.view.getCellIndex(n);
36313
36314         if(this.grid.colModel.isFixed(newIndex)){
36315             return false;
36316         }
36317
36318         var locked = this.grid.colModel.isLocked(newIndex);
36319
36320         if(pt == "after"){
36321             newIndex++;
36322         }
36323         if(oldIndex < newIndex){
36324             newIndex--;
36325         }
36326         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
36327             return false;
36328         }
36329         px +=  this.proxyOffsets[0];
36330         this.proxyTop.setLeftTop(px, py);
36331         this.proxyTop.show();
36332         if(!this.bottomOffset){
36333             this.bottomOffset = this.view.mainHd.getHeight();
36334         }
36335         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
36336         this.proxyBottom.show();
36337         return pt;
36338     },
36339
36340     onNodeEnter : function(n, dd, e, data){
36341         if(data.header != n){
36342             this.positionIndicator(data.header, n, e);
36343         }
36344     },
36345
36346     onNodeOver : function(n, dd, e, data){
36347         var result = false;
36348         if(data.header != n){
36349             result = this.positionIndicator(data.header, n, e);
36350         }
36351         if(!result){
36352             this.proxyTop.hide();
36353             this.proxyBottom.hide();
36354         }
36355         return result ? this.dropAllowed : this.dropNotAllowed;
36356     },
36357
36358     onNodeOut : function(n, dd, e, data){
36359         this.proxyTop.hide();
36360         this.proxyBottom.hide();
36361     },
36362
36363     onNodeDrop : function(n, dd, e, data){
36364         var h = data.header;
36365         if(h != n){
36366             var cm = this.grid.colModel;
36367             var x = Roo.lib.Event.getPageX(e);
36368             var r = Roo.lib.Dom.getRegion(n.firstChild);
36369             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
36370             var oldIndex = this.view.getCellIndex(h);
36371             var newIndex = this.view.getCellIndex(n);
36372             var locked = cm.isLocked(newIndex);
36373             if(pt == "after"){
36374                 newIndex++;
36375             }
36376             if(oldIndex < newIndex){
36377                 newIndex--;
36378             }
36379             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
36380                 return false;
36381             }
36382             cm.setLocked(oldIndex, locked, true);
36383             cm.moveColumn(oldIndex, newIndex);
36384             this.grid.fireEvent("columnmove", oldIndex, newIndex);
36385             return true;
36386         }
36387         return false;
36388     }
36389 });
36390 /*
36391  * Based on:
36392  * Ext JS Library 1.1.1
36393  * Copyright(c) 2006-2007, Ext JS, LLC.
36394  *
36395  * Originally Released Under LGPL - original licence link has changed is not relivant.
36396  *
36397  * Fork - LGPL
36398  * <script type="text/javascript">
36399  */
36400   
36401 /**
36402  * @class Roo.grid.GridView
36403  * @extends Roo.util.Observable
36404  *
36405  * @constructor
36406  * @param {Object} config
36407  */
36408 Roo.grid.GridView = function(config){
36409     Roo.grid.GridView.superclass.constructor.call(this);
36410     this.el = null;
36411
36412     Roo.apply(this, config);
36413 };
36414
36415 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
36416
36417     unselectable :  'unselectable="on"',
36418     unselectableCls :  'x-unselectable',
36419     
36420     
36421     rowClass : "x-grid-row",
36422
36423     cellClass : "x-grid-col",
36424
36425     tdClass : "x-grid-td",
36426
36427     hdClass : "x-grid-hd",
36428
36429     splitClass : "x-grid-split",
36430
36431     sortClasses : ["sort-asc", "sort-desc"],
36432
36433     enableMoveAnim : false,
36434
36435     hlColor: "C3DAF9",
36436
36437     dh : Roo.DomHelper,
36438
36439     fly : Roo.Element.fly,
36440
36441     css : Roo.util.CSS,
36442
36443     borderWidth: 1,
36444
36445     splitOffset: 3,
36446
36447     scrollIncrement : 22,
36448
36449     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
36450
36451     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
36452
36453     bind : function(ds, cm){
36454         if(this.ds){
36455             this.ds.un("load", this.onLoad, this);
36456             this.ds.un("datachanged", this.onDataChange, this);
36457             this.ds.un("add", this.onAdd, this);
36458             this.ds.un("remove", this.onRemove, this);
36459             this.ds.un("update", this.onUpdate, this);
36460             this.ds.un("clear", this.onClear, this);
36461         }
36462         if(ds){
36463             ds.on("load", this.onLoad, this);
36464             ds.on("datachanged", this.onDataChange, this);
36465             ds.on("add", this.onAdd, this);
36466             ds.on("remove", this.onRemove, this);
36467             ds.on("update", this.onUpdate, this);
36468             ds.on("clear", this.onClear, this);
36469         }
36470         this.ds = ds;
36471
36472         if(this.cm){
36473             this.cm.un("widthchange", this.onColWidthChange, this);
36474             this.cm.un("headerchange", this.onHeaderChange, this);
36475             this.cm.un("hiddenchange", this.onHiddenChange, this);
36476             this.cm.un("columnmoved", this.onColumnMove, this);
36477             this.cm.un("columnlockchange", this.onColumnLock, this);
36478         }
36479         if(cm){
36480             this.generateRules(cm);
36481             cm.on("widthchange", this.onColWidthChange, this);
36482             cm.on("headerchange", this.onHeaderChange, this);
36483             cm.on("hiddenchange", this.onHiddenChange, this);
36484             cm.on("columnmoved", this.onColumnMove, this);
36485             cm.on("columnlockchange", this.onColumnLock, this);
36486         }
36487         this.cm = cm;
36488     },
36489
36490     init: function(grid){
36491         Roo.grid.GridView.superclass.init.call(this, grid);
36492
36493         this.bind(grid.dataSource, grid.colModel);
36494
36495         grid.on("headerclick", this.handleHeaderClick, this);
36496
36497         if(grid.trackMouseOver){
36498             grid.on("mouseover", this.onRowOver, this);
36499             grid.on("mouseout", this.onRowOut, this);
36500         }
36501         grid.cancelTextSelection = function(){};
36502         this.gridId = grid.id;
36503
36504         var tpls = this.templates || {};
36505
36506         if(!tpls.master){
36507             tpls.master = new Roo.Template(
36508                '<div class="x-grid" hidefocus="true">',
36509                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
36510                   '<div class="x-grid-topbar"></div>',
36511                   '<div class="x-grid-scroller"><div></div></div>',
36512                   '<div class="x-grid-locked">',
36513                       '<div class="x-grid-header">{lockedHeader}</div>',
36514                       '<div class="x-grid-body">{lockedBody}</div>',
36515                   "</div>",
36516                   '<div class="x-grid-viewport">',
36517                       '<div class="x-grid-header">{header}</div>',
36518                       '<div class="x-grid-body">{body}</div>',
36519                   "</div>",
36520                   '<div class="x-grid-bottombar"></div>',
36521                  
36522                   '<div class="x-grid-resize-proxy">&#160;</div>',
36523                "</div>"
36524             );
36525             tpls.master.disableformats = true;
36526         }
36527
36528         if(!tpls.header){
36529             tpls.header = new Roo.Template(
36530                '<table border="0" cellspacing="0" cellpadding="0">',
36531                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
36532                "</table>{splits}"
36533             );
36534             tpls.header.disableformats = true;
36535         }
36536         tpls.header.compile();
36537
36538         if(!tpls.hcell){
36539             tpls.hcell = new Roo.Template(
36540                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
36541                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
36542                 "</div></td>"
36543              );
36544              tpls.hcell.disableFormats = true;
36545         }
36546         tpls.hcell.compile();
36547
36548         if(!tpls.hsplit){
36549             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
36550                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
36551             tpls.hsplit.disableFormats = true;
36552         }
36553         tpls.hsplit.compile();
36554
36555         if(!tpls.body){
36556             tpls.body = new Roo.Template(
36557                '<table border="0" cellspacing="0" cellpadding="0">',
36558                "<tbody>{rows}</tbody>",
36559                "</table>"
36560             );
36561             tpls.body.disableFormats = true;
36562         }
36563         tpls.body.compile();
36564
36565         if(!tpls.row){
36566             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
36567             tpls.row.disableFormats = true;
36568         }
36569         tpls.row.compile();
36570
36571         if(!tpls.cell){
36572             tpls.cell = new Roo.Template(
36573                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
36574                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
36575                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
36576                 "</td>"
36577             );
36578             tpls.cell.disableFormats = true;
36579         }
36580         tpls.cell.compile();
36581
36582         this.templates = tpls;
36583     },
36584
36585     // remap these for backwards compat
36586     onColWidthChange : function(){
36587         this.updateColumns.apply(this, arguments);
36588     },
36589     onHeaderChange : function(){
36590         this.updateHeaders.apply(this, arguments);
36591     }, 
36592     onHiddenChange : function(){
36593         this.handleHiddenChange.apply(this, arguments);
36594     },
36595     onColumnMove : function(){
36596         this.handleColumnMove.apply(this, arguments);
36597     },
36598     onColumnLock : function(){
36599         this.handleLockChange.apply(this, arguments);
36600     },
36601
36602     onDataChange : function(){
36603         this.refresh();
36604         this.updateHeaderSortState();
36605     },
36606
36607     onClear : function(){
36608         this.refresh();
36609     },
36610
36611     onUpdate : function(ds, record){
36612         this.refreshRow(record);
36613     },
36614
36615     refreshRow : function(record){
36616         var ds = this.ds, index;
36617         if(typeof record == 'number'){
36618             index = record;
36619             record = ds.getAt(index);
36620         }else{
36621             index = ds.indexOf(record);
36622         }
36623         this.insertRows(ds, index, index, true);
36624         this.onRemove(ds, record, index+1, true);
36625         this.syncRowHeights(index, index);
36626         this.layout();
36627         this.fireEvent("rowupdated", this, index, record);
36628     },
36629
36630     onAdd : function(ds, records, index){
36631         this.insertRows(ds, index, index + (records.length-1));
36632     },
36633
36634     onRemove : function(ds, record, index, isUpdate){
36635         if(isUpdate !== true){
36636             this.fireEvent("beforerowremoved", this, index, record);
36637         }
36638         var bt = this.getBodyTable(), lt = this.getLockedTable();
36639         if(bt.rows[index]){
36640             bt.firstChild.removeChild(bt.rows[index]);
36641         }
36642         if(lt.rows[index]){
36643             lt.firstChild.removeChild(lt.rows[index]);
36644         }
36645         if(isUpdate !== true){
36646             this.stripeRows(index);
36647             this.syncRowHeights(index, index);
36648             this.layout();
36649             this.fireEvent("rowremoved", this, index, record);
36650         }
36651     },
36652
36653     onLoad : function(){
36654         this.scrollToTop();
36655     },
36656
36657     /**
36658      * Scrolls the grid to the top
36659      */
36660     scrollToTop : function(){
36661         if(this.scroller){
36662             this.scroller.dom.scrollTop = 0;
36663             this.syncScroll();
36664         }
36665     },
36666
36667     /**
36668      * Gets a panel in the header of the grid that can be used for toolbars etc.
36669      * After modifying the contents of this panel a call to grid.autoSize() may be
36670      * required to register any changes in size.
36671      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
36672      * @return Roo.Element
36673      */
36674     getHeaderPanel : function(doShow){
36675         if(doShow){
36676             this.headerPanel.show();
36677         }
36678         return this.headerPanel;
36679     },
36680
36681     /**
36682      * Gets a panel in the footer of the grid that can be used for toolbars etc.
36683      * After modifying the contents of this panel a call to grid.autoSize() may be
36684      * required to register any changes in size.
36685      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
36686      * @return Roo.Element
36687      */
36688     getFooterPanel : function(doShow){
36689         if(doShow){
36690             this.footerPanel.show();
36691         }
36692         return this.footerPanel;
36693     },
36694
36695     initElements : function(){
36696         var E = Roo.Element;
36697         var el = this.grid.getGridEl().dom.firstChild;
36698         var cs = el.childNodes;
36699
36700         this.el = new E(el);
36701         
36702          this.focusEl = new E(el.firstChild);
36703         this.focusEl.swallowEvent("click", true);
36704         
36705         this.headerPanel = new E(cs[1]);
36706         this.headerPanel.enableDisplayMode("block");
36707
36708         this.scroller = new E(cs[2]);
36709         this.scrollSizer = new E(this.scroller.dom.firstChild);
36710
36711         this.lockedWrap = new E(cs[3]);
36712         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
36713         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
36714
36715         this.mainWrap = new E(cs[4]);
36716         this.mainHd = new E(this.mainWrap.dom.firstChild);
36717         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
36718
36719         this.footerPanel = new E(cs[5]);
36720         this.footerPanel.enableDisplayMode("block");
36721
36722         this.resizeProxy = new E(cs[6]);
36723
36724         this.headerSelector = String.format(
36725            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
36726            this.lockedHd.id, this.mainHd.id
36727         );
36728
36729         this.splitterSelector = String.format(
36730            '#{0} div.x-grid-split, #{1} div.x-grid-split',
36731            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
36732         );
36733     },
36734     idToCssName : function(s)
36735     {
36736         return s.replace(/[^a-z0-9]+/ig, '-');
36737     },
36738
36739     getHeaderCell : function(index){
36740         return Roo.DomQuery.select(this.headerSelector)[index];
36741     },
36742
36743     getHeaderCellMeasure : function(index){
36744         return this.getHeaderCell(index).firstChild;
36745     },
36746
36747     getHeaderCellText : function(index){
36748         return this.getHeaderCell(index).firstChild.firstChild;
36749     },
36750
36751     getLockedTable : function(){
36752         return this.lockedBody.dom.firstChild;
36753     },
36754
36755     getBodyTable : function(){
36756         return this.mainBody.dom.firstChild;
36757     },
36758
36759     getLockedRow : function(index){
36760         return this.getLockedTable().rows[index];
36761     },
36762
36763     getRow : function(index){
36764         return this.getBodyTable().rows[index];
36765     },
36766
36767     getRowComposite : function(index){
36768         if(!this.rowEl){
36769             this.rowEl = new Roo.CompositeElementLite();
36770         }
36771         var els = [], lrow, mrow;
36772         if(lrow = this.getLockedRow(index)){
36773             els.push(lrow);
36774         }
36775         if(mrow = this.getRow(index)){
36776             els.push(mrow);
36777         }
36778         this.rowEl.elements = els;
36779         return this.rowEl;
36780     },
36781     /**
36782      * Gets the 'td' of the cell
36783      * 
36784      * @param {Integer} rowIndex row to select
36785      * @param {Integer} colIndex column to select
36786      * 
36787      * @return {Object} 
36788      */
36789     getCell : function(rowIndex, colIndex){
36790         var locked = this.cm.getLockedCount();
36791         var source;
36792         if(colIndex < locked){
36793             source = this.lockedBody.dom.firstChild;
36794         }else{
36795             source = this.mainBody.dom.firstChild;
36796             colIndex -= locked;
36797         }
36798         return source.rows[rowIndex].childNodes[colIndex];
36799     },
36800
36801     getCellText : function(rowIndex, colIndex){
36802         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
36803     },
36804
36805     getCellBox : function(cell){
36806         var b = this.fly(cell).getBox();
36807         if(Roo.isOpera){ // opera fails to report the Y
36808             b.y = cell.offsetTop + this.mainBody.getY();
36809         }
36810         return b;
36811     },
36812
36813     getCellIndex : function(cell){
36814         var id = String(cell.className).match(this.cellRE);
36815         if(id){
36816             return parseInt(id[1], 10);
36817         }
36818         return 0;
36819     },
36820
36821     findHeaderIndex : function(n){
36822         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
36823         return r ? this.getCellIndex(r) : false;
36824     },
36825
36826     findHeaderCell : function(n){
36827         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
36828         return r ? r : false;
36829     },
36830
36831     findRowIndex : function(n){
36832         if(!n){
36833             return false;
36834         }
36835         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
36836         return r ? r.rowIndex : false;
36837     },
36838
36839     findCellIndex : function(node){
36840         var stop = this.el.dom;
36841         while(node && node != stop){
36842             if(this.findRE.test(node.className)){
36843                 return this.getCellIndex(node);
36844             }
36845             node = node.parentNode;
36846         }
36847         return false;
36848     },
36849
36850     getColumnId : function(index){
36851         return this.cm.getColumnId(index);
36852     },
36853
36854     getSplitters : function()
36855     {
36856         if(this.splitterSelector){
36857            return Roo.DomQuery.select(this.splitterSelector);
36858         }else{
36859             return null;
36860       }
36861     },
36862
36863     getSplitter : function(index){
36864         return this.getSplitters()[index];
36865     },
36866
36867     onRowOver : function(e, t){
36868         var row;
36869         if((row = this.findRowIndex(t)) !== false){
36870             this.getRowComposite(row).addClass("x-grid-row-over");
36871         }
36872     },
36873
36874     onRowOut : function(e, t){
36875         var row;
36876         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
36877             this.getRowComposite(row).removeClass("x-grid-row-over");
36878         }
36879     },
36880
36881     renderHeaders : function(){
36882         var cm = this.cm;
36883         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
36884         var cb = [], lb = [], sb = [], lsb = [], p = {};
36885         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
36886             p.cellId = "x-grid-hd-0-" + i;
36887             p.splitId = "x-grid-csplit-0-" + i;
36888             p.id = cm.getColumnId(i);
36889             p.title = cm.getColumnTooltip(i) || "";
36890             p.value = cm.getColumnHeader(i) || "";
36891             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
36892             if(!cm.isLocked(i)){
36893                 cb[cb.length] = ct.apply(p);
36894                 sb[sb.length] = st.apply(p);
36895             }else{
36896                 lb[lb.length] = ct.apply(p);
36897                 lsb[lsb.length] = st.apply(p);
36898             }
36899         }
36900         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
36901                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
36902     },
36903
36904     updateHeaders : function(){
36905         var html = this.renderHeaders();
36906         this.lockedHd.update(html[0]);
36907         this.mainHd.update(html[1]);
36908     },
36909
36910     /**
36911      * Focuses the specified row.
36912      * @param {Number} row The row index
36913      */
36914     focusRow : function(row)
36915     {
36916         //Roo.log('GridView.focusRow');
36917         var x = this.scroller.dom.scrollLeft;
36918         this.focusCell(row, 0, false);
36919         this.scroller.dom.scrollLeft = x;
36920     },
36921
36922     /**
36923      * Focuses the specified cell.
36924      * @param {Number} row The row index
36925      * @param {Number} col The column index
36926      * @param {Boolean} hscroll false to disable horizontal scrolling
36927      */
36928     focusCell : function(row, col, hscroll)
36929     {
36930         //Roo.log('GridView.focusCell');
36931         var el = this.ensureVisible(row, col, hscroll);
36932         this.focusEl.alignTo(el, "tl-tl");
36933         if(Roo.isGecko){
36934             this.focusEl.focus();
36935         }else{
36936             this.focusEl.focus.defer(1, this.focusEl);
36937         }
36938     },
36939
36940     /**
36941      * Scrolls the specified cell into view
36942      * @param {Number} row The row index
36943      * @param {Number} col The column index
36944      * @param {Boolean} hscroll false to disable horizontal scrolling
36945      */
36946     ensureVisible : function(row, col, hscroll)
36947     {
36948         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
36949         //return null; //disable for testing.
36950         if(typeof row != "number"){
36951             row = row.rowIndex;
36952         }
36953         if(row < 0 && row >= this.ds.getCount()){
36954             return  null;
36955         }
36956         col = (col !== undefined ? col : 0);
36957         var cm = this.grid.colModel;
36958         while(cm.isHidden(col)){
36959             col++;
36960         }
36961
36962         var el = this.getCell(row, col);
36963         if(!el){
36964             return null;
36965         }
36966         var c = this.scroller.dom;
36967
36968         var ctop = parseInt(el.offsetTop, 10);
36969         var cleft = parseInt(el.offsetLeft, 10);
36970         var cbot = ctop + el.offsetHeight;
36971         var cright = cleft + el.offsetWidth;
36972         
36973         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
36974         var stop = parseInt(c.scrollTop, 10);
36975         var sleft = parseInt(c.scrollLeft, 10);
36976         var sbot = stop + ch;
36977         var sright = sleft + c.clientWidth;
36978         /*
36979         Roo.log('GridView.ensureVisible:' +
36980                 ' ctop:' + ctop +
36981                 ' c.clientHeight:' + c.clientHeight +
36982                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
36983                 ' stop:' + stop +
36984                 ' cbot:' + cbot +
36985                 ' sbot:' + sbot +
36986                 ' ch:' + ch  
36987                 );
36988         */
36989         if(ctop < stop){
36990              c.scrollTop = ctop;
36991             //Roo.log("set scrolltop to ctop DISABLE?");
36992         }else if(cbot > sbot){
36993             //Roo.log("set scrolltop to cbot-ch");
36994             c.scrollTop = cbot-ch;
36995         }
36996         
36997         if(hscroll !== false){
36998             if(cleft < sleft){
36999                 c.scrollLeft = cleft;
37000             }else if(cright > sright){
37001                 c.scrollLeft = cright-c.clientWidth;
37002             }
37003         }
37004          
37005         return el;
37006     },
37007
37008     updateColumns : function(){
37009         this.grid.stopEditing();
37010         var cm = this.grid.colModel, colIds = this.getColumnIds();
37011         //var totalWidth = cm.getTotalWidth();
37012         var pos = 0;
37013         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37014             //if(cm.isHidden(i)) continue;
37015             var w = cm.getColumnWidth(i);
37016             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
37017             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
37018         }
37019         this.updateSplitters();
37020     },
37021
37022     generateRules : function(cm){
37023         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
37024         Roo.util.CSS.removeStyleSheet(rulesId);
37025         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37026             var cid = cm.getColumnId(i);
37027             var align = '';
37028             if(cm.config[i].align){
37029                 align = 'text-align:'+cm.config[i].align+';';
37030             }
37031             var hidden = '';
37032             if(cm.isHidden(i)){
37033                 hidden = 'display:none;';
37034             }
37035             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
37036             ruleBuf.push(
37037                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
37038                     this.hdSelector, cid, " {\n", align, width, "}\n",
37039                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
37040                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
37041         }
37042         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
37043     },
37044
37045     updateSplitters : function(){
37046         var cm = this.cm, s = this.getSplitters();
37047         if(s){ // splitters not created yet
37048             var pos = 0, locked = true;
37049             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37050                 if(cm.isHidden(i)) continue;
37051                 var w = cm.getColumnWidth(i); // make sure it's a number
37052                 if(!cm.isLocked(i) && locked){
37053                     pos = 0;
37054                     locked = false;
37055                 }
37056                 pos += w;
37057                 s[i].style.left = (pos-this.splitOffset) + "px";
37058             }
37059         }
37060     },
37061
37062     handleHiddenChange : function(colModel, colIndex, hidden){
37063         if(hidden){
37064             this.hideColumn(colIndex);
37065         }else{
37066             this.unhideColumn(colIndex);
37067         }
37068     },
37069
37070     hideColumn : function(colIndex){
37071         var cid = this.getColumnId(colIndex);
37072         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
37073         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
37074         if(Roo.isSafari){
37075             this.updateHeaders();
37076         }
37077         this.updateSplitters();
37078         this.layout();
37079     },
37080
37081     unhideColumn : function(colIndex){
37082         var cid = this.getColumnId(colIndex);
37083         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
37084         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
37085
37086         if(Roo.isSafari){
37087             this.updateHeaders();
37088         }
37089         this.updateSplitters();
37090         this.layout();
37091     },
37092
37093     insertRows : function(dm, firstRow, lastRow, isUpdate){
37094         if(firstRow == 0 && lastRow == dm.getCount()-1){
37095             this.refresh();
37096         }else{
37097             if(!isUpdate){
37098                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
37099             }
37100             var s = this.getScrollState();
37101             var markup = this.renderRows(firstRow, lastRow);
37102             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
37103             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
37104             this.restoreScroll(s);
37105             if(!isUpdate){
37106                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
37107                 this.syncRowHeights(firstRow, lastRow);
37108                 this.stripeRows(firstRow);
37109                 this.layout();
37110             }
37111         }
37112     },
37113
37114     bufferRows : function(markup, target, index){
37115         var before = null, trows = target.rows, tbody = target.tBodies[0];
37116         if(index < trows.length){
37117             before = trows[index];
37118         }
37119         var b = document.createElement("div");
37120         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
37121         var rows = b.firstChild.rows;
37122         for(var i = 0, len = rows.length; i < len; i++){
37123             if(before){
37124                 tbody.insertBefore(rows[0], before);
37125             }else{
37126                 tbody.appendChild(rows[0]);
37127             }
37128         }
37129         b.innerHTML = "";
37130         b = null;
37131     },
37132
37133     deleteRows : function(dm, firstRow, lastRow){
37134         if(dm.getRowCount()<1){
37135             this.fireEvent("beforerefresh", this);
37136             this.mainBody.update("");
37137             this.lockedBody.update("");
37138             this.fireEvent("refresh", this);
37139         }else{
37140             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
37141             var bt = this.getBodyTable();
37142             var tbody = bt.firstChild;
37143             var rows = bt.rows;
37144             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
37145                 tbody.removeChild(rows[firstRow]);
37146             }
37147             this.stripeRows(firstRow);
37148             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
37149         }
37150     },
37151
37152     updateRows : function(dataSource, firstRow, lastRow){
37153         var s = this.getScrollState();
37154         this.refresh();
37155         this.restoreScroll(s);
37156     },
37157
37158     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
37159         if(!noRefresh){
37160            this.refresh();
37161         }
37162         this.updateHeaderSortState();
37163     },
37164
37165     getScrollState : function(){
37166         
37167         var sb = this.scroller.dom;
37168         return {left: sb.scrollLeft, top: sb.scrollTop};
37169     },
37170
37171     stripeRows : function(startRow){
37172         if(!this.grid.stripeRows || this.ds.getCount() < 1){
37173             return;
37174         }
37175         startRow = startRow || 0;
37176         var rows = this.getBodyTable().rows;
37177         var lrows = this.getLockedTable().rows;
37178         var cls = ' x-grid-row-alt ';
37179         for(var i = startRow, len = rows.length; i < len; i++){
37180             var row = rows[i], lrow = lrows[i];
37181             var isAlt = ((i+1) % 2 == 0);
37182             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
37183             if(isAlt == hasAlt){
37184                 continue;
37185             }
37186             if(isAlt){
37187                 row.className += " x-grid-row-alt";
37188             }else{
37189                 row.className = row.className.replace("x-grid-row-alt", "");
37190             }
37191             if(lrow){
37192                 lrow.className = row.className;
37193             }
37194         }
37195     },
37196
37197     restoreScroll : function(state){
37198         //Roo.log('GridView.restoreScroll');
37199         var sb = this.scroller.dom;
37200         sb.scrollLeft = state.left;
37201         sb.scrollTop = state.top;
37202         this.syncScroll();
37203     },
37204
37205     syncScroll : function(){
37206         //Roo.log('GridView.syncScroll');
37207         var sb = this.scroller.dom;
37208         var sh = this.mainHd.dom;
37209         var bs = this.mainBody.dom;
37210         var lv = this.lockedBody.dom;
37211         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
37212         lv.scrollTop = bs.scrollTop = sb.scrollTop;
37213     },
37214
37215     handleScroll : function(e){
37216         this.syncScroll();
37217         var sb = this.scroller.dom;
37218         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
37219         e.stopEvent();
37220     },
37221
37222     handleWheel : function(e){
37223         var d = e.getWheelDelta();
37224         this.scroller.dom.scrollTop -= d*22;
37225         // set this here to prevent jumpy scrolling on large tables
37226         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
37227         e.stopEvent();
37228     },
37229
37230     renderRows : function(startRow, endRow){
37231         // pull in all the crap needed to render rows
37232         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
37233         var colCount = cm.getColumnCount();
37234
37235         if(ds.getCount() < 1){
37236             return ["", ""];
37237         }
37238
37239         // build a map for all the columns
37240         var cs = [];
37241         for(var i = 0; i < colCount; i++){
37242             var name = cm.getDataIndex(i);
37243             cs[i] = {
37244                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
37245                 renderer : cm.getRenderer(i),
37246                 id : cm.getColumnId(i),
37247                 locked : cm.isLocked(i)
37248             };
37249         }
37250
37251         startRow = startRow || 0;
37252         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
37253
37254         // records to render
37255         var rs = ds.getRange(startRow, endRow);
37256
37257         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
37258     },
37259
37260     // As much as I hate to duplicate code, this was branched because FireFox really hates
37261     // [].join("") on strings. The performance difference was substantial enough to
37262     // branch this function
37263     doRender : Roo.isGecko ?
37264             function(cs, rs, ds, startRow, colCount, stripe){
37265                 var ts = this.templates, ct = ts.cell, rt = ts.row;
37266                 // buffers
37267                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
37268                 
37269                 var hasListener = this.grid.hasListener('rowclass');
37270                 var rowcfg = {};
37271                 for(var j = 0, len = rs.length; j < len; j++){
37272                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
37273                     for(var i = 0; i < colCount; i++){
37274                         c = cs[i];
37275                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
37276                         p.id = c.id;
37277                         p.css = p.attr = "";
37278                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
37279                         if(p.value == undefined || p.value === "") p.value = "&#160;";
37280                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
37281                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
37282                         }
37283                         var markup = ct.apply(p);
37284                         if(!c.locked){
37285                             cb+= markup;
37286                         }else{
37287                             lcb+= markup;
37288                         }
37289                     }
37290                     var alt = [];
37291                     if(stripe && ((rowIndex+1) % 2 == 0)){
37292                         alt.push("x-grid-row-alt")
37293                     }
37294                     if(r.dirty){
37295                         alt.push(  " x-grid-dirty-row");
37296                     }
37297                     rp.cells = lcb;
37298                     if(this.getRowClass){
37299                         alt.push(this.getRowClass(r, rowIndex));
37300                     }
37301                     if (hasListener) {
37302                         rowcfg = {
37303                              
37304                             record: r,
37305                             rowIndex : rowIndex,
37306                             rowClass : ''
37307                         }
37308                         this.grid.fireEvent('rowclass', this, rowcfg);
37309                         alt.push(rowcfg.rowClass);
37310                     }
37311                     rp.alt = alt.join(" ");
37312                     lbuf+= rt.apply(rp);
37313                     rp.cells = cb;
37314                     buf+=  rt.apply(rp);
37315                 }
37316                 return [lbuf, buf];
37317             } :
37318             function(cs, rs, ds, startRow, colCount, stripe){
37319                 var ts = this.templates, ct = ts.cell, rt = ts.row;
37320                 // buffers
37321                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
37322                 var hasListener = this.grid.hasListener('rowclass');
37323  
37324                 var rowcfg = {};
37325                 for(var j = 0, len = rs.length; j < len; j++){
37326                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
37327                     for(var i = 0; i < colCount; i++){
37328                         c = cs[i];
37329                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
37330                         p.id = c.id;
37331                         p.css = p.attr = "";
37332                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
37333                         if(p.value == undefined || p.value === "") p.value = "&#160;";
37334                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
37335                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
37336                         }
37337                         
37338                         var markup = ct.apply(p);
37339                         if(!c.locked){
37340                             cb[cb.length] = markup;
37341                         }else{
37342                             lcb[lcb.length] = markup;
37343                         }
37344                     }
37345                     var alt = [];
37346                     if(stripe && ((rowIndex+1) % 2 == 0)){
37347                         alt.push( "x-grid-row-alt");
37348                     }
37349                     if(r.dirty){
37350                         alt.push(" x-grid-dirty-row");
37351                     }
37352                     rp.cells = lcb;
37353                     if(this.getRowClass){
37354                         alt.push( this.getRowClass(r, rowIndex));
37355                     }
37356                     if (hasListener) {
37357                         rowcfg = {
37358                              
37359                             record: r,
37360                             rowIndex : rowIndex,
37361                             rowClass : ''
37362                         }
37363                         this.grid.fireEvent('rowclass', this, rowcfg);
37364                         alt.push(rowcfg.rowClass);
37365                     }
37366                     rp.alt = alt.join(" ");
37367                     rp.cells = lcb.join("");
37368                     lbuf[lbuf.length] = rt.apply(rp);
37369                     rp.cells = cb.join("");
37370                     buf[buf.length] =  rt.apply(rp);
37371                 }
37372                 return [lbuf.join(""), buf.join("")];
37373             },
37374
37375     renderBody : function(){
37376         var markup = this.renderRows();
37377         var bt = this.templates.body;
37378         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
37379     },
37380
37381     /**
37382      * Refreshes the grid
37383      * @param {Boolean} headersToo
37384      */
37385     refresh : function(headersToo){
37386         this.fireEvent("beforerefresh", this);
37387         this.grid.stopEditing();
37388         var result = this.renderBody();
37389         this.lockedBody.update(result[0]);
37390         this.mainBody.update(result[1]);
37391         if(headersToo === true){
37392             this.updateHeaders();
37393             this.updateColumns();
37394             this.updateSplitters();
37395             this.updateHeaderSortState();
37396         }
37397         this.syncRowHeights();
37398         this.layout();
37399         this.fireEvent("refresh", this);
37400     },
37401
37402     handleColumnMove : function(cm, oldIndex, newIndex){
37403         this.indexMap = null;
37404         var s = this.getScrollState();
37405         this.refresh(true);
37406         this.restoreScroll(s);
37407         this.afterMove(newIndex);
37408     },
37409
37410     afterMove : function(colIndex){
37411         if(this.enableMoveAnim && Roo.enableFx){
37412             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
37413         }
37414         // if multisort - fix sortOrder, and reload..
37415         if (this.grid.dataSource.multiSort) {
37416             // the we can call sort again..
37417             var dm = this.grid.dataSource;
37418             var cm = this.grid.colModel;
37419             var so = [];
37420             for(var i = 0; i < cm.config.length; i++ ) {
37421                 
37422                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
37423                     continue; // dont' bother, it's not in sort list or being set.
37424                 }
37425                 
37426                 so.push(cm.config[i].dataIndex);
37427             };
37428             dm.sortOrder = so;
37429             dm.load(dm.lastOptions);
37430             
37431             
37432         }
37433         
37434     },
37435
37436     updateCell : function(dm, rowIndex, dataIndex){
37437         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
37438         if(typeof colIndex == "undefined"){ // not present in grid
37439             return;
37440         }
37441         var cm = this.grid.colModel;
37442         var cell = this.getCell(rowIndex, colIndex);
37443         var cellText = this.getCellText(rowIndex, colIndex);
37444
37445         var p = {
37446             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
37447             id : cm.getColumnId(colIndex),
37448             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
37449         };
37450         var renderer = cm.getRenderer(colIndex);
37451         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
37452         if(typeof val == "undefined" || val === "") val = "&#160;";
37453         cellText.innerHTML = val;
37454         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
37455         this.syncRowHeights(rowIndex, rowIndex);
37456     },
37457
37458     calcColumnWidth : function(colIndex, maxRowsToMeasure){
37459         var maxWidth = 0;
37460         if(this.grid.autoSizeHeaders){
37461             var h = this.getHeaderCellMeasure(colIndex);
37462             maxWidth = Math.max(maxWidth, h.scrollWidth);
37463         }
37464         var tb, index;
37465         if(this.cm.isLocked(colIndex)){
37466             tb = this.getLockedTable();
37467             index = colIndex;
37468         }else{
37469             tb = this.getBodyTable();
37470             index = colIndex - this.cm.getLockedCount();
37471         }
37472         if(tb && tb.rows){
37473             var rows = tb.rows;
37474             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
37475             for(var i = 0; i < stopIndex; i++){
37476                 var cell = rows[i].childNodes[index].firstChild;
37477                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
37478             }
37479         }
37480         return maxWidth + /*margin for error in IE*/ 5;
37481     },
37482     /**
37483      * Autofit a column to its content.
37484      * @param {Number} colIndex
37485      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
37486      */
37487      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
37488          if(this.cm.isHidden(colIndex)){
37489              return; // can't calc a hidden column
37490          }
37491         if(forceMinSize){
37492             var cid = this.cm.getColumnId(colIndex);
37493             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
37494            if(this.grid.autoSizeHeaders){
37495                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
37496            }
37497         }
37498         var newWidth = this.calcColumnWidth(colIndex);
37499         this.cm.setColumnWidth(colIndex,
37500             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
37501         if(!suppressEvent){
37502             this.grid.fireEvent("columnresize", colIndex, newWidth);
37503         }
37504     },
37505
37506     /**
37507      * Autofits all columns to their content and then expands to fit any extra space in the grid
37508      */
37509      autoSizeColumns : function(){
37510         var cm = this.grid.colModel;
37511         var colCount = cm.getColumnCount();
37512         for(var i = 0; i < colCount; i++){
37513             this.autoSizeColumn(i, true, true);
37514         }
37515         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
37516             this.fitColumns();
37517         }else{
37518             this.updateColumns();
37519             this.layout();
37520         }
37521     },
37522
37523     /**
37524      * Autofits all columns to the grid's width proportionate with their current size
37525      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
37526      */
37527     fitColumns : function(reserveScrollSpace){
37528         var cm = this.grid.colModel;
37529         var colCount = cm.getColumnCount();
37530         var cols = [];
37531         var width = 0;
37532         var i, w;
37533         for (i = 0; i < colCount; i++){
37534             if(!cm.isHidden(i) && !cm.isFixed(i)){
37535                 w = cm.getColumnWidth(i);
37536                 cols.push(i);
37537                 cols.push(w);
37538                 width += w;
37539             }
37540         }
37541         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
37542         if(reserveScrollSpace){
37543             avail -= 17;
37544         }
37545         var frac = (avail - cm.getTotalWidth())/width;
37546         while (cols.length){
37547             w = cols.pop();
37548             i = cols.pop();
37549             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
37550         }
37551         this.updateColumns();
37552         this.layout();
37553     },
37554
37555     onRowSelect : function(rowIndex){
37556         var row = this.getRowComposite(rowIndex);
37557         row.addClass("x-grid-row-selected");
37558     },
37559
37560     onRowDeselect : function(rowIndex){
37561         var row = this.getRowComposite(rowIndex);
37562         row.removeClass("x-grid-row-selected");
37563     },
37564
37565     onCellSelect : function(row, col){
37566         var cell = this.getCell(row, col);
37567         if(cell){
37568             Roo.fly(cell).addClass("x-grid-cell-selected");
37569         }
37570     },
37571
37572     onCellDeselect : function(row, col){
37573         var cell = this.getCell(row, col);
37574         if(cell){
37575             Roo.fly(cell).removeClass("x-grid-cell-selected");
37576         }
37577     },
37578
37579     updateHeaderSortState : function(){
37580         
37581         // sort state can be single { field: xxx, direction : yyy}
37582         // or   { xxx=>ASC , yyy : DESC ..... }
37583         
37584         var mstate = {};
37585         if (!this.ds.multiSort) { 
37586             var state = this.ds.getSortState();
37587             if(!state){
37588                 return;
37589             }
37590             mstate[state.field] = state.direction;
37591             // FIXME... - this is not used here.. but might be elsewhere..
37592             this.sortState = state;
37593             
37594         } else {
37595             mstate = this.ds.sortToggle;
37596         }
37597         //remove existing sort classes..
37598         
37599         var sc = this.sortClasses;
37600         var hds = this.el.select(this.headerSelector).removeClass(sc);
37601         
37602         for(var f in mstate) {
37603         
37604             var sortColumn = this.cm.findColumnIndex(f);
37605             
37606             if(sortColumn != -1){
37607                 var sortDir = mstate[f];        
37608                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
37609             }
37610         }
37611         
37612          
37613         
37614     },
37615
37616
37617     handleHeaderClick : function(g, index){
37618         if(this.headersDisabled){
37619             return;
37620         }
37621         var dm = g.dataSource, cm = g.colModel;
37622         if(!cm.isSortable(index)){
37623             return;
37624         }
37625         g.stopEditing();
37626         
37627         if (dm.multiSort) {
37628             // update the sortOrder
37629             var so = [];
37630             for(var i = 0; i < cm.config.length; i++ ) {
37631                 
37632                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
37633                     continue; // dont' bother, it's not in sort list or being set.
37634                 }
37635                 
37636                 so.push(cm.config[i].dataIndex);
37637             };
37638             dm.sortOrder = so;
37639         }
37640         
37641         
37642         dm.sort(cm.getDataIndex(index));
37643     },
37644
37645
37646     destroy : function(){
37647         if(this.colMenu){
37648             this.colMenu.removeAll();
37649             Roo.menu.MenuMgr.unregister(this.colMenu);
37650             this.colMenu.getEl().remove();
37651             delete this.colMenu;
37652         }
37653         if(this.hmenu){
37654             this.hmenu.removeAll();
37655             Roo.menu.MenuMgr.unregister(this.hmenu);
37656             this.hmenu.getEl().remove();
37657             delete this.hmenu;
37658         }
37659         if(this.grid.enableColumnMove){
37660             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
37661             if(dds){
37662                 for(var dd in dds){
37663                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
37664                         var elid = dds[dd].dragElId;
37665                         dds[dd].unreg();
37666                         Roo.get(elid).remove();
37667                     } else if(dds[dd].config.isTarget){
37668                         dds[dd].proxyTop.remove();
37669                         dds[dd].proxyBottom.remove();
37670                         dds[dd].unreg();
37671                     }
37672                     if(Roo.dd.DDM.locationCache[dd]){
37673                         delete Roo.dd.DDM.locationCache[dd];
37674                     }
37675                 }
37676                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
37677             }
37678         }
37679         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
37680         this.bind(null, null);
37681         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
37682     },
37683
37684     handleLockChange : function(){
37685         this.refresh(true);
37686     },
37687
37688     onDenyColumnLock : function(){
37689
37690     },
37691
37692     onDenyColumnHide : function(){
37693
37694     },
37695
37696     handleHdMenuClick : function(item){
37697         var index = this.hdCtxIndex;
37698         var cm = this.cm, ds = this.ds;
37699         switch(item.id){
37700             case "asc":
37701                 ds.sort(cm.getDataIndex(index), "ASC");
37702                 break;
37703             case "desc":
37704                 ds.sort(cm.getDataIndex(index), "DESC");
37705                 break;
37706             case "lock":
37707                 var lc = cm.getLockedCount();
37708                 if(cm.getColumnCount(true) <= lc+1){
37709                     this.onDenyColumnLock();
37710                     return;
37711                 }
37712                 if(lc != index){
37713                     cm.setLocked(index, true, true);
37714                     cm.moveColumn(index, lc);
37715                     this.grid.fireEvent("columnmove", index, lc);
37716                 }else{
37717                     cm.setLocked(index, true);
37718                 }
37719             break;
37720             case "unlock":
37721                 var lc = cm.getLockedCount();
37722                 if((lc-1) != index){
37723                     cm.setLocked(index, false, true);
37724                     cm.moveColumn(index, lc-1);
37725                     this.grid.fireEvent("columnmove", index, lc-1);
37726                 }else{
37727                     cm.setLocked(index, false);
37728                 }
37729             break;
37730             default:
37731                 index = cm.getIndexById(item.id.substr(4));
37732                 if(index != -1){
37733                     if(item.checked && cm.getColumnCount(true) <= 1){
37734                         this.onDenyColumnHide();
37735                         return false;
37736                     }
37737                     cm.setHidden(index, item.checked);
37738                 }
37739         }
37740         return true;
37741     },
37742
37743     beforeColMenuShow : function(){
37744         var cm = this.cm,  colCount = cm.getColumnCount();
37745         this.colMenu.removeAll();
37746         for(var i = 0; i < colCount; i++){
37747             this.colMenu.add(new Roo.menu.CheckItem({
37748                 id: "col-"+cm.getColumnId(i),
37749                 text: cm.getColumnHeader(i),
37750                 checked: !cm.isHidden(i),
37751                 hideOnClick:false
37752             }));
37753         }
37754     },
37755
37756     handleHdCtx : function(g, index, e){
37757         e.stopEvent();
37758         var hd = this.getHeaderCell(index);
37759         this.hdCtxIndex = index;
37760         var ms = this.hmenu.items, cm = this.cm;
37761         ms.get("asc").setDisabled(!cm.isSortable(index));
37762         ms.get("desc").setDisabled(!cm.isSortable(index));
37763         if(this.grid.enableColLock !== false){
37764             ms.get("lock").setDisabled(cm.isLocked(index));
37765             ms.get("unlock").setDisabled(!cm.isLocked(index));
37766         }
37767         this.hmenu.show(hd, "tl-bl");
37768     },
37769
37770     handleHdOver : function(e){
37771         var hd = this.findHeaderCell(e.getTarget());
37772         if(hd && !this.headersDisabled){
37773             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
37774                this.fly(hd).addClass("x-grid-hd-over");
37775             }
37776         }
37777     },
37778
37779     handleHdOut : function(e){
37780         var hd = this.findHeaderCell(e.getTarget());
37781         if(hd){
37782             this.fly(hd).removeClass("x-grid-hd-over");
37783         }
37784     },
37785
37786     handleSplitDblClick : function(e, t){
37787         var i = this.getCellIndex(t);
37788         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
37789             this.autoSizeColumn(i, true);
37790             this.layout();
37791         }
37792     },
37793
37794     render : function(){
37795
37796         var cm = this.cm;
37797         var colCount = cm.getColumnCount();
37798
37799         if(this.grid.monitorWindowResize === true){
37800             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37801         }
37802         var header = this.renderHeaders();
37803         var body = this.templates.body.apply({rows:""});
37804         var html = this.templates.master.apply({
37805             lockedBody: body,
37806             body: body,
37807             lockedHeader: header[0],
37808             header: header[1]
37809         });
37810
37811         //this.updateColumns();
37812
37813         this.grid.getGridEl().dom.innerHTML = html;
37814
37815         this.initElements();
37816         
37817         // a kludge to fix the random scolling effect in webkit
37818         this.el.on("scroll", function() {
37819             this.el.dom.scrollTop=0; // hopefully not recursive..
37820         },this);
37821
37822         this.scroller.on("scroll", this.handleScroll, this);
37823         this.lockedBody.on("mousewheel", this.handleWheel, this);
37824         this.mainBody.on("mousewheel", this.handleWheel, this);
37825
37826         this.mainHd.on("mouseover", this.handleHdOver, this);
37827         this.mainHd.on("mouseout", this.handleHdOut, this);
37828         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
37829                 {delegate: "."+this.splitClass});
37830
37831         this.lockedHd.on("mouseover", this.handleHdOver, this);
37832         this.lockedHd.on("mouseout", this.handleHdOut, this);
37833         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
37834                 {delegate: "."+this.splitClass});
37835
37836         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
37837             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
37838         }
37839
37840         this.updateSplitters();
37841
37842         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
37843             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
37844             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
37845         }
37846
37847         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
37848             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
37849             this.hmenu.add(
37850                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
37851                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
37852             );
37853             if(this.grid.enableColLock !== false){
37854                 this.hmenu.add('-',
37855                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
37856                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
37857                 );
37858             }
37859             if(this.grid.enableColumnHide !== false){
37860
37861                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
37862                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
37863                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
37864
37865                 this.hmenu.add('-',
37866                     {id:"columns", text: this.columnsText, menu: this.colMenu}
37867                 );
37868             }
37869             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
37870
37871             this.grid.on("headercontextmenu", this.handleHdCtx, this);
37872         }
37873
37874         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
37875             this.dd = new Roo.grid.GridDragZone(this.grid, {
37876                 ddGroup : this.grid.ddGroup || 'GridDD'
37877             });
37878             
37879         }
37880
37881         /*
37882         for(var i = 0; i < colCount; i++){
37883             if(cm.isHidden(i)){
37884                 this.hideColumn(i);
37885             }
37886             if(cm.config[i].align){
37887                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
37888                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
37889             }
37890         }*/
37891         
37892         this.updateHeaderSortState();
37893
37894         this.beforeInitialResize();
37895         this.layout(true);
37896
37897         // two part rendering gives faster view to the user
37898         this.renderPhase2.defer(1, this);
37899     },
37900
37901     renderPhase2 : function(){
37902         // render the rows now
37903         this.refresh();
37904         if(this.grid.autoSizeColumns){
37905             this.autoSizeColumns();
37906         }
37907     },
37908
37909     beforeInitialResize : function(){
37910
37911     },
37912
37913     onColumnSplitterMoved : function(i, w){
37914         this.userResized = true;
37915         var cm = this.grid.colModel;
37916         cm.setColumnWidth(i, w, true);
37917         var cid = cm.getColumnId(i);
37918         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
37919         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
37920         this.updateSplitters();
37921         this.layout();
37922         this.grid.fireEvent("columnresize", i, w);
37923     },
37924
37925     syncRowHeights : function(startIndex, endIndex){
37926         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
37927             startIndex = startIndex || 0;
37928             var mrows = this.getBodyTable().rows;
37929             var lrows = this.getLockedTable().rows;
37930             var len = mrows.length-1;
37931             endIndex = Math.min(endIndex || len, len);
37932             for(var i = startIndex; i <= endIndex; i++){
37933                 var m = mrows[i], l = lrows[i];
37934                 var h = Math.max(m.offsetHeight, l.offsetHeight);
37935                 m.style.height = l.style.height = h + "px";
37936             }
37937         }
37938     },
37939
37940     layout : function(initialRender, is2ndPass){
37941         var g = this.grid;
37942         var auto = g.autoHeight;
37943         var scrollOffset = 16;
37944         var c = g.getGridEl(), cm = this.cm,
37945                 expandCol = g.autoExpandColumn,
37946                 gv = this;
37947         //c.beginMeasure();
37948
37949         if(!c.dom.offsetWidth){ // display:none?
37950             if(initialRender){
37951                 this.lockedWrap.show();
37952                 this.mainWrap.show();
37953             }
37954             return;
37955         }
37956
37957         var hasLock = this.cm.isLocked(0);
37958
37959         var tbh = this.headerPanel.getHeight();
37960         var bbh = this.footerPanel.getHeight();
37961
37962         if(auto){
37963             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
37964             var newHeight = ch + c.getBorderWidth("tb");
37965             if(g.maxHeight){
37966                 newHeight = Math.min(g.maxHeight, newHeight);
37967             }
37968             c.setHeight(newHeight);
37969         }
37970
37971         if(g.autoWidth){
37972             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
37973         }
37974
37975         var s = this.scroller;
37976
37977         var csize = c.getSize(true);
37978
37979         this.el.setSize(csize.width, csize.height);
37980
37981         this.headerPanel.setWidth(csize.width);
37982         this.footerPanel.setWidth(csize.width);
37983
37984         var hdHeight = this.mainHd.getHeight();
37985         var vw = csize.width;
37986         var vh = csize.height - (tbh + bbh);
37987
37988         s.setSize(vw, vh);
37989
37990         var bt = this.getBodyTable();
37991         var ltWidth = hasLock ?
37992                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
37993
37994         var scrollHeight = bt.offsetHeight;
37995         var scrollWidth = ltWidth + bt.offsetWidth;
37996         var vscroll = false, hscroll = false;
37997
37998         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
37999
38000         var lw = this.lockedWrap, mw = this.mainWrap;
38001         var lb = this.lockedBody, mb = this.mainBody;
38002
38003         setTimeout(function(){
38004             var t = s.dom.offsetTop;
38005             var w = s.dom.clientWidth,
38006                 h = s.dom.clientHeight;
38007
38008             lw.setTop(t);
38009             lw.setSize(ltWidth, h);
38010
38011             mw.setLeftTop(ltWidth, t);
38012             mw.setSize(w-ltWidth, h);
38013
38014             lb.setHeight(h-hdHeight);
38015             mb.setHeight(h-hdHeight);
38016
38017             if(is2ndPass !== true && !gv.userResized && expandCol){
38018                 // high speed resize without full column calculation
38019                 
38020                 var ci = cm.getIndexById(expandCol);
38021                 if (ci < 0) {
38022                     ci = cm.findColumnIndex(expandCol);
38023                 }
38024                 ci = Math.max(0, ci); // make sure it's got at least the first col.
38025                 var expandId = cm.getColumnId(ci);
38026                 var  tw = cm.getTotalWidth(false);
38027                 var currentWidth = cm.getColumnWidth(ci);
38028                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
38029                 if(currentWidth != cw){
38030                     cm.setColumnWidth(ci, cw, true);
38031                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
38032                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
38033                     gv.updateSplitters();
38034                     gv.layout(false, true);
38035                 }
38036             }
38037
38038             if(initialRender){
38039                 lw.show();
38040                 mw.show();
38041             }
38042             //c.endMeasure();
38043         }, 10);
38044     },
38045
38046     onWindowResize : function(){
38047         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
38048             return;
38049         }
38050         this.layout();
38051     },
38052
38053     appendFooter : function(parentEl){
38054         return null;
38055     },
38056
38057     sortAscText : "Sort Ascending",
38058     sortDescText : "Sort Descending",
38059     lockText : "Lock Column",
38060     unlockText : "Unlock Column",
38061     columnsText : "Columns"
38062 });
38063
38064
38065 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
38066     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
38067     this.proxy.el.addClass('x-grid3-col-dd');
38068 };
38069
38070 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
38071     handleMouseDown : function(e){
38072
38073     },
38074
38075     callHandleMouseDown : function(e){
38076         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
38077     }
38078 });
38079 /*
38080  * Based on:
38081  * Ext JS Library 1.1.1
38082  * Copyright(c) 2006-2007, Ext JS, LLC.
38083  *
38084  * Originally Released Under LGPL - original licence link has changed is not relivant.
38085  *
38086  * Fork - LGPL
38087  * <script type="text/javascript">
38088  */
38089  
38090 // private
38091 // This is a support class used internally by the Grid components
38092 Roo.grid.SplitDragZone = function(grid, hd, hd2){
38093     this.grid = grid;
38094     this.view = grid.getView();
38095     this.proxy = this.view.resizeProxy;
38096     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
38097         "gridSplitters" + this.grid.getGridEl().id, {
38098         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
38099     });
38100     this.setHandleElId(Roo.id(hd));
38101     this.setOuterHandleElId(Roo.id(hd2));
38102     this.scroll = false;
38103 };
38104 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
38105     fly: Roo.Element.fly,
38106
38107     b4StartDrag : function(x, y){
38108         this.view.headersDisabled = true;
38109         this.proxy.setHeight(this.view.mainWrap.getHeight());
38110         var w = this.cm.getColumnWidth(this.cellIndex);
38111         var minw = Math.max(w-this.grid.minColumnWidth, 0);
38112         this.resetConstraints();
38113         this.setXConstraint(minw, 1000);
38114         this.setYConstraint(0, 0);
38115         this.minX = x - minw;
38116         this.maxX = x + 1000;
38117         this.startPos = x;
38118         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
38119     },
38120
38121
38122     handleMouseDown : function(e){
38123         ev = Roo.EventObject.setEvent(e);
38124         var t = this.fly(ev.getTarget());
38125         if(t.hasClass("x-grid-split")){
38126             this.cellIndex = this.view.getCellIndex(t.dom);
38127             this.split = t.dom;
38128             this.cm = this.grid.colModel;
38129             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
38130                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
38131             }
38132         }
38133     },
38134
38135     endDrag : function(e){
38136         this.view.headersDisabled = false;
38137         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
38138         var diff = endX - this.startPos;
38139         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
38140     },
38141
38142     autoOffset : function(){
38143         this.setDelta(0,0);
38144     }
38145 });/*
38146  * Based on:
38147  * Ext JS Library 1.1.1
38148  * Copyright(c) 2006-2007, Ext JS, LLC.
38149  *
38150  * Originally Released Under LGPL - original licence link has changed is not relivant.
38151  *
38152  * Fork - LGPL
38153  * <script type="text/javascript">
38154  */
38155  
38156 // private
38157 // This is a support class used internally by the Grid components
38158 Roo.grid.GridDragZone = function(grid, config){
38159     this.view = grid.getView();
38160     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
38161     if(this.view.lockedBody){
38162         this.setHandleElId(Roo.id(this.view.mainBody.dom));
38163         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
38164     }
38165     this.scroll = false;
38166     this.grid = grid;
38167     this.ddel = document.createElement('div');
38168     this.ddel.className = 'x-grid-dd-wrap';
38169 };
38170
38171 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
38172     ddGroup : "GridDD",
38173
38174     getDragData : function(e){
38175         var t = Roo.lib.Event.getTarget(e);
38176         var rowIndex = this.view.findRowIndex(t);
38177         var sm = this.grid.selModel;
38178             
38179         //Roo.log(rowIndex);
38180         
38181         if (sm.getSelectedCell) {
38182             // cell selection..
38183             if (!sm.getSelectedCell()) {
38184                 return false;
38185             }
38186             if (rowIndex != sm.getSelectedCell()[0]) {
38187                 return false;
38188             }
38189         
38190         }
38191         
38192         if(rowIndex !== false){
38193             
38194             // if editorgrid.. 
38195             
38196             
38197             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
38198                
38199             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
38200               //  
38201             //}
38202             if (e.hasModifier()){
38203                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
38204             }
38205             
38206             Roo.log("getDragData");
38207             
38208             return {
38209                 grid: this.grid,
38210                 ddel: this.ddel,
38211                 rowIndex: rowIndex,
38212                 selections:sm.getSelections ? sm.getSelections() : (
38213                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
38214                 )
38215             };
38216         }
38217         return false;
38218     },
38219
38220     onInitDrag : function(e){
38221         var data = this.dragData;
38222         this.ddel.innerHTML = this.grid.getDragDropText();
38223         this.proxy.update(this.ddel);
38224         // fire start drag?
38225     },
38226
38227     afterRepair : function(){
38228         this.dragging = false;
38229     },
38230
38231     getRepairXY : function(e, data){
38232         return false;
38233     },
38234
38235     onEndDrag : function(data, e){
38236         // fire end drag?
38237     },
38238
38239     onValidDrop : function(dd, e, id){
38240         // fire drag drop?
38241         this.hideProxy();
38242     },
38243
38244     beforeInvalidDrop : function(e, id){
38245
38246     }
38247 });/*
38248  * Based on:
38249  * Ext JS Library 1.1.1
38250  * Copyright(c) 2006-2007, Ext JS, LLC.
38251  *
38252  * Originally Released Under LGPL - original licence link has changed is not relivant.
38253  *
38254  * Fork - LGPL
38255  * <script type="text/javascript">
38256  */
38257  
38258
38259 /**
38260  * @class Roo.grid.ColumnModel
38261  * @extends Roo.util.Observable
38262  * This is the default implementation of a ColumnModel used by the Grid. It defines
38263  * the columns in the grid.
38264  * <br>Usage:<br>
38265  <pre><code>
38266  var colModel = new Roo.grid.ColumnModel([
38267         {header: "Ticker", width: 60, sortable: true, locked: true},
38268         {header: "Company Name", width: 150, sortable: true},
38269         {header: "Market Cap.", width: 100, sortable: true},
38270         {header: "$ Sales", width: 100, sortable: true, renderer: money},
38271         {header: "Employees", width: 100, sortable: true, resizable: false}
38272  ]);
38273  </code></pre>
38274  * <p>
38275  
38276  * The config options listed for this class are options which may appear in each
38277  * individual column definition.
38278  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
38279  * @constructor
38280  * @param {Object} config An Array of column config objects. See this class's
38281  * config objects for details.
38282 */
38283 Roo.grid.ColumnModel = function(config){
38284         /**
38285      * The config passed into the constructor
38286      */
38287     this.config = config;
38288     this.lookup = {};
38289
38290     // if no id, create one
38291     // if the column does not have a dataIndex mapping,
38292     // map it to the order it is in the config
38293     for(var i = 0, len = config.length; i < len; i++){
38294         var c = config[i];
38295         if(typeof c.dataIndex == "undefined"){
38296             c.dataIndex = i;
38297         }
38298         if(typeof c.renderer == "string"){
38299             c.renderer = Roo.util.Format[c.renderer];
38300         }
38301         if(typeof c.id == "undefined"){
38302             c.id = Roo.id();
38303         }
38304         if(c.editor && c.editor.xtype){
38305             c.editor  = Roo.factory(c.editor, Roo.grid);
38306         }
38307         if(c.editor && c.editor.isFormField){
38308             c.editor = new Roo.grid.GridEditor(c.editor);
38309         }
38310         this.lookup[c.id] = c;
38311     }
38312
38313     /**
38314      * The width of columns which have no width specified (defaults to 100)
38315      * @type Number
38316      */
38317     this.defaultWidth = 100;
38318
38319     /**
38320      * Default sortable of columns which have no sortable specified (defaults to false)
38321      * @type Boolean
38322      */
38323     this.defaultSortable = false;
38324
38325     this.addEvents({
38326         /**
38327              * @event widthchange
38328              * Fires when the width of a column changes.
38329              * @param {ColumnModel} this
38330              * @param {Number} columnIndex The column index
38331              * @param {Number} newWidth The new width
38332              */
38333             "widthchange": true,
38334         /**
38335              * @event headerchange
38336              * Fires when the text of a header changes.
38337              * @param {ColumnModel} this
38338              * @param {Number} columnIndex The column index
38339              * @param {Number} newText The new header text
38340              */
38341             "headerchange": true,
38342         /**
38343              * @event hiddenchange
38344              * Fires when a column is hidden or "unhidden".
38345              * @param {ColumnModel} this
38346              * @param {Number} columnIndex The column index
38347              * @param {Boolean} hidden true if hidden, false otherwise
38348              */
38349             "hiddenchange": true,
38350             /**
38351          * @event columnmoved
38352          * Fires when a column is moved.
38353          * @param {ColumnModel} this
38354          * @param {Number} oldIndex
38355          * @param {Number} newIndex
38356          */
38357         "columnmoved" : true,
38358         /**
38359          * @event columlockchange
38360          * Fires when a column's locked state is changed
38361          * @param {ColumnModel} this
38362          * @param {Number} colIndex
38363          * @param {Boolean} locked true if locked
38364          */
38365         "columnlockchange" : true
38366     });
38367     Roo.grid.ColumnModel.superclass.constructor.call(this);
38368 };
38369 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
38370     /**
38371      * @cfg {String} header The header text to display in the Grid view.
38372      */
38373     /**
38374      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
38375      * {@link Roo.data.Record} definition from which to draw the column's value. If not
38376      * specified, the column's index is used as an index into the Record's data Array.
38377      */
38378     /**
38379      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
38380      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
38381      */
38382     /**
38383      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
38384      * Defaults to the value of the {@link #defaultSortable} property.
38385      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
38386      */
38387     /**
38388      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
38389      */
38390     /**
38391      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
38392      */
38393     /**
38394      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
38395      */
38396     /**
38397      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
38398      */
38399     /**
38400      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
38401      * given the cell's data value. See {@link #setRenderer}. If not specified, the
38402      * default renderer uses the raw data value.
38403      */
38404        /**
38405      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
38406      */
38407     /**
38408      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
38409      */
38410
38411     /**
38412      * Returns the id of the column at the specified index.
38413      * @param {Number} index The column index
38414      * @return {String} the id
38415      */
38416     getColumnId : function(index){
38417         return this.config[index].id;
38418     },
38419
38420     /**
38421      * Returns the column for a specified id.
38422      * @param {String} id The column id
38423      * @return {Object} the column
38424      */
38425     getColumnById : function(id){
38426         return this.lookup[id];
38427     },
38428
38429     
38430     /**
38431      * Returns the column for a specified dataIndex.
38432      * @param {String} dataIndex The column dataIndex
38433      * @return {Object|Boolean} the column or false if not found
38434      */
38435     getColumnByDataIndex: function(dataIndex){
38436         var index = this.findColumnIndex(dataIndex);
38437         return index > -1 ? this.config[index] : false;
38438     },
38439     
38440     /**
38441      * Returns the index for a specified column id.
38442      * @param {String} id The column id
38443      * @return {Number} the index, or -1 if not found
38444      */
38445     getIndexById : function(id){
38446         for(var i = 0, len = this.config.length; i < len; i++){
38447             if(this.config[i].id == id){
38448                 return i;
38449             }
38450         }
38451         return -1;
38452     },
38453     
38454     /**
38455      * Returns the index for a specified column dataIndex.
38456      * @param {String} dataIndex The column dataIndex
38457      * @return {Number} the index, or -1 if not found
38458      */
38459     
38460     findColumnIndex : function(dataIndex){
38461         for(var i = 0, len = this.config.length; i < len; i++){
38462             if(this.config[i].dataIndex == dataIndex){
38463                 return i;
38464             }
38465         }
38466         return -1;
38467     },
38468     
38469     
38470     moveColumn : function(oldIndex, newIndex){
38471         var c = this.config[oldIndex];
38472         this.config.splice(oldIndex, 1);
38473         this.config.splice(newIndex, 0, c);
38474         this.dataMap = null;
38475         this.fireEvent("columnmoved", this, oldIndex, newIndex);
38476     },
38477
38478     isLocked : function(colIndex){
38479         return this.config[colIndex].locked === true;
38480     },
38481
38482     setLocked : function(colIndex, value, suppressEvent){
38483         if(this.isLocked(colIndex) == value){
38484             return;
38485         }
38486         this.config[colIndex].locked = value;
38487         if(!suppressEvent){
38488             this.fireEvent("columnlockchange", this, colIndex, value);
38489         }
38490     },
38491
38492     getTotalLockedWidth : function(){
38493         var totalWidth = 0;
38494         for(var i = 0; i < this.config.length; i++){
38495             if(this.isLocked(i) && !this.isHidden(i)){
38496                 this.totalWidth += this.getColumnWidth(i);
38497             }
38498         }
38499         return totalWidth;
38500     },
38501
38502     getLockedCount : function(){
38503         for(var i = 0, len = this.config.length; i < len; i++){
38504             if(!this.isLocked(i)){
38505                 return i;
38506             }
38507         }
38508     },
38509
38510     /**
38511      * Returns the number of columns.
38512      * @return {Number}
38513      */
38514     getColumnCount : function(visibleOnly){
38515         if(visibleOnly === true){
38516             var c = 0;
38517             for(var i = 0, len = this.config.length; i < len; i++){
38518                 if(!this.isHidden(i)){
38519                     c++;
38520                 }
38521             }
38522             return c;
38523         }
38524         return this.config.length;
38525     },
38526
38527     /**
38528      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
38529      * @param {Function} fn
38530      * @param {Object} scope (optional)
38531      * @return {Array} result
38532      */
38533     getColumnsBy : function(fn, scope){
38534         var r = [];
38535         for(var i = 0, len = this.config.length; i < len; i++){
38536             var c = this.config[i];
38537             if(fn.call(scope||this, c, i) === true){
38538                 r[r.length] = c;
38539             }
38540         }
38541         return r;
38542     },
38543
38544     /**
38545      * Returns true if the specified column is sortable.
38546      * @param {Number} col The column index
38547      * @return {Boolean}
38548      */
38549     isSortable : function(col){
38550         if(typeof this.config[col].sortable == "undefined"){
38551             return this.defaultSortable;
38552         }
38553         return this.config[col].sortable;
38554     },
38555
38556     /**
38557      * Returns the rendering (formatting) function defined for the column.
38558      * @param {Number} col The column index.
38559      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
38560      */
38561     getRenderer : function(col){
38562         if(!this.config[col].renderer){
38563             return Roo.grid.ColumnModel.defaultRenderer;
38564         }
38565         return this.config[col].renderer;
38566     },
38567
38568     /**
38569      * Sets the rendering (formatting) function for a column.
38570      * @param {Number} col The column index
38571      * @param {Function} fn The function to use to process the cell's raw data
38572      * to return HTML markup for the grid view. The render function is called with
38573      * the following parameters:<ul>
38574      * <li>Data value.</li>
38575      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
38576      * <li>css A CSS style string to apply to the table cell.</li>
38577      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
38578      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
38579      * <li>Row index</li>
38580      * <li>Column index</li>
38581      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
38582      */
38583     setRenderer : function(col, fn){
38584         this.config[col].renderer = fn;
38585     },
38586
38587     /**
38588      * Returns the width for the specified column.
38589      * @param {Number} col The column index
38590      * @return {Number}
38591      */
38592     getColumnWidth : function(col){
38593         return this.config[col].width * 1 || this.defaultWidth;
38594     },
38595
38596     /**
38597      * Sets the width for a column.
38598      * @param {Number} col The column index
38599      * @param {Number} width The new width
38600      */
38601     setColumnWidth : function(col, width, suppressEvent){
38602         this.config[col].width = width;
38603         this.totalWidth = null;
38604         if(!suppressEvent){
38605              this.fireEvent("widthchange", this, col, width);
38606         }
38607     },
38608
38609     /**
38610      * Returns the total width of all columns.
38611      * @param {Boolean} includeHidden True to include hidden column widths
38612      * @return {Number}
38613      */
38614     getTotalWidth : function(includeHidden){
38615         if(!this.totalWidth){
38616             this.totalWidth = 0;
38617             for(var i = 0, len = this.config.length; i < len; i++){
38618                 if(includeHidden || !this.isHidden(i)){
38619                     this.totalWidth += this.getColumnWidth(i);
38620                 }
38621             }
38622         }
38623         return this.totalWidth;
38624     },
38625
38626     /**
38627      * Returns the header for the specified column.
38628      * @param {Number} col The column index
38629      * @return {String}
38630      */
38631     getColumnHeader : function(col){
38632         return this.config[col].header;
38633     },
38634
38635     /**
38636      * Sets the header for a column.
38637      * @param {Number} col The column index
38638      * @param {String} header The new header
38639      */
38640     setColumnHeader : function(col, header){
38641         this.config[col].header = header;
38642         this.fireEvent("headerchange", this, col, header);
38643     },
38644
38645     /**
38646      * Returns the tooltip for the specified column.
38647      * @param {Number} col The column index
38648      * @return {String}
38649      */
38650     getColumnTooltip : function(col){
38651             return this.config[col].tooltip;
38652     },
38653     /**
38654      * Sets the tooltip for a column.
38655      * @param {Number} col The column index
38656      * @param {String} tooltip The new tooltip
38657      */
38658     setColumnTooltip : function(col, tooltip){
38659             this.config[col].tooltip = tooltip;
38660     },
38661
38662     /**
38663      * Returns the dataIndex for the specified column.
38664      * @param {Number} col The column index
38665      * @return {Number}
38666      */
38667     getDataIndex : function(col){
38668         return this.config[col].dataIndex;
38669     },
38670
38671     /**
38672      * Sets the dataIndex for a column.
38673      * @param {Number} col The column index
38674      * @param {Number} dataIndex The new dataIndex
38675      */
38676     setDataIndex : function(col, dataIndex){
38677         this.config[col].dataIndex = dataIndex;
38678     },
38679
38680     
38681     
38682     /**
38683      * Returns true if the cell is editable.
38684      * @param {Number} colIndex The column index
38685      * @param {Number} rowIndex The row index
38686      * @return {Boolean}
38687      */
38688     isCellEditable : function(colIndex, rowIndex){
38689         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
38690     },
38691
38692     /**
38693      * Returns the editor defined for the cell/column.
38694      * return false or null to disable editing.
38695      * @param {Number} colIndex The column index
38696      * @param {Number} rowIndex The row index
38697      * @return {Object}
38698      */
38699     getCellEditor : function(colIndex, rowIndex){
38700         return this.config[colIndex].editor;
38701     },
38702
38703     /**
38704      * Sets if a column is editable.
38705      * @param {Number} col The column index
38706      * @param {Boolean} editable True if the column is editable
38707      */
38708     setEditable : function(col, editable){
38709         this.config[col].editable = editable;
38710     },
38711
38712
38713     /**
38714      * Returns true if the column is hidden.
38715      * @param {Number} colIndex The column index
38716      * @return {Boolean}
38717      */
38718     isHidden : function(colIndex){
38719         return this.config[colIndex].hidden;
38720     },
38721
38722
38723     /**
38724      * Returns true if the column width cannot be changed
38725      */
38726     isFixed : function(colIndex){
38727         return this.config[colIndex].fixed;
38728     },
38729
38730     /**
38731      * Returns true if the column can be resized
38732      * @return {Boolean}
38733      */
38734     isResizable : function(colIndex){
38735         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
38736     },
38737     /**
38738      * Sets if a column is hidden.
38739      * @param {Number} colIndex The column index
38740      * @param {Boolean} hidden True if the column is hidden
38741      */
38742     setHidden : function(colIndex, hidden){
38743         this.config[colIndex].hidden = hidden;
38744         this.totalWidth = null;
38745         this.fireEvent("hiddenchange", this, colIndex, hidden);
38746     },
38747
38748     /**
38749      * Sets the editor for a column.
38750      * @param {Number} col The column index
38751      * @param {Object} editor The editor object
38752      */
38753     setEditor : function(col, editor){
38754         this.config[col].editor = editor;
38755     }
38756 });
38757
38758 Roo.grid.ColumnModel.defaultRenderer = function(value){
38759         if(typeof value == "string" && value.length < 1){
38760             return "&#160;";
38761         }
38762         return value;
38763 };
38764
38765 // Alias for backwards compatibility
38766 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
38767 /*
38768  * Based on:
38769  * Ext JS Library 1.1.1
38770  * Copyright(c) 2006-2007, Ext JS, LLC.
38771  *
38772  * Originally Released Under LGPL - original licence link has changed is not relivant.
38773  *
38774  * Fork - LGPL
38775  * <script type="text/javascript">
38776  */
38777
38778 /**
38779  * @class Roo.grid.AbstractSelectionModel
38780  * @extends Roo.util.Observable
38781  * Abstract base class for grid SelectionModels.  It provides the interface that should be
38782  * implemented by descendant classes.  This class should not be directly instantiated.
38783  * @constructor
38784  */
38785 Roo.grid.AbstractSelectionModel = function(){
38786     this.locked = false;
38787     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
38788 };
38789
38790 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
38791     /** @ignore Called by the grid automatically. Do not call directly. */
38792     init : function(grid){
38793         this.grid = grid;
38794         this.initEvents();
38795     },
38796
38797     /**
38798      * Locks the selections.
38799      */
38800     lock : function(){
38801         this.locked = true;
38802     },
38803
38804     /**
38805      * Unlocks the selections.
38806      */
38807     unlock : function(){
38808         this.locked = false;
38809     },
38810
38811     /**
38812      * Returns true if the selections are locked.
38813      * @return {Boolean}
38814      */
38815     isLocked : function(){
38816         return this.locked;
38817     }
38818 });/*
38819  * Based on:
38820  * Ext JS Library 1.1.1
38821  * Copyright(c) 2006-2007, Ext JS, LLC.
38822  *
38823  * Originally Released Under LGPL - original licence link has changed is not relivant.
38824  *
38825  * Fork - LGPL
38826  * <script type="text/javascript">
38827  */
38828 /**
38829  * @extends Roo.grid.AbstractSelectionModel
38830  * @class Roo.grid.RowSelectionModel
38831  * The default SelectionModel used by {@link Roo.grid.Grid}.
38832  * It supports multiple selections and keyboard selection/navigation. 
38833  * @constructor
38834  * @param {Object} config
38835  */
38836 Roo.grid.RowSelectionModel = function(config){
38837     Roo.apply(this, config);
38838     this.selections = new Roo.util.MixedCollection(false, function(o){
38839         return o.id;
38840     });
38841
38842     this.last = false;
38843     this.lastActive = false;
38844
38845     this.addEvents({
38846         /**
38847              * @event selectionchange
38848              * Fires when the selection changes
38849              * @param {SelectionModel} this
38850              */
38851             "selectionchange" : true,
38852         /**
38853              * @event afterselectionchange
38854              * Fires after the selection changes (eg. by key press or clicking)
38855              * @param {SelectionModel} this
38856              */
38857             "afterselectionchange" : true,
38858         /**
38859              * @event beforerowselect
38860              * Fires when a row is selected being selected, return false to cancel.
38861              * @param {SelectionModel} this
38862              * @param {Number} rowIndex The selected index
38863              * @param {Boolean} keepExisting False if other selections will be cleared
38864              */
38865             "beforerowselect" : true,
38866         /**
38867              * @event rowselect
38868              * Fires when a row is selected.
38869              * @param {SelectionModel} this
38870              * @param {Number} rowIndex The selected index
38871              * @param {Roo.data.Record} r The record
38872              */
38873             "rowselect" : true,
38874         /**
38875              * @event rowdeselect
38876              * Fires when a row is deselected.
38877              * @param {SelectionModel} this
38878              * @param {Number} rowIndex The selected index
38879              */
38880         "rowdeselect" : true
38881     });
38882     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
38883     this.locked = false;
38884 };
38885
38886 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
38887     /**
38888      * @cfg {Boolean} singleSelect
38889      * True to allow selection of only one row at a time (defaults to false)
38890      */
38891     singleSelect : false,
38892
38893     // private
38894     initEvents : function(){
38895
38896         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
38897             this.grid.on("mousedown", this.handleMouseDown, this);
38898         }else{ // allow click to work like normal
38899             this.grid.on("rowclick", this.handleDragableRowClick, this);
38900         }
38901
38902         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
38903             "up" : function(e){
38904                 if(!e.shiftKey){
38905                     this.selectPrevious(e.shiftKey);
38906                 }else if(this.last !== false && this.lastActive !== false){
38907                     var last = this.last;
38908                     this.selectRange(this.last,  this.lastActive-1);
38909                     this.grid.getView().focusRow(this.lastActive);
38910                     if(last !== false){
38911                         this.last = last;
38912                     }
38913                 }else{
38914                     this.selectFirstRow();
38915                 }
38916                 this.fireEvent("afterselectionchange", this);
38917             },
38918             "down" : function(e){
38919                 if(!e.shiftKey){
38920                     this.selectNext(e.shiftKey);
38921                 }else if(this.last !== false && this.lastActive !== false){
38922                     var last = this.last;
38923                     this.selectRange(this.last,  this.lastActive+1);
38924                     this.grid.getView().focusRow(this.lastActive);
38925                     if(last !== false){
38926                         this.last = last;
38927                     }
38928                 }else{
38929                     this.selectFirstRow();
38930                 }
38931                 this.fireEvent("afterselectionchange", this);
38932             },
38933             scope: this
38934         });
38935
38936         var view = this.grid.view;
38937         view.on("refresh", this.onRefresh, this);
38938         view.on("rowupdated", this.onRowUpdated, this);
38939         view.on("rowremoved", this.onRemove, this);
38940     },
38941
38942     // private
38943     onRefresh : function(){
38944         var ds = this.grid.dataSource, i, v = this.grid.view;
38945         var s = this.selections;
38946         s.each(function(r){
38947             if((i = ds.indexOfId(r.id)) != -1){
38948                 v.onRowSelect(i);
38949             }else{
38950                 s.remove(r);
38951             }
38952         });
38953     },
38954
38955     // private
38956     onRemove : function(v, index, r){
38957         this.selections.remove(r);
38958     },
38959
38960     // private
38961     onRowUpdated : function(v, index, r){
38962         if(this.isSelected(r)){
38963             v.onRowSelect(index);
38964         }
38965     },
38966
38967     /**
38968      * Select records.
38969      * @param {Array} records The records to select
38970      * @param {Boolean} keepExisting (optional) True to keep existing selections
38971      */
38972     selectRecords : function(records, keepExisting){
38973         if(!keepExisting){
38974             this.clearSelections();
38975         }
38976         var ds = this.grid.dataSource;
38977         for(var i = 0, len = records.length; i < len; i++){
38978             this.selectRow(ds.indexOf(records[i]), true);
38979         }
38980     },
38981
38982     /**
38983      * Gets the number of selected rows.
38984      * @return {Number}
38985      */
38986     getCount : function(){
38987         return this.selections.length;
38988     },
38989
38990     /**
38991      * Selects the first row in the grid.
38992      */
38993     selectFirstRow : function(){
38994         this.selectRow(0);
38995     },
38996
38997     /**
38998      * Select the last row.
38999      * @param {Boolean} keepExisting (optional) True to keep existing selections
39000      */
39001     selectLastRow : function(keepExisting){
39002         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
39003     },
39004
39005     /**
39006      * Selects the row immediately following the last selected row.
39007      * @param {Boolean} keepExisting (optional) True to keep existing selections
39008      */
39009     selectNext : function(keepExisting){
39010         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
39011             this.selectRow(this.last+1, keepExisting);
39012             this.grid.getView().focusRow(this.last);
39013         }
39014     },
39015
39016     /**
39017      * Selects the row that precedes the last selected row.
39018      * @param {Boolean} keepExisting (optional) True to keep existing selections
39019      */
39020     selectPrevious : function(keepExisting){
39021         if(this.last){
39022             this.selectRow(this.last-1, keepExisting);
39023             this.grid.getView().focusRow(this.last);
39024         }
39025     },
39026
39027     /**
39028      * Returns the selected records
39029      * @return {Array} Array of selected records
39030      */
39031     getSelections : function(){
39032         return [].concat(this.selections.items);
39033     },
39034
39035     /**
39036      * Returns the first selected record.
39037      * @return {Record}
39038      */
39039     getSelected : function(){
39040         return this.selections.itemAt(0);
39041     },
39042
39043
39044     /**
39045      * Clears all selections.
39046      */
39047     clearSelections : function(fast){
39048         if(this.locked) return;
39049         if(fast !== true){
39050             var ds = this.grid.dataSource;
39051             var s = this.selections;
39052             s.each(function(r){
39053                 this.deselectRow(ds.indexOfId(r.id));
39054             }, this);
39055             s.clear();
39056         }else{
39057             this.selections.clear();
39058         }
39059         this.last = false;
39060     },
39061
39062
39063     /**
39064      * Selects all rows.
39065      */
39066     selectAll : function(){
39067         if(this.locked) return;
39068         this.selections.clear();
39069         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
39070             this.selectRow(i, true);
39071         }
39072     },
39073
39074     /**
39075      * Returns True if there is a selection.
39076      * @return {Boolean}
39077      */
39078     hasSelection : function(){
39079         return this.selections.length > 0;
39080     },
39081
39082     /**
39083      * Returns True if the specified row is selected.
39084      * @param {Number/Record} record The record or index of the record to check
39085      * @return {Boolean}
39086      */
39087     isSelected : function(index){
39088         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
39089         return (r && this.selections.key(r.id) ? true : false);
39090     },
39091
39092     /**
39093      * Returns True if the specified record id is selected.
39094      * @param {String} id The id of record to check
39095      * @return {Boolean}
39096      */
39097     isIdSelected : function(id){
39098         return (this.selections.key(id) ? true : false);
39099     },
39100
39101     // private
39102     handleMouseDown : function(e, t){
39103         var view = this.grid.getView(), rowIndex;
39104         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
39105             return;
39106         };
39107         if(e.shiftKey && this.last !== false){
39108             var last = this.last;
39109             this.selectRange(last, rowIndex, e.ctrlKey);
39110             this.last = last; // reset the last
39111             view.focusRow(rowIndex);
39112         }else{
39113             var isSelected = this.isSelected(rowIndex);
39114             if(e.button !== 0 && isSelected){
39115                 view.focusRow(rowIndex);
39116             }else if(e.ctrlKey && isSelected){
39117                 this.deselectRow(rowIndex);
39118             }else if(!isSelected){
39119                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
39120                 view.focusRow(rowIndex);
39121             }
39122         }
39123         this.fireEvent("afterselectionchange", this);
39124     },
39125     // private
39126     handleDragableRowClick :  function(grid, rowIndex, e) 
39127     {
39128         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
39129             this.selectRow(rowIndex, false);
39130             grid.view.focusRow(rowIndex);
39131              this.fireEvent("afterselectionchange", this);
39132         }
39133     },
39134     
39135     /**
39136      * Selects multiple rows.
39137      * @param {Array} rows Array of the indexes of the row to select
39138      * @param {Boolean} keepExisting (optional) True to keep existing selections
39139      */
39140     selectRows : function(rows, keepExisting){
39141         if(!keepExisting){
39142             this.clearSelections();
39143         }
39144         for(var i = 0, len = rows.length; i < len; i++){
39145             this.selectRow(rows[i], true);
39146         }
39147     },
39148
39149     /**
39150      * Selects a range of rows. All rows in between startRow and endRow are also selected.
39151      * @param {Number} startRow The index of the first row in the range
39152      * @param {Number} endRow The index of the last row in the range
39153      * @param {Boolean} keepExisting (optional) True to retain existing selections
39154      */
39155     selectRange : function(startRow, endRow, keepExisting){
39156         if(this.locked) return;
39157         if(!keepExisting){
39158             this.clearSelections();
39159         }
39160         if(startRow <= endRow){
39161             for(var i = startRow; i <= endRow; i++){
39162                 this.selectRow(i, true);
39163             }
39164         }else{
39165             for(var i = startRow; i >= endRow; i--){
39166                 this.selectRow(i, true);
39167             }
39168         }
39169     },
39170
39171     /**
39172      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
39173      * @param {Number} startRow The index of the first row in the range
39174      * @param {Number} endRow The index of the last row in the range
39175      */
39176     deselectRange : function(startRow, endRow, preventViewNotify){
39177         if(this.locked) return;
39178         for(var i = startRow; i <= endRow; i++){
39179             this.deselectRow(i, preventViewNotify);
39180         }
39181     },
39182
39183     /**
39184      * Selects a row.
39185      * @param {Number} row The index of the row to select
39186      * @param {Boolean} keepExisting (optional) True to keep existing selections
39187      */
39188     selectRow : function(index, keepExisting, preventViewNotify){
39189         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
39190         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
39191             if(!keepExisting || this.singleSelect){
39192                 this.clearSelections();
39193             }
39194             var r = this.grid.dataSource.getAt(index);
39195             this.selections.add(r);
39196             this.last = this.lastActive = index;
39197             if(!preventViewNotify){
39198                 this.grid.getView().onRowSelect(index);
39199             }
39200             this.fireEvent("rowselect", this, index, r);
39201             this.fireEvent("selectionchange", this);
39202         }
39203     },
39204
39205     /**
39206      * Deselects a row.
39207      * @param {Number} row The index of the row to deselect
39208      */
39209     deselectRow : function(index, preventViewNotify){
39210         if(this.locked) return;
39211         if(this.last == index){
39212             this.last = false;
39213         }
39214         if(this.lastActive == index){
39215             this.lastActive = false;
39216         }
39217         var r = this.grid.dataSource.getAt(index);
39218         this.selections.remove(r);
39219         if(!preventViewNotify){
39220             this.grid.getView().onRowDeselect(index);
39221         }
39222         this.fireEvent("rowdeselect", this, index);
39223         this.fireEvent("selectionchange", this);
39224     },
39225
39226     // private
39227     restoreLast : function(){
39228         if(this._last){
39229             this.last = this._last;
39230         }
39231     },
39232
39233     // private
39234     acceptsNav : function(row, col, cm){
39235         return !cm.isHidden(col) && cm.isCellEditable(col, row);
39236     },
39237
39238     // private
39239     onEditorKey : function(field, e){
39240         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
39241         if(k == e.TAB){
39242             e.stopEvent();
39243             ed.completeEdit();
39244             if(e.shiftKey){
39245                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
39246             }else{
39247                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
39248             }
39249         }else if(k == e.ENTER && !e.ctrlKey){
39250             e.stopEvent();
39251             ed.completeEdit();
39252             if(e.shiftKey){
39253                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
39254             }else{
39255                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
39256             }
39257         }else if(k == e.ESC){
39258             ed.cancelEdit();
39259         }
39260         if(newCell){
39261             g.startEditing(newCell[0], newCell[1]);
39262         }
39263     }
39264 });/*
39265  * Based on:
39266  * Ext JS Library 1.1.1
39267  * Copyright(c) 2006-2007, Ext JS, LLC.
39268  *
39269  * Originally Released Under LGPL - original licence link has changed is not relivant.
39270  *
39271  * Fork - LGPL
39272  * <script type="text/javascript">
39273  */
39274 /**
39275  * @class Roo.grid.CellSelectionModel
39276  * @extends Roo.grid.AbstractSelectionModel
39277  * This class provides the basic implementation for cell selection in a grid.
39278  * @constructor
39279  * @param {Object} config The object containing the configuration of this model.
39280  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
39281  */
39282 Roo.grid.CellSelectionModel = function(config){
39283     Roo.apply(this, config);
39284
39285     this.selection = null;
39286
39287     this.addEvents({
39288         /**
39289              * @event beforerowselect
39290              * Fires before a cell is selected.
39291              * @param {SelectionModel} this
39292              * @param {Number} rowIndex The selected row index
39293              * @param {Number} colIndex The selected cell index
39294              */
39295             "beforecellselect" : true,
39296         /**
39297              * @event cellselect
39298              * Fires when a cell is selected.
39299              * @param {SelectionModel} this
39300              * @param {Number} rowIndex The selected row index
39301              * @param {Number} colIndex The selected cell index
39302              */
39303             "cellselect" : true,
39304         /**
39305              * @event selectionchange
39306              * Fires when the active selection changes.
39307              * @param {SelectionModel} this
39308              * @param {Object} selection null for no selection or an object (o) with two properties
39309                 <ul>
39310                 <li>o.record: the record object for the row the selection is in</li>
39311                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
39312                 </ul>
39313              */
39314             "selectionchange" : true,
39315         /**
39316              * @event tabend
39317              * Fires when the tab (or enter) was pressed on the last editable cell
39318              * You can use this to trigger add new row.
39319              * @param {SelectionModel} this
39320              */
39321             "tabend" : true,
39322          /**
39323              * @event beforeeditnext
39324              * Fires before the next editable sell is made active
39325              * You can use this to skip to another cell or fire the tabend
39326              *    if you set cell to false
39327              * @param {Object} eventdata object : { cell : [ row, col ] } 
39328              */
39329             "beforeeditnext" : true
39330     });
39331     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
39332 };
39333
39334 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
39335     
39336     enter_is_tab: false,
39337
39338     /** @ignore */
39339     initEvents : function(){
39340         this.grid.on("mousedown", this.handleMouseDown, this);
39341         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
39342         var view = this.grid.view;
39343         view.on("refresh", this.onViewChange, this);
39344         view.on("rowupdated", this.onRowUpdated, this);
39345         view.on("beforerowremoved", this.clearSelections, this);
39346         view.on("beforerowsinserted", this.clearSelections, this);
39347         if(this.grid.isEditor){
39348             this.grid.on("beforeedit", this.beforeEdit,  this);
39349         }
39350     },
39351
39352         //private
39353     beforeEdit : function(e){
39354         this.select(e.row, e.column, false, true, e.record);
39355     },
39356
39357         //private
39358     onRowUpdated : function(v, index, r){
39359         if(this.selection && this.selection.record == r){
39360             v.onCellSelect(index, this.selection.cell[1]);
39361         }
39362     },
39363
39364         //private
39365     onViewChange : function(){
39366         this.clearSelections(true);
39367     },
39368
39369         /**
39370          * Returns the currently selected cell,.
39371          * @return {Array} The selected cell (row, column) or null if none selected.
39372          */
39373     getSelectedCell : function(){
39374         return this.selection ? this.selection.cell : null;
39375     },
39376
39377     /**
39378      * Clears all selections.
39379      * @param {Boolean} true to prevent the gridview from being notified about the change.
39380      */
39381     clearSelections : function(preventNotify){
39382         var s = this.selection;
39383         if(s){
39384             if(preventNotify !== true){
39385                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
39386             }
39387             this.selection = null;
39388             this.fireEvent("selectionchange", this, null);
39389         }
39390     },
39391
39392     /**
39393      * Returns true if there is a selection.
39394      * @return {Boolean}
39395      */
39396     hasSelection : function(){
39397         return this.selection ? true : false;
39398     },
39399
39400     /** @ignore */
39401     handleMouseDown : function(e, t){
39402         var v = this.grid.getView();
39403         if(this.isLocked()){
39404             return;
39405         };
39406         var row = v.findRowIndex(t);
39407         var cell = v.findCellIndex(t);
39408         if(row !== false && cell !== false){
39409             this.select(row, cell);
39410         }
39411     },
39412
39413     /**
39414      * Selects a cell.
39415      * @param {Number} rowIndex
39416      * @param {Number} collIndex
39417      */
39418     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
39419         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
39420             this.clearSelections();
39421             r = r || this.grid.dataSource.getAt(rowIndex);
39422             this.selection = {
39423                 record : r,
39424                 cell : [rowIndex, colIndex]
39425             };
39426             if(!preventViewNotify){
39427                 var v = this.grid.getView();
39428                 v.onCellSelect(rowIndex, colIndex);
39429                 if(preventFocus !== true){
39430                     v.focusCell(rowIndex, colIndex);
39431                 }
39432             }
39433             this.fireEvent("cellselect", this, rowIndex, colIndex);
39434             this.fireEvent("selectionchange", this, this.selection);
39435         }
39436     },
39437
39438         //private
39439     isSelectable : function(rowIndex, colIndex, cm){
39440         return !cm.isHidden(colIndex);
39441     },
39442
39443     /** @ignore */
39444     handleKeyDown : function(e){
39445         //Roo.log('Cell Sel Model handleKeyDown');
39446         if(!e.isNavKeyPress()){
39447             return;
39448         }
39449         var g = this.grid, s = this.selection;
39450         if(!s){
39451             e.stopEvent();
39452             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
39453             if(cell){
39454                 this.select(cell[0], cell[1]);
39455             }
39456             return;
39457         }
39458         var sm = this;
39459         var walk = function(row, col, step){
39460             return g.walkCells(row, col, step, sm.isSelectable,  sm);
39461         };
39462         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
39463         var newCell;
39464
39465       
39466
39467         switch(k){
39468             case e.TAB:
39469                 // handled by onEditorKey
39470                 if (g.isEditor && g.editing) {
39471                     return;
39472                 }
39473                 if(e.shiftKey) {
39474                     newCell = walk(r, c-1, -1);
39475                 } else {
39476                     newCell = walk(r, c+1, 1);
39477                 }
39478                 break;
39479             
39480             case e.DOWN:
39481                newCell = walk(r+1, c, 1);
39482                 break;
39483             
39484             case e.UP:
39485                 newCell = walk(r-1, c, -1);
39486                 break;
39487             
39488             case e.RIGHT:
39489                 newCell = walk(r, c+1, 1);
39490                 break;
39491             
39492             case e.LEFT:
39493                 newCell = walk(r, c-1, -1);
39494                 break;
39495             
39496             case e.ENTER:
39497                 
39498                 if(g.isEditor && !g.editing){
39499                    g.startEditing(r, c);
39500                    e.stopEvent();
39501                    return;
39502                 }
39503                 
39504                 
39505              break;
39506         };
39507         if(newCell){
39508             this.select(newCell[0], newCell[1]);
39509             e.stopEvent();
39510             
39511         }
39512     },
39513
39514     acceptsNav : function(row, col, cm){
39515         return !cm.isHidden(col) && cm.isCellEditable(col, row);
39516     },
39517     /**
39518      * Selects a cell.
39519      * @param {Number} field (not used) - as it's normally used as a listener
39520      * @param {Number} e - event - fake it by using
39521      *
39522      * var e = Roo.EventObjectImpl.prototype;
39523      * e.keyCode = e.TAB
39524      *
39525      * 
39526      */
39527     onEditorKey : function(field, e){
39528         
39529         var k = e.getKey(),
39530             newCell,
39531             g = this.grid,
39532             ed = g.activeEditor,
39533             forward = false;
39534         ///Roo.log('onEditorKey' + k);
39535         
39536         
39537         if (this.enter_is_tab && k == e.ENTER) {
39538             k = e.TAB;
39539         }
39540         
39541         if(k == e.TAB){
39542             if(e.shiftKey){
39543                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
39544             }else{
39545                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
39546                 forward = true;
39547             }
39548             
39549             e.stopEvent();
39550             
39551         } else if(k == e.ENTER &&  !e.ctrlKey){
39552             ed.completeEdit();
39553             e.stopEvent();
39554             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
39555         
39556                 } else if(k == e.ESC){
39557             ed.cancelEdit();
39558         }
39559                 
39560         if (newCell) {
39561             var ecall = { cell : newCell, forward : forward };
39562             this.fireEvent('beforeeditnext', ecall );
39563             newCell = ecall.cell;
39564                         forward = ecall.forward;
39565         }
39566                 
39567         if(newCell){
39568             //Roo.log('next cell after edit');
39569             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
39570         } else if (forward) {
39571             // tabbed past last
39572             this.fireEvent.defer(100, this, ['tabend',this]);
39573         }
39574     }
39575 });/*
39576  * Based on:
39577  * Ext JS Library 1.1.1
39578  * Copyright(c) 2006-2007, Ext JS, LLC.
39579  *
39580  * Originally Released Under LGPL - original licence link has changed is not relivant.
39581  *
39582  * Fork - LGPL
39583  * <script type="text/javascript">
39584  */
39585  
39586 /**
39587  * @class Roo.grid.EditorGrid
39588  * @extends Roo.grid.Grid
39589  * Class for creating and editable grid.
39590  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
39591  * The container MUST have some type of size defined for the grid to fill. The container will be 
39592  * automatically set to position relative if it isn't already.
39593  * @param {Object} dataSource The data model to bind to
39594  * @param {Object} colModel The column model with info about this grid's columns
39595  */
39596 Roo.grid.EditorGrid = function(container, config){
39597     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
39598     this.getGridEl().addClass("xedit-grid");
39599
39600     if(!this.selModel){
39601         this.selModel = new Roo.grid.CellSelectionModel();
39602     }
39603
39604     this.activeEditor = null;
39605
39606         this.addEvents({
39607             /**
39608              * @event beforeedit
39609              * Fires before cell editing is triggered. The edit event object has the following properties <br />
39610              * <ul style="padding:5px;padding-left:16px;">
39611              * <li>grid - This grid</li>
39612              * <li>record - The record being edited</li>
39613              * <li>field - The field name being edited</li>
39614              * <li>value - The value for the field being edited.</li>
39615              * <li>row - The grid row index</li>
39616              * <li>column - The grid column index</li>
39617              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
39618              * </ul>
39619              * @param {Object} e An edit event (see above for description)
39620              */
39621             "beforeedit" : true,
39622             /**
39623              * @event afteredit
39624              * Fires after a cell is edited. <br />
39625              * <ul style="padding:5px;padding-left:16px;">
39626              * <li>grid - This grid</li>
39627              * <li>record - The record being edited</li>
39628              * <li>field - The field name being edited</li>
39629              * <li>value - The value being set</li>
39630              * <li>originalValue - The original value for the field, before the edit.</li>
39631              * <li>row - The grid row index</li>
39632              * <li>column - The grid column index</li>
39633              * </ul>
39634              * @param {Object} e An edit event (see above for description)
39635              */
39636             "afteredit" : true,
39637             /**
39638              * @event validateedit
39639              * Fires after a cell is edited, but before the value is set in the record. 
39640          * You can use this to modify the value being set in the field, Return false
39641              * to cancel the change. The edit event object has the following properties <br />
39642              * <ul style="padding:5px;padding-left:16px;">
39643          * <li>editor - This editor</li>
39644              * <li>grid - This grid</li>
39645              * <li>record - The record being edited</li>
39646              * <li>field - The field name being edited</li>
39647              * <li>value - The value being set</li>
39648              * <li>originalValue - The original value for the field, before the edit.</li>
39649              * <li>row - The grid row index</li>
39650              * <li>column - The grid column index</li>
39651              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
39652              * </ul>
39653              * @param {Object} e An edit event (see above for description)
39654              */
39655             "validateedit" : true
39656         });
39657     this.on("bodyscroll", this.stopEditing,  this);
39658     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
39659 };
39660
39661 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
39662     /**
39663      * @cfg {Number} clicksToEdit
39664      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
39665      */
39666     clicksToEdit: 2,
39667
39668     // private
39669     isEditor : true,
39670     // private
39671     trackMouseOver: false, // causes very odd FF errors
39672
39673     onCellDblClick : function(g, row, col){
39674         this.startEditing(row, col);
39675     },
39676
39677     onEditComplete : function(ed, value, startValue){
39678         this.editing = false;
39679         this.activeEditor = null;
39680         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
39681         var r = ed.record;
39682         var field = this.colModel.getDataIndex(ed.col);
39683         var e = {
39684             grid: this,
39685             record: r,
39686             field: field,
39687             originalValue: startValue,
39688             value: value,
39689             row: ed.row,
39690             column: ed.col,
39691             cancel:false,
39692             editor: ed
39693         };
39694         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
39695         cell.show();
39696           
39697         if(String(value) !== String(startValue)){
39698             
39699             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
39700                 r.set(field, e.value);
39701                 // if we are dealing with a combo box..
39702                 // then we also set the 'name' colum to be the displayField
39703                 if (ed.field.displayField && ed.field.name) {
39704                     r.set(ed.field.name, ed.field.el.dom.value);
39705                 }
39706                 
39707                 delete e.cancel; //?? why!!!
39708                 this.fireEvent("afteredit", e);
39709             }
39710         } else {
39711             this.fireEvent("afteredit", e); // always fire it!
39712         }
39713         this.view.focusCell(ed.row, ed.col);
39714     },
39715
39716     /**
39717      * Starts editing the specified for the specified row/column
39718      * @param {Number} rowIndex
39719      * @param {Number} colIndex
39720      */
39721     startEditing : function(row, col){
39722         this.stopEditing();
39723         if(this.colModel.isCellEditable(col, row)){
39724             this.view.ensureVisible(row, col, true);
39725           
39726             var r = this.dataSource.getAt(row);
39727             var field = this.colModel.getDataIndex(col);
39728             var cell = Roo.get(this.view.getCell(row,col));
39729             var e = {
39730                 grid: this,
39731                 record: r,
39732                 field: field,
39733                 value: r.data[field],
39734                 row: row,
39735                 column: col,
39736                 cancel:false 
39737             };
39738             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
39739                 this.editing = true;
39740                 var ed = this.colModel.getCellEditor(col, row);
39741                 
39742                 if (!ed) {
39743                     return;
39744                 }
39745                 if(!ed.rendered){
39746                     ed.render(ed.parentEl || document.body);
39747                 }
39748                 ed.field.reset();
39749                
39750                 cell.hide();
39751                 
39752                 (function(){ // complex but required for focus issues in safari, ie and opera
39753                     ed.row = row;
39754                     ed.col = col;
39755                     ed.record = r;
39756                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
39757                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
39758                     this.activeEditor = ed;
39759                     var v = r.data[field];
39760                     ed.startEdit(this.view.getCell(row, col), v);
39761                     // combo's with 'displayField and name set
39762                     if (ed.field.displayField && ed.field.name) {
39763                         ed.field.el.dom.value = r.data[ed.field.name];
39764                     }
39765                     
39766                     
39767                 }).defer(50, this);
39768             }
39769         }
39770     },
39771         
39772     /**
39773      * Stops any active editing
39774      */
39775     stopEditing : function(){
39776         if(this.activeEditor){
39777             this.activeEditor.completeEdit();
39778         }
39779         this.activeEditor = null;
39780     },
39781         
39782          /**
39783      * Called to get grid's drag proxy text, by default returns this.ddText.
39784      * @return {String}
39785      */
39786     getDragDropText : function(){
39787         var count = this.selModel.getSelectedCell() ? 1 : 0;
39788         return String.format(this.ddText, count, count == 1 ? '' : 's');
39789     }
39790         
39791 });/*
39792  * Based on:
39793  * Ext JS Library 1.1.1
39794  * Copyright(c) 2006-2007, Ext JS, LLC.
39795  *
39796  * Originally Released Under LGPL - original licence link has changed is not relivant.
39797  *
39798  * Fork - LGPL
39799  * <script type="text/javascript">
39800  */
39801
39802 // private - not really -- you end up using it !
39803 // This is a support class used internally by the Grid components
39804
39805 /**
39806  * @class Roo.grid.GridEditor
39807  * @extends Roo.Editor
39808  * Class for creating and editable grid elements.
39809  * @param {Object} config any settings (must include field)
39810  */
39811 Roo.grid.GridEditor = function(field, config){
39812     if (!config && field.field) {
39813         config = field;
39814         field = Roo.factory(config.field, Roo.form);
39815     }
39816     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
39817     field.monitorTab = false;
39818 };
39819
39820 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
39821     
39822     /**
39823      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
39824      */
39825     
39826     alignment: "tl-tl",
39827     autoSize: "width",
39828     hideEl : false,
39829     cls: "x-small-editor x-grid-editor",
39830     shim:false,
39831     shadow:"frame"
39832 });/*
39833  * Based on:
39834  * Ext JS Library 1.1.1
39835  * Copyright(c) 2006-2007, Ext JS, LLC.
39836  *
39837  * Originally Released Under LGPL - original licence link has changed is not relivant.
39838  *
39839  * Fork - LGPL
39840  * <script type="text/javascript">
39841  */
39842   
39843
39844   
39845 Roo.grid.PropertyRecord = Roo.data.Record.create([
39846     {name:'name',type:'string'},  'value'
39847 ]);
39848
39849
39850 Roo.grid.PropertyStore = function(grid, source){
39851     this.grid = grid;
39852     this.store = new Roo.data.Store({
39853         recordType : Roo.grid.PropertyRecord
39854     });
39855     this.store.on('update', this.onUpdate,  this);
39856     if(source){
39857         this.setSource(source);
39858     }
39859     Roo.grid.PropertyStore.superclass.constructor.call(this);
39860 };
39861
39862
39863
39864 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
39865     setSource : function(o){
39866         this.source = o;
39867         this.store.removeAll();
39868         var data = [];
39869         for(var k in o){
39870             if(this.isEditableValue(o[k])){
39871                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
39872             }
39873         }
39874         this.store.loadRecords({records: data}, {}, true);
39875     },
39876
39877     onUpdate : function(ds, record, type){
39878         if(type == Roo.data.Record.EDIT){
39879             var v = record.data['value'];
39880             var oldValue = record.modified['value'];
39881             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
39882                 this.source[record.id] = v;
39883                 record.commit();
39884                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
39885             }else{
39886                 record.reject();
39887             }
39888         }
39889     },
39890
39891     getProperty : function(row){
39892        return this.store.getAt(row);
39893     },
39894
39895     isEditableValue: function(val){
39896         if(val && val instanceof Date){
39897             return true;
39898         }else if(typeof val == 'object' || typeof val == 'function'){
39899             return false;
39900         }
39901         return true;
39902     },
39903
39904     setValue : function(prop, value){
39905         this.source[prop] = value;
39906         this.store.getById(prop).set('value', value);
39907     },
39908
39909     getSource : function(){
39910         return this.source;
39911     }
39912 });
39913
39914 Roo.grid.PropertyColumnModel = function(grid, store){
39915     this.grid = grid;
39916     var g = Roo.grid;
39917     g.PropertyColumnModel.superclass.constructor.call(this, [
39918         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
39919         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
39920     ]);
39921     this.store = store;
39922     this.bselect = Roo.DomHelper.append(document.body, {
39923         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
39924             {tag: 'option', value: 'true', html: 'true'},
39925             {tag: 'option', value: 'false', html: 'false'}
39926         ]
39927     });
39928     Roo.id(this.bselect);
39929     var f = Roo.form;
39930     this.editors = {
39931         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
39932         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
39933         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
39934         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
39935         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
39936     };
39937     this.renderCellDelegate = this.renderCell.createDelegate(this);
39938     this.renderPropDelegate = this.renderProp.createDelegate(this);
39939 };
39940
39941 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
39942     
39943     
39944     nameText : 'Name',
39945     valueText : 'Value',
39946     
39947     dateFormat : 'm/j/Y',
39948     
39949     
39950     renderDate : function(dateVal){
39951         return dateVal.dateFormat(this.dateFormat);
39952     },
39953
39954     renderBool : function(bVal){
39955         return bVal ? 'true' : 'false';
39956     },
39957
39958     isCellEditable : function(colIndex, rowIndex){
39959         return colIndex == 1;
39960     },
39961
39962     getRenderer : function(col){
39963         return col == 1 ?
39964             this.renderCellDelegate : this.renderPropDelegate;
39965     },
39966
39967     renderProp : function(v){
39968         return this.getPropertyName(v);
39969     },
39970
39971     renderCell : function(val){
39972         var rv = val;
39973         if(val instanceof Date){
39974             rv = this.renderDate(val);
39975         }else if(typeof val == 'boolean'){
39976             rv = this.renderBool(val);
39977         }
39978         return Roo.util.Format.htmlEncode(rv);
39979     },
39980
39981     getPropertyName : function(name){
39982         var pn = this.grid.propertyNames;
39983         return pn && pn[name] ? pn[name] : name;
39984     },
39985
39986     getCellEditor : function(colIndex, rowIndex){
39987         var p = this.store.getProperty(rowIndex);
39988         var n = p.data['name'], val = p.data['value'];
39989         
39990         if(typeof(this.grid.customEditors[n]) == 'string'){
39991             return this.editors[this.grid.customEditors[n]];
39992         }
39993         if(typeof(this.grid.customEditors[n]) != 'undefined'){
39994             return this.grid.customEditors[n];
39995         }
39996         if(val instanceof Date){
39997             return this.editors['date'];
39998         }else if(typeof val == 'number'){
39999             return this.editors['number'];
40000         }else if(typeof val == 'boolean'){
40001             return this.editors['boolean'];
40002         }else{
40003             return this.editors['string'];
40004         }
40005     }
40006 });
40007
40008 /**
40009  * @class Roo.grid.PropertyGrid
40010  * @extends Roo.grid.EditorGrid
40011  * This class represents the  interface of a component based property grid control.
40012  * <br><br>Usage:<pre><code>
40013  var grid = new Roo.grid.PropertyGrid("my-container-id", {
40014       
40015  });
40016  // set any options
40017  grid.render();
40018  * </code></pre>
40019   
40020  * @constructor
40021  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
40022  * The container MUST have some type of size defined for the grid to fill. The container will be
40023  * automatically set to position relative if it isn't already.
40024  * @param {Object} config A config object that sets properties on this grid.
40025  */
40026 Roo.grid.PropertyGrid = function(container, config){
40027     config = config || {};
40028     var store = new Roo.grid.PropertyStore(this);
40029     this.store = store;
40030     var cm = new Roo.grid.PropertyColumnModel(this, store);
40031     store.store.sort('name', 'ASC');
40032     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
40033         ds: store.store,
40034         cm: cm,
40035         enableColLock:false,
40036         enableColumnMove:false,
40037         stripeRows:false,
40038         trackMouseOver: false,
40039         clicksToEdit:1
40040     }, config));
40041     this.getGridEl().addClass('x-props-grid');
40042     this.lastEditRow = null;
40043     this.on('columnresize', this.onColumnResize, this);
40044     this.addEvents({
40045          /**
40046              * @event beforepropertychange
40047              * Fires before a property changes (return false to stop?)
40048              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
40049              * @param {String} id Record Id
40050              * @param {String} newval New Value
40051          * @param {String} oldval Old Value
40052              */
40053         "beforepropertychange": true,
40054         /**
40055              * @event propertychange
40056              * Fires after a property changes
40057              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
40058              * @param {String} id Record Id
40059              * @param {String} newval New Value
40060          * @param {String} oldval Old Value
40061              */
40062         "propertychange": true
40063     });
40064     this.customEditors = this.customEditors || {};
40065 };
40066 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
40067     
40068      /**
40069      * @cfg {Object} customEditors map of colnames=> custom editors.
40070      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
40071      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
40072      * false disables editing of the field.
40073          */
40074     
40075       /**
40076      * @cfg {Object} propertyNames map of property Names to their displayed value
40077          */
40078     
40079     render : function(){
40080         Roo.grid.PropertyGrid.superclass.render.call(this);
40081         this.autoSize.defer(100, this);
40082     },
40083
40084     autoSize : function(){
40085         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
40086         if(this.view){
40087             this.view.fitColumns();
40088         }
40089     },
40090
40091     onColumnResize : function(){
40092         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
40093         this.autoSize();
40094     },
40095     /**
40096      * Sets the data for the Grid
40097      * accepts a Key => Value object of all the elements avaiable.
40098      * @param {Object} data  to appear in grid.
40099      */
40100     setSource : function(source){
40101         this.store.setSource(source);
40102         //this.autoSize();
40103     },
40104     /**
40105      * Gets all the data from the grid.
40106      * @return {Object} data  data stored in grid
40107      */
40108     getSource : function(){
40109         return this.store.getSource();
40110     }
40111 });/*
40112  * Based on:
40113  * Ext JS Library 1.1.1
40114  * Copyright(c) 2006-2007, Ext JS, LLC.
40115  *
40116  * Originally Released Under LGPL - original licence link has changed is not relivant.
40117  *
40118  * Fork - LGPL
40119  * <script type="text/javascript">
40120  */
40121  
40122 /**
40123  * @class Roo.LoadMask
40124  * A simple utility class for generically masking elements while loading data.  If the element being masked has
40125  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
40126  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
40127  * element's UpdateManager load indicator and will be destroyed after the initial load.
40128  * @constructor
40129  * Create a new LoadMask
40130  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
40131  * @param {Object} config The config object
40132  */
40133 Roo.LoadMask = function(el, config){
40134     this.el = Roo.get(el);
40135     Roo.apply(this, config);
40136     if(this.store){
40137         this.store.on('beforeload', this.onBeforeLoad, this);
40138         this.store.on('load', this.onLoad, this);
40139         this.store.on('loadexception', this.onLoadException, this);
40140         this.removeMask = false;
40141     }else{
40142         var um = this.el.getUpdateManager();
40143         um.showLoadIndicator = false; // disable the default indicator
40144         um.on('beforeupdate', this.onBeforeLoad, this);
40145         um.on('update', this.onLoad, this);
40146         um.on('failure', this.onLoad, this);
40147         this.removeMask = true;
40148     }
40149 };
40150
40151 Roo.LoadMask.prototype = {
40152     /**
40153      * @cfg {Boolean} removeMask
40154      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
40155      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
40156      */
40157     /**
40158      * @cfg {String} msg
40159      * The text to display in a centered loading message box (defaults to 'Loading...')
40160      */
40161     msg : 'Loading...',
40162     /**
40163      * @cfg {String} msgCls
40164      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
40165      */
40166     msgCls : 'x-mask-loading',
40167
40168     /**
40169      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
40170      * @type Boolean
40171      */
40172     disabled: false,
40173
40174     /**
40175      * Disables the mask to prevent it from being displayed
40176      */
40177     disable : function(){
40178        this.disabled = true;
40179     },
40180
40181     /**
40182      * Enables the mask so that it can be displayed
40183      */
40184     enable : function(){
40185         this.disabled = false;
40186     },
40187     
40188     onLoadException : function()
40189     {
40190         Roo.log(arguments);
40191         
40192         if (typeof(arguments[3]) != 'undefined') {
40193             Roo.MessageBox.alert("Error loading",arguments[3]);
40194         } 
40195         /*
40196         try {
40197             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
40198                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
40199             }   
40200         } catch(e) {
40201             
40202         }
40203         */
40204     
40205         
40206         
40207         this.el.unmask(this.removeMask);
40208     },
40209     // private
40210     onLoad : function()
40211     {
40212         this.el.unmask(this.removeMask);
40213     },
40214
40215     // private
40216     onBeforeLoad : function(){
40217         if(!this.disabled){
40218             this.el.mask(this.msg, this.msgCls);
40219         }
40220     },
40221
40222     // private
40223     destroy : function(){
40224         if(this.store){
40225             this.store.un('beforeload', this.onBeforeLoad, this);
40226             this.store.un('load', this.onLoad, this);
40227             this.store.un('loadexception', this.onLoadException, this);
40228         }else{
40229             var um = this.el.getUpdateManager();
40230             um.un('beforeupdate', this.onBeforeLoad, this);
40231             um.un('update', this.onLoad, this);
40232             um.un('failure', this.onLoad, this);
40233         }
40234     }
40235 };/*
40236  * Based on:
40237  * Ext JS Library 1.1.1
40238  * Copyright(c) 2006-2007, Ext JS, LLC.
40239  *
40240  * Originally Released Under LGPL - original licence link has changed is not relivant.
40241  *
40242  * Fork - LGPL
40243  * <script type="text/javascript">
40244  */
40245
40246
40247 /**
40248  * @class Roo.XTemplate
40249  * @extends Roo.Template
40250  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
40251 <pre><code>
40252 var t = new Roo.XTemplate(
40253         '&lt;select name="{name}"&gt;',
40254                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
40255         '&lt;/select&gt;'
40256 );
40257  
40258 // then append, applying the master template values
40259  </code></pre>
40260  *
40261  * Supported features:
40262  *
40263  *  Tags:
40264
40265 <pre><code>
40266       {a_variable} - output encoded.
40267       {a_variable.format:("Y-m-d")} - call a method on the variable
40268       {a_variable:raw} - unencoded output
40269       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
40270       {a_variable:this.method_on_template(...)} - call a method on the template object.
40271  
40272 </code></pre>
40273  *  The tpl tag:
40274 <pre><code>
40275         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
40276         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
40277         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
40278         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
40279   
40280         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
40281         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
40282 </code></pre>
40283  *      
40284  */
40285 Roo.XTemplate = function()
40286 {
40287     Roo.XTemplate.superclass.constructor.apply(this, arguments);
40288     if (this.html) {
40289         this.compile();
40290     }
40291 };
40292
40293
40294 Roo.extend(Roo.XTemplate, Roo.Template, {
40295
40296     /**
40297      * The various sub templates
40298      */
40299     tpls : false,
40300     /**
40301      *
40302      * basic tag replacing syntax
40303      * WORD:WORD()
40304      *
40305      * // you can fake an object call by doing this
40306      *  x.t:(test,tesT) 
40307      * 
40308      */
40309     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
40310
40311     /**
40312      * compile the template
40313      *
40314      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
40315      *
40316      */
40317     compile: function()
40318     {
40319         var s = this.html;
40320      
40321         s = ['<tpl>', s, '</tpl>'].join('');
40322     
40323         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
40324             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
40325             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
40326             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
40327             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
40328             m,
40329             id     = 0,
40330             tpls   = [];
40331     
40332         while(true == !!(m = s.match(re))){
40333             var forMatch   = m[0].match(nameRe),
40334                 ifMatch   = m[0].match(ifRe),
40335                 execMatch   = m[0].match(execRe),
40336                 namedMatch   = m[0].match(namedRe),
40337                 
40338                 exp  = null, 
40339                 fn   = null,
40340                 exec = null,
40341                 name = forMatch && forMatch[1] ? forMatch[1] : '';
40342                 
40343             if (ifMatch) {
40344                 // if - puts fn into test..
40345                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
40346                 if(exp){
40347                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
40348                 }
40349             }
40350             
40351             if (execMatch) {
40352                 // exec - calls a function... returns empty if true is  returned.
40353                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
40354                 if(exp){
40355                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
40356                 }
40357             }
40358             
40359             
40360             if (name) {
40361                 // for = 
40362                 switch(name){
40363                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
40364                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
40365                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
40366                 }
40367             }
40368             var uid = namedMatch ? namedMatch[1] : id;
40369             
40370             
40371             tpls.push({
40372                 id:     namedMatch ? namedMatch[1] : id,
40373                 target: name,
40374                 exec:   exec,
40375                 test:   fn,
40376                 body:   m[1] || ''
40377             });
40378             if (namedMatch) {
40379                 s = s.replace(m[0], '');
40380             } else { 
40381                 s = s.replace(m[0], '{xtpl'+ id + '}');
40382             }
40383             ++id;
40384         }
40385         this.tpls = [];
40386         for(var i = tpls.length-1; i >= 0; --i){
40387             this.compileTpl(tpls[i]);
40388             this.tpls[tpls[i].id] = tpls[i];
40389         }
40390         this.master = tpls[tpls.length-1];
40391         return this;
40392     },
40393     /**
40394      * same as applyTemplate, except it's done to one of the subTemplates
40395      * when using named templates, you can do:
40396      *
40397      * var str = pl.applySubTemplate('your-name', values);
40398      *
40399      * 
40400      * @param {Number} id of the template
40401      * @param {Object} values to apply to template
40402      * @param {Object} parent (normaly the instance of this object)
40403      */
40404     applySubTemplate : function(id, values, parent)
40405     {
40406         
40407         
40408         var t = this.tpls[id];
40409         
40410         
40411         try { 
40412             if(t.test && !t.test.call(this, values, parent)){
40413                 return '';
40414             }
40415         } catch(e) {
40416             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
40417             Roo.log(e.toString());
40418             Roo.log(t.test);
40419             return ''
40420         }
40421         try { 
40422             
40423             if(t.exec && t.exec.call(this, values, parent)){
40424                 return '';
40425             }
40426         } catch(e) {
40427             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
40428             Roo.log(e.toString());
40429             Roo.log(t.exec);
40430             return ''
40431         }
40432         try {
40433             var vs = t.target ? t.target.call(this, values, parent) : values;
40434             parent = t.target ? values : parent;
40435             if(t.target && vs instanceof Array){
40436                 var buf = [];
40437                 for(var i = 0, len = vs.length; i < len; i++){
40438                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
40439                 }
40440                 return buf.join('');
40441             }
40442             return t.compiled.call(this, vs, parent);
40443         } catch (e) {
40444             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
40445             Roo.log(e.toString());
40446             Roo.log(t.compiled);
40447             return '';
40448         }
40449     },
40450
40451     compileTpl : function(tpl)
40452     {
40453         var fm = Roo.util.Format;
40454         var useF = this.disableFormats !== true;
40455         var sep = Roo.isGecko ? "+" : ",";
40456         var undef = function(str) {
40457             Roo.log("Property not found :"  + str);
40458             return '';
40459         };
40460         
40461         var fn = function(m, name, format, args)
40462         {
40463             //Roo.log(arguments);
40464             args = args ? args.replace(/\\'/g,"'") : args;
40465             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
40466             if (typeof(format) == 'undefined') {
40467                 format= 'htmlEncode';
40468             }
40469             if (format == 'raw' ) {
40470                 format = false;
40471             }
40472             
40473             if(name.substr(0, 4) == 'xtpl'){
40474                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
40475             }
40476             
40477             // build an array of options to determine if value is undefined..
40478             
40479             // basically get 'xxxx.yyyy' then do
40480             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
40481             //    (function () { Roo.log("Property not found"); return ''; })() :
40482             //    ......
40483             
40484             var udef_ar = [];
40485             var lookfor = '';
40486             Roo.each(name.split('.'), function(st) {
40487                 lookfor += (lookfor.length ? '.': '') + st;
40488                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
40489             });
40490             
40491             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
40492             
40493             
40494             if(format && useF){
40495                 
40496                 args = args ? ',' + args : "";
40497                  
40498                 if(format.substr(0, 5) != "this."){
40499                     format = "fm." + format + '(';
40500                 }else{
40501                     format = 'this.call("'+ format.substr(5) + '", ';
40502                     args = ", values";
40503                 }
40504                 
40505                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
40506             }
40507              
40508             if (args.length) {
40509                 // called with xxyx.yuu:(test,test)
40510                 // change to ()
40511                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
40512             }
40513             // raw.. - :raw modifier..
40514             return "'"+ sep + udef_st  + name + ")"+sep+"'";
40515             
40516         };
40517         var body;
40518         // branched to use + in gecko and [].join() in others
40519         if(Roo.isGecko){
40520             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
40521                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
40522                     "';};};";
40523         }else{
40524             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
40525             body.push(tpl.body.replace(/(\r\n|\n)/g,
40526                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
40527             body.push("'].join('');};};");
40528             body = body.join('');
40529         }
40530         
40531         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
40532        
40533         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
40534         eval(body);
40535         
40536         return this;
40537     },
40538
40539     applyTemplate : function(values){
40540         return this.master.compiled.call(this, values, {});
40541         //var s = this.subs;
40542     },
40543
40544     apply : function(){
40545         return this.applyTemplate.apply(this, arguments);
40546     }
40547
40548  });
40549
40550 Roo.XTemplate.from = function(el){
40551     el = Roo.getDom(el);
40552     return new Roo.XTemplate(el.value || el.innerHTML);
40553 };/*
40554  * Original code for Roojs - LGPL
40555  * <script type="text/javascript">
40556  */
40557  
40558 /**
40559  * @class Roo.XComponent
40560  * A delayed Element creator...
40561  * Or a way to group chunks of interface together.
40562  * 
40563  * Mypart.xyx = new Roo.XComponent({
40564
40565     parent : 'Mypart.xyz', // empty == document.element.!!
40566     order : '001',
40567     name : 'xxxx'
40568     region : 'xxxx'
40569     disabled : function() {} 
40570      
40571     tree : function() { // return an tree of xtype declared components
40572         var MODULE = this;
40573         return 
40574         {
40575             xtype : 'NestedLayoutPanel',
40576             // technicall
40577         }
40578      ]
40579  *})
40580  *
40581  *
40582  * It can be used to build a big heiracy, with parent etc.
40583  * or you can just use this to render a single compoent to a dom element
40584  * MYPART.render(Roo.Element | String(id) | dom_element )
40585  * 
40586  * @extends Roo.util.Observable
40587  * @constructor
40588  * @param cfg {Object} configuration of component
40589  * 
40590  */
40591 Roo.XComponent = function(cfg) {
40592     Roo.apply(this, cfg);
40593     this.addEvents({ 
40594         /**
40595              * @event built
40596              * Fires when this the componnt is built
40597              * @param {Roo.XComponent} c the component
40598              */
40599         'built' : true
40600         
40601     });
40602     this.region = this.region || 'center'; // default..
40603     Roo.XComponent.register(this);
40604     this.modules = false;
40605     this.el = false; // where the layout goes..
40606     
40607     
40608 }
40609 Roo.extend(Roo.XComponent, Roo.util.Observable, {
40610     /**
40611      * @property el
40612      * The created element (with Roo.factory())
40613      * @type {Roo.Layout}
40614      */
40615     el  : false,
40616     
40617     /**
40618      * @property el
40619      * for BC  - use el in new code
40620      * @type {Roo.Layout}
40621      */
40622     panel : false,
40623     
40624     /**
40625      * @property layout
40626      * for BC  - use el in new code
40627      * @type {Roo.Layout}
40628      */
40629     layout : false,
40630     
40631      /**
40632      * @cfg {Function|boolean} disabled
40633      * If this module is disabled by some rule, return true from the funtion
40634      */
40635     disabled : false,
40636     
40637     /**
40638      * @cfg {String} parent 
40639      * Name of parent element which it get xtype added to..
40640      */
40641     parent: false,
40642     
40643     /**
40644      * @cfg {String} order
40645      * Used to set the order in which elements are created (usefull for multiple tabs)
40646      */
40647     
40648     order : false,
40649     /**
40650      * @cfg {String} name
40651      * String to display while loading.
40652      */
40653     name : false,
40654     /**
40655      * @cfg {String} region
40656      * Region to render component to (defaults to center)
40657      */
40658     region : 'center',
40659     
40660     /**
40661      * @cfg {Array} items
40662      * A single item array - the first element is the root of the tree..
40663      * It's done this way to stay compatible with the Xtype system...
40664      */
40665     items : false,
40666     
40667     /**
40668      * @property _tree
40669      * The method that retuns the tree of parts that make up this compoennt 
40670      * @type {function}
40671      */
40672     _tree  : false,
40673     
40674      /**
40675      * render
40676      * render element to dom or tree
40677      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
40678      */
40679     
40680     render : function(el)
40681     {
40682         
40683         el = el || false;
40684         var hp = this.parent ? 1 : 0;
40685         
40686         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
40687             // if parent is a '#.....' string, then let's use that..
40688             var ename = this.parent.substr(1)
40689             this.parent = false;
40690             el = Roo.get(ename);
40691             if (!el) {
40692                 Roo.log("Warning - element can not be found :#" + ename );
40693                 return;
40694             }
40695         }
40696         
40697         
40698         if (!this.parent) {
40699             
40700             el = el ? Roo.get(el) : false;      
40701             
40702             // it's a top level one..
40703             this.parent =  {
40704                 el : new Roo.BorderLayout(el || document.body, {
40705                 
40706                      center: {
40707                          titlebar: false,
40708                          autoScroll:false,
40709                          closeOnTab: true,
40710                          tabPosition: 'top',
40711                           //resizeTabs: true,
40712                          alwaysShowTabs: el && hp? false :  true,
40713                          hideTabs: el || !hp ? true :  false,
40714                          minTabWidth: 140
40715                      }
40716                  })
40717             }
40718         }
40719         
40720                 if (!this.parent.el) {
40721                         // probably an old style ctor, which has been disabled.
40722                         return;
40723                         
40724                 }
40725                 // The 'tree' method is  '_tree now' 
40726             
40727         var tree = this._tree ? this._tree() : this.tree();
40728         tree.region = tree.region || this.region;
40729         this.el = this.parent.el.addxtype(tree);
40730         this.fireEvent('built', this);
40731         
40732         this.panel = this.el;
40733         this.layout = this.panel.layout;
40734                 this.parentLayout = this.parent.layout  || false;  
40735          
40736     }
40737     
40738 });
40739
40740 Roo.apply(Roo.XComponent, {
40741     /**
40742      * @property  hideProgress
40743      * true to disable the building progress bar.. usefull on single page renders.
40744      * @type Boolean
40745      */
40746     hideProgress : false,
40747     /**
40748      * @property  buildCompleted
40749      * True when the builder has completed building the interface.
40750      * @type Boolean
40751      */
40752     buildCompleted : false,
40753      
40754     /**
40755      * @property  topModule
40756      * the upper most module - uses document.element as it's constructor.
40757      * @type Object
40758      */
40759      
40760     topModule  : false,
40761       
40762     /**
40763      * @property  modules
40764      * array of modules to be created by registration system.
40765      * @type {Array} of Roo.XComponent
40766      */
40767     
40768     modules : [],
40769     /**
40770      * @property  elmodules
40771      * array of modules to be created by which use #ID 
40772      * @type {Array} of Roo.XComponent
40773      */
40774      
40775     elmodules : [],
40776
40777     
40778     /**
40779      * Register components to be built later.
40780      *
40781      * This solves the following issues
40782      * - Building is not done on page load, but after an authentication process has occured.
40783      * - Interface elements are registered on page load
40784      * - Parent Interface elements may not be loaded before child, so this handles that..
40785      * 
40786      *
40787      * example:
40788      * 
40789      * MyApp.register({
40790           order : '000001',
40791           module : 'Pman.Tab.projectMgr',
40792           region : 'center',
40793           parent : 'Pman.layout',
40794           disabled : false,  // or use a function..
40795         })
40796      
40797      * * @param {Object} details about module
40798      */
40799     register : function(obj) {
40800                 
40801         Roo.XComponent.event.fireEvent('register', obj);
40802         switch(typeof(obj.disabled) ) {
40803                 
40804             case 'undefined':
40805                 break;
40806             
40807             case 'function':
40808                 if ( obj.disabled() ) {
40809                         return;
40810                 }
40811                 break;
40812             
40813             default:
40814                 if (obj.disabled) {
40815                         return;
40816                 }
40817                 break;
40818         }
40819                 
40820         this.modules.push(obj);
40821          
40822     },
40823     /**
40824      * convert a string to an object..
40825      * eg. 'AAA.BBB' -> finds AAA.BBB
40826
40827      */
40828     
40829     toObject : function(str)
40830     {
40831         if (!str || typeof(str) == 'object') {
40832             return str;
40833         }
40834         if (str.substring(0,1) == '#') {
40835             return str;
40836         }
40837
40838         var ar = str.split('.');
40839         var rt, o;
40840         rt = ar.shift();
40841             /** eval:var:o */
40842         try {
40843             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
40844         } catch (e) {
40845             throw "Module not found : " + str;
40846         }
40847         
40848         if (o === false) {
40849             throw "Module not found : " + str;
40850         }
40851         Roo.each(ar, function(e) {
40852             if (typeof(o[e]) == 'undefined') {
40853                 throw "Module not found : " + str;
40854             }
40855             o = o[e];
40856         });
40857         
40858         return o;
40859         
40860     },
40861     
40862     
40863     /**
40864      * move modules into their correct place in the tree..
40865      * 
40866      */
40867     preBuild : function ()
40868     {
40869         var _t = this;
40870         Roo.each(this.modules , function (obj)
40871         {
40872             Roo.XComponent.event.fireEvent('beforebuild', obj);
40873             
40874             var opar = obj.parent;
40875             try { 
40876                 obj.parent = this.toObject(opar);
40877             } catch(e) {
40878                 Roo.log("parent:toObject failed: " + e.toString());
40879                 return;
40880             }
40881             
40882             if (!obj.parent) {
40883                 Roo.debug && Roo.log("GOT top level module");
40884                 Roo.debug && Roo.log(obj);
40885                 obj.modules = new Roo.util.MixedCollection(false, 
40886                     function(o) { return o.order + '' }
40887                 );
40888                 this.topModule = obj;
40889                 return;
40890             }
40891                         // parent is a string (usually a dom element name..)
40892             if (typeof(obj.parent) == 'string') {
40893                 this.elmodules.push(obj);
40894                 return;
40895             }
40896             if (obj.parent.constructor != Roo.XComponent) {
40897                 Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
40898             }
40899             if (!obj.parent.modules) {
40900                 obj.parent.modules = new Roo.util.MixedCollection(false, 
40901                     function(o) { return o.order + '' }
40902                 );
40903             }
40904             if (obj.parent.disabled) {
40905                 obj.disabled = true;
40906             }
40907             obj.parent.modules.add(obj);
40908         }, this);
40909     },
40910     
40911      /**
40912      * make a list of modules to build.
40913      * @return {Array} list of modules. 
40914      */ 
40915     
40916     buildOrder : function()
40917     {
40918         var _this = this;
40919         var cmp = function(a,b) {   
40920             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
40921         };
40922         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
40923             throw "No top level modules to build";
40924         }
40925         
40926         // make a flat list in order of modules to build.
40927         var mods = this.topModule ? [ this.topModule ] : [];
40928                 
40929         
40930         // elmodules (is a list of DOM based modules )
40931         Roo.each(this.elmodules, function(e) {
40932             mods.push(e);
40933             if (!this.topModule &&
40934                 typeof(e.parent) == 'string' &&
40935                 e.parent.substring(0,1) == '#' &&
40936                 Roo.get(e.parent.substr(1))
40937                ) {
40938                 
40939                 _this.topModule = e;
40940             }
40941             
40942         });
40943
40944         
40945         // add modules to their parents..
40946         var addMod = function(m) {
40947             Roo.debug && Roo.log("build Order: add: " + m.name);
40948                 
40949             mods.push(m);
40950             if (m.modules && !m.disabled) {
40951                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
40952                 m.modules.keySort('ASC',  cmp );
40953                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
40954     
40955                 m.modules.each(addMod);
40956             } else {
40957                 Roo.debug && Roo.log("build Order: no child modules");
40958             }
40959             // not sure if this is used any more..
40960             if (m.finalize) {
40961                 m.finalize.name = m.name + " (clean up) ";
40962                 mods.push(m.finalize);
40963             }
40964             
40965         }
40966         if (this.topModule && this.topModule.modules) { 
40967             this.topModule.modules.keySort('ASC',  cmp );
40968             this.topModule.modules.each(addMod);
40969         } 
40970         return mods;
40971     },
40972     
40973      /**
40974      * Build the registered modules.
40975      * @param {Object} parent element.
40976      * @param {Function} optional method to call after module has been added.
40977      * 
40978      */ 
40979    
40980     build : function() 
40981     {
40982         
40983         this.preBuild();
40984         var mods = this.buildOrder();
40985       
40986         //this.allmods = mods;
40987         //Roo.debug && Roo.log(mods);
40988         //return;
40989         if (!mods.length) { // should not happen
40990             throw "NO modules!!!";
40991         }
40992         
40993         
40994         var msg = "Building Interface...";
40995         // flash it up as modal - so we store the mask!?
40996         if (!this.hideProgress) {
40997             Roo.MessageBox.show({ title: 'loading' });
40998             Roo.MessageBox.show({
40999                title: "Please wait...",
41000                msg: msg,
41001                width:450,
41002                progress:true,
41003                closable:false,
41004                modal: false
41005               
41006             });
41007         }
41008         var total = mods.length;
41009         
41010         var _this = this;
41011         var progressRun = function() {
41012             if (!mods.length) {
41013                 Roo.debug && Roo.log('hide?');
41014                 if (!this.hideProgress) {
41015                     Roo.MessageBox.hide();
41016                 }
41017                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
41018                 
41019                 // THE END...
41020                 return false;   
41021             }
41022             
41023             var m = mods.shift();
41024             
41025             
41026             Roo.debug && Roo.log(m);
41027             // not sure if this is supported any more.. - modules that are are just function
41028             if (typeof(m) == 'function') { 
41029                 m.call(this);
41030                 return progressRun.defer(10, _this);
41031             } 
41032             
41033             
41034             msg = "Building Interface " + (total  - mods.length) + 
41035                     " of " + total + 
41036                     (m.name ? (' - ' + m.name) : '');
41037                         Roo.debug && Roo.log(msg);
41038             if (!this.hideProgress) { 
41039                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
41040             }
41041             
41042          
41043             // is the module disabled?
41044             var disabled = (typeof(m.disabled) == 'function') ?
41045                 m.disabled.call(m.module.disabled) : m.disabled;    
41046             
41047             
41048             if (disabled) {
41049                 return progressRun(); // we do not update the display!
41050             }
41051             
41052             // now build 
41053             
41054                         
41055                         
41056             m.render();
41057             // it's 10 on top level, and 1 on others??? why...
41058             return progressRun.defer(10, _this);
41059              
41060         }
41061         progressRun.defer(1, _this);
41062      
41063         
41064         
41065     },
41066         
41067         
41068         /**
41069          * Event Object.
41070          *
41071          *
41072          */
41073         event: false, 
41074     /**
41075          * wrapper for event.on - aliased later..  
41076          * Typically use to register a event handler for register:
41077          *
41078          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
41079          *
41080          */
41081     on : false
41082    
41083     
41084     
41085 });
41086
41087 Roo.XComponent.event = new Roo.util.Observable({
41088                 events : { 
41089                         /**
41090                          * @event register
41091                          * Fires when an Component is registered,
41092                          * set the disable property on the Component to stop registration.
41093                          * @param {Roo.XComponent} c the component being registerd.
41094                          * 
41095                          */
41096                         'register' : true,
41097             /**
41098                          * @event beforebuild
41099                          * Fires before each Component is built
41100                          * can be used to apply permissions.
41101                          * @param {Roo.XComponent} c the component being registerd.
41102                          * 
41103                          */
41104                         'beforebuild' : true,
41105                         /**
41106                          * @event buildcomplete
41107                          * Fires on the top level element when all elements have been built
41108                          * @param {Roo.XComponent} the top level component.
41109                          */
41110                         'buildcomplete' : true
41111                         
41112                 }
41113 });
41114
41115 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
41116