sync
[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  * Defines the interface and base operation of items that that can be
30  * dragged or can be drop targets.  It was designed to be extended, overriding
31  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
32  * Up to three html elements can be associated with a DragDrop instance:
33  * <ul>
34  * <li>linked element: the element that is passed into the constructor.
35  * This is the element which defines the boundaries for interaction with
36  * other DragDrop objects.</li>
37  * <li>handle element(s): The drag operation only occurs if the element that
38  * was clicked matches a handle element.  By default this is the linked
39  * element, but there are times that you will want only a portion of the
40  * linked element to initiate the drag operation, and the setHandleElId()
41  * method provides a way to define this.</li>
42  * <li>drag element: this represents the element that would be moved along
43  * with the cursor during a drag operation.  By default, this is the linked
44  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
45  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
46  * </li>
47  * </ul>
48  * This class should not be instantiated until the onload event to ensure that
49  * the associated elements are available.
50  * The following would define a DragDrop obj that would interact with any
51  * other DragDrop obj in the "group1" group:
52  * <pre>
53  *  dd = new Roo.dd.DragDrop("div1", "group1");
54  * </pre>
55  * Since none of the event handlers have been implemented, nothing would
56  * actually happen if you were to run the code above.  Normally you would
57  * override this class or one of the default implementations, but you can
58  * also override the methods you want on an instance of the class...
59  * <pre>
60  *  dd.onDragDrop = function(e, id) {
61  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
62  *  }
63  * </pre>
64  * @constructor
65  * @param {String} id of the element that is linked to this instance
66  * @param {String} sGroup the group of related DragDrop objects
67  * @param {object} config an object containing configurable attributes
68  *                Valid properties for DragDrop:
69  *                    padding, isTarget, maintainOffset, primaryButtonOnly
70  */
71 Roo.dd.DragDrop = function(id, sGroup, config) {
72     if (id) {
73         this.init(id, sGroup, config);
74     }
75 };
76
77 Roo.dd.DragDrop.prototype = {
78
79     /**
80      * The id of the element associated with this object.  This is what we
81      * refer to as the "linked element" because the size and position of
82      * this element is used to determine when the drag and drop objects have
83      * interacted.
84      * @property id
85      * @type String
86      */
87     id: null,
88
89     /**
90      * Configuration attributes passed into the constructor
91      * @property config
92      * @type object
93      */
94     config: null,
95
96     /**
97      * The id of the element that will be dragged.  By default this is same
98      * as the linked element , but could be changed to another element. Ex:
99      * Roo.dd.DDProxy
100      * @property dragElId
101      * @type String
102      * @private
103      */
104     dragElId: null,
105
106     /**
107      * the id of the element that initiates the drag operation.  By default
108      * this is the linked element, but could be changed to be a child of this
109      * element.  This lets us do things like only starting the drag when the
110      * header element within the linked html element is clicked.
111      * @property handleElId
112      * @type String
113      * @private
114      */
115     handleElId: null,
116
117     /**
118      * An associative array of HTML tags that will be ignored if clicked.
119      * @property invalidHandleTypes
120      * @type {string: string}
121      */
122     invalidHandleTypes: null,
123
124     /**
125      * An associative array of ids for elements that will be ignored if clicked
126      * @property invalidHandleIds
127      * @type {string: string}
128      */
129     invalidHandleIds: null,
130
131     /**
132      * An indexted array of css class names for elements that will be ignored
133      * if clicked.
134      * @property invalidHandleClasses
135      * @type string[]
136      */
137     invalidHandleClasses: null,
138
139     /**
140      * The linked element's absolute X position at the time the drag was
141      * started
142      * @property startPageX
143      * @type int
144      * @private
145      */
146     startPageX: 0,
147
148     /**
149      * The linked element's absolute X position at the time the drag was
150      * started
151      * @property startPageY
152      * @type int
153      * @private
154      */
155     startPageY: 0,
156
157     /**
158      * The group defines a logical collection of DragDrop objects that are
159      * related.  Instances only get events when interacting with other
160      * DragDrop object in the same group.  This lets us define multiple
161      * groups using a single DragDrop subclass if we want.
162      * @property groups
163      * @type {string: string}
164      */
165     groups: null,
166
167     /**
168      * Individual drag/drop instances can be locked.  This will prevent
169      * onmousedown start drag.
170      * @property locked
171      * @type boolean
172      * @private
173      */
174     locked: false,
175
176     /**
177      * Lock this instance
178      * @method lock
179      */
180     lock: function() { this.locked = true; },
181
182     /**
183      * Unlock this instace
184      * @method unlock
185      */
186     unlock: function() { this.locked = false; },
187
188     /**
189      * By default, all insances can be a drop target.  This can be disabled by
190      * setting isTarget to false.
191      * @method isTarget
192      * @type boolean
193      */
194     isTarget: true,
195
196     /**
197      * The padding configured for this drag and drop object for calculating
198      * the drop zone intersection with this object.
199      * @method padding
200      * @type int[]
201      */
202     padding: null,
203
204     /**
205      * Cached reference to the linked element
206      * @property _domRef
207      * @private
208      */
209     _domRef: null,
210
211     /**
212      * Internal typeof flag
213      * @property __ygDragDrop
214      * @private
215      */
216     __ygDragDrop: true,
217
218     /**
219      * Set to true when horizontal contraints are applied
220      * @property constrainX
221      * @type boolean
222      * @private
223      */
224     constrainX: false,
225
226     /**
227      * Set to true when vertical contraints are applied
228      * @property constrainY
229      * @type boolean
230      * @private
231      */
232     constrainY: false,
233
234     /**
235      * The left constraint
236      * @property minX
237      * @type int
238      * @private
239      */
240     minX: 0,
241
242     /**
243      * The right constraint
244      * @property maxX
245      * @type int
246      * @private
247      */
248     maxX: 0,
249
250     /**
251      * The up constraint
252      * @property minY
253      * @type int
254      * @type int
255      * @private
256      */
257     minY: 0,
258
259     /**
260      * The down constraint
261      * @property maxY
262      * @type int
263      * @private
264      */
265     maxY: 0,
266
267     /**
268      * Maintain offsets when we resetconstraints.  Set to true when you want
269      * the position of the element relative to its parent to stay the same
270      * when the page changes
271      *
272      * @property maintainOffset
273      * @type boolean
274      */
275     maintainOffset: false,
276
277     /**
278      * Array of pixel locations the element will snap to if we specified a
279      * horizontal graduation/interval.  This array is generated automatically
280      * when you define a tick interval.
281      * @property xTicks
282      * @type int[]
283      */
284     xTicks: null,
285
286     /**
287      * Array of pixel locations the element will snap to if we specified a
288      * vertical graduation/interval.  This array is generated automatically
289      * when you define a tick interval.
290      * @property yTicks
291      * @type int[]
292      */
293     yTicks: null,
294
295     /**
296      * By default the drag and drop instance will only respond to the primary
297      * button click (left button for a right-handed mouse).  Set to true to
298      * allow drag and drop to start with any mouse click that is propogated
299      * by the browser
300      * @property primaryButtonOnly
301      * @type boolean
302      */
303     primaryButtonOnly: true,
304
305     /**
306      * The availabe property is false until the linked dom element is accessible.
307      * @property available
308      * @type boolean
309      */
310     available: false,
311
312     /**
313      * By default, drags can only be initiated if the mousedown occurs in the
314      * region the linked element is.  This is done in part to work around a
315      * bug in some browsers that mis-report the mousedown if the previous
316      * mouseup happened outside of the window.  This property is set to true
317      * if outer handles are defined.
318      *
319      * @property hasOuterHandles
320      * @type boolean
321      * @default false
322      */
323     hasOuterHandles: false,
324
325     /**
326      * Code that executes immediately before the startDrag event
327      * @method b4StartDrag
328      * @private
329      */
330     b4StartDrag: function(x, y) { },
331
332     /**
333      * Abstract method called after a drag/drop object is clicked
334      * and the drag or mousedown time thresholds have beeen met.
335      * @method startDrag
336      * @param {int} X click location
337      * @param {int} Y click location
338      */
339     startDrag: function(x, y) { /* override this */ },
340
341     /**
342      * Code that executes immediately before the onDrag event
343      * @method b4Drag
344      * @private
345      */
346     b4Drag: function(e) { },
347
348     /**
349      * Abstract method called during the onMouseMove event while dragging an
350      * object.
351      * @method onDrag
352      * @param {Event} e the mousemove event
353      */
354     onDrag: function(e) { /* override this */ },
355
356     /**
357      * Abstract method called when this element fist begins hovering over
358      * another DragDrop obj
359      * @method onDragEnter
360      * @param {Event} e the mousemove event
361      * @param {String|DragDrop[]} id In POINT mode, the element
362      * id this is hovering over.  In INTERSECT mode, an array of one or more
363      * dragdrop items being hovered over.
364      */
365     onDragEnter: function(e, id) { /* override this */ },
366
367     /**
368      * Code that executes immediately before the onDragOver event
369      * @method b4DragOver
370      * @private
371      */
372     b4DragOver: function(e) { },
373
374     /**
375      * Abstract method called when this element is hovering over another
376      * DragDrop obj
377      * @method onDragOver
378      * @param {Event} e the mousemove event
379      * @param {String|DragDrop[]} id In POINT mode, the element
380      * id this is hovering over.  In INTERSECT mode, an array of dd items
381      * being hovered over.
382      */
383     onDragOver: function(e, id) { /* override this */ },
384
385     /**
386      * Code that executes immediately before the onDragOut event
387      * @method b4DragOut
388      * @private
389      */
390     b4DragOut: function(e) { },
391
392     /**
393      * Abstract method called when we are no longer hovering over an element
394      * @method onDragOut
395      * @param {Event} e the mousemove event
396      * @param {String|DragDrop[]} id In POINT mode, the element
397      * id this was hovering over.  In INTERSECT mode, an array of dd items
398      * that the mouse is no longer over.
399      */
400     onDragOut: function(e, id) { /* override this */ },
401
402     /**
403      * Code that executes immediately before the onDragDrop event
404      * @method b4DragDrop
405      * @private
406      */
407     b4DragDrop: function(e) { },
408
409     /**
410      * Abstract method called when this item is dropped on another DragDrop
411      * obj
412      * @method onDragDrop
413      * @param {Event} e the mouseup event
414      * @param {String|DragDrop[]} id In POINT mode, the element
415      * id this was dropped on.  In INTERSECT mode, an array of dd items this
416      * was dropped on.
417      */
418     onDragDrop: function(e, id) { /* override this */ },
419
420     /**
421      * Abstract method called when this item is dropped on an area with no
422      * drop target
423      * @method onInvalidDrop
424      * @param {Event} e the mouseup event
425      */
426     onInvalidDrop: function(e) { /* override this */ },
427
428     /**
429      * Code that executes immediately before the endDrag event
430      * @method b4EndDrag
431      * @private
432      */
433     b4EndDrag: function(e) { },
434
435     /**
436      * Fired when we are done dragging the object
437      * @method endDrag
438      * @param {Event} e the mouseup event
439      */
440     endDrag: function(e) { /* override this */ },
441
442     /**
443      * Code executed immediately before the onMouseDown event
444      * @method b4MouseDown
445      * @param {Event} e the mousedown event
446      * @private
447      */
448     b4MouseDown: function(e) {  },
449
450     /**
451      * Event handler that fires when a drag/drop obj gets a mousedown
452      * @method onMouseDown
453      * @param {Event} e the mousedown event
454      */
455     onMouseDown: function(e) { /* override this */ },
456
457     /**
458      * Event handler that fires when a drag/drop obj gets a mouseup
459      * @method onMouseUp
460      * @param {Event} e the mouseup event
461      */
462     onMouseUp: function(e) { /* override this */ },
463
464     /**
465      * Override the onAvailable method to do what is needed after the initial
466      * position was determined.
467      * @method onAvailable
468      */
469     onAvailable: function () {
470     },
471
472     /*
473      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
474      * @type Object
475      */
476     defaultPadding : {left:0, right:0, top:0, bottom:0},
477
478     /*
479      * Initializes the drag drop object's constraints to restrict movement to a certain element.
480  *
481  * Usage:
482  <pre><code>
483  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
484                 { dragElId: "existingProxyDiv" });
485  dd.startDrag = function(){
486      this.constrainTo("parent-id");
487  };
488  </code></pre>
489  * Or you can initalize it using the {@link Roo.Element} object:
490  <pre><code>
491  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
492      startDrag : function(){
493          this.constrainTo("parent-id");
494      }
495  });
496  </code></pre>
497      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
498      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
499      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
500      * an object containing the sides to pad. For example: {right:10, bottom:10}
501      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
502      */
503     constrainTo : function(constrainTo, pad, inContent){
504         if(typeof pad == "number"){
505             pad = {left: pad, right:pad, top:pad, bottom:pad};
506         }
507         pad = pad || this.defaultPadding;
508         var b = Roo.get(this.getEl()).getBox();
509         var ce = Roo.get(constrainTo);
510         var s = ce.getScroll();
511         var c, cd = ce.dom;
512         if(cd == document.body){
513             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
514         }else{
515             xy = ce.getXY();
516             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
517         }
518
519
520         var topSpace = b.y - c.y;
521         var leftSpace = b.x - c.x;
522
523         this.resetConstraints();
524         this.setXConstraint(leftSpace - (pad.left||0), // left
525                 c.width - leftSpace - b.width - (pad.right||0) //right
526         );
527         this.setYConstraint(topSpace - (pad.top||0), //top
528                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
529         );
530     },
531
532     /**
533      * Returns a reference to the linked element
534      * @method getEl
535      * @return {HTMLElement} the html element
536      */
537     getEl: function() {
538         if (!this._domRef) {
539             this._domRef = Roo.getDom(this.id);
540         }
541
542         return this._domRef;
543     },
544
545     /**
546      * Returns a reference to the actual element to drag.  By default this is
547      * the same as the html element, but it can be assigned to another
548      * element. An example of this can be found in Roo.dd.DDProxy
549      * @method getDragEl
550      * @return {HTMLElement} the html element
551      */
552     getDragEl: function() {
553         return Roo.getDom(this.dragElId);
554     },
555
556     /**
557      * Sets up the DragDrop object.  Must be called in the constructor of any
558      * Roo.dd.DragDrop subclass
559      * @method init
560      * @param id the id of the linked element
561      * @param {String} sGroup the group of related items
562      * @param {object} config configuration attributes
563      */
564     init: function(id, sGroup, config) {
565         this.initTarget(id, sGroup, config);
566         Event.on(this.id, "mousedown", this.handleMouseDown, this);
567         // Event.on(this.id, "selectstart", Event.preventDefault);
568     },
569
570     /**
571      * Initializes Targeting functionality only... the object does not
572      * get a mousedown handler.
573      * @method initTarget
574      * @param id the id of the linked element
575      * @param {String} sGroup the group of related items
576      * @param {object} config configuration attributes
577      */
578     initTarget: function(id, sGroup, config) {
579
580         // configuration attributes
581         this.config = config || {};
582
583         // create a local reference to the drag and drop manager
584         this.DDM = Roo.dd.DDM;
585         // initialize the groups array
586         this.groups = {};
587
588         // assume that we have an element reference instead of an id if the
589         // parameter is not a string
590         if (typeof id !== "string") {
591             id = Roo.id(id);
592         }
593
594         // set the id
595         this.id = id;
596
597         // add to an interaction group
598         this.addToGroup((sGroup) ? sGroup : "default");
599
600         // We don't want to register this as the handle with the manager
601         // so we just set the id rather than calling the setter.
602         this.handleElId = id;
603
604         // the linked element is the element that gets dragged by default
605         this.setDragElId(id);
606
607         // by default, clicked anchors will not start drag operations.
608         this.invalidHandleTypes = { A: "A" };
609         this.invalidHandleIds = {};
610         this.invalidHandleClasses = [];
611
612         this.applyConfig();
613
614         this.handleOnAvailable();
615     },
616
617     /**
618      * Applies the configuration parameters that were passed into the constructor.
619      * This is supposed to happen at each level through the inheritance chain.  So
620      * a DDProxy implentation will execute apply config on DDProxy, DD, and
621      * DragDrop in order to get all of the parameters that are available in
622      * each object.
623      * @method applyConfig
624      */
625     applyConfig: function() {
626
627         // configurable properties:
628         //    padding, isTarget, maintainOffset, primaryButtonOnly
629         this.padding           = this.config.padding || [0, 0, 0, 0];
630         this.isTarget          = (this.config.isTarget !== false);
631         this.maintainOffset    = (this.config.maintainOffset);
632         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
633
634     },
635
636     /**
637      * Executed when the linked element is available
638      * @method handleOnAvailable
639      * @private
640      */
641     handleOnAvailable: function() {
642         this.available = true;
643         this.resetConstraints();
644         this.onAvailable();
645     },
646
647      /**
648      * Configures the padding for the target zone in px.  Effectively expands
649      * (or reduces) the virtual object size for targeting calculations.
650      * Supports css-style shorthand; if only one parameter is passed, all sides
651      * will have that padding, and if only two are passed, the top and bottom
652      * will have the first param, the left and right the second.
653      * @method setPadding
654      * @param {int} iTop    Top pad
655      * @param {int} iRight  Right pad
656      * @param {int} iBot    Bot pad
657      * @param {int} iLeft   Left pad
658      */
659     setPadding: function(iTop, iRight, iBot, iLeft) {
660         // this.padding = [iLeft, iRight, iTop, iBot];
661         if (!iRight && 0 !== iRight) {
662             this.padding = [iTop, iTop, iTop, iTop];
663         } else if (!iBot && 0 !== iBot) {
664             this.padding = [iTop, iRight, iTop, iRight];
665         } else {
666             this.padding = [iTop, iRight, iBot, iLeft];
667         }
668     },
669
670     /**
671      * Stores the initial placement of the linked element.
672      * @method setInitialPosition
673      * @param {int} diffX   the X offset, default 0
674      * @param {int} diffY   the Y offset, default 0
675      */
676     setInitPosition: function(diffX, diffY) {
677         var el = this.getEl();
678
679         if (!this.DDM.verifyEl(el)) {
680             return;
681         }
682
683         var dx = diffX || 0;
684         var dy = diffY || 0;
685
686         var p = Dom.getXY( el );
687
688         this.initPageX = p[0] - dx;
689         this.initPageY = p[1] - dy;
690
691         this.lastPageX = p[0];
692         this.lastPageY = p[1];
693
694
695         this.setStartPosition(p);
696     },
697
698     /**
699      * Sets the start position of the element.  This is set when the obj
700      * is initialized, the reset when a drag is started.
701      * @method setStartPosition
702      * @param pos current position (from previous lookup)
703      * @private
704      */
705     setStartPosition: function(pos) {
706         var p = pos || Dom.getXY( this.getEl() );
707         this.deltaSetXY = null;
708
709         this.startPageX = p[0];
710         this.startPageY = p[1];
711     },
712
713     /**
714      * Add this instance to a group of related drag/drop objects.  All
715      * instances belong to at least one group, and can belong to as many
716      * groups as needed.
717      * @method addToGroup
718      * @param sGroup {string} the name of the group
719      */
720     addToGroup: function(sGroup) {
721         this.groups[sGroup] = true;
722         this.DDM.regDragDrop(this, sGroup);
723     },
724
725     /**
726      * Remove's this instance from the supplied interaction group
727      * @method removeFromGroup
728      * @param {string}  sGroup  The group to drop
729      */
730     removeFromGroup: function(sGroup) {
731         if (this.groups[sGroup]) {
732             delete this.groups[sGroup];
733         }
734
735         this.DDM.removeDDFromGroup(this, sGroup);
736     },
737
738     /**
739      * Allows you to specify that an element other than the linked element
740      * will be moved with the cursor during a drag
741      * @method setDragElId
742      * @param id {string} the id of the element that will be used to initiate the drag
743      */
744     setDragElId: function(id) {
745         this.dragElId = id;
746     },
747
748     /**
749      * Allows you to specify a child of the linked element that should be
750      * used to initiate the drag operation.  An example of this would be if
751      * you have a content div with text and links.  Clicking anywhere in the
752      * content area would normally start the drag operation.  Use this method
753      * to specify that an element inside of the content div is the element
754      * that starts the drag operation.
755      * @method setHandleElId
756      * @param id {string} the id of the element that will be used to
757      * initiate the drag.
758      */
759     setHandleElId: function(id) {
760         if (typeof id !== "string") {
761             id = Roo.id(id);
762         }
763         this.handleElId = id;
764         this.DDM.regHandle(this.id, id);
765     },
766
767     /**
768      * Allows you to set an element outside of the linked element as a drag
769      * handle
770      * @method setOuterHandleElId
771      * @param id the id of the element that will be used to initiate the drag
772      */
773     setOuterHandleElId: function(id) {
774         if (typeof id !== "string") {
775             id = Roo.id(id);
776         }
777         Event.on(id, "mousedown",
778                 this.handleMouseDown, this);
779         this.setHandleElId(id);
780
781         this.hasOuterHandles = true;
782     },
783
784     /**
785      * Remove all drag and drop hooks for this element
786      * @method unreg
787      */
788     unreg: function() {
789         Event.un(this.id, "mousedown",
790                 this.handleMouseDown);
791         this._domRef = null;
792         this.DDM._remove(this);
793     },
794
795     destroy : function(){
796         this.unreg();
797     },
798
799     /**
800      * Returns true if this instance is locked, or the drag drop mgr is locked
801      * (meaning that all drag/drop is disabled on the page.)
802      * @method isLocked
803      * @return {boolean} true if this obj or all drag/drop is locked, else
804      * false
805      */
806     isLocked: function() {
807         return (this.DDM.isLocked() || this.locked);
808     },
809
810     /**
811      * Fired when this object is clicked
812      * @method handleMouseDown
813      * @param {Event} e
814      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
815      * @private
816      */
817     handleMouseDown: function(e, oDD){
818         if (this.primaryButtonOnly && e.button != 0) {
819             return;
820         }
821
822         if (this.isLocked()) {
823             return;
824         }
825
826         this.DDM.refreshCache(this.groups);
827
828         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
829         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
830         } else {
831             if (this.clickValidator(e)) {
832
833                 // set the initial element position
834                 this.setStartPosition();
835
836
837                 this.b4MouseDown(e);
838                 this.onMouseDown(e);
839
840                 this.DDM.handleMouseDown(e, this);
841
842                 this.DDM.stopEvent(e);
843             } else {
844
845
846             }
847         }
848     },
849
850     clickValidator: function(e) {
851         var target = e.getTarget();
852         return ( this.isValidHandleChild(target) &&
853                     (this.id == this.handleElId ||
854                         this.DDM.handleWasClicked(target, this.id)) );
855     },
856
857     /**
858      * Allows you to specify a tag name that should not start a drag operation
859      * when clicked.  This is designed to facilitate embedding links within a
860      * drag handle that do something other than start the drag.
861      * @method addInvalidHandleType
862      * @param {string} tagName the type of element to exclude
863      */
864     addInvalidHandleType: function(tagName) {
865         var type = tagName.toUpperCase();
866         this.invalidHandleTypes[type] = type;
867     },
868
869     /**
870      * Lets you to specify an element id for a child of a drag handle
871      * that should not initiate a drag
872      * @method addInvalidHandleId
873      * @param {string} id the element id of the element you wish to ignore
874      */
875     addInvalidHandleId: function(id) {
876         if (typeof id !== "string") {
877             id = Roo.id(id);
878         }
879         this.invalidHandleIds[id] = id;
880     },
881
882     /**
883      * Lets you specify a css class of elements that will not initiate a drag
884      * @method addInvalidHandleClass
885      * @param {string} cssClass the class of the elements you wish to ignore
886      */
887     addInvalidHandleClass: function(cssClass) {
888         this.invalidHandleClasses.push(cssClass);
889     },
890
891     /**
892      * Unsets an excluded tag name set by addInvalidHandleType
893      * @method removeInvalidHandleType
894      * @param {string} tagName the type of element to unexclude
895      */
896     removeInvalidHandleType: function(tagName) {
897         var type = tagName.toUpperCase();
898         // this.invalidHandleTypes[type] = null;
899         delete this.invalidHandleTypes[type];
900     },
901
902     /**
903      * Unsets an invalid handle id
904      * @method removeInvalidHandleId
905      * @param {string} id the id of the element to re-enable
906      */
907     removeInvalidHandleId: function(id) {
908         if (typeof id !== "string") {
909             id = Roo.id(id);
910         }
911         delete this.invalidHandleIds[id];
912     },
913
914     /**
915      * Unsets an invalid css class
916      * @method removeInvalidHandleClass
917      * @param {string} cssClass the class of the element(s) you wish to
918      * re-enable
919      */
920     removeInvalidHandleClass: function(cssClass) {
921         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
922             if (this.invalidHandleClasses[i] == cssClass) {
923                 delete this.invalidHandleClasses[i];
924             }
925         }
926     },
927
928     /**
929      * Checks the tag exclusion list to see if this click should be ignored
930      * @method isValidHandleChild
931      * @param {HTMLElement} node the HTMLElement to evaluate
932      * @return {boolean} true if this is a valid tag type, false if not
933      */
934     isValidHandleChild: function(node) {
935
936         var valid = true;
937         // var n = (node.nodeName == "#text") ? node.parentNode : node;
938         var nodeName;
939         try {
940             nodeName = node.nodeName.toUpperCase();
941         } catch(e) {
942             nodeName = node.nodeName;
943         }
944         valid = valid && !this.invalidHandleTypes[nodeName];
945         valid = valid && !this.invalidHandleIds[node.id];
946
947         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
948             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
949         }
950
951
952         return valid;
953
954     },
955
956     /**
957      * Create the array of horizontal tick marks if an interval was specified
958      * in setXConstraint().
959      * @method setXTicks
960      * @private
961      */
962     setXTicks: function(iStartX, iTickSize) {
963         this.xTicks = [];
964         this.xTickSize = iTickSize;
965
966         var tickMap = {};
967
968         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
969             if (!tickMap[i]) {
970                 this.xTicks[this.xTicks.length] = i;
971                 tickMap[i] = true;
972             }
973         }
974
975         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
976             if (!tickMap[i]) {
977                 this.xTicks[this.xTicks.length] = i;
978                 tickMap[i] = true;
979             }
980         }
981
982         this.xTicks.sort(this.DDM.numericSort) ;
983     },
984
985     /**
986      * Create the array of vertical tick marks if an interval was specified in
987      * setYConstraint().
988      * @method setYTicks
989      * @private
990      */
991     setYTicks: function(iStartY, iTickSize) {
992         this.yTicks = [];
993         this.yTickSize = iTickSize;
994
995         var tickMap = {};
996
997         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
998             if (!tickMap[i]) {
999                 this.yTicks[this.yTicks.length] = i;
1000                 tickMap[i] = true;
1001             }
1002         }
1003
1004         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
1005             if (!tickMap[i]) {
1006                 this.yTicks[this.yTicks.length] = i;
1007                 tickMap[i] = true;
1008             }
1009         }
1010
1011         this.yTicks.sort(this.DDM.numericSort) ;
1012     },
1013
1014     /**
1015      * By default, the element can be dragged any place on the screen.  Use
1016      * this method to limit the horizontal travel of the element.  Pass in
1017      * 0,0 for the parameters if you want to lock the drag to the y axis.
1018      * @method setXConstraint
1019      * @param {int} iLeft the number of pixels the element can move to the left
1020      * @param {int} iRight the number of pixels the element can move to the
1021      * right
1022      * @param {int} iTickSize optional parameter for specifying that the
1023      * element
1024      * should move iTickSize pixels at a time.
1025      */
1026     setXConstraint: function(iLeft, iRight, iTickSize) {
1027         this.leftConstraint = iLeft;
1028         this.rightConstraint = iRight;
1029
1030         this.minX = this.initPageX - iLeft;
1031         this.maxX = this.initPageX + iRight;
1032         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
1033
1034         this.constrainX = true;
1035     },
1036
1037     /**
1038      * Clears any constraints applied to this instance.  Also clears ticks
1039      * since they can't exist independent of a constraint at this time.
1040      * @method clearConstraints
1041      */
1042     clearConstraints: function() {
1043         this.constrainX = false;
1044         this.constrainY = false;
1045         this.clearTicks();
1046     },
1047
1048     /**
1049      * Clears any tick interval defined for this instance
1050      * @method clearTicks
1051      */
1052     clearTicks: function() {
1053         this.xTicks = null;
1054         this.yTicks = null;
1055         this.xTickSize = 0;
1056         this.yTickSize = 0;
1057     },
1058
1059     /**
1060      * By default, the element can be dragged any place on the screen.  Set
1061      * this to limit the vertical travel of the element.  Pass in 0,0 for the
1062      * parameters if you want to lock the drag to the x axis.
1063      * @method setYConstraint
1064      * @param {int} iUp the number of pixels the element can move up
1065      * @param {int} iDown the number of pixels the element can move down
1066      * @param {int} iTickSize optional parameter for specifying that the
1067      * element should move iTickSize pixels at a time.
1068      */
1069     setYConstraint: function(iUp, iDown, iTickSize) {
1070         this.topConstraint = iUp;
1071         this.bottomConstraint = iDown;
1072
1073         this.minY = this.initPageY - iUp;
1074         this.maxY = this.initPageY + iDown;
1075         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
1076
1077         this.constrainY = true;
1078
1079     },
1080
1081     /**
1082      * resetConstraints must be called if you manually reposition a dd element.
1083      * @method resetConstraints
1084      * @param {boolean} maintainOffset
1085      */
1086     resetConstraints: function() {
1087
1088
1089         // Maintain offsets if necessary
1090         if (this.initPageX || this.initPageX === 0) {
1091             // figure out how much this thing has moved
1092             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
1093             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
1094
1095             this.setInitPosition(dx, dy);
1096
1097         // This is the first time we have detected the element's position
1098         } else {
1099             this.setInitPosition();
1100         }
1101
1102         if (this.constrainX) {
1103             this.setXConstraint( this.leftConstraint,
1104                                  this.rightConstraint,
1105                                  this.xTickSize        );
1106         }
1107
1108         if (this.constrainY) {
1109             this.setYConstraint( this.topConstraint,
1110                                  this.bottomConstraint,
1111                                  this.yTickSize         );
1112         }
1113     },
1114
1115     /**
1116      * Normally the drag element is moved pixel by pixel, but we can specify
1117      * that it move a number of pixels at a time.  This method resolves the
1118      * location when we have it set up like this.
1119      * @method getTick
1120      * @param {int} val where we want to place the object
1121      * @param {int[]} tickArray sorted array of valid points
1122      * @return {int} the closest tick
1123      * @private
1124      */
1125     getTick: function(val, tickArray) {
1126
1127         if (!tickArray) {
1128             // If tick interval is not defined, it is effectively 1 pixel,
1129             // so we return the value passed to us.
1130             return val;
1131         } else if (tickArray[0] >= val) {
1132             // The value is lower than the first tick, so we return the first
1133             // tick.
1134             return tickArray[0];
1135         } else {
1136             for (var i=0, len=tickArray.length; i<len; ++i) {
1137                 var next = i + 1;
1138                 if (tickArray[next] && tickArray[next] >= val) {
1139                     var diff1 = val - tickArray[i];
1140                     var diff2 = tickArray[next] - val;
1141                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
1142                 }
1143             }
1144
1145             // The value is larger than the last tick, so we return the last
1146             // tick.
1147             return tickArray[tickArray.length - 1];
1148         }
1149     },
1150
1151     /**
1152      * toString method
1153      * @method toString
1154      * @return {string} string representation of the dd obj
1155      */
1156     toString: function() {
1157         return ("DragDrop " + this.id);
1158     }
1159
1160 };
1161
1162 })();
1163 /*
1164  * Based on:
1165  * Ext JS Library 1.1.1
1166  * Copyright(c) 2006-2007, Ext JS, LLC.
1167  *
1168  * Originally Released Under LGPL - original licence link has changed is not relivant.
1169  *
1170  * Fork - LGPL
1171  * <script type="text/javascript">
1172  */
1173
1174
1175 /**
1176  * The drag and drop utility provides a framework for building drag and drop
1177  * applications.  In addition to enabling drag and drop for specific elements,
1178  * the drag and drop elements are tracked by the manager class, and the
1179  * interactions between the various elements are tracked during the drag and
1180  * the implementing code is notified about these important moments.
1181  */
1182
1183 // Only load the library once.  Rewriting the manager class would orphan
1184 // existing drag and drop instances.
1185 if (!Roo.dd.DragDropMgr) {
1186
1187 /**
1188  * @class Roo.dd.DragDropMgr
1189  * DragDropMgr is a singleton that tracks the element interaction for
1190  * all DragDrop items in the window.  Generally, you will not call
1191  * this class directly, but it does have helper methods that could
1192  * be useful in your DragDrop implementations.
1193  * @singleton
1194  */
1195 Roo.dd.DragDropMgr = function() {
1196
1197     var Event = Roo.EventManager;
1198
1199     return {
1200
1201         /**
1202          * Two dimensional Array of registered DragDrop objects.  The first
1203          * dimension is the DragDrop item group, the second the DragDrop
1204          * object.
1205          * @property ids
1206          * @type {string: string}
1207          * @private
1208          * @static
1209          */
1210         ids: {},
1211
1212         /**
1213          * Array of element ids defined as drag handles.  Used to determine
1214          * if the element that generated the mousedown event is actually the
1215          * handle and not the html element itself.
1216          * @property handleIds
1217          * @type {string: string}
1218          * @private
1219          * @static
1220          */
1221         handleIds: {},
1222
1223         /**
1224          * the DragDrop object that is currently being dragged
1225          * @property dragCurrent
1226          * @type DragDrop
1227          * @private
1228          * @static
1229          **/
1230         dragCurrent: null,
1231
1232         /**
1233          * the DragDrop object(s) that are being hovered over
1234          * @property dragOvers
1235          * @type Array
1236          * @private
1237          * @static
1238          */
1239         dragOvers: {},
1240
1241         /**
1242          * the X distance between the cursor and the object being dragged
1243          * @property deltaX
1244          * @type int
1245          * @private
1246          * @static
1247          */
1248         deltaX: 0,
1249
1250         /**
1251          * the Y distance between the cursor and the object being dragged
1252          * @property deltaY
1253          * @type int
1254          * @private
1255          * @static
1256          */
1257         deltaY: 0,
1258
1259         /**
1260          * Flag to determine if we should prevent the default behavior of the
1261          * events we define. By default this is true, but this can be set to
1262          * false if you need the default behavior (not recommended)
1263          * @property preventDefault
1264          * @type boolean
1265          * @static
1266          */
1267         preventDefault: true,
1268
1269         /**
1270          * Flag to determine if we should stop the propagation of the events
1271          * we generate. This is true by default but you may want to set it to
1272          * false if the html element contains other features that require the
1273          * mouse click.
1274          * @property stopPropagation
1275          * @type boolean
1276          * @static
1277          */
1278         stopPropagation: true,
1279
1280         /**
1281          * Internal flag that is set to true when drag and drop has been
1282          * intialized
1283          * @property initialized
1284          * @private
1285          * @static
1286          */
1287         initalized: false,
1288
1289         /**
1290          * All drag and drop can be disabled.
1291          * @property locked
1292          * @private
1293          * @static
1294          */
1295         locked: false,
1296
1297         /**
1298          * Called the first time an element is registered.
1299          * @method init
1300          * @private
1301          * @static
1302          */
1303         init: function() {
1304             this.initialized = true;
1305         },
1306
1307         /**
1308          * In point mode, drag and drop interaction is defined by the
1309          * location of the cursor during the drag/drop
1310          * @property POINT
1311          * @type int
1312          * @static
1313          */
1314         POINT: 0,
1315
1316         /**
1317          * In intersect mode, drag and drop interactio nis defined by the
1318          * overlap of two or more drag and drop objects.
1319          * @property INTERSECT
1320          * @type int
1321          * @static
1322          */
1323         INTERSECT: 1,
1324
1325         /**
1326          * The current drag and drop mode.  Default: POINT
1327          * @property mode
1328          * @type int
1329          * @static
1330          */
1331         mode: 0,
1332
1333         /**
1334          * Runs method on all drag and drop objects
1335          * @method _execOnAll
1336          * @private
1337          * @static
1338          */
1339         _execOnAll: function(sMethod, args) {
1340             for (var i in this.ids) {
1341                 for (var j in this.ids[i]) {
1342                     var oDD = this.ids[i][j];
1343                     if (! this.isTypeOfDD(oDD)) {
1344                         continue;
1345                     }
1346                     oDD[sMethod].apply(oDD, args);
1347                 }
1348             }
1349         },
1350
1351         /**
1352          * Drag and drop initialization.  Sets up the global event handlers
1353          * @method _onLoad
1354          * @private
1355          * @static
1356          */
1357         _onLoad: function() {
1358
1359             this.init();
1360
1361
1362             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
1363             Event.on(document, "mousemove", this.handleMouseMove, this, true);
1364             Event.on(window,   "unload",    this._onUnload, this, true);
1365             Event.on(window,   "resize",    this._onResize, this, true);
1366             // Event.on(window,   "mouseout",    this._test);
1367
1368         },
1369
1370         /**
1371          * Reset constraints on all drag and drop objs
1372          * @method _onResize
1373          * @private
1374          * @static
1375          */
1376         _onResize: function(e) {
1377             this._execOnAll("resetConstraints", []);
1378         },
1379
1380         /**
1381          * Lock all drag and drop functionality
1382          * @method lock
1383          * @static
1384          */
1385         lock: function() { this.locked = true; },
1386
1387         /**
1388          * Unlock all drag and drop functionality
1389          * @method unlock
1390          * @static
1391          */
1392         unlock: function() { this.locked = false; },
1393
1394         /**
1395          * Is drag and drop locked?
1396          * @method isLocked
1397          * @return {boolean} True if drag and drop is locked, false otherwise.
1398          * @static
1399          */
1400         isLocked: function() { return this.locked; },
1401
1402         /**
1403          * Location cache that is set for all drag drop objects when a drag is
1404          * initiated, cleared when the drag is finished.
1405          * @property locationCache
1406          * @private
1407          * @static
1408          */
1409         locationCache: {},
1410
1411         /**
1412          * Set useCache to false if you want to force object the lookup of each
1413          * drag and drop linked element constantly during a drag.
1414          * @property useCache
1415          * @type boolean
1416          * @static
1417          */
1418         useCache: true,
1419
1420         /**
1421          * The number of pixels that the mouse needs to move after the
1422          * mousedown before the drag is initiated.  Default=3;
1423          * @property clickPixelThresh
1424          * @type int
1425          * @static
1426          */
1427         clickPixelThresh: 3,
1428
1429         /**
1430          * The number of milliseconds after the mousedown event to initiate the
1431          * drag if we don't get a mouseup event. Default=1000
1432          * @property clickTimeThresh
1433          * @type int
1434          * @static
1435          */
1436         clickTimeThresh: 350,
1437
1438         /**
1439          * Flag that indicates that either the drag pixel threshold or the
1440          * mousdown time threshold has been met
1441          * @property dragThreshMet
1442          * @type boolean
1443          * @private
1444          * @static
1445          */
1446         dragThreshMet: false,
1447
1448         /**
1449          * Timeout used for the click time threshold
1450          * @property clickTimeout
1451          * @type Object
1452          * @private
1453          * @static
1454          */
1455         clickTimeout: null,
1456
1457         /**
1458          * The X position of the mousedown event stored for later use when a
1459          * drag threshold is met.
1460          * @property startX
1461          * @type int
1462          * @private
1463          * @static
1464          */
1465         startX: 0,
1466
1467         /**
1468          * The Y position of the mousedown event stored for later use when a
1469          * drag threshold is met.
1470          * @property startY
1471          * @type int
1472          * @private
1473          * @static
1474          */
1475         startY: 0,
1476
1477         /**
1478          * Each DragDrop instance must be registered with the DragDropMgr.
1479          * This is executed in DragDrop.init()
1480          * @method regDragDrop
1481          * @param {DragDrop} oDD the DragDrop object to register
1482          * @param {String} sGroup the name of the group this element belongs to
1483          * @static
1484          */
1485         regDragDrop: function(oDD, sGroup) {
1486             if (!this.initialized) { this.init(); }
1487
1488             if (!this.ids[sGroup]) {
1489                 this.ids[sGroup] = {};
1490             }
1491             this.ids[sGroup][oDD.id] = oDD;
1492         },
1493
1494         /**
1495          * Removes the supplied dd instance from the supplied group. Executed
1496          * by DragDrop.removeFromGroup, so don't call this function directly.
1497          * @method removeDDFromGroup
1498          * @private
1499          * @static
1500          */
1501         removeDDFromGroup: function(oDD, sGroup) {
1502             if (!this.ids[sGroup]) {
1503                 this.ids[sGroup] = {};
1504             }
1505
1506             var obj = this.ids[sGroup];
1507             if (obj && obj[oDD.id]) {
1508                 delete obj[oDD.id];
1509             }
1510         },
1511
1512         /**
1513          * Unregisters a drag and drop item.  This is executed in
1514          * DragDrop.unreg, use that method instead of calling this directly.
1515          * @method _remove
1516          * @private
1517          * @static
1518          */
1519         _remove: function(oDD) {
1520             for (var g in oDD.groups) {
1521                 if (g && this.ids[g][oDD.id]) {
1522                     delete this.ids[g][oDD.id];
1523                 }
1524             }
1525             delete this.handleIds[oDD.id];
1526         },
1527
1528         /**
1529          * Each DragDrop handle element must be registered.  This is done
1530          * automatically when executing DragDrop.setHandleElId()
1531          * @method regHandle
1532          * @param {String} sDDId the DragDrop id this element is a handle for
1533          * @param {String} sHandleId the id of the element that is the drag
1534          * handle
1535          * @static
1536          */
1537         regHandle: function(sDDId, sHandleId) {
1538             if (!this.handleIds[sDDId]) {
1539                 this.handleIds[sDDId] = {};
1540             }
1541             this.handleIds[sDDId][sHandleId] = sHandleId;
1542         },
1543
1544         /**
1545          * Utility function to determine if a given element has been
1546          * registered as a drag drop item.
1547          * @method isDragDrop
1548          * @param {String} id the element id to check
1549          * @return {boolean} true if this element is a DragDrop item,
1550          * false otherwise
1551          * @static
1552          */
1553         isDragDrop: function(id) {
1554             return ( this.getDDById(id) ) ? true : false;
1555         },
1556
1557         /**
1558          * Returns the drag and drop instances that are in all groups the
1559          * passed in instance belongs to.
1560          * @method getRelated
1561          * @param {DragDrop} p_oDD the obj to get related data for
1562          * @param {boolean} bTargetsOnly if true, only return targetable objs
1563          * @return {DragDrop[]} the related instances
1564          * @static
1565          */
1566         getRelated: function(p_oDD, bTargetsOnly) {
1567             var oDDs = [];
1568             for (var i in p_oDD.groups) {
1569                 for (j in this.ids[i]) {
1570                     var dd = this.ids[i][j];
1571                     if (! this.isTypeOfDD(dd)) {
1572                         continue;
1573                     }
1574                     if (!bTargetsOnly || dd.isTarget) {
1575                         oDDs[oDDs.length] = dd;
1576                     }
1577                 }
1578             }
1579
1580             return oDDs;
1581         },
1582
1583         /**
1584          * Returns true if the specified dd target is a legal target for
1585          * the specifice drag obj
1586          * @method isLegalTarget
1587          * @param {DragDrop} the drag obj
1588          * @param {DragDrop} the target
1589          * @return {boolean} true if the target is a legal target for the
1590          * dd obj
1591          * @static
1592          */
1593         isLegalTarget: function (oDD, oTargetDD) {
1594             var targets = this.getRelated(oDD, true);
1595             for (var i=0, len=targets.length;i<len;++i) {
1596                 if (targets[i].id == oTargetDD.id) {
1597                     return true;
1598                 }
1599             }
1600
1601             return false;
1602         },
1603
1604         /**
1605          * My goal is to be able to transparently determine if an object is
1606          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
1607          * returns "object", oDD.constructor.toString() always returns
1608          * "DragDrop" and not the name of the subclass.  So for now it just
1609          * evaluates a well-known variable in DragDrop.
1610          * @method isTypeOfDD
1611          * @param {Object} the object to evaluate
1612          * @return {boolean} true if typeof oDD = DragDrop
1613          * @static
1614          */
1615         isTypeOfDD: function (oDD) {
1616             return (oDD && oDD.__ygDragDrop);
1617         },
1618
1619         /**
1620          * Utility function to determine if a given element has been
1621          * registered as a drag drop handle for the given Drag Drop object.
1622          * @method isHandle
1623          * @param {String} id the element id to check
1624          * @return {boolean} true if this element is a DragDrop handle, false
1625          * otherwise
1626          * @static
1627          */
1628         isHandle: function(sDDId, sHandleId) {
1629             return ( this.handleIds[sDDId] &&
1630                             this.handleIds[sDDId][sHandleId] );
1631         },
1632
1633         /**
1634          * Returns the DragDrop instance for a given id
1635          * @method getDDById
1636          * @param {String} id the id of the DragDrop object
1637          * @return {DragDrop} the drag drop object, null if it is not found
1638          * @static
1639          */
1640         getDDById: function(id) {
1641             for (var i in this.ids) {
1642                 if (this.ids[i][id]) {
1643                     return this.ids[i][id];
1644                 }
1645             }
1646             return null;
1647         },
1648
1649         /**
1650          * Fired after a registered DragDrop object gets the mousedown event.
1651          * Sets up the events required to track the object being dragged
1652          * @method handleMouseDown
1653          * @param {Event} e the event
1654          * @param oDD the DragDrop object being dragged
1655          * @private
1656          * @static
1657          */
1658         handleMouseDown: function(e, oDD) {
1659             if(Roo.QuickTips){
1660                 Roo.QuickTips.disable();
1661             }
1662             this.currentTarget = e.getTarget();
1663
1664             this.dragCurrent = oDD;
1665
1666             var el = oDD.getEl();
1667
1668             // track start position
1669             this.startX = e.getPageX();
1670             this.startY = e.getPageY();
1671
1672             this.deltaX = this.startX - el.offsetLeft;
1673             this.deltaY = this.startY - el.offsetTop;
1674
1675             this.dragThreshMet = false;
1676
1677             this.clickTimeout = setTimeout(
1678                     function() {
1679                         var DDM = Roo.dd.DDM;
1680                         DDM.startDrag(DDM.startX, DDM.startY);
1681                     },
1682                     this.clickTimeThresh );
1683         },
1684
1685         /**
1686          * Fired when either the drag pixel threshol or the mousedown hold
1687          * time threshold has been met.
1688          * @method startDrag
1689          * @param x {int} the X position of the original mousedown
1690          * @param y {int} the Y position of the original mousedown
1691          * @static
1692          */
1693         startDrag: function(x, y) {
1694             clearTimeout(this.clickTimeout);
1695             if (this.dragCurrent) {
1696                 this.dragCurrent.b4StartDrag(x, y);
1697                 this.dragCurrent.startDrag(x, y);
1698             }
1699             this.dragThreshMet = true;
1700         },
1701
1702         /**
1703          * Internal function to handle the mouseup event.  Will be invoked
1704          * from the context of the document.
1705          * @method handleMouseUp
1706          * @param {Event} e the event
1707          * @private
1708          * @static
1709          */
1710         handleMouseUp: function(e) {
1711
1712             if(Roo.QuickTips){
1713                 Roo.QuickTips.enable();
1714             }
1715             if (! this.dragCurrent) {
1716                 return;
1717             }
1718
1719             clearTimeout(this.clickTimeout);
1720
1721             if (this.dragThreshMet) {
1722                 this.fireEvents(e, true);
1723             } else {
1724             }
1725
1726             this.stopDrag(e);
1727
1728             this.stopEvent(e);
1729         },
1730
1731         /**
1732          * Utility to stop event propagation and event default, if these
1733          * features are turned on.
1734          * @method stopEvent
1735          * @param {Event} e the event as returned by this.getEvent()
1736          * @static
1737          */
1738         stopEvent: function(e){
1739             if(this.stopPropagation) {
1740                 e.stopPropagation();
1741             }
1742
1743             if (this.preventDefault) {
1744                 e.preventDefault();
1745             }
1746         },
1747
1748         /**
1749          * Internal function to clean up event handlers after the drag
1750          * operation is complete
1751          * @method stopDrag
1752          * @param {Event} e the event
1753          * @private
1754          * @static
1755          */
1756         stopDrag: function(e) {
1757             // Fire the drag end event for the item that was dragged
1758             if (this.dragCurrent) {
1759                 if (this.dragThreshMet) {
1760                     this.dragCurrent.b4EndDrag(e);
1761                     this.dragCurrent.endDrag(e);
1762                 }
1763
1764                 this.dragCurrent.onMouseUp(e);
1765             }
1766
1767             this.dragCurrent = null;
1768             this.dragOvers = {};
1769         },
1770
1771         /**
1772          * Internal function to handle the mousemove event.  Will be invoked
1773          * from the context of the html element.
1774          *
1775          * @TODO figure out what we can do about mouse events lost when the
1776          * user drags objects beyond the window boundary.  Currently we can
1777          * detect this in internet explorer by verifying that the mouse is
1778          * down during the mousemove event.  Firefox doesn't give us the
1779          * button state on the mousemove event.
1780          * @method handleMouseMove
1781          * @param {Event} e the event
1782          * @private
1783          * @static
1784          */
1785         handleMouseMove: function(e) {
1786             if (! this.dragCurrent) {
1787                 return true;
1788             }
1789
1790             // var button = e.which || e.button;
1791
1792             // check for IE mouseup outside of page boundary
1793             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
1794                 this.stopEvent(e);
1795                 return this.handleMouseUp(e);
1796             }
1797
1798             if (!this.dragThreshMet) {
1799                 var diffX = Math.abs(this.startX - e.getPageX());
1800                 var diffY = Math.abs(this.startY - e.getPageY());
1801                 if (diffX > this.clickPixelThresh ||
1802                             diffY > this.clickPixelThresh) {
1803                     this.startDrag(this.startX, this.startY);
1804                 }
1805             }
1806
1807             if (this.dragThreshMet) {
1808                 this.dragCurrent.b4Drag(e);
1809                 this.dragCurrent.onDrag(e);
1810                 if(!this.dragCurrent.moveOnly){
1811                     this.fireEvents(e, false);
1812                 }
1813             }
1814
1815             this.stopEvent(e);
1816
1817             return true;
1818         },
1819
1820         /**
1821          * Iterates over all of the DragDrop elements to find ones we are
1822          * hovering over or dropping on
1823          * @method fireEvents
1824          * @param {Event} e the event
1825          * @param {boolean} isDrop is this a drop op or a mouseover op?
1826          * @private
1827          * @static
1828          */
1829         fireEvents: function(e, isDrop) {
1830             var dc = this.dragCurrent;
1831
1832             // If the user did the mouse up outside of the window, we could
1833             // get here even though we have ended the drag.
1834             if (!dc || dc.isLocked()) {
1835                 return;
1836             }
1837
1838             var pt = e.getPoint();
1839
1840             // cache the previous dragOver array
1841             var oldOvers = [];
1842
1843             var outEvts   = [];
1844             var overEvts  = [];
1845             var dropEvts  = [];
1846             var enterEvts = [];
1847
1848             // Check to see if the object(s) we were hovering over is no longer
1849             // being hovered over so we can fire the onDragOut event
1850             for (var i in this.dragOvers) {
1851
1852                 var ddo = this.dragOvers[i];
1853
1854                 if (! this.isTypeOfDD(ddo)) {
1855                     continue;
1856                 }
1857
1858                 if (! this.isOverTarget(pt, ddo, this.mode)) {
1859                     outEvts.push( ddo );
1860                 }
1861
1862                 oldOvers[i] = true;
1863                 delete this.dragOvers[i];
1864             }
1865
1866             for (var sGroup in dc.groups) {
1867
1868                 if ("string" != typeof sGroup) {
1869                     continue;
1870                 }
1871
1872                 for (i in this.ids[sGroup]) {
1873                     var oDD = this.ids[sGroup][i];
1874                     if (! this.isTypeOfDD(oDD)) {
1875                         continue;
1876                     }
1877
1878                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
1879                         if (this.isOverTarget(pt, oDD, this.mode)) {
1880                             // look for drop interactions
1881                             if (isDrop) {
1882                                 dropEvts.push( oDD );
1883                             // look for drag enter and drag over interactions
1884                             } else {
1885
1886                                 // initial drag over: dragEnter fires
1887                                 if (!oldOvers[oDD.id]) {
1888                                     enterEvts.push( oDD );
1889                                 // subsequent drag overs: dragOver fires
1890                                 } else {
1891                                     overEvts.push( oDD );
1892                                 }
1893
1894                                 this.dragOvers[oDD.id] = oDD;
1895                             }
1896                         }
1897                     }
1898                 }
1899             }
1900
1901             if (this.mode) {
1902                 if (outEvts.length) {
1903                     dc.b4DragOut(e, outEvts);
1904                     dc.onDragOut(e, outEvts);
1905                 }
1906
1907                 if (enterEvts.length) {
1908                     dc.onDragEnter(e, enterEvts);
1909                 }
1910
1911                 if (overEvts.length) {
1912                     dc.b4DragOver(e, overEvts);
1913                     dc.onDragOver(e, overEvts);
1914                 }
1915
1916                 if (dropEvts.length) {
1917                     dc.b4DragDrop(e, dropEvts);
1918                     dc.onDragDrop(e, dropEvts);
1919                 }
1920
1921             } else {
1922                 // fire dragout events
1923                 var len = 0;
1924                 for (i=0, len=outEvts.length; i<len; ++i) {
1925                     dc.b4DragOut(e, outEvts[i].id);
1926                     dc.onDragOut(e, outEvts[i].id);
1927                 }
1928
1929                 // fire enter events
1930                 for (i=0,len=enterEvts.length; i<len; ++i) {
1931                     // dc.b4DragEnter(e, oDD.id);
1932                     dc.onDragEnter(e, enterEvts[i].id);
1933                 }
1934
1935                 // fire over events
1936                 for (i=0,len=overEvts.length; i<len; ++i) {
1937                     dc.b4DragOver(e, overEvts[i].id);
1938                     dc.onDragOver(e, overEvts[i].id);
1939                 }
1940
1941                 // fire drop events
1942                 for (i=0, len=dropEvts.length; i<len; ++i) {
1943                     dc.b4DragDrop(e, dropEvts[i].id);
1944                     dc.onDragDrop(e, dropEvts[i].id);
1945                 }
1946
1947             }
1948
1949             // notify about a drop that did not find a target
1950             if (isDrop && !dropEvts.length) {
1951                 dc.onInvalidDrop(e);
1952             }
1953
1954         },
1955
1956         /**
1957          * Helper function for getting the best match from the list of drag
1958          * and drop objects returned by the drag and drop events when we are
1959          * in INTERSECT mode.  It returns either the first object that the
1960          * cursor is over, or the object that has the greatest overlap with
1961          * the dragged element.
1962          * @method getBestMatch
1963          * @param  {DragDrop[]} dds The array of drag and drop objects
1964          * targeted
1965          * @return {DragDrop}       The best single match
1966          * @static
1967          */
1968         getBestMatch: function(dds) {
1969             var winner = null;
1970             // Return null if the input is not what we expect
1971             //if (!dds || !dds.length || dds.length == 0) {
1972                // winner = null;
1973             // If there is only one item, it wins
1974             //} else if (dds.length == 1) {
1975
1976             var len = dds.length;
1977
1978             if (len == 1) {
1979                 winner = dds[0];
1980             } else {
1981                 // Loop through the targeted items
1982                 for (var i=0; i<len; ++i) {
1983                     var dd = dds[i];
1984                     // If the cursor is over the object, it wins.  If the
1985                     // cursor is over multiple matches, the first one we come
1986                     // to wins.
1987                     if (dd.cursorIsOver) {
1988                         winner = dd;
1989                         break;
1990                     // Otherwise the object with the most overlap wins
1991                     } else {
1992                         if (!winner ||
1993                             winner.overlap.getArea() < dd.overlap.getArea()) {
1994                             winner = dd;
1995                         }
1996                     }
1997                 }
1998             }
1999
2000             return winner;
2001         },
2002
2003         /**
2004          * Refreshes the cache of the top-left and bottom-right points of the
2005          * drag and drop objects in the specified group(s).  This is in the
2006          * format that is stored in the drag and drop instance, so typical
2007          * usage is:
2008          * <code>
2009          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
2010          * </code>
2011          * Alternatively:
2012          * <code>
2013          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
2014          * </code>
2015          * @TODO this really should be an indexed array.  Alternatively this
2016          * method could accept both.
2017          * @method refreshCache
2018          * @param {Object} groups an associative array of groups to refresh
2019          * @static
2020          */
2021         refreshCache: function(groups) {
2022             for (var sGroup in groups) {
2023                 if ("string" != typeof sGroup) {
2024                     continue;
2025                 }
2026                 for (var i in this.ids[sGroup]) {
2027                     var oDD = this.ids[sGroup][i];
2028
2029                     if (this.isTypeOfDD(oDD)) {
2030                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
2031                         var loc = this.getLocation(oDD);
2032                         if (loc) {
2033                             this.locationCache[oDD.id] = loc;
2034                         } else {
2035                             delete this.locationCache[oDD.id];
2036                             // this will unregister the drag and drop object if
2037                             // the element is not in a usable state
2038                             // oDD.unreg();
2039                         }
2040                     }
2041                 }
2042             }
2043         },
2044
2045         /**
2046          * This checks to make sure an element exists and is in the DOM.  The
2047          * main purpose is to handle cases where innerHTML is used to remove
2048          * drag and drop objects from the DOM.  IE provides an 'unspecified
2049          * error' when trying to access the offsetParent of such an element
2050          * @method verifyEl
2051          * @param {HTMLElement} el the element to check
2052          * @return {boolean} true if the element looks usable
2053          * @static
2054          */
2055         verifyEl: function(el) {
2056             if (el) {
2057                 var parent;
2058                 if(Roo.isIE){
2059                     try{
2060                         parent = el.offsetParent;
2061                     }catch(e){}
2062                 }else{
2063                     parent = el.offsetParent;
2064                 }
2065                 if (parent) {
2066                     return true;
2067                 }
2068             }
2069
2070             return false;
2071         },
2072
2073         /**
2074          * Returns a Region object containing the drag and drop element's position
2075          * and size, including the padding configured for it
2076          * @method getLocation
2077          * @param {DragDrop} oDD the drag and drop object to get the
2078          *                       location for
2079          * @return {Roo.lib.Region} a Region object representing the total area
2080          *                             the element occupies, including any padding
2081          *                             the instance is configured for.
2082          * @static
2083          */
2084         getLocation: function(oDD) {
2085             if (! this.isTypeOfDD(oDD)) {
2086                 return null;
2087             }
2088
2089             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
2090
2091             try {
2092                 pos= Roo.lib.Dom.getXY(el);
2093             } catch (e) { }
2094
2095             if (!pos) {
2096                 return null;
2097             }
2098
2099             x1 = pos[0];
2100             x2 = x1 + el.offsetWidth;
2101             y1 = pos[1];
2102             y2 = y1 + el.offsetHeight;
2103
2104             t = y1 - oDD.padding[0];
2105             r = x2 + oDD.padding[1];
2106             b = y2 + oDD.padding[2];
2107             l = x1 - oDD.padding[3];
2108
2109             return new Roo.lib.Region( t, r, b, l );
2110         },
2111
2112         /**
2113          * Checks the cursor location to see if it over the target
2114          * @method isOverTarget
2115          * @param {Roo.lib.Point} pt The point to evaluate
2116          * @param {DragDrop} oTarget the DragDrop object we are inspecting
2117          * @return {boolean} true if the mouse is over the target
2118          * @private
2119          * @static
2120          */
2121         isOverTarget: function(pt, oTarget, intersect) {
2122             // use cache if available
2123             var loc = this.locationCache[oTarget.id];
2124             if (!loc || !this.useCache) {
2125                 loc = this.getLocation(oTarget);
2126                 this.locationCache[oTarget.id] = loc;
2127
2128             }
2129
2130             if (!loc) {
2131                 return false;
2132             }
2133
2134             oTarget.cursorIsOver = loc.contains( pt );
2135
2136             // DragDrop is using this as a sanity check for the initial mousedown
2137             // in this case we are done.  In POINT mode, if the drag obj has no
2138             // contraints, we are also done. Otherwise we need to evaluate the
2139             // location of the target as related to the actual location of the
2140             // dragged element.
2141             var dc = this.dragCurrent;
2142             if (!dc || !dc.getTargetCoord ||
2143                     (!intersect && !dc.constrainX && !dc.constrainY)) {
2144                 return oTarget.cursorIsOver;
2145             }
2146
2147             oTarget.overlap = null;
2148
2149             // Get the current location of the drag element, this is the
2150             // location of the mouse event less the delta that represents
2151             // where the original mousedown happened on the element.  We
2152             // need to consider constraints and ticks as well.
2153             var pos = dc.getTargetCoord(pt.x, pt.y);
2154
2155             var el = dc.getDragEl();
2156             var curRegion = new Roo.lib.Region( pos.y,
2157                                                    pos.x + el.offsetWidth,
2158                                                    pos.y + el.offsetHeight,
2159                                                    pos.x );
2160
2161             var overlap = curRegion.intersect(loc);
2162
2163             if (overlap) {
2164                 oTarget.overlap = overlap;
2165                 return (intersect) ? true : oTarget.cursorIsOver;
2166             } else {
2167                 return false;
2168             }
2169         },
2170
2171         /**
2172          * unload event handler
2173          * @method _onUnload
2174          * @private
2175          * @static
2176          */
2177         _onUnload: function(e, me) {
2178             Roo.dd.DragDropMgr.unregAll();
2179         },
2180
2181         /**
2182          * Cleans up the drag and drop events and objects.
2183          * @method unregAll
2184          * @private
2185          * @static
2186          */
2187         unregAll: function() {
2188
2189             if (this.dragCurrent) {
2190                 this.stopDrag();
2191                 this.dragCurrent = null;
2192             }
2193
2194             this._execOnAll("unreg", []);
2195
2196             for (i in this.elementCache) {
2197                 delete this.elementCache[i];
2198             }
2199
2200             this.elementCache = {};
2201             this.ids = {};
2202         },
2203
2204         /**
2205          * A cache of DOM elements
2206          * @property elementCache
2207          * @private
2208          * @static
2209          */
2210         elementCache: {},
2211
2212         /**
2213          * Get the wrapper for the DOM element specified
2214          * @method getElWrapper
2215          * @param {String} id the id of the element to get
2216          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
2217          * @private
2218          * @deprecated This wrapper isn't that useful
2219          * @static
2220          */
2221         getElWrapper: function(id) {
2222             var oWrapper = this.elementCache[id];
2223             if (!oWrapper || !oWrapper.el) {
2224                 oWrapper = this.elementCache[id] =
2225                     new this.ElementWrapper(Roo.getDom(id));
2226             }
2227             return oWrapper;
2228         },
2229
2230         /**
2231          * Returns the actual DOM element
2232          * @method getElement
2233          * @param {String} id the id of the elment to get
2234          * @return {Object} The element
2235          * @deprecated use Roo.getDom instead
2236          * @static
2237          */
2238         getElement: function(id) {
2239             return Roo.getDom(id);
2240         },
2241
2242         /**
2243          * Returns the style property for the DOM element (i.e.,
2244          * document.getElById(id).style)
2245          * @method getCss
2246          * @param {String} id the id of the elment to get
2247          * @return {Object} The style property of the element
2248          * @deprecated use Roo.getDom instead
2249          * @static
2250          */
2251         getCss: function(id) {
2252             var el = Roo.getDom(id);
2253             return (el) ? el.style : null;
2254         },
2255
2256         /**
2257          * Inner class for cached elements
2258          * @class DragDropMgr.ElementWrapper
2259          * @for DragDropMgr
2260          * @private
2261          * @deprecated
2262          */
2263         ElementWrapper: function(el) {
2264                 /**
2265                  * The element
2266                  * @property el
2267                  */
2268                 this.el = el || null;
2269                 /**
2270                  * The element id
2271                  * @property id
2272                  */
2273                 this.id = this.el && el.id;
2274                 /**
2275                  * A reference to the style property
2276                  * @property css
2277                  */
2278                 this.css = this.el && el.style;
2279             },
2280
2281         /**
2282          * Returns the X position of an html element
2283          * @method getPosX
2284          * @param el the element for which to get the position
2285          * @return {int} the X coordinate
2286          * @for DragDropMgr
2287          * @deprecated use Roo.lib.Dom.getX instead
2288          * @static
2289          */
2290         getPosX: function(el) {
2291             return Roo.lib.Dom.getX(el);
2292         },
2293
2294         /**
2295          * Returns the Y position of an html element
2296          * @method getPosY
2297          * @param el the element for which to get the position
2298          * @return {int} the Y coordinate
2299          * @deprecated use Roo.lib.Dom.getY instead
2300          * @static
2301          */
2302         getPosY: function(el) {
2303             return Roo.lib.Dom.getY(el);
2304         },
2305
2306         /**
2307          * Swap two nodes.  In IE, we use the native method, for others we
2308          * emulate the IE behavior
2309          * @method swapNode
2310          * @param n1 the first node to swap
2311          * @param n2 the other node to swap
2312          * @static
2313          */
2314         swapNode: function(n1, n2) {
2315             if (n1.swapNode) {
2316                 n1.swapNode(n2);
2317             } else {
2318                 var p = n2.parentNode;
2319                 var s = n2.nextSibling;
2320
2321                 if (s == n1) {
2322                     p.insertBefore(n1, n2);
2323                 } else if (n2 == n1.nextSibling) {
2324                     p.insertBefore(n2, n1);
2325                 } else {
2326                     n1.parentNode.replaceChild(n2, n1);
2327                     p.insertBefore(n1, s);
2328                 }
2329             }
2330         },
2331
2332         /**
2333          * Returns the current scroll position
2334          * @method getScroll
2335          * @private
2336          * @static
2337          */
2338         getScroll: function () {
2339             var t, l, dde=document.documentElement, db=document.body;
2340             if (dde && (dde.scrollTop || dde.scrollLeft)) {
2341                 t = dde.scrollTop;
2342                 l = dde.scrollLeft;
2343             } else if (db) {
2344                 t = db.scrollTop;
2345                 l = db.scrollLeft;
2346             } else {
2347
2348             }
2349             return { top: t, left: l };
2350         },
2351
2352         /**
2353          * Returns the specified element style property
2354          * @method getStyle
2355          * @param {HTMLElement} el          the element
2356          * @param {string}      styleProp   the style property
2357          * @return {string} The value of the style property
2358          * @deprecated use Roo.lib.Dom.getStyle
2359          * @static
2360          */
2361         getStyle: function(el, styleProp) {
2362             return Roo.fly(el).getStyle(styleProp);
2363         },
2364
2365         /**
2366          * Gets the scrollTop
2367          * @method getScrollTop
2368          * @return {int} the document's scrollTop
2369          * @static
2370          */
2371         getScrollTop: function () { return this.getScroll().top; },
2372
2373         /**
2374          * Gets the scrollLeft
2375          * @method getScrollLeft
2376          * @return {int} the document's scrollTop
2377          * @static
2378          */
2379         getScrollLeft: function () { return this.getScroll().left; },
2380
2381         /**
2382          * Sets the x/y position of an element to the location of the
2383          * target element.
2384          * @method moveToEl
2385          * @param {HTMLElement} moveEl      The element to move
2386          * @param {HTMLElement} targetEl    The position reference element
2387          * @static
2388          */
2389         moveToEl: function (moveEl, targetEl) {
2390             var aCoord = Roo.lib.Dom.getXY(targetEl);
2391             Roo.lib.Dom.setXY(moveEl, aCoord);
2392         },
2393
2394         /**
2395          * Numeric array sort function
2396          * @method numericSort
2397          * @static
2398          */
2399         numericSort: function(a, b) { return (a - b); },
2400
2401         /**
2402          * Internal counter
2403          * @property _timeoutCount
2404          * @private
2405          * @static
2406          */
2407         _timeoutCount: 0,
2408
2409         /**
2410          * Trying to make the load order less important.  Without this we get
2411          * an error if this file is loaded before the Event Utility.
2412          * @method _addListeners
2413          * @private
2414          * @static
2415          */
2416         _addListeners: function() {
2417             var DDM = Roo.dd.DDM;
2418             if ( Roo.lib.Event && document ) {
2419                 DDM._onLoad();
2420             } else {
2421                 if (DDM._timeoutCount > 2000) {
2422                 } else {
2423                     setTimeout(DDM._addListeners, 10);
2424                     if (document && document.body) {
2425                         DDM._timeoutCount += 1;
2426                     }
2427                 }
2428             }
2429         },
2430
2431         /**
2432          * Recursively searches the immediate parent and all child nodes for
2433          * the handle element in order to determine wheter or not it was
2434          * clicked.
2435          * @method handleWasClicked
2436          * @param node the html element to inspect
2437          * @static
2438          */
2439         handleWasClicked: function(node, id) {
2440             if (this.isHandle(id, node.id)) {
2441                 return true;
2442             } else {
2443                 // check to see if this is a text node child of the one we want
2444                 var p = node.parentNode;
2445
2446                 while (p) {
2447                     if (this.isHandle(id, p.id)) {
2448                         return true;
2449                     } else {
2450                         p = p.parentNode;
2451                     }
2452                 }
2453             }
2454
2455             return false;
2456         }
2457
2458     };
2459
2460 }();
2461
2462 // shorter alias, save a few bytes
2463 Roo.dd.DDM = Roo.dd.DragDropMgr;
2464 Roo.dd.DDM._addListeners();
2465
2466 }/*
2467  * Based on:
2468  * Ext JS Library 1.1.1
2469  * Copyright(c) 2006-2007, Ext JS, LLC.
2470  *
2471  * Originally Released Under LGPL - original licence link has changed is not relivant.
2472  *
2473  * Fork - LGPL
2474  * <script type="text/javascript">
2475  */
2476
2477 /**
2478  * @class Roo.dd.DD
2479  * A DragDrop implementation where the linked element follows the
2480  * mouse cursor during a drag.
2481  * @extends Roo.dd.DragDrop
2482  * @constructor
2483  * @param {String} id the id of the linked element
2484  * @param {String} sGroup the group of related DragDrop items
2485  * @param {object} config an object containing configurable attributes
2486  *                Valid properties for DD:
2487  *                    scroll
2488  */
2489 Roo.dd.DD = function(id, sGroup, config) {
2490     if (id) {
2491         this.init(id, sGroup, config);
2492     }
2493 };
2494
2495 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
2496
2497     /**
2498      * When set to true, the utility automatically tries to scroll the browser
2499      * window wehn a drag and drop element is dragged near the viewport boundary.
2500      * Defaults to true.
2501      * @property scroll
2502      * @type boolean
2503      */
2504     scroll: true,
2505
2506     /**
2507      * Sets the pointer offset to the distance between the linked element's top
2508      * left corner and the location the element was clicked
2509      * @method autoOffset
2510      * @param {int} iPageX the X coordinate of the click
2511      * @param {int} iPageY the Y coordinate of the click
2512      */
2513     autoOffset: function(iPageX, iPageY) {
2514         var x = iPageX - this.startPageX;
2515         var y = iPageY - this.startPageY;
2516         this.setDelta(x, y);
2517     },
2518
2519     /**
2520      * Sets the pointer offset.  You can call this directly to force the
2521      * offset to be in a particular location (e.g., pass in 0,0 to set it
2522      * to the center of the object)
2523      * @method setDelta
2524      * @param {int} iDeltaX the distance from the left
2525      * @param {int} iDeltaY the distance from the top
2526      */
2527     setDelta: function(iDeltaX, iDeltaY) {
2528         this.deltaX = iDeltaX;
2529         this.deltaY = iDeltaY;
2530     },
2531
2532     /**
2533      * Sets the drag element to the location of the mousedown or click event,
2534      * maintaining the cursor location relative to the location on the element
2535      * that was clicked.  Override this if you want to place the element in a
2536      * location other than where the cursor is.
2537      * @method setDragElPos
2538      * @param {int} iPageX the X coordinate of the mousedown or drag event
2539      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2540      */
2541     setDragElPos: function(iPageX, iPageY) {
2542         // the first time we do this, we are going to check to make sure
2543         // the element has css positioning
2544
2545         var el = this.getDragEl();
2546         this.alignElWithMouse(el, iPageX, iPageY);
2547     },
2548
2549     /**
2550      * Sets the element to the location of the mousedown or click event,
2551      * maintaining the cursor location relative to the location on the element
2552      * that was clicked.  Override this if you want to place the element in a
2553      * location other than where the cursor is.
2554      * @method alignElWithMouse
2555      * @param {HTMLElement} el the element to move
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     alignElWithMouse: function(el, iPageX, iPageY) {
2560         var oCoord = this.getTargetCoord(iPageX, iPageY);
2561         var fly = el.dom ? el : Roo.fly(el);
2562         if (!this.deltaSetXY) {
2563             var aCoord = [oCoord.x, oCoord.y];
2564             fly.setXY(aCoord);
2565             var newLeft = fly.getLeft(true);
2566             var newTop  = fly.getTop(true);
2567             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
2568         } else {
2569             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
2570         }
2571
2572         this.cachePosition(oCoord.x, oCoord.y);
2573         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
2574         return oCoord;
2575     },
2576
2577     /**
2578      * Saves the most recent position so that we can reset the constraints and
2579      * tick marks on-demand.  We need to know this so that we can calculate the
2580      * number of pixels the element is offset from its original position.
2581      * @method cachePosition
2582      * @param iPageX the current x position (optional, this just makes it so we
2583      * don't have to look it up again)
2584      * @param iPageY the current y position (optional, this just makes it so we
2585      * don't have to look it up again)
2586      */
2587     cachePosition: function(iPageX, iPageY) {
2588         if (iPageX) {
2589             this.lastPageX = iPageX;
2590             this.lastPageY = iPageY;
2591         } else {
2592             var aCoord = Roo.lib.Dom.getXY(this.getEl());
2593             this.lastPageX = aCoord[0];
2594             this.lastPageY = aCoord[1];
2595         }
2596     },
2597
2598     /**
2599      * Auto-scroll the window if the dragged object has been moved beyond the
2600      * visible window boundary.
2601      * @method autoScroll
2602      * @param {int} x the drag element's x position
2603      * @param {int} y the drag element's y position
2604      * @param {int} h the height of the drag element
2605      * @param {int} w the width of the drag element
2606      * @private
2607      */
2608     autoScroll: function(x, y, h, w) {
2609
2610         if (this.scroll) {
2611             // The client height
2612             var clientH = Roo.lib.Dom.getViewWidth();
2613
2614             // The client width
2615             var clientW = Roo.lib.Dom.getViewHeight();
2616
2617             // The amt scrolled down
2618             var st = this.DDM.getScrollTop();
2619
2620             // The amt scrolled right
2621             var sl = this.DDM.getScrollLeft();
2622
2623             // Location of the bottom of the element
2624             var bot = h + y;
2625
2626             // Location of the right of the element
2627             var right = w + x;
2628
2629             // The distance from the cursor to the bottom of the visible area,
2630             // adjusted so that we don't scroll if the cursor is beyond the
2631             // element drag constraints
2632             var toBot = (clientH + st - y - this.deltaY);
2633
2634             // The distance from the cursor to the right of the visible area
2635             var toRight = (clientW + sl - x - this.deltaX);
2636
2637
2638             // How close to the edge the cursor must be before we scroll
2639             // var thresh = (document.all) ? 100 : 40;
2640             var thresh = 40;
2641
2642             // How many pixels to scroll per autoscroll op.  This helps to reduce
2643             // clunky scrolling. IE is more sensitive about this ... it needs this
2644             // value to be higher.
2645             var scrAmt = (document.all) ? 80 : 30;
2646
2647             // Scroll down if we are near the bottom of the visible page and the
2648             // obj extends below the crease
2649             if ( bot > clientH && toBot < thresh ) {
2650                 window.scrollTo(sl, st + scrAmt);
2651             }
2652
2653             // Scroll up if the window is scrolled down and the top of the object
2654             // goes above the top border
2655             if ( y < st && st > 0 && y - st < thresh ) {
2656                 window.scrollTo(sl, st - scrAmt);
2657             }
2658
2659             // Scroll right if the obj is beyond the right border and the cursor is
2660             // near the border.
2661             if ( right > clientW && toRight < thresh ) {
2662                 window.scrollTo(sl + scrAmt, st);
2663             }
2664
2665             // Scroll left if the window has been scrolled to the right and the obj
2666             // extends past the left border
2667             if ( x < sl && sl > 0 && x - sl < thresh ) {
2668                 window.scrollTo(sl - scrAmt, st);
2669             }
2670         }
2671     },
2672
2673     /**
2674      * Finds the location the element should be placed if we want to move
2675      * it to where the mouse location less the click offset would place us.
2676      * @method getTargetCoord
2677      * @param {int} iPageX the X coordinate of the click
2678      * @param {int} iPageY the Y coordinate of the click
2679      * @return an object that contains the coordinates (Object.x and Object.y)
2680      * @private
2681      */
2682     getTargetCoord: function(iPageX, iPageY) {
2683
2684
2685         var x = iPageX - this.deltaX;
2686         var y = iPageY - this.deltaY;
2687
2688         if (this.constrainX) {
2689             if (x < this.minX) { x = this.minX; }
2690             if (x > this.maxX) { x = this.maxX; }
2691         }
2692
2693         if (this.constrainY) {
2694             if (y < this.minY) { y = this.minY; }
2695             if (y > this.maxY) { y = this.maxY; }
2696         }
2697
2698         x = this.getTick(x, this.xTicks);
2699         y = this.getTick(y, this.yTicks);
2700
2701
2702         return {x:x, y:y};
2703     },
2704
2705     /*
2706      * Sets up config options specific to this class. Overrides
2707      * Roo.dd.DragDrop, but all versions of this method through the
2708      * inheritance chain are called
2709      */
2710     applyConfig: function() {
2711         Roo.dd.DD.superclass.applyConfig.call(this);
2712         this.scroll = (this.config.scroll !== false);
2713     },
2714
2715     /*
2716      * Event that fires prior to the onMouseDown event.  Overrides
2717      * Roo.dd.DragDrop.
2718      */
2719     b4MouseDown: function(e) {
2720         // this.resetConstraints();
2721         this.autoOffset(e.getPageX(),
2722                             e.getPageY());
2723     },
2724
2725     /*
2726      * Event that fires prior to the onDrag event.  Overrides
2727      * Roo.dd.DragDrop.
2728      */
2729     b4Drag: function(e) {
2730         this.setDragElPos(e.getPageX(),
2731                             e.getPageY());
2732     },
2733
2734     toString: function() {
2735         return ("DD " + this.id);
2736     }
2737
2738     //////////////////////////////////////////////////////////////////////////
2739     // Debugging ygDragDrop events that can be overridden
2740     //////////////////////////////////////////////////////////////////////////
2741     /*
2742     startDrag: function(x, y) {
2743     },
2744
2745     onDrag: function(e) {
2746     },
2747
2748     onDragEnter: function(e, id) {
2749     },
2750
2751     onDragOver: function(e, id) {
2752     },
2753
2754     onDragOut: function(e, id) {
2755     },
2756
2757     onDragDrop: function(e, id) {
2758     },
2759
2760     endDrag: function(e) {
2761     }
2762
2763     */
2764
2765 });/*
2766  * Based on:
2767  * Ext JS Library 1.1.1
2768  * Copyright(c) 2006-2007, Ext JS, LLC.
2769  *
2770  * Originally Released Under LGPL - original licence link has changed is not relivant.
2771  *
2772  * Fork - LGPL
2773  * <script type="text/javascript">
2774  */
2775
2776 /**
2777  * @class Roo.dd.DDProxy
2778  * A DragDrop implementation that inserts an empty, bordered div into
2779  * the document that follows the cursor during drag operations.  At the time of
2780  * the click, the frame div is resized to the dimensions of the linked html
2781  * element, and moved to the exact location of the linked element.
2782  *
2783  * References to the "frame" element refer to the single proxy element that
2784  * was created to be dragged in place of all DDProxy elements on the
2785  * page.
2786  *
2787  * @extends Roo.dd.DD
2788  * @constructor
2789  * @param {String} id the id of the linked html element
2790  * @param {String} sGroup the group of related DragDrop objects
2791  * @param {object} config an object containing configurable attributes
2792  *                Valid properties for DDProxy in addition to those in DragDrop:
2793  *                   resizeFrame, centerFrame, dragElId
2794  */
2795 Roo.dd.DDProxy = function(id, sGroup, config) {
2796     if (id) {
2797         this.init(id, sGroup, config);
2798         this.initFrame();
2799     }
2800 };
2801
2802 /**
2803  * The default drag frame div id
2804  * @property Roo.dd.DDProxy.dragElId
2805  * @type String
2806  * @static
2807  */
2808 Roo.dd.DDProxy.dragElId = "ygddfdiv";
2809
2810 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
2811
2812     /**
2813      * By default we resize the drag frame to be the same size as the element
2814      * we want to drag (this is to get the frame effect).  We can turn it off
2815      * if we want a different behavior.
2816      * @property resizeFrame
2817      * @type boolean
2818      */
2819     resizeFrame: true,
2820
2821     /**
2822      * By default the frame is positioned exactly where the drag element is, so
2823      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
2824      * you do not have constraints on the obj is to have the drag frame centered
2825      * around the cursor.  Set centerFrame to true for this effect.
2826      * @property centerFrame
2827      * @type boolean
2828      */
2829     centerFrame: false,
2830
2831     /**
2832      * Creates the proxy element if it does not yet exist
2833      * @method createFrame
2834      */
2835     createFrame: function() {
2836         var self = this;
2837         var body = document.body;
2838
2839         if (!body || !body.firstChild) {
2840             setTimeout( function() { self.createFrame(); }, 50 );
2841             return;
2842         }
2843
2844         var div = this.getDragEl();
2845
2846         if (!div) {
2847             div    = document.createElement("div");
2848             div.id = this.dragElId;
2849             var s  = div.style;
2850
2851             s.position   = "absolute";
2852             s.visibility = "hidden";
2853             s.cursor     = "move";
2854             s.border     = "2px solid #aaa";
2855             s.zIndex     = 999;
2856
2857             // appendChild can blow up IE if invoked prior to the window load event
2858             // while rendering a table.  It is possible there are other scenarios
2859             // that would cause this to happen as well.
2860             body.insertBefore(div, body.firstChild);
2861         }
2862     },
2863
2864     /**
2865      * Initialization for the drag frame element.  Must be called in the
2866      * constructor of all subclasses
2867      * @method initFrame
2868      */
2869     initFrame: function() {
2870         this.createFrame();
2871     },
2872
2873     applyConfig: function() {
2874         Roo.dd.DDProxy.superclass.applyConfig.call(this);
2875
2876         this.resizeFrame = (this.config.resizeFrame !== false);
2877         this.centerFrame = (this.config.centerFrame);
2878         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
2879     },
2880
2881     /**
2882      * Resizes the drag frame to the dimensions of the clicked object, positions
2883      * it over the object, and finally displays it
2884      * @method showFrame
2885      * @param {int} iPageX X click position
2886      * @param {int} iPageY Y click position
2887      * @private
2888      */
2889     showFrame: function(iPageX, iPageY) {
2890         var el = this.getEl();
2891         var dragEl = this.getDragEl();
2892         var s = dragEl.style;
2893
2894         this._resizeProxy();
2895
2896         if (this.centerFrame) {
2897             this.setDelta( Math.round(parseInt(s.width,  10)/2),
2898                            Math.round(parseInt(s.height, 10)/2) );
2899         }
2900
2901         this.setDragElPos(iPageX, iPageY);
2902
2903         Roo.fly(dragEl).show();
2904     },
2905
2906     /**
2907      * The proxy is automatically resized to the dimensions of the linked
2908      * element when a drag is initiated, unless resizeFrame is set to false
2909      * @method _resizeProxy
2910      * @private
2911      */
2912     _resizeProxy: function() {
2913         if (this.resizeFrame) {
2914             var el = this.getEl();
2915             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
2916         }
2917     },
2918
2919     // overrides Roo.dd.DragDrop
2920     b4MouseDown: function(e) {
2921         var x = e.getPageX();
2922         var y = e.getPageY();
2923         this.autoOffset(x, y);
2924         this.setDragElPos(x, y);
2925     },
2926
2927     // overrides Roo.dd.DragDrop
2928     b4StartDrag: function(x, y) {
2929         // show the drag frame
2930         this.showFrame(x, y);
2931     },
2932
2933     // overrides Roo.dd.DragDrop
2934     b4EndDrag: function(e) {
2935         Roo.fly(this.getDragEl()).hide();
2936     },
2937
2938     // overrides Roo.dd.DragDrop
2939     // By default we try to move the element to the last location of the frame.
2940     // This is so that the default behavior mirrors that of Roo.dd.DD.
2941     endDrag: function(e) {
2942
2943         var lel = this.getEl();
2944         var del = this.getDragEl();
2945
2946         // Show the drag frame briefly so we can get its position
2947         del.style.visibility = "";
2948
2949         this.beforeMove();
2950         // Hide the linked element before the move to get around a Safari
2951         // rendering bug.
2952         lel.style.visibility = "hidden";
2953         Roo.dd.DDM.moveToEl(lel, del);
2954         del.style.visibility = "hidden";
2955         lel.style.visibility = "";
2956
2957         this.afterDrag();
2958     },
2959
2960     beforeMove : function(){
2961
2962     },
2963
2964     afterDrag : function(){
2965
2966     },
2967
2968     toString: function() {
2969         return ("DDProxy " + this.id);
2970     }
2971
2972 });
2973 /*
2974  * Based on:
2975  * Ext JS Library 1.1.1
2976  * Copyright(c) 2006-2007, Ext JS, LLC.
2977  *
2978  * Originally Released Under LGPL - original licence link has changed is not relivant.
2979  *
2980  * Fork - LGPL
2981  * <script type="text/javascript">
2982  */
2983
2984  /**
2985  * @class Roo.dd.DDTarget
2986  * A DragDrop implementation that does not move, but can be a drop
2987  * target.  You would get the same result by simply omitting implementation
2988  * for the event callbacks, but this way we reduce the processing cost of the
2989  * event listener and the callbacks.
2990  * @extends Roo.dd.DragDrop
2991  * @constructor
2992  * @param {String} id the id of the element that is a drop target
2993  * @param {String} sGroup the group of related DragDrop objects
2994  * @param {object} config an object containing configurable attributes
2995  *                 Valid properties for DDTarget in addition to those in
2996  *                 DragDrop:
2997  *                    none
2998  */
2999 Roo.dd.DDTarget = function(id, sGroup, config) {
3000     if (id) {
3001         this.initTarget(id, sGroup, config);
3002     }
3003 };
3004
3005 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
3006 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
3007     toString: function() {
3008         return ("DDTarget " + this.id);
3009     }
3010 });
3011 /*
3012  * Based on:
3013  * Ext JS Library 1.1.1
3014  * Copyright(c) 2006-2007, Ext JS, LLC.
3015  *
3016  * Originally Released Under LGPL - original licence link has changed is not relivant.
3017  *
3018  * Fork - LGPL
3019  * <script type="text/javascript">
3020  */
3021  
3022
3023 /**
3024  * @class Roo.dd.ScrollManager
3025  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
3026  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
3027  * @singleton
3028  */
3029 Roo.dd.ScrollManager = function(){
3030     var ddm = Roo.dd.DragDropMgr;
3031     var els = {};
3032     var dragEl = null;
3033     var proc = {};
3034     
3035     var onStop = function(e){
3036         dragEl = null;
3037         clearProc();
3038     };
3039     
3040     var triggerRefresh = function(){
3041         if(ddm.dragCurrent){
3042              ddm.refreshCache(ddm.dragCurrent.groups);
3043         }
3044     };
3045     
3046     var doScroll = function(){
3047         if(ddm.dragCurrent){
3048             var dds = Roo.dd.ScrollManager;
3049             if(!dds.animate){
3050                 if(proc.el.scroll(proc.dir, dds.increment)){
3051                     triggerRefresh();
3052                 }
3053             }else{
3054                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
3055             }
3056         }
3057     };
3058     
3059     var clearProc = function(){
3060         if(proc.id){
3061             clearInterval(proc.id);
3062         }
3063         proc.id = 0;
3064         proc.el = null;
3065         proc.dir = "";
3066     };
3067     
3068     var startProc = function(el, dir){
3069         clearProc();
3070         proc.el = el;
3071         proc.dir = dir;
3072         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
3073     };
3074     
3075     var onFire = function(e, isDrop){
3076         if(isDrop || !ddm.dragCurrent){ return; }
3077         var dds = Roo.dd.ScrollManager;
3078         if(!dragEl || dragEl != ddm.dragCurrent){
3079             dragEl = ddm.dragCurrent;
3080             // refresh regions on drag start
3081             dds.refreshCache();
3082         }
3083         
3084         var xy = Roo.lib.Event.getXY(e);
3085         var pt = new Roo.lib.Point(xy[0], xy[1]);
3086         for(var id in els){
3087             var el = els[id], r = el._region;
3088             if(r && r.contains(pt) && el.isScrollable()){
3089                 if(r.bottom - pt.y <= dds.thresh){
3090                     if(proc.el != el){
3091                         startProc(el, "down");
3092                     }
3093                     return;
3094                 }else if(r.right - pt.x <= dds.thresh){
3095                     if(proc.el != el){
3096                         startProc(el, "left");
3097                     }
3098                     return;
3099                 }else if(pt.y - r.top <= dds.thresh){
3100                     if(proc.el != el){
3101                         startProc(el, "up");
3102                     }
3103                     return;
3104                 }else if(pt.x - r.left <= dds.thresh){
3105                     if(proc.el != el){
3106                         startProc(el, "right");
3107                     }
3108                     return;
3109                 }
3110             }
3111         }
3112         clearProc();
3113     };
3114     
3115     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
3116     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
3117     
3118     return {
3119         /**
3120          * Registers new overflow element(s) to auto scroll
3121          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
3122          */
3123         register : function(el){
3124             if(el instanceof Array){
3125                 for(var i = 0, len = el.length; i < len; i++) {
3126                         this.register(el[i]);
3127                 }
3128             }else{
3129                 el = Roo.get(el);
3130                 els[el.id] = el;
3131             }
3132         },
3133         
3134         /**
3135          * Unregisters overflow element(s) so they are no longer scrolled
3136          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
3137          */
3138         unregister : function(el){
3139             if(el instanceof Array){
3140                 for(var i = 0, len = el.length; i < len; i++) {
3141                         this.unregister(el[i]);
3142                 }
3143             }else{
3144                 el = Roo.get(el);
3145                 delete els[el.id];
3146             }
3147         },
3148         
3149         /**
3150          * The number of pixels from the edge of a container the pointer needs to be to 
3151          * trigger scrolling (defaults to 25)
3152          * @type Number
3153          */
3154         thresh : 25,
3155         
3156         /**
3157          * The number of pixels to scroll in each scroll increment (defaults to 50)
3158          * @type Number
3159          */
3160         increment : 100,
3161         
3162         /**
3163          * The frequency of scrolls in milliseconds (defaults to 500)
3164          * @type Number
3165          */
3166         frequency : 500,
3167         
3168         /**
3169          * True to animate the scroll (defaults to true)
3170          * @type Boolean
3171          */
3172         animate: true,
3173         
3174         /**
3175          * The animation duration in seconds - 
3176          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
3177          * @type Number
3178          */
3179         animDuration: .4,
3180         
3181         /**
3182          * Manually trigger a cache refresh.
3183          */
3184         refreshCache : function(){
3185             for(var id in els){
3186                 if(typeof els[id] == 'object'){ // for people extending the object prototype
3187                     els[id]._region = els[id].getRegion();
3188                 }
3189             }
3190         }
3191     };
3192 }();/*
3193  * Based on:
3194  * Ext JS Library 1.1.1
3195  * Copyright(c) 2006-2007, Ext JS, LLC.
3196  *
3197  * Originally Released Under LGPL - original licence link has changed is not relivant.
3198  *
3199  * Fork - LGPL
3200  * <script type="text/javascript">
3201  */
3202  
3203
3204 /**
3205  * @class Roo.dd.Registry
3206  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
3207  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
3208  * @singleton
3209  */
3210 Roo.dd.Registry = function(){
3211     var elements = {}; 
3212     var handles = {}; 
3213     var autoIdSeed = 0;
3214
3215     var getId = function(el, autogen){
3216         if(typeof el == "string"){
3217             return el;
3218         }
3219         var id = el.id;
3220         if(!id && autogen !== false){
3221             id = "roodd-" + (++autoIdSeed);
3222             el.id = id;
3223         }
3224         return id;
3225     };
3226     
3227     return {
3228     /**
3229      * Register a drag drop element
3230      * @param {String|HTMLElement} element The id or DOM node to register
3231      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
3232      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
3233      * knows how to interpret, plus there are some specific properties known to the Registry that should be
3234      * populated in the data object (if applicable):
3235      * <pre>
3236 Value      Description<br />
3237 ---------  ------------------------------------------<br />
3238 handles    Array of DOM nodes that trigger dragging<br />
3239            for the element being registered<br />
3240 isHandle   True if the element passed in triggers<br />
3241            dragging itself, else false
3242 </pre>
3243      */
3244         register : function(el, data){
3245             data = data || {};
3246             if(typeof el == "string"){
3247                 el = document.getElementById(el);
3248             }
3249             data.ddel = el;
3250             elements[getId(el)] = data;
3251             if(data.isHandle !== false){
3252                 handles[data.ddel.id] = data;
3253             }
3254             if(data.handles){
3255                 var hs = data.handles;
3256                 for(var i = 0, len = hs.length; i < len; i++){
3257                         handles[getId(hs[i])] = data;
3258                 }
3259             }
3260         },
3261
3262     /**
3263      * Unregister a drag drop element
3264      * @param {String|HTMLElement}  element The id or DOM node to unregister
3265      */
3266         unregister : function(el){
3267             var id = getId(el, false);
3268             var data = elements[id];
3269             if(data){
3270                 delete elements[id];
3271                 if(data.handles){
3272                     var hs = data.handles;
3273                     for(var i = 0, len = hs.length; i < len; i++){
3274                         delete handles[getId(hs[i], false)];
3275                     }
3276                 }
3277             }
3278         },
3279
3280     /**
3281      * Returns the handle registered for a DOM Node by id
3282      * @param {String|HTMLElement} id The DOM node or id to look up
3283      * @return {Object} handle The custom handle data
3284      */
3285         getHandle : function(id){
3286             if(typeof id != "string"){ // must be element?
3287                 id = id.id;
3288             }
3289             return handles[id];
3290         },
3291
3292     /**
3293      * Returns the handle that is registered for the DOM node that is the target of the event
3294      * @param {Event} e The event
3295      * @return {Object} handle The custom handle data
3296      */
3297         getHandleFromEvent : function(e){
3298             var t = Roo.lib.Event.getTarget(e);
3299             return t ? handles[t.id] : null;
3300         },
3301
3302     /**
3303      * Returns a custom data object that is registered for a DOM node by id
3304      * @param {String|HTMLElement} id The DOM node or id to look up
3305      * @return {Object} data The custom data
3306      */
3307         getTarget : function(id){
3308             if(typeof id != "string"){ // must be element?
3309                 id = id.id;
3310             }
3311             return elements[id];
3312         },
3313
3314     /**
3315      * Returns a custom data object that is registered for the DOM node that is the target of the event
3316      * @param {Event} e The event
3317      * @return {Object} data The custom data
3318      */
3319         getTargetFromEvent : function(e){
3320             var t = Roo.lib.Event.getTarget(e);
3321             return t ? elements[t.id] || handles[t.id] : null;
3322         }
3323     };
3324 }();/*
3325  * Based on:
3326  * Ext JS Library 1.1.1
3327  * Copyright(c) 2006-2007, Ext JS, LLC.
3328  *
3329  * Originally Released Under LGPL - original licence link has changed is not relivant.
3330  *
3331  * Fork - LGPL
3332  * <script type="text/javascript">
3333  */
3334  
3335
3336 /**
3337  * @class Roo.dd.StatusProxy
3338  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
3339  * default drag proxy used by all Roo.dd components.
3340  * @constructor
3341  * @param {Object} config
3342  */
3343 Roo.dd.StatusProxy = function(config){
3344     Roo.apply(this, config);
3345     this.id = this.id || Roo.id();
3346     this.el = new Roo.Layer({
3347         dh: {
3348             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
3349                 {tag: "div", cls: "x-dd-drop-icon"},
3350                 {tag: "div", cls: "x-dd-drag-ghost"}
3351             ]
3352         }, 
3353         shadow: !config || config.shadow !== false
3354     });
3355     this.ghost = Roo.get(this.el.dom.childNodes[1]);
3356     this.dropStatus = this.dropNotAllowed;
3357 };
3358
3359 Roo.dd.StatusProxy.prototype = {
3360     /**
3361      * @cfg {String} dropAllowed
3362      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
3363      */
3364     dropAllowed : "x-dd-drop-ok",
3365     /**
3366      * @cfg {String} dropNotAllowed
3367      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
3368      */
3369     dropNotAllowed : "x-dd-drop-nodrop",
3370
3371     /**
3372      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
3373      * over the current target element.
3374      * @param {String} cssClass The css class for the new drop status indicator image
3375      */
3376     setStatus : function(cssClass){
3377         cssClass = cssClass || this.dropNotAllowed;
3378         if(this.dropStatus != cssClass){
3379             this.el.replaceClass(this.dropStatus, cssClass);
3380             this.dropStatus = cssClass;
3381         }
3382     },
3383
3384     /**
3385      * Resets the status indicator to the default dropNotAllowed value
3386      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
3387      */
3388     reset : function(clearGhost){
3389         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
3390         this.dropStatus = this.dropNotAllowed;
3391         if(clearGhost){
3392             this.ghost.update("");
3393         }
3394     },
3395
3396     /**
3397      * Updates the contents of the ghost element
3398      * @param {String} html The html that will replace the current innerHTML of the ghost element
3399      */
3400     update : function(html){
3401         if(typeof html == "string"){
3402             this.ghost.update(html);
3403         }else{
3404             this.ghost.update("");
3405             html.style.margin = "0";
3406             this.ghost.dom.appendChild(html);
3407         }
3408         // ensure float = none set?? cant remember why though.
3409         var el = this.ghost.dom.firstChild;
3410                 if(el){
3411                         Roo.fly(el).setStyle('float', 'none');
3412                 }
3413     },
3414     
3415     /**
3416      * Returns the underlying proxy {@link Roo.Layer}
3417      * @return {Roo.Layer} el
3418     */
3419     getEl : function(){
3420         return this.el;
3421     },
3422
3423     /**
3424      * Returns the ghost element
3425      * @return {Roo.Element} el
3426      */
3427     getGhost : function(){
3428         return this.ghost;
3429     },
3430
3431     /**
3432      * Hides the proxy
3433      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
3434      */
3435     hide : function(clear){
3436         this.el.hide();
3437         if(clear){
3438             this.reset(true);
3439         }
3440     },
3441
3442     /**
3443      * Stops the repair animation if it's currently running
3444      */
3445     stop : function(){
3446         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
3447             this.anim.stop();
3448         }
3449     },
3450
3451     /**
3452      * Displays this proxy
3453      */
3454     show : function(){
3455         this.el.show();
3456     },
3457
3458     /**
3459      * Force the Layer to sync its shadow and shim positions to the element
3460      */
3461     sync : function(){
3462         this.el.sync();
3463     },
3464
3465     /**
3466      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
3467      * invalid drop operation by the item being dragged.
3468      * @param {Array} xy The XY position of the element ([x, y])
3469      * @param {Function} callback The function to call after the repair is complete
3470      * @param {Object} scope The scope in which to execute the callback
3471      */
3472     repair : function(xy, callback, scope){
3473         this.callback = callback;
3474         this.scope = scope;
3475         if(xy && this.animRepair !== false){
3476             this.el.addClass("x-dd-drag-repair");
3477             this.el.hideUnders(true);
3478             this.anim = this.el.shift({
3479                 duration: this.repairDuration || .5,
3480                 easing: 'easeOut',
3481                 xy: xy,
3482                 stopFx: true,
3483                 callback: this.afterRepair,
3484                 scope: this
3485             });
3486         }else{
3487             this.afterRepair();
3488         }
3489     },
3490
3491     // private
3492     afterRepair : function(){
3493         this.hide(true);
3494         if(typeof this.callback == "function"){
3495             this.callback.call(this.scope || this);
3496         }
3497         this.callback = null;
3498         this.scope = null;
3499     }
3500 };/*
3501  * Based on:
3502  * Ext JS Library 1.1.1
3503  * Copyright(c) 2006-2007, Ext JS, LLC.
3504  *
3505  * Originally Released Under LGPL - original licence link has changed is not relivant.
3506  *
3507  * Fork - LGPL
3508  * <script type="text/javascript">
3509  */
3510
3511 /**
3512  * @class Roo.dd.DragSource
3513  * @extends Roo.dd.DDProxy
3514  * A simple class that provides the basic implementation needed to make any element draggable.
3515  * @constructor
3516  * @param {String/HTMLElement/Element} el The container element
3517  * @param {Object} config
3518  */
3519 Roo.dd.DragSource = function(el, config){
3520     this.el = Roo.get(el);
3521     this.dragData = {};
3522     
3523     Roo.apply(this, config);
3524     
3525     if(!this.proxy){
3526         this.proxy = new Roo.dd.StatusProxy();
3527     }
3528
3529     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
3530           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
3531     
3532     this.dragging = false;
3533 };
3534
3535 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
3536     /**
3537      * @cfg {String} dropAllowed
3538      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3539      */
3540     dropAllowed : "x-dd-drop-ok",
3541     /**
3542      * @cfg {String} dropNotAllowed
3543      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3544      */
3545     dropNotAllowed : "x-dd-drop-nodrop",
3546
3547     /**
3548      * Returns the data object associated with this drag source
3549      * @return {Object} data An object containing arbitrary data
3550      */
3551     getDragData : function(e){
3552         return this.dragData;
3553     },
3554
3555     // private
3556     onDragEnter : function(e, id){
3557         var target = Roo.dd.DragDropMgr.getDDById(id);
3558         this.cachedTarget = target;
3559         if(this.beforeDragEnter(target, e, id) !== false){
3560             if(target.isNotifyTarget){
3561                 var status = target.notifyEnter(this, e, this.dragData);
3562                 this.proxy.setStatus(status);
3563             }else{
3564                 this.proxy.setStatus(this.dropAllowed);
3565             }
3566             
3567             if(this.afterDragEnter){
3568                 /**
3569                  * An empty function by default, but provided so that you can perform a custom action
3570                  * when the dragged item enters the drop target by providing an implementation.
3571                  * @param {Roo.dd.DragDrop} target The drop target
3572                  * @param {Event} e The event object
3573                  * @param {String} id The id of the dragged element
3574                  * @method afterDragEnter
3575                  */
3576                 this.afterDragEnter(target, e, id);
3577             }
3578         }
3579     },
3580
3581     /**
3582      * An empty function by default, but provided so that you can perform a custom action
3583      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
3584      * @param {Roo.dd.DragDrop} target The drop target
3585      * @param {Event} e The event object
3586      * @param {String} id The id of the dragged element
3587      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3588      */
3589     beforeDragEnter : function(target, e, id){
3590         return true;
3591     },
3592
3593     // private
3594     alignElWithMouse: function() {
3595         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
3596         this.proxy.sync();
3597     },
3598
3599     // private
3600     onDragOver : function(e, id){
3601         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3602         if(this.beforeDragOver(target, e, id) !== false){
3603             if(target.isNotifyTarget){
3604                 var status = target.notifyOver(this, e, this.dragData);
3605                 this.proxy.setStatus(status);
3606             }
3607
3608             if(this.afterDragOver){
3609                 /**
3610                  * An empty function by default, but provided so that you can perform a custom action
3611                  * while the dragged item is over the drop target by providing an implementation.
3612                  * @param {Roo.dd.DragDrop} target The drop target
3613                  * @param {Event} e The event object
3614                  * @param {String} id The id of the dragged element
3615                  * @method afterDragOver
3616                  */
3617                 this.afterDragOver(target, e, id);
3618             }
3619         }
3620     },
3621
3622     /**
3623      * An empty function by default, but provided so that you can perform a custom action
3624      * while the dragged item is over the drop target and optionally cancel the onDragOver.
3625      * @param {Roo.dd.DragDrop} target The drop target
3626      * @param {Event} e The event object
3627      * @param {String} id The id of the dragged element
3628      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3629      */
3630     beforeDragOver : function(target, e, id){
3631         return true;
3632     },
3633
3634     // private
3635     onDragOut : function(e, id){
3636         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3637         if(this.beforeDragOut(target, e, id) !== false){
3638             if(target.isNotifyTarget){
3639                 target.notifyOut(this, e, this.dragData);
3640             }
3641             this.proxy.reset();
3642             if(this.afterDragOut){
3643                 /**
3644                  * An empty function by default, but provided so that you can perform a custom action
3645                  * after the dragged item is dragged out of the target without dropping.
3646                  * @param {Roo.dd.DragDrop} target The drop target
3647                  * @param {Event} e The event object
3648                  * @param {String} id The id of the dragged element
3649                  * @method afterDragOut
3650                  */
3651                 this.afterDragOut(target, e, id);
3652             }
3653         }
3654         this.cachedTarget = null;
3655     },
3656
3657     /**
3658      * An empty function by default, but provided so that you can perform a custom action before the dragged
3659      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
3660      * @param {Roo.dd.DragDrop} target The drop target
3661      * @param {Event} e The event object
3662      * @param {String} id The id of the dragged element
3663      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3664      */
3665     beforeDragOut : function(target, e, id){
3666         return true;
3667     },
3668     
3669     // private
3670     onDragDrop : function(e, id){
3671         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3672         if(this.beforeDragDrop(target, e, id) !== false){
3673             if(target.isNotifyTarget){
3674                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
3675                     this.onValidDrop(target, e, id);
3676                 }else{
3677                     this.onInvalidDrop(target, e, id);
3678                 }
3679             }else{
3680                 this.onValidDrop(target, e, id);
3681             }
3682             
3683             if(this.afterDragDrop){
3684                 /**
3685                  * An empty function by default, but provided so that you can perform a custom action
3686                  * after a valid drag drop has occurred by providing an implementation.
3687                  * @param {Roo.dd.DragDrop} target The drop target
3688                  * @param {Event} e The event object
3689                  * @param {String} id The id of the dropped element
3690                  * @method afterDragDrop
3691                  */
3692                 this.afterDragDrop(target, e, id);
3693             }
3694         }
3695         delete this.cachedTarget;
3696     },
3697
3698     /**
3699      * An empty function by default, but provided so that you can perform a custom action before the dragged
3700      * item is dropped onto the target and optionally cancel the onDragDrop.
3701      * @param {Roo.dd.DragDrop} target The drop target
3702      * @param {Event} e The event object
3703      * @param {String} id The id of the dragged element
3704      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
3705      */
3706     beforeDragDrop : function(target, e, id){
3707         return true;
3708     },
3709
3710     // private
3711     onValidDrop : function(target, e, id){
3712         this.hideProxy();
3713         if(this.afterValidDrop){
3714             /**
3715              * An empty function by default, but provided so that you can perform a custom action
3716              * after a valid drop has occurred by providing an implementation.
3717              * @param {Object} target The target DD 
3718              * @param {Event} e The event object
3719              * @param {String} id The id of the dropped element
3720              * @method afterInvalidDrop
3721              */
3722             this.afterValidDrop(target, e, id);
3723         }
3724     },
3725
3726     // private
3727     getRepairXY : function(e, data){
3728         return this.el.getXY();  
3729     },
3730
3731     // private
3732     onInvalidDrop : function(target, e, id){
3733         this.beforeInvalidDrop(target, e, id);
3734         if(this.cachedTarget){
3735             if(this.cachedTarget.isNotifyTarget){
3736                 this.cachedTarget.notifyOut(this, e, this.dragData);
3737             }
3738             this.cacheTarget = null;
3739         }
3740         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
3741
3742         if(this.afterInvalidDrop){
3743             /**
3744              * An empty function by default, but provided so that you can perform a custom action
3745              * after an invalid drop has occurred by providing an implementation.
3746              * @param {Event} e The event object
3747              * @param {String} id The id of the dropped element
3748              * @method afterInvalidDrop
3749              */
3750             this.afterInvalidDrop(e, id);
3751         }
3752     },
3753
3754     // private
3755     afterRepair : function(){
3756         if(Roo.enableFx){
3757             this.el.highlight(this.hlColor || "c3daf9");
3758         }
3759         this.dragging = false;
3760     },
3761
3762     /**
3763      * An empty function by default, but provided so that you can perform a custom action after an invalid
3764      * drop has occurred.
3765      * @param {Roo.dd.DragDrop} target The drop target
3766      * @param {Event} e The event object
3767      * @param {String} id The id of the dragged element
3768      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
3769      */
3770     beforeInvalidDrop : function(target, e, id){
3771         return true;
3772     },
3773
3774     // private
3775     handleMouseDown : function(e){
3776         if(this.dragging) {
3777             return;
3778         }
3779         var data = this.getDragData(e);
3780         if(data && this.onBeforeDrag(data, e) !== false){
3781             this.dragData = data;
3782             this.proxy.stop();
3783             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
3784         } 
3785     },
3786
3787     /**
3788      * An empty function by default, but provided so that you can perform a custom action before the initial
3789      * drag event begins and optionally cancel it.
3790      * @param {Object} data An object containing arbitrary data to be shared with drop targets
3791      * @param {Event} e The event object
3792      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3793      */
3794     onBeforeDrag : function(data, e){
3795         return true;
3796     },
3797
3798     /**
3799      * An empty function by default, but provided so that you can perform a custom action once the initial
3800      * drag event has begun.  The drag cannot be canceled from this function.
3801      * @param {Number} x The x position of the click on the dragged object
3802      * @param {Number} y The y position of the click on the dragged object
3803      */
3804     onStartDrag : Roo.emptyFn,
3805
3806     // private - YUI override
3807     startDrag : function(x, y){
3808         this.proxy.reset();
3809         this.dragging = true;
3810         this.proxy.update("");
3811         this.onInitDrag(x, y);
3812         this.proxy.show();
3813     },
3814
3815     // private
3816     onInitDrag : function(x, y){
3817         var clone = this.el.dom.cloneNode(true);
3818         clone.id = Roo.id(); // prevent duplicate ids
3819         this.proxy.update(clone);
3820         this.onStartDrag(x, y);
3821         return true;
3822     },
3823
3824     /**
3825      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
3826      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
3827      */
3828     getProxy : function(){
3829         return this.proxy;  
3830     },
3831
3832     /**
3833      * Hides the drag source's {@link Roo.dd.StatusProxy}
3834      */
3835     hideProxy : function(){
3836         this.proxy.hide();  
3837         this.proxy.reset(true);
3838         this.dragging = false;
3839     },
3840
3841     // private
3842     triggerCacheRefresh : function(){
3843         Roo.dd.DDM.refreshCache(this.groups);
3844     },
3845
3846     // private - override to prevent hiding
3847     b4EndDrag: function(e) {
3848     },
3849
3850     // private - override to prevent moving
3851     endDrag : function(e){
3852         this.onEndDrag(this.dragData, e);
3853     },
3854
3855     // private
3856     onEndDrag : function(data, e){
3857     },
3858     
3859     // private - pin to cursor
3860     autoOffset : function(x, y) {
3861         this.setDelta(-12, -20);
3862     }    
3863 });/*
3864  * Based on:
3865  * Ext JS Library 1.1.1
3866  * Copyright(c) 2006-2007, Ext JS, LLC.
3867  *
3868  * Originally Released Under LGPL - original licence link has changed is not relivant.
3869  *
3870  * Fork - LGPL
3871  * <script type="text/javascript">
3872  */
3873
3874
3875 /**
3876  * @class Roo.dd.DropTarget
3877  * @extends Roo.dd.DDTarget
3878  * A simple class that provides the basic implementation needed to make any element a drop target that can have
3879  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
3880  * @constructor
3881  * @param {String/HTMLElement/Element} el The container element
3882  * @param {Object} config
3883  */
3884 Roo.dd.DropTarget = function(el, config){
3885     this.el = Roo.get(el);
3886     
3887     Roo.apply(this, config);
3888     
3889     if(this.containerScroll){
3890         Roo.dd.ScrollManager.register(this.el);
3891     }
3892     
3893     Roo.dd.DropTarget.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group, 
3894           {isTarget: true});
3895
3896 };
3897
3898 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
3899     /**
3900      * @cfg {String} overClass
3901      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
3902      */
3903     /**
3904      * @cfg {String} dropAllowed
3905      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3906      */
3907     dropAllowed : "x-dd-drop-ok",
3908     /**
3909      * @cfg {String} dropNotAllowed
3910      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3911      */
3912     dropNotAllowed : "x-dd-drop-nodrop",
3913
3914     // private
3915     isTarget : true,
3916
3917     // private
3918     isNotifyTarget : true,
3919
3920     /**
3921      * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
3922      * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
3923      * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
3924      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3925      * @param {Event} e The event
3926      * @param {Object} data An object containing arbitrary data supplied by the drag source
3927      * @return {String} status The CSS class that communicates the drop status back to the source so that the
3928      * underlying {@link Roo.dd.StatusProxy} can be updated
3929      */
3930     notifyEnter : function(dd, e, data){
3931         if(this.overClass){
3932             this.el.addClass(this.overClass);
3933         }
3934         return this.dropAllowed;
3935     },
3936
3937     /**
3938      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
3939      * This method will be called on every mouse movement while the drag source is over the drop target.
3940      * This default implementation simply returns the dropAllowed config value.
3941      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3942      * @param {Event} e The event
3943      * @param {Object} data An object containing arbitrary data supplied by the drag source
3944      * @return {String} status The CSS class that communicates the drop status back to the source so that the
3945      * underlying {@link Roo.dd.StatusProxy} can be updated
3946      */
3947     notifyOver : function(dd, e, data){
3948         return this.dropAllowed;
3949     },
3950
3951     /**
3952      * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
3953      * out of the target without dropping.  This default implementation simply removes the CSS class specified by
3954      * overClass (if any) from the drop element.
3955      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3956      * @param {Event} e The event
3957      * @param {Object} data An object containing arbitrary data supplied by the drag source
3958      */
3959     notifyOut : function(dd, e, data){
3960         if(this.overClass){
3961             this.el.removeClass(this.overClass);
3962         }
3963     },
3964
3965     /**
3966      * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
3967      * been dropped on it.  This method has no default implementation and returns false, so you must provide an
3968      * implementation that does something to process the drop event and returns true so that the drag source's
3969      * repair action does not run.
3970      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3971      * @param {Event} e The event
3972      * @param {Object} data An object containing arbitrary data supplied by the drag source
3973      * @return {Boolean} True if the drop was valid, else false
3974      */
3975     notifyDrop : function(dd, e, data){
3976         return false;
3977     }
3978 });/*
3979  * Based on:
3980  * Ext JS Library 1.1.1
3981  * Copyright(c) 2006-2007, Ext JS, LLC.
3982  *
3983  * Originally Released Under LGPL - original licence link has changed is not relivant.
3984  *
3985  * Fork - LGPL
3986  * <script type="text/javascript">
3987  */
3988
3989
3990 /**
3991  * @class Roo.dd.DragZone
3992  * @extends Roo.dd.DragSource
3993  * This class provides a container DD instance that proxies for multiple child node sources.<br />
3994  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
3995  * @constructor
3996  * @param {String/HTMLElement/Element} el The container element
3997  * @param {Object} config
3998  */
3999 Roo.dd.DragZone = function(el, config){
4000     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
4001     if(this.containerScroll){
4002         Roo.dd.ScrollManager.register(this.el);
4003     }
4004 };
4005
4006 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
4007     /**
4008      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
4009      * for auto scrolling during drag operations.
4010      */
4011     /**
4012      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
4013      * method after a failed drop (defaults to "c3daf9" - light blue)
4014      */
4015
4016     /**
4017      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
4018      * for a valid target to drag based on the mouse down. Override this method
4019      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
4020      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
4021      * @param {EventObject} e The mouse down event
4022      * @return {Object} The dragData
4023      */
4024     getDragData : function(e){
4025         return Roo.dd.Registry.getHandleFromEvent(e);
4026     },
4027     
4028     /**
4029      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
4030      * this.dragData.ddel
4031      * @param {Number} x The x position of the click on the dragged object
4032      * @param {Number} y The y position of the click on the dragged object
4033      * @return {Boolean} true to continue the drag, false to cancel
4034      */
4035     onInitDrag : function(x, y){
4036         this.proxy.update(this.dragData.ddel.cloneNode(true));
4037         this.onStartDrag(x, y);
4038         return true;
4039     },
4040     
4041     /**
4042      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
4043      */
4044     afterRepair : function(){
4045         if(Roo.enableFx){
4046             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
4047         }
4048         this.dragging = false;
4049     },
4050
4051     /**
4052      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
4053      * the XY of this.dragData.ddel
4054      * @param {EventObject} e The mouse up event
4055      * @return {Array} The xy location (e.g. [100, 200])
4056      */
4057     getRepairXY : function(e){
4058         return Roo.Element.fly(this.dragData.ddel).getXY();  
4059     }
4060 });/*
4061  * Based on:
4062  * Ext JS Library 1.1.1
4063  * Copyright(c) 2006-2007, Ext JS, LLC.
4064  *
4065  * Originally Released Under LGPL - original licence link has changed is not relivant.
4066  *
4067  * Fork - LGPL
4068  * <script type="text/javascript">
4069  */
4070 /**
4071  * @class Roo.dd.DropZone
4072  * @extends Roo.dd.DropTarget
4073  * This class provides a container DD instance that proxies for multiple child node targets.<br />
4074  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
4075  * @constructor
4076  * @param {String/HTMLElement/Element} el The container element
4077  * @param {Object} config
4078  */
4079 Roo.dd.DropZone = function(el, config){
4080     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
4081 };
4082
4083 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
4084     /**
4085      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
4086      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
4087      * provide your own custom lookup.
4088      * @param {Event} e The event
4089      * @return {Object} data The custom data
4090      */
4091     getTargetFromEvent : function(e){
4092         return Roo.dd.Registry.getTargetFromEvent(e);
4093     },
4094
4095     /**
4096      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
4097      * that it has registered.  This method has no default implementation and should be overridden to provide
4098      * node-specific processing if necessary.
4099      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
4100      * {@link #getTargetFromEvent} for this node)
4101      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4102      * @param {Event} e The event
4103      * @param {Object} data An object containing arbitrary data supplied by the drag source
4104      */
4105     onNodeEnter : function(n, dd, e, data){
4106         
4107     },
4108
4109     /**
4110      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
4111      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
4112      * overridden to provide the proper feedback.
4113      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4114      * {@link #getTargetFromEvent} for this node)
4115      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4116      * @param {Event} e The event
4117      * @param {Object} data An object containing arbitrary data supplied by the drag source
4118      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4119      * underlying {@link Roo.dd.StatusProxy} can be updated
4120      */
4121     onNodeOver : function(n, dd, e, data){
4122         return this.dropAllowed;
4123     },
4124
4125     /**
4126      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
4127      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
4128      * node-specific processing if necessary.
4129      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4130      * {@link #getTargetFromEvent} for this node)
4131      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4132      * @param {Event} e The event
4133      * @param {Object} data An object containing arbitrary data supplied by the drag source
4134      */
4135     onNodeOut : function(n, dd, e, data){
4136         
4137     },
4138
4139     /**
4140      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
4141      * the drop node.  The default implementation returns false, so it should be overridden to provide the
4142      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
4143      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4144      * {@link #getTargetFromEvent} for this node)
4145      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4146      * @param {Event} e The event
4147      * @param {Object} data An object containing arbitrary data supplied by the drag source
4148      * @return {Boolean} True if the drop was valid, else false
4149      */
4150     onNodeDrop : function(n, dd, e, data){
4151         return false;
4152     },
4153
4154     /**
4155      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
4156      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
4157      * it should be overridden to provide the proper feedback if necessary.
4158      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4159      * @param {Event} e The event
4160      * @param {Object} data An object containing arbitrary data supplied by the drag source
4161      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4162      * underlying {@link Roo.dd.StatusProxy} can be updated
4163      */
4164     onContainerOver : function(dd, e, data){
4165         return this.dropNotAllowed;
4166     },
4167
4168     /**
4169      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
4170      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
4171      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
4172      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
4173      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4174      * @param {Event} e The event
4175      * @param {Object} data An object containing arbitrary data supplied by the drag source
4176      * @return {Boolean} True if the drop was valid, else false
4177      */
4178     onContainerDrop : function(dd, e, data){
4179         return false;
4180     },
4181
4182     /**
4183      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
4184      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
4185      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
4186      * you should override this method and provide a custom implementation.
4187      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4188      * @param {Event} e The event
4189      * @param {Object} data An object containing arbitrary data supplied by the drag source
4190      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4191      * underlying {@link Roo.dd.StatusProxy} can be updated
4192      */
4193     notifyEnter : function(dd, e, data){
4194         return this.dropNotAllowed;
4195     },
4196
4197     /**
4198      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
4199      * This method will be called on every mouse movement while the drag source is over the drop zone.
4200      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
4201      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
4202      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
4203      * registered node, it will call {@link #onContainerOver}.
4204      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4205      * @param {Event} e The event
4206      * @param {Object} data An object containing arbitrary data supplied by the drag source
4207      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4208      * underlying {@link Roo.dd.StatusProxy} can be updated
4209      */
4210     notifyOver : function(dd, e, data){
4211         var n = this.getTargetFromEvent(e);
4212         if(!n){ // not over valid drop target
4213             if(this.lastOverNode){
4214                 this.onNodeOut(this.lastOverNode, dd, e, data);
4215                 this.lastOverNode = null;
4216             }
4217             return this.onContainerOver(dd, e, data);
4218         }
4219         if(this.lastOverNode != n){
4220             if(this.lastOverNode){
4221                 this.onNodeOut(this.lastOverNode, dd, e, data);
4222             }
4223             this.onNodeEnter(n, dd, e, data);
4224             this.lastOverNode = n;
4225         }
4226         return this.onNodeOver(n, dd, e, data);
4227     },
4228
4229     /**
4230      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
4231      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
4232      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
4233      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
4234      * @param {Event} e The event
4235      * @param {Object} data An object containing arbitrary data supplied by the drag zone
4236      */
4237     notifyOut : function(dd, e, data){
4238         if(this.lastOverNode){
4239             this.onNodeOut(this.lastOverNode, dd, e, data);
4240             this.lastOverNode = null;
4241         }
4242     },
4243
4244     /**
4245      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
4246      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
4247      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
4248      * otherwise it will call {@link #onContainerDrop}.
4249      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4250      * @param {Event} e The event
4251      * @param {Object} data An object containing arbitrary data supplied by the drag source
4252      * @return {Boolean} True if the drop was valid, else false
4253      */
4254     notifyDrop : function(dd, e, data){
4255         if(this.lastOverNode){
4256             this.onNodeOut(this.lastOverNode, dd, e, data);
4257             this.lastOverNode = null;
4258         }
4259         var n = this.getTargetFromEvent(e);
4260         return n ?
4261             this.onNodeDrop(n, dd, e, data) :
4262             this.onContainerDrop(dd, e, data);
4263     },
4264
4265     // private
4266     triggerCacheRefresh : function(){
4267         Roo.dd.DDM.refreshCache(this.groups);
4268     }  
4269 });/*
4270  * Based on:
4271  * Ext JS Library 1.1.1
4272  * Copyright(c) 2006-2007, Ext JS, LLC.
4273  *
4274  * Originally Released Under LGPL - original licence link has changed is not relivant.
4275  *
4276  * Fork - LGPL
4277  * <script type="text/javascript">
4278  */
4279
4280
4281 /**
4282  * @class Roo.data.SortTypes
4283  * @singleton
4284  * Defines the default sorting (casting?) comparison functions used when sorting data.
4285  */
4286 Roo.data.SortTypes = {
4287     /**
4288      * Default sort that does nothing
4289      * @param {Mixed} s The value being converted
4290      * @return {Mixed} The comparison value
4291      */
4292     none : function(s){
4293         return s;
4294     },
4295     
4296     /**
4297      * The regular expression used to strip tags
4298      * @type {RegExp}
4299      * @property
4300      */
4301     stripTagsRE : /<\/?[^>]+>/gi,
4302     
4303     /**
4304      * Strips all HTML tags to sort on text only
4305      * @param {Mixed} s The value being converted
4306      * @return {String} The comparison value
4307      */
4308     asText : function(s){
4309         return String(s).replace(this.stripTagsRE, "");
4310     },
4311     
4312     /**
4313      * Strips all HTML tags to sort on text only - Case insensitive
4314      * @param {Mixed} s The value being converted
4315      * @return {String} The comparison value
4316      */
4317     asUCText : function(s){
4318         return String(s).toUpperCase().replace(this.stripTagsRE, "");
4319     },
4320     
4321     /**
4322      * Case insensitive string
4323      * @param {Mixed} s The value being converted
4324      * @return {String} The comparison value
4325      */
4326     asUCString : function(s) {
4327         return String(s).toUpperCase();
4328     },
4329     
4330     /**
4331      * Date sorting
4332      * @param {Mixed} s The value being converted
4333      * @return {Number} The comparison value
4334      */
4335     asDate : function(s) {
4336         if(!s){
4337             return 0;
4338         }
4339         if(s instanceof Date){
4340             return s.getTime();
4341         }
4342         return Date.parse(String(s));
4343     },
4344     
4345     /**
4346      * Float sorting
4347      * @param {Mixed} s The value being converted
4348      * @return {Float} The comparison value
4349      */
4350     asFloat : function(s) {
4351         var val = parseFloat(String(s).replace(/,/g, ""));
4352         if(isNaN(val)) val = 0;
4353         return val;
4354     },
4355     
4356     /**
4357      * Integer sorting
4358      * @param {Mixed} s The value being converted
4359      * @return {Number} The comparison value
4360      */
4361     asInt : function(s) {
4362         var val = parseInt(String(s).replace(/,/g, ""));
4363         if(isNaN(val)) val = 0;
4364         return val;
4365     }
4366 };/*
4367  * Based on:
4368  * Ext JS Library 1.1.1
4369  * Copyright(c) 2006-2007, Ext JS, LLC.
4370  *
4371  * Originally Released Under LGPL - original licence link has changed is not relivant.
4372  *
4373  * Fork - LGPL
4374  * <script type="text/javascript">
4375  */
4376
4377 /**
4378 * @class Roo.data.Record
4379  * Instances of this class encapsulate both record <em>definition</em> information, and record
4380  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
4381  * to access Records cached in an {@link Roo.data.Store} object.<br>
4382  * <p>
4383  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
4384  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
4385  * objects.<br>
4386  * <p>
4387  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
4388  * @constructor
4389  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
4390  * {@link #create}. The parameters are the same.
4391  * @param {Array} data An associative Array of data values keyed by the field name.
4392  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
4393  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
4394  * not specified an integer id is generated.
4395  */
4396 Roo.data.Record = function(data, id){
4397     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
4398     this.data = data;
4399 };
4400
4401 /**
4402  * Generate a constructor for a specific record layout.
4403  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
4404  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
4405  * Each field definition object may contain the following properties: <ul>
4406  * <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,
4407  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
4408  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
4409  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
4410  * is being used, then this is a string containing the javascript expression to reference the data relative to 
4411  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
4412  * to the data item relative to the record element. If the mapping expression is the same as the field name,
4413  * this may be omitted.</p></li>
4414  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
4415  * <ul><li>auto (Default, implies no conversion)</li>
4416  * <li>string</li>
4417  * <li>int</li>
4418  * <li>float</li>
4419  * <li>boolean</li>
4420  * <li>date</li></ul></p></li>
4421  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
4422  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
4423  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
4424  * by the Reader into an object that will be stored in the Record. It is passed the
4425  * following parameters:<ul>
4426  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
4427  * </ul></p></li>
4428  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
4429  * </ul>
4430  * <br>usage:<br><pre><code>
4431 var TopicRecord = Roo.data.Record.create(
4432     {name: 'title', mapping: 'topic_title'},
4433     {name: 'author', mapping: 'username'},
4434     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
4435     {name: 'lastPost', mapping: 'post_time', type: 'date'},
4436     {name: 'lastPoster', mapping: 'user2'},
4437     {name: 'excerpt', mapping: 'post_text'}
4438 );
4439
4440 var myNewRecord = new TopicRecord({
4441     title: 'Do my job please',
4442     author: 'noobie',
4443     totalPosts: 1,
4444     lastPost: new Date(),
4445     lastPoster: 'Animal',
4446     excerpt: 'No way dude!'
4447 });
4448 myStore.add(myNewRecord);
4449 </code></pre>
4450  * @method create
4451  * @static
4452  */
4453 Roo.data.Record.create = function(o){
4454     var f = function(){
4455         f.superclass.constructor.apply(this, arguments);
4456     };
4457     Roo.extend(f, Roo.data.Record);
4458     var p = f.prototype;
4459     p.fields = new Roo.util.MixedCollection(false, function(field){
4460         return field.name;
4461     });
4462     for(var i = 0, len = o.length; i < len; i++){
4463         p.fields.add(new Roo.data.Field(o[i]));
4464     }
4465     f.getField = function(name){
4466         return p.fields.get(name);  
4467     };
4468     return f;
4469 };
4470
4471 Roo.data.Record.AUTO_ID = 1000;
4472 Roo.data.Record.EDIT = 'edit';
4473 Roo.data.Record.REJECT = 'reject';
4474 Roo.data.Record.COMMIT = 'commit';
4475
4476 Roo.data.Record.prototype = {
4477     /**
4478      * Readonly flag - true if this record has been modified.
4479      * @type Boolean
4480      */
4481     dirty : false,
4482     editing : false,
4483     error: null,
4484     modified: null,
4485
4486     // private
4487     join : function(store){
4488         this.store = store;
4489     },
4490
4491     /**
4492      * Set the named field to the specified value.
4493      * @param {String} name The name of the field to set.
4494      * @param {Object} value The value to set the field to.
4495      */
4496     set : function(name, value){
4497         if(this.data[name] == value){
4498             return;
4499         }
4500         this.dirty = true;
4501         if(!this.modified){
4502             this.modified = {};
4503         }
4504         if(typeof this.modified[name] == 'undefined'){
4505             this.modified[name] = this.data[name];
4506         }
4507         this.data[name] = value;
4508         if(!this.editing){
4509             this.store.afterEdit(this);
4510         }       
4511     },
4512
4513     /**
4514      * Get the value of the named field.
4515      * @param {String} name The name of the field to get the value of.
4516      * @return {Object} The value of the field.
4517      */
4518     get : function(name){
4519         return this.data[name]; 
4520     },
4521
4522     // private
4523     beginEdit : function(){
4524         this.editing = true;
4525         this.modified = {}; 
4526     },
4527
4528     // private
4529     cancelEdit : function(){
4530         this.editing = false;
4531         delete this.modified;
4532     },
4533
4534     // private
4535     endEdit : function(){
4536         this.editing = false;
4537         if(this.dirty && this.store){
4538             this.store.afterEdit(this);
4539         }
4540     },
4541
4542     /**
4543      * Usually called by the {@link Roo.data.Store} which owns the Record.
4544      * Rejects all changes made to the Record since either creation, or the last commit operation.
4545      * Modified fields are reverted to their original values.
4546      * <p>
4547      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4548      * of reject operations.
4549      */
4550     reject : function(){
4551         var m = this.modified;
4552         for(var n in m){
4553             if(typeof m[n] != "function"){
4554                 this.data[n] = m[n];
4555             }
4556         }
4557         this.dirty = false;
4558         delete this.modified;
4559         this.editing = false;
4560         if(this.store){
4561             this.store.afterReject(this);
4562         }
4563     },
4564
4565     /**
4566      * Usually called by the {@link Roo.data.Store} which owns the Record.
4567      * Commits all changes made to the Record since either creation, or the last commit operation.
4568      * <p>
4569      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4570      * of commit operations.
4571      */
4572     commit : function(){
4573         this.dirty = false;
4574         delete this.modified;
4575         this.editing = false;
4576         if(this.store){
4577             this.store.afterCommit(this);
4578         }
4579     },
4580
4581     // private
4582     hasError : function(){
4583         return this.error != null;
4584     },
4585
4586     // private
4587     clearError : function(){
4588         this.error = null;
4589     },
4590
4591     /**
4592      * Creates a copy of this record.
4593      * @param {String} id (optional) A new record id if you don't want to use this record's id
4594      * @return {Record}
4595      */
4596     copy : function(newId) {
4597         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
4598     }
4599 };/*
4600  * Based on:
4601  * Ext JS Library 1.1.1
4602  * Copyright(c) 2006-2007, Ext JS, LLC.
4603  *
4604  * Originally Released Under LGPL - original licence link has changed is not relivant.
4605  *
4606  * Fork - LGPL
4607  * <script type="text/javascript">
4608  */
4609
4610
4611
4612 /**
4613  * @class Roo.data.Store
4614  * @extends Roo.util.Observable
4615  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
4616  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
4617  * <p>
4618  * 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
4619  * has no knowledge of the format of the data returned by the Proxy.<br>
4620  * <p>
4621  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
4622  * instances from the data object. These records are cached and made available through accessor functions.
4623  * @constructor
4624  * Creates a new Store.
4625  * @param {Object} config A config object containing the objects needed for the Store to access data,
4626  * and read the data into Records.
4627  */
4628 Roo.data.Store = function(config){
4629     this.data = new Roo.util.MixedCollection(false);
4630     this.data.getKey = function(o){
4631         return o.id;
4632     };
4633     this.baseParams = {};
4634     // private
4635     this.paramNames = {
4636         "start" : "start",
4637         "limit" : "limit",
4638         "sort" : "sort",
4639         "dir" : "dir"
4640     };
4641
4642     if(config && config.data){
4643         this.inlineData = config.data;
4644         delete config.data;
4645     }
4646
4647     Roo.apply(this, config);
4648     
4649     if(this.reader){ // reader passed
4650         this.reader = Roo.factory(this.reader, Roo.data);
4651         this.reader.xmodule = this.xmodule || false;
4652         if(!this.recordType){
4653             this.recordType = this.reader.recordType;
4654         }
4655         if(this.reader.onMetaChange){
4656             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
4657         }
4658     }
4659
4660     if(this.recordType){
4661         this.fields = this.recordType.prototype.fields;
4662     }
4663     this.modified = [];
4664
4665     this.addEvents({
4666         /**
4667          * @event datachanged
4668          * Fires when the data cache has changed, and a widget which is using this Store
4669          * as a Record cache should refresh its view.
4670          * @param {Store} this
4671          */
4672         datachanged : true,
4673         /**
4674          * @event metachange
4675          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
4676          * @param {Store} this
4677          * @param {Object} meta The JSON metadata
4678          */
4679         metachange : true,
4680         /**
4681          * @event add
4682          * Fires when Records have been added to the Store
4683          * @param {Store} this
4684          * @param {Roo.data.Record[]} records The array of Records added
4685          * @param {Number} index The index at which the record(s) were added
4686          */
4687         add : true,
4688         /**
4689          * @event remove
4690          * Fires when a Record has been removed from the Store
4691          * @param {Store} this
4692          * @param {Roo.data.Record} record The Record that was removed
4693          * @param {Number} index The index at which the record was removed
4694          */
4695         remove : true,
4696         /**
4697          * @event update
4698          * Fires when a Record has been updated
4699          * @param {Store} this
4700          * @param {Roo.data.Record} record The Record that was updated
4701          * @param {String} operation The update operation being performed.  Value may be one of:
4702          * <pre><code>
4703  Roo.data.Record.EDIT
4704  Roo.data.Record.REJECT
4705  Roo.data.Record.COMMIT
4706          * </code></pre>
4707          */
4708         update : true,
4709         /**
4710          * @event clear
4711          * Fires when the data cache has been cleared.
4712          * @param {Store} this
4713          */
4714         clear : true,
4715         /**
4716          * @event beforeload
4717          * Fires before a request is made for a new data object.  If the beforeload handler returns false
4718          * the load action will be canceled.
4719          * @param {Store} this
4720          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4721          */
4722         beforeload : true,
4723         /**
4724          * @event load
4725          * Fires after a new set of Records has been loaded.
4726          * @param {Store} this
4727          * @param {Roo.data.Record[]} records The Records that were loaded
4728          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4729          */
4730         load : true,
4731         /**
4732          * @event loadexception
4733          * Fires if an exception occurs in the Proxy during loading.
4734          * Called with the signature of the Proxy's "loadexception" event.
4735          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
4736          * 
4737          * @param {Proxy} 
4738          * @param {Object} return from JsonData.reader() - success, totalRecords, records
4739          * @param {Object} load options 
4740          * @param {Object} jsonData from your request (normally this contains the Exception)
4741          */
4742         loadexception : true
4743     });
4744     
4745     if(this.proxy){
4746         this.proxy = Roo.factory(this.proxy, Roo.data);
4747         this.proxy.xmodule = this.xmodule || false;
4748         this.relayEvents(this.proxy,  ["loadexception"]);
4749     }
4750     this.sortToggle = {};
4751
4752     Roo.data.Store.superclass.constructor.call(this);
4753
4754     if(this.inlineData){
4755         this.loadData(this.inlineData);
4756         delete this.inlineData;
4757     }
4758 };
4759 Roo.extend(Roo.data.Store, Roo.util.Observable, {
4760      /**
4761     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
4762     * without a remote query - used by combo/forms at present.
4763     */
4764     
4765     /**
4766     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
4767     */
4768     /**
4769     * @cfg {Array} data Inline data to be loaded when the store is initialized.
4770     */
4771     /**
4772     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
4773     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
4774     */
4775     /**
4776     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
4777     * on any HTTP request
4778     */
4779     /**
4780     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
4781     */
4782     /**
4783     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
4784     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
4785     */
4786     remoteSort : false,
4787
4788     /**
4789     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
4790      * loaded or when a record is removed. (defaults to false).
4791     */
4792     pruneModifiedRecords : false,
4793
4794     // private
4795     lastOptions : null,
4796
4797     /**
4798      * Add Records to the Store and fires the add event.
4799      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4800      */
4801     add : function(records){
4802         records = [].concat(records);
4803         for(var i = 0, len = records.length; i < len; i++){
4804             records[i].join(this);
4805         }
4806         var index = this.data.length;
4807         this.data.addAll(records);
4808         this.fireEvent("add", this, records, index);
4809     },
4810
4811     /**
4812      * Remove a Record from the Store and fires the remove event.
4813      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
4814      */
4815     remove : function(record){
4816         var index = this.data.indexOf(record);
4817         this.data.removeAt(index);
4818         if(this.pruneModifiedRecords){
4819             this.modified.remove(record);
4820         }
4821         this.fireEvent("remove", this, record, index);
4822     },
4823
4824     /**
4825      * Remove all Records from the Store and fires the clear event.
4826      */
4827     removeAll : function(){
4828         this.data.clear();
4829         if(this.pruneModifiedRecords){
4830             this.modified = [];
4831         }
4832         this.fireEvent("clear", this);
4833     },
4834
4835     /**
4836      * Inserts Records to the Store at the given index and fires the add event.
4837      * @param {Number} index The start index at which to insert the passed Records.
4838      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4839      */
4840     insert : function(index, records){
4841         records = [].concat(records);
4842         for(var i = 0, len = records.length; i < len; i++){
4843             this.data.insert(index, records[i]);
4844             records[i].join(this);
4845         }
4846         this.fireEvent("add", this, records, index);
4847     },
4848
4849     /**
4850      * Get the index within the cache of the passed Record.
4851      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
4852      * @return {Number} The index of the passed Record. Returns -1 if not found.
4853      */
4854     indexOf : function(record){
4855         return this.data.indexOf(record);
4856     },
4857
4858     /**
4859      * Get the index within the cache of the Record with the passed id.
4860      * @param {String} id The id of the Record to find.
4861      * @return {Number} The index of the Record. Returns -1 if not found.
4862      */
4863     indexOfId : function(id){
4864         return this.data.indexOfKey(id);
4865     },
4866
4867     /**
4868      * Get the Record with the specified id.
4869      * @param {String} id The id of the Record to find.
4870      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
4871      */
4872     getById : function(id){
4873         return this.data.key(id);
4874     },
4875
4876     /**
4877      * Get the Record at the specified index.
4878      * @param {Number} index The index of the Record to find.
4879      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
4880      */
4881     getAt : function(index){
4882         return this.data.itemAt(index);
4883     },
4884
4885     /**
4886      * Returns a range of Records between specified indices.
4887      * @param {Number} startIndex (optional) The starting index (defaults to 0)
4888      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
4889      * @return {Roo.data.Record[]} An array of Records
4890      */
4891     getRange : function(start, end){
4892         return this.data.getRange(start, end);
4893     },
4894
4895     // private
4896     storeOptions : function(o){
4897         o = Roo.apply({}, o);
4898         delete o.callback;
4899         delete o.scope;
4900         this.lastOptions = o;
4901     },
4902
4903     /**
4904      * Loads the Record cache from the configured Proxy using the configured Reader.
4905      * <p>
4906      * If using remote paging, then the first load call must specify the <em>start</em>
4907      * and <em>limit</em> properties in the options.params property to establish the initial
4908      * position within the dataset, and the number of Records to cache on each read from the Proxy.
4909      * <p>
4910      * <strong>It is important to note that for remote data sources, loading is asynchronous,
4911      * and this call will return before the new data has been loaded. Perform any post-processing
4912      * in a callback function, or in a "load" event handler.</strong>
4913      * <p>
4914      * @param {Object} options An object containing properties which control loading options:<ul>
4915      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
4916      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
4917      * passed the following arguments:<ul>
4918      * <li>r : Roo.data.Record[]</li>
4919      * <li>options: Options object from the load call</li>
4920      * <li>success: Boolean success indicator</li></ul></li>
4921      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
4922      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
4923      * </ul>
4924      */
4925     load : function(options){
4926         options = options || {};
4927         if(this.fireEvent("beforeload", this, options) !== false){
4928             this.storeOptions(options);
4929             var p = Roo.apply(options.params || {}, this.baseParams);
4930             if(this.sortInfo && this.remoteSort){
4931                 var pn = this.paramNames;
4932                 p[pn["sort"]] = this.sortInfo.field;
4933                 p[pn["dir"]] = this.sortInfo.direction;
4934             }
4935             this.proxy.load(p, this.reader, this.loadRecords, this, options);
4936         }
4937     },
4938
4939     /**
4940      * Reloads the Record cache from the configured Proxy using the configured Reader and
4941      * the options from the last load operation performed.
4942      * @param {Object} options (optional) An object containing properties which may override the options
4943      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
4944      * the most recently used options are reused).
4945      */
4946     reload : function(options){
4947         this.load(Roo.applyIf(options||{}, this.lastOptions));
4948     },
4949
4950     // private
4951     // Called as a callback by the Reader during a load operation.
4952     loadRecords : function(o, options, success){
4953         if(!o || success === false){
4954             if(success !== false){
4955                 this.fireEvent("load", this, [], options);
4956             }
4957             if(options.callback){
4958                 options.callback.call(options.scope || this, [], options, false);
4959             }
4960             return;
4961         }
4962         // if data returned failure - throw an exception.
4963         if (o.success === false) {
4964             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
4965             return;
4966         }
4967         var r = o.records, t = o.totalRecords || r.length;
4968         if(!options || options.add !== true){
4969             if(this.pruneModifiedRecords){
4970                 this.modified = [];
4971             }
4972             for(var i = 0, len = r.length; i < len; i++){
4973                 r[i].join(this);
4974             }
4975             if(this.snapshot){
4976                 this.data = this.snapshot;
4977                 delete this.snapshot;
4978             }
4979             this.data.clear();
4980             this.data.addAll(r);
4981             this.totalLength = t;
4982             this.applySort();
4983             this.fireEvent("datachanged", this);
4984         }else{
4985             this.totalLength = Math.max(t, this.data.length+r.length);
4986             this.add(r);
4987         }
4988         this.fireEvent("load", this, r, options);
4989         if(options.callback){
4990             options.callback.call(options.scope || this, r, options, true);
4991         }
4992     },
4993
4994     /**
4995      * Loads data from a passed data block. A Reader which understands the format of the data
4996      * must have been configured in the constructor.
4997      * @param {Object} data The data block from which to read the Records.  The format of the data expected
4998      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
4999      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
5000      */
5001     loadData : function(o, append){
5002         var r = this.reader.readRecords(o);
5003         this.loadRecords(r, {add: append}, true);
5004     },
5005
5006     /**
5007      * Gets the number of cached records.
5008      * <p>
5009      * <em>If using paging, this may not be the total size of the dataset. If the data object
5010      * used by the Reader contains the dataset size, then the getTotalCount() function returns
5011      * the data set size</em>
5012      */
5013     getCount : function(){
5014         return this.data.length || 0;
5015     },
5016
5017     /**
5018      * Gets the total number of records in the dataset as returned by the server.
5019      * <p>
5020      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
5021      * the dataset size</em>
5022      */
5023     getTotalCount : function(){
5024         return this.totalLength || 0;
5025     },
5026
5027     /**
5028      * Returns the sort state of the Store as an object with two properties:
5029      * <pre><code>
5030  field {String} The name of the field by which the Records are sorted
5031  direction {String} The sort order, "ASC" or "DESC"
5032      * </code></pre>
5033      */
5034     getSortState : function(){
5035         return this.sortInfo;
5036     },
5037
5038     // private
5039     applySort : function(){
5040         if(this.sortInfo && !this.remoteSort){
5041             var s = this.sortInfo, f = s.field;
5042             var st = this.fields.get(f).sortType;
5043             var fn = function(r1, r2){
5044                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
5045                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
5046             };
5047             this.data.sort(s.direction, fn);
5048             if(this.snapshot && this.snapshot != this.data){
5049                 this.snapshot.sort(s.direction, fn);
5050             }
5051         }
5052     },
5053
5054     /**
5055      * Sets the default sort column and order to be used by the next load operation.
5056      * @param {String} fieldName The name of the field to sort by.
5057      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5058      */
5059     setDefaultSort : function(field, dir){
5060         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
5061     },
5062
5063     /**
5064      * Sort the Records.
5065      * If remote sorting is used, the sort is performed on the server, and the cache is
5066      * reloaded. If local sorting is used, the cache is sorted internally.
5067      * @param {String} fieldName The name of the field to sort by.
5068      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5069      */
5070     sort : function(fieldName, dir){
5071         var f = this.fields.get(fieldName);
5072         if(!dir){
5073             if(this.sortInfo && this.sortInfo.field == f.name){ // toggle sort dir
5074                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
5075             }else{
5076                 dir = f.sortDir;
5077             }
5078         }
5079         this.sortToggle[f.name] = dir;
5080         this.sortInfo = {field: f.name, direction: dir};
5081         if(!this.remoteSort){
5082             this.applySort();
5083             this.fireEvent("datachanged", this);
5084         }else{
5085             this.load(this.lastOptions);
5086         }
5087     },
5088
5089     /**
5090      * Calls the specified function for each of the Records in the cache.
5091      * @param {Function} fn The function to call. The Record is passed as the first parameter.
5092      * Returning <em>false</em> aborts and exits the iteration.
5093      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
5094      */
5095     each : function(fn, scope){
5096         this.data.each(fn, scope);
5097     },
5098
5099     /**
5100      * Gets all records modified since the last commit.  Modified records are persisted across load operations
5101      * (e.g., during paging).
5102      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
5103      */
5104     getModifiedRecords : function(){
5105         return this.modified;
5106     },
5107
5108     // private
5109     createFilterFn : function(property, value, anyMatch){
5110         if(!value.exec){ // not a regex
5111             value = String(value);
5112             if(value.length == 0){
5113                 return false;
5114             }
5115             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
5116         }
5117         return function(r){
5118             return value.test(r.data[property]);
5119         };
5120     },
5121
5122     /**
5123      * Sums the value of <i>property</i> for each record between start and end and returns the result.
5124      * @param {String} property A field on your records
5125      * @param {Number} start The record index to start at (defaults to 0)
5126      * @param {Number} end The last record index to include (defaults to length - 1)
5127      * @return {Number} The sum
5128      */
5129     sum : function(property, start, end){
5130         var rs = this.data.items, v = 0;
5131         start = start || 0;
5132         end = (end || end === 0) ? end : rs.length-1;
5133
5134         for(var i = start; i <= end; i++){
5135             v += (rs[i].data[property] || 0);
5136         }
5137         return v;
5138     },
5139
5140     /**
5141      * Filter the records by a specified property.
5142      * @param {String} field A field on your records
5143      * @param {String/RegExp} value Either a string that the field
5144      * should start with or a RegExp to test against the field
5145      * @param {Boolean} anyMatch True to match any part not just the beginning
5146      */
5147     filter : function(property, value, anyMatch){
5148         var fn = this.createFilterFn(property, value, anyMatch);
5149         return fn ? this.filterBy(fn) : this.clearFilter();
5150     },
5151
5152     /**
5153      * Filter by a function. The specified function will be called with each
5154      * record in this data source. If the function returns true the record is included,
5155      * otherwise it is filtered.
5156      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5157      * @param {Object} scope (optional) The scope of the function (defaults to this)
5158      */
5159     filterBy : function(fn, scope){
5160         this.snapshot = this.snapshot || this.data;
5161         this.data = this.queryBy(fn, scope||this);
5162         this.fireEvent("datachanged", this);
5163     },
5164
5165     /**
5166      * Query the records by a specified property.
5167      * @param {String} field A field on your records
5168      * @param {String/RegExp} value Either a string that the field
5169      * should start with or a RegExp to test against the field
5170      * @param {Boolean} anyMatch True to match any part not just the beginning
5171      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5172      */
5173     query : function(property, value, anyMatch){
5174         var fn = this.createFilterFn(property, value, anyMatch);
5175         return fn ? this.queryBy(fn) : this.data.clone();
5176     },
5177
5178     /**
5179      * Query by a function. The specified function will be called with each
5180      * record in this data source. If the function returns true the record is included
5181      * in the results.
5182      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5183      * @param {Object} scope (optional) The scope of the function (defaults to this)
5184       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5185      **/
5186     queryBy : function(fn, scope){
5187         var data = this.snapshot || this.data;
5188         return data.filterBy(fn, scope||this);
5189     },
5190
5191     /**
5192      * Collects unique values for a particular dataIndex from this store.
5193      * @param {String} dataIndex The property to collect
5194      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
5195      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
5196      * @return {Array} An array of the unique values
5197      **/
5198     collect : function(dataIndex, allowNull, bypassFilter){
5199         var d = (bypassFilter === true && this.snapshot) ?
5200                 this.snapshot.items : this.data.items;
5201         var v, sv, r = [], l = {};
5202         for(var i = 0, len = d.length; i < len; i++){
5203             v = d[i].data[dataIndex];
5204             sv = String(v);
5205             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
5206                 l[sv] = true;
5207                 r[r.length] = v;
5208             }
5209         }
5210         return r;
5211     },
5212
5213     /**
5214      * Revert to a view of the Record cache with no filtering applied.
5215      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
5216      */
5217     clearFilter : function(suppressEvent){
5218         if(this.snapshot && this.snapshot != this.data){
5219             this.data = this.snapshot;
5220             delete this.snapshot;
5221             if(suppressEvent !== true){
5222                 this.fireEvent("datachanged", this);
5223             }
5224         }
5225     },
5226
5227     // private
5228     afterEdit : function(record){
5229         if(this.modified.indexOf(record) == -1){
5230             this.modified.push(record);
5231         }
5232         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
5233     },
5234
5235     // private
5236     afterReject : function(record){
5237         this.modified.remove(record);
5238         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
5239     },
5240
5241     // private
5242     afterCommit : function(record){
5243         this.modified.remove(record);
5244         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
5245     },
5246
5247     /**
5248      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
5249      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
5250      */
5251     commitChanges : function(){
5252         var m = this.modified.slice(0);
5253         this.modified = [];
5254         for(var i = 0, len = m.length; i < len; i++){
5255             m[i].commit();
5256         }
5257     },
5258
5259     /**
5260      * Cancel outstanding changes on all changed records.
5261      */
5262     rejectChanges : function(){
5263         var m = this.modified.slice(0);
5264         this.modified = [];
5265         for(var i = 0, len = m.length; i < len; i++){
5266             m[i].reject();
5267         }
5268     },
5269
5270     onMetaChange : function(meta, rtype, o){
5271         this.recordType = rtype;
5272         this.fields = rtype.prototype.fields;
5273         delete this.snapshot;
5274         this.sortInfo = meta.sortInfo;
5275         this.modified = [];
5276         this.fireEvent('metachange', this, this.reader.meta);
5277     }
5278 });/*
5279  * Based on:
5280  * Ext JS Library 1.1.1
5281  * Copyright(c) 2006-2007, Ext JS, LLC.
5282  *
5283  * Originally Released Under LGPL - original licence link has changed is not relivant.
5284  *
5285  * Fork - LGPL
5286  * <script type="text/javascript">
5287  */
5288
5289 /**
5290  * @class Roo.data.SimpleStore
5291  * @extends Roo.data.Store
5292  * Small helper class to make creating Stores from Array data easier.
5293  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
5294  * @cfg {Array} fields An array of field definition objects, or field name strings.
5295  * @cfg {Array} data The multi-dimensional array of data
5296  * @constructor
5297  * @param {Object} config
5298  */
5299 Roo.data.SimpleStore = function(config){
5300     Roo.data.SimpleStore.superclass.constructor.call(this, {
5301         isLocal : true,
5302         reader: new Roo.data.ArrayReader({
5303                 id: config.id
5304             },
5305             Roo.data.Record.create(config.fields)
5306         ),
5307         proxy : new Roo.data.MemoryProxy(config.data)
5308     });
5309     this.load();
5310 };
5311 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
5312  * Based on:
5313  * Ext JS Library 1.1.1
5314  * Copyright(c) 2006-2007, Ext JS, LLC.
5315  *
5316  * Originally Released Under LGPL - original licence link has changed is not relivant.
5317  *
5318  * Fork - LGPL
5319  * <script type="text/javascript">
5320  */
5321
5322 /**
5323 /**
5324  * @extends Roo.data.Store
5325  * @class Roo.data.JsonStore
5326  * Small helper class to make creating Stores for JSON data easier. <br/>
5327 <pre><code>
5328 var store = new Roo.data.JsonStore({
5329     url: 'get-images.php',
5330     root: 'images',
5331     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
5332 });
5333 </code></pre>
5334  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
5335  * JsonReader and HttpProxy (unless inline data is provided).</b>
5336  * @cfg {Array} fields An array of field definition objects, or field name strings.
5337  * @constructor
5338  * @param {Object} config
5339  */
5340 Roo.data.JsonStore = function(c){
5341     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
5342         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
5343         reader: new Roo.data.JsonReader(c, c.fields)
5344     }));
5345 };
5346 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
5347  * Based on:
5348  * Ext JS Library 1.1.1
5349  * Copyright(c) 2006-2007, Ext JS, LLC.
5350  *
5351  * Originally Released Under LGPL - original licence link has changed is not relivant.
5352  *
5353  * Fork - LGPL
5354  * <script type="text/javascript">
5355  */
5356
5357  
5358 Roo.data.Field = function(config){
5359     if(typeof config == "string"){
5360         config = {name: config};
5361     }
5362     Roo.apply(this, config);
5363     
5364     if(!this.type){
5365         this.type = "auto";
5366     }
5367     
5368     var st = Roo.data.SortTypes;
5369     // named sortTypes are supported, here we look them up
5370     if(typeof this.sortType == "string"){
5371         this.sortType = st[this.sortType];
5372     }
5373     
5374     // set default sortType for strings and dates
5375     if(!this.sortType){
5376         switch(this.type){
5377             case "string":
5378                 this.sortType = st.asUCString;
5379                 break;
5380             case "date":
5381                 this.sortType = st.asDate;
5382                 break;
5383             default:
5384                 this.sortType = st.none;
5385         }
5386     }
5387
5388     // define once
5389     var stripRe = /[\$,%]/g;
5390
5391     // prebuilt conversion function for this field, instead of
5392     // switching every time we're reading a value
5393     if(!this.convert){
5394         var cv, dateFormat = this.dateFormat;
5395         switch(this.type){
5396             case "":
5397             case "auto":
5398             case undefined:
5399                 cv = function(v){ return v; };
5400                 break;
5401             case "string":
5402                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
5403                 break;
5404             case "int":
5405                 cv = function(v){
5406                     return v !== undefined && v !== null && v !== '' ?
5407                            parseInt(String(v).replace(stripRe, ""), 10) : '';
5408                     };
5409                 break;
5410             case "float":
5411                 cv = function(v){
5412                     return v !== undefined && v !== null && v !== '' ?
5413                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
5414                     };
5415                 break;
5416             case "bool":
5417             case "boolean":
5418                 cv = function(v){ return v === true || v === "true" || v == 1; };
5419                 break;
5420             case "date":
5421                 cv = function(v){
5422                     if(!v){
5423                         return '';
5424                     }
5425                     if(v instanceof Date){
5426                         return v;
5427                     }
5428                     if(dateFormat){
5429                         if(dateFormat == "timestamp"){
5430                             return new Date(v*1000);
5431                         }
5432                         return Date.parseDate(v, dateFormat);
5433                     }
5434                     var parsed = Date.parse(v);
5435                     return parsed ? new Date(parsed) : null;
5436                 };
5437              break;
5438             
5439         }
5440         this.convert = cv;
5441     }
5442 };
5443
5444 Roo.data.Field.prototype = {
5445     dateFormat: null,
5446     defaultValue: "",
5447     mapping: null,
5448     sortType : null,
5449     sortDir : "ASC"
5450 };/*
5451  * Based on:
5452  * Ext JS Library 1.1.1
5453  * Copyright(c) 2006-2007, Ext JS, LLC.
5454  *
5455  * Originally Released Under LGPL - original licence link has changed is not relivant.
5456  *
5457  * Fork - LGPL
5458  * <script type="text/javascript">
5459  */
5460  
5461 // Base class for reading structured data from a data source.  This class is intended to be
5462 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
5463
5464 /**
5465  * @class Roo.data.DataReader
5466  * Base class for reading structured data from a data source.  This class is intended to be
5467  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
5468  */
5469
5470 Roo.data.DataReader = function(meta, recordType){
5471     
5472     this.meta = meta;
5473     
5474     this.recordType = recordType instanceof Array ? 
5475         Roo.data.Record.create(recordType) : recordType;
5476 };
5477
5478 Roo.data.DataReader.prototype = {
5479      /**
5480      * Create an empty record
5481      * @param {Object} data (optional) - overlay some values
5482      * @return {Roo.data.Record} record created.
5483      */
5484     newRow :  function(d) {
5485         var da =  {};
5486         this.recordType.prototype.fields.each(function(c) {
5487             switch( c.type) {
5488                 case 'int' : da[c.name] = 0; break;
5489                 case 'date' : da[c.name] = new Date(); break;
5490                 case 'float' : da[c.name] = 0.0; break;
5491                 case 'boolean' : da[c.name] = false; break;
5492                 default : da[c.name] = ""; break;
5493             }
5494             
5495         });
5496         return new this.recordType(Roo.apply(da, d));
5497     }
5498     
5499 };/*
5500  * Based on:
5501  * Ext JS Library 1.1.1
5502  * Copyright(c) 2006-2007, Ext JS, LLC.
5503  *
5504  * Originally Released Under LGPL - original licence link has changed is not relivant.
5505  *
5506  * Fork - LGPL
5507  * <script type="text/javascript">
5508  */
5509
5510 /**
5511  * @class Roo.data.DataProxy
5512  * @extends Roo.data.Observable
5513  * This class is an abstract base class for implementations which provide retrieval of
5514  * unformatted data objects.<br>
5515  * <p>
5516  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
5517  * (of the appropriate type which knows how to parse the data object) to provide a block of
5518  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
5519  * <p>
5520  * Custom implementations must implement the load method as described in
5521  * {@link Roo.data.HttpProxy#load}.
5522  */
5523 Roo.data.DataProxy = function(){
5524     this.addEvents({
5525         /**
5526          * @event beforeload
5527          * Fires before a network request is made to retrieve a data object.
5528          * @param {Object} This DataProxy object.
5529          * @param {Object} params The params parameter to the load function.
5530          */
5531         beforeload : true,
5532         /**
5533          * @event load
5534          * Fires before the load method's callback is called.
5535          * @param {Object} This DataProxy object.
5536          * @param {Object} o The data object.
5537          * @param {Object} arg The callback argument object passed to the load function.
5538          */
5539         load : true,
5540         /**
5541          * @event loadexception
5542          * Fires if an Exception occurs during data retrieval.
5543          * @param {Object} This DataProxy object.
5544          * @param {Object} o The data object.
5545          * @param {Object} arg The callback argument object passed to the load function.
5546          * @param {Object} e The Exception.
5547          */
5548         loadexception : true
5549     });
5550     Roo.data.DataProxy.superclass.constructor.call(this);
5551 };
5552
5553 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
5554
5555     /**
5556      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
5557      */
5558 /*
5559  * Based on:
5560  * Ext JS Library 1.1.1
5561  * Copyright(c) 2006-2007, Ext JS, LLC.
5562  *
5563  * Originally Released Under LGPL - original licence link has changed is not relivant.
5564  *
5565  * Fork - LGPL
5566  * <script type="text/javascript">
5567  */
5568 /**
5569  * @class Roo.data.MemoryProxy
5570  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
5571  * to the Reader when its load method is called.
5572  * @constructor
5573  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
5574  */
5575 Roo.data.MemoryProxy = function(data){
5576     if (data.data) {
5577         data = data.data;
5578     }
5579     Roo.data.MemoryProxy.superclass.constructor.call(this);
5580     this.data = data;
5581 };
5582
5583 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
5584     /**
5585      * Load data from the requested source (in this case an in-memory
5586      * data object passed to the constructor), read the data object into
5587      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5588      * process that block using the passed callback.
5589      * @param {Object} params This parameter is not used by the MemoryProxy class.
5590      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5591      * object into a block of Roo.data.Records.
5592      * @param {Function} callback The function into which to pass the block of Roo.data.records.
5593      * The function must be passed <ul>
5594      * <li>The Record block object</li>
5595      * <li>The "arg" argument from the load function</li>
5596      * <li>A boolean success indicator</li>
5597      * </ul>
5598      * @param {Object} scope The scope in which to call the callback
5599      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5600      */
5601     load : function(params, reader, callback, scope, arg){
5602         params = params || {};
5603         var result;
5604         try {
5605             result = reader.readRecords(this.data);
5606         }catch(e){
5607             this.fireEvent("loadexception", this, arg, null, e);
5608             callback.call(scope, null, arg, false);
5609             return;
5610         }
5611         callback.call(scope, result, arg, true);
5612     },
5613     
5614     // private
5615     update : function(params, records){
5616         
5617     }
5618 });/*
5619  * Based on:
5620  * Ext JS Library 1.1.1
5621  * Copyright(c) 2006-2007, Ext JS, LLC.
5622  *
5623  * Originally Released Under LGPL - original licence link has changed is not relivant.
5624  *
5625  * Fork - LGPL
5626  * <script type="text/javascript">
5627  */
5628 /**
5629  * @class Roo.data.HttpProxy
5630  * @extends Roo.data.DataProxy
5631  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
5632  * configured to reference a certain URL.<br><br>
5633  * <p>
5634  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
5635  * from which the running page was served.<br><br>
5636  * <p>
5637  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
5638  * <p>
5639  * Be aware that to enable the browser to parse an XML document, the server must set
5640  * the Content-Type header in the HTTP response to "text/xml".
5641  * @constructor
5642  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
5643  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
5644  * will be used to make the request.
5645  */
5646 Roo.data.HttpProxy = function(conn){
5647     Roo.data.HttpProxy.superclass.constructor.call(this);
5648     // is conn a conn config or a real conn?
5649     this.conn = conn;
5650     this.useAjax = !conn || !conn.events;
5651   
5652 };
5653
5654 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
5655     // thse are take from connection...
5656     
5657     /**
5658      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
5659      */
5660     /**
5661      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
5662      * extra parameters to each request made by this object. (defaults to undefined)
5663      */
5664     /**
5665      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
5666      *  to each request made by this object. (defaults to undefined)
5667      */
5668     /**
5669      * @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)
5670      */
5671     /**
5672      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
5673      */
5674      /**
5675      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
5676      * @type Boolean
5677      */
5678   
5679
5680     /**
5681      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
5682      * @type Boolean
5683      */
5684     /**
5685      * Return the {@link Roo.data.Connection} object being used by this Proxy.
5686      * @return {Connection} The Connection object. This object may be used to subscribe to events on
5687      * a finer-grained basis than the DataProxy events.
5688      */
5689     getConnection : function(){
5690         return this.useAjax ? Roo.Ajax : this.conn;
5691     },
5692
5693     /**
5694      * Load data from the configured {@link Roo.data.Connection}, read the data object into
5695      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
5696      * process that block using the passed callback.
5697      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5698      * for the request to the remote server.
5699      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5700      * object into a block of Roo.data.Records.
5701      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5702      * The function must be passed <ul>
5703      * <li>The Record block object</li>
5704      * <li>The "arg" argument from the load function</li>
5705      * <li>A boolean success indicator</li>
5706      * </ul>
5707      * @param {Object} scope The scope in which to call the callback
5708      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5709      */
5710     load : function(params, reader, callback, scope, arg){
5711         if(this.fireEvent("beforeload", this, params) !== false){
5712             var  o = {
5713                 params : params || {},
5714                 request: {
5715                     callback : callback,
5716                     scope : scope,
5717                     arg : arg
5718                 },
5719                 reader: reader,
5720                 callback : this.loadResponse,
5721                 scope: this
5722             };
5723             if(this.useAjax){
5724                 Roo.applyIf(o, this.conn);
5725                 if(this.activeRequest){
5726                     Roo.Ajax.abort(this.activeRequest);
5727                 }
5728                 this.activeRequest = Roo.Ajax.request(o);
5729             }else{
5730                 this.conn.request(o);
5731             }
5732         }else{
5733             callback.call(scope||this, null, arg, false);
5734         }
5735     },
5736
5737     // private
5738     loadResponse : function(o, success, response){
5739         delete this.activeRequest;
5740         if(!success){
5741             this.fireEvent("loadexception", this, o, response);
5742             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5743             return;
5744         }
5745         var result;
5746         try {
5747             result = o.reader.read(response);
5748         }catch(e){
5749             this.fireEvent("loadexception", this, o, response, e);
5750             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5751             return;
5752         }
5753         
5754         this.fireEvent("load", this, o, o.request.arg);
5755         o.request.callback.call(o.request.scope, result, o.request.arg, true);
5756     },
5757
5758     // private
5759     update : function(dataSet){
5760
5761     },
5762
5763     // private
5764     updateResponse : function(dataSet){
5765
5766     }
5767 });/*
5768  * Based on:
5769  * Ext JS Library 1.1.1
5770  * Copyright(c) 2006-2007, Ext JS, LLC.
5771  *
5772  * Originally Released Under LGPL - original licence link has changed is not relivant.
5773  *
5774  * Fork - LGPL
5775  * <script type="text/javascript">
5776  */
5777
5778 /**
5779  * @class Roo.data.ScriptTagProxy
5780  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
5781  * other than the originating domain of the running page.<br><br>
5782  * <p>
5783  * <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
5784  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
5785  * <p>
5786  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
5787  * source code that is used as the source inside a &lt;script> tag.<br><br>
5788  * <p>
5789  * In order for the browser to process the returned data, the server must wrap the data object
5790  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
5791  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
5792  * depending on whether the callback name was passed:
5793  * <p>
5794  * <pre><code>
5795 boolean scriptTag = false;
5796 String cb = request.getParameter("callback");
5797 if (cb != null) {
5798     scriptTag = true;
5799     response.setContentType("text/javascript");
5800 } else {
5801     response.setContentType("application/x-json");
5802 }
5803 Writer out = response.getWriter();
5804 if (scriptTag) {
5805     out.write(cb + "(");
5806 }
5807 out.print(dataBlock.toJsonString());
5808 if (scriptTag) {
5809     out.write(");");
5810 }
5811 </pre></code>
5812  *
5813  * @constructor
5814  * @param {Object} config A configuration object.
5815  */
5816 Roo.data.ScriptTagProxy = function(config){
5817     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
5818     Roo.apply(this, config);
5819     this.head = document.getElementsByTagName("head")[0];
5820 };
5821
5822 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
5823
5824 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
5825     /**
5826      * @cfg {String} url The URL from which to request the data object.
5827      */
5828     /**
5829      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
5830      */
5831     timeout : 30000,
5832     /**
5833      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
5834      * the server the name of the callback function set up by the load call to process the returned data object.
5835      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
5836      * javascript output which calls this named function passing the data object as its only parameter.
5837      */
5838     callbackParam : "callback",
5839     /**
5840      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
5841      * name to the request.
5842      */
5843     nocache : true,
5844
5845     /**
5846      * Load data from the configured URL, read the data object into
5847      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5848      * process that block using the passed callback.
5849      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5850      * for the request to the remote server.
5851      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5852      * object into a block of Roo.data.Records.
5853      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5854      * The function must be passed <ul>
5855      * <li>The Record block object</li>
5856      * <li>The "arg" argument from the load function</li>
5857      * <li>A boolean success indicator</li>
5858      * </ul>
5859      * @param {Object} scope The scope in which to call the callback
5860      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5861      */
5862     load : function(params, reader, callback, scope, arg){
5863         if(this.fireEvent("beforeload", this, params) !== false){
5864
5865             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
5866
5867             var url = this.url;
5868             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
5869             if(this.nocache){
5870                 url += "&_dc=" + (new Date().getTime());
5871             }
5872             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
5873             var trans = {
5874                 id : transId,
5875                 cb : "stcCallback"+transId,
5876                 scriptId : "stcScript"+transId,
5877                 params : params,
5878                 arg : arg,
5879                 url : url,
5880                 callback : callback,
5881                 scope : scope,
5882                 reader : reader
5883             };
5884             var conn = this;
5885
5886             window[trans.cb] = function(o){
5887                 conn.handleResponse(o, trans);
5888             };
5889
5890             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
5891
5892             if(this.autoAbort !== false){
5893                 this.abort();
5894             }
5895
5896             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
5897
5898             var script = document.createElement("script");
5899             script.setAttribute("src", url);
5900             script.setAttribute("type", "text/javascript");
5901             script.setAttribute("id", trans.scriptId);
5902             this.head.appendChild(script);
5903
5904             this.trans = trans;
5905         }else{
5906             callback.call(scope||this, null, arg, false);
5907         }
5908     },
5909
5910     // private
5911     isLoading : function(){
5912         return this.trans ? true : false;
5913     },
5914
5915     /**
5916      * Abort the current server request.
5917      */
5918     abort : function(){
5919         if(this.isLoading()){
5920             this.destroyTrans(this.trans);
5921         }
5922     },
5923
5924     // private
5925     destroyTrans : function(trans, isLoaded){
5926         this.head.removeChild(document.getElementById(trans.scriptId));
5927         clearTimeout(trans.timeoutId);
5928         if(isLoaded){
5929             window[trans.cb] = undefined;
5930             try{
5931                 delete window[trans.cb];
5932             }catch(e){}
5933         }else{
5934             // if hasn't been loaded, wait for load to remove it to prevent script error
5935             window[trans.cb] = function(){
5936                 window[trans.cb] = undefined;
5937                 try{
5938                     delete window[trans.cb];
5939                 }catch(e){}
5940             };
5941         }
5942     },
5943
5944     // private
5945     handleResponse : function(o, trans){
5946         this.trans = false;
5947         this.destroyTrans(trans, true);
5948         var result;
5949         try {
5950             result = trans.reader.readRecords(o);
5951         }catch(e){
5952             this.fireEvent("loadexception", this, o, trans.arg, e);
5953             trans.callback.call(trans.scope||window, null, trans.arg, false);
5954             return;
5955         }
5956         this.fireEvent("load", this, o, trans.arg);
5957         trans.callback.call(trans.scope||window, result, trans.arg, true);
5958     },
5959
5960     // private
5961     handleFailure : function(trans){
5962         this.trans = false;
5963         this.destroyTrans(trans, false);
5964         this.fireEvent("loadexception", this, null, trans.arg);
5965         trans.callback.call(trans.scope||window, null, trans.arg, false);
5966     }
5967 });/*
5968  * Based on:
5969  * Ext JS Library 1.1.1
5970  * Copyright(c) 2006-2007, Ext JS, LLC.
5971  *
5972  * Originally Released Under LGPL - original licence link has changed is not relivant.
5973  *
5974  * Fork - LGPL
5975  * <script type="text/javascript">
5976  */
5977
5978 /**
5979  * @class Roo.data.JsonReader
5980  * @extends Roo.data.DataReader
5981  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
5982  * based on mappings in a provided Roo.data.Record constructor.
5983  * <p>
5984  * Example code:
5985  * <pre><code>
5986 var RecordDef = Roo.data.Record.create([
5987     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
5988     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
5989 ]);
5990 var myReader = new Roo.data.JsonReader({
5991     totalProperty: "results",    // The property which contains the total dataset size (optional)
5992     root: "rows",                // The property which contains an Array of row objects
5993     id: "id"                     // The property within each row object that provides an ID for the record (optional)
5994 }, RecordDef);
5995 </code></pre>
5996  * <p>
5997  * This would consume a JSON file like this:
5998  * <pre><code>
5999 { 'results': 2, 'rows': [
6000     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
6001     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
6002 }
6003 </code></pre>
6004  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
6005  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6006  * paged from the remote server.
6007  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
6008  * @cfg {String} root name of the property which contains the Array of row objects.
6009  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
6010  * @constructor
6011  * Create a new JsonReader
6012  * @param {Object} meta Metadata configuration options
6013  * @param {Object} recordType Either an Array of field definition objects,
6014  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
6015  */
6016 Roo.data.JsonReader = function(meta, recordType){
6017     
6018     meta = meta || {};
6019     // set some defaults:
6020     Roo.applyIf(meta, {
6021         totalProperty: 'total',
6022         successProperty : 'success',
6023         root : 'data',
6024         id : 'id'
6025     });
6026     
6027     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6028 };
6029 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
6030     /**
6031      * This method is only used by a DataProxy which has retrieved data from a remote server.
6032      * @param {Object} response The XHR object which contains the JSON data in its responseText.
6033      * @return {Object} data A data block which is used by an Roo.data.Store object as
6034      * a cache of Roo.data.Records.
6035      */
6036     read : function(response){
6037         var json = response.responseText;
6038         /* eval:var:o */
6039         var o = eval("("+json+")");
6040         if(!o) {
6041             throw {message: "JsonReader.read: Json object not found"};
6042         }
6043         
6044         if(o.metaData){
6045             delete this.ef;
6046             this.meta = o.metaData;
6047             this.recordType = Roo.data.Record.create(o.metaData.fields);
6048             this.onMetaChange(this.meta, this.recordType, o);
6049         }
6050         return this.readRecords(o);
6051     },
6052
6053     // private function a store will implement
6054     onMetaChange : function(meta, recordType, o){
6055
6056     },
6057
6058     /**
6059          * @ignore
6060          */
6061     simpleAccess: function(obj, subsc) {
6062         return obj[subsc];
6063     },
6064
6065         /**
6066          * @ignore
6067          */
6068     getJsonAccessor: function(){
6069         var re = /[\[\.]/;
6070         return function(expr) {
6071             try {
6072                 return(re.test(expr))
6073                     ? new Function("obj", "return obj." + expr)
6074                     : function(obj){
6075                         return obj[expr];
6076                     };
6077             } catch(e){}
6078             return Roo.emptyFn;
6079         };
6080     }(),
6081
6082     /**
6083      * Create a data block containing Roo.data.Records from an XML document.
6084      * @param {Object} o An object which contains an Array of row objects in the property specified
6085      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
6086      * which contains the total size of the dataset.
6087      * @return {Object} data A data block which is used by an Roo.data.Store object as
6088      * a cache of Roo.data.Records.
6089      */
6090     readRecords : function(o){
6091         /**
6092          * After any data loads, the raw JSON data is available for further custom processing.
6093          * @type Object
6094          */
6095         this.jsonData = o;
6096         var s = this.meta, Record = this.recordType,
6097             f = Record.prototype.fields, fi = f.items, fl = f.length;
6098
6099 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
6100         if (!this.ef) {
6101             if(s.totalProperty) {
6102                     this.getTotal = this.getJsonAccessor(s.totalProperty);
6103                 }
6104                 if(s.successProperty) {
6105                     this.getSuccess = this.getJsonAccessor(s.successProperty);
6106                 }
6107                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
6108                 if (s.id) {
6109                         var g = this.getJsonAccessor(s.id);
6110                         this.getId = function(rec) {
6111                                 var r = g(rec);
6112                                 return (r === undefined || r === "") ? null : r;
6113                         };
6114                 } else {
6115                         this.getId = function(){return null;};
6116                 }
6117             this.ef = [];
6118             for(var i = 0; i < fl; i++){
6119                 f = fi[i];
6120                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
6121                 this.ef[i] = this.getJsonAccessor(map);
6122             }
6123         }
6124
6125         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
6126         if(s.totalProperty){
6127             var v = parseInt(this.getTotal(o), 10);
6128             if(!isNaN(v)){
6129                 totalRecords = v;
6130             }
6131         }
6132         if(s.successProperty){
6133             var v = this.getSuccess(o);
6134             if(v === false || v === 'false'){
6135                 success = false;
6136             }
6137         }
6138         var records = [];
6139             for(var i = 0; i < c; i++){
6140                     var n = root[i];
6141                 var values = {};
6142                 var id = this.getId(n);
6143                 for(var j = 0; j < fl; j++){
6144                     f = fi[j];
6145                 var v = this.ef[j](n);
6146                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
6147                 }
6148                 var record = new Record(values, id);
6149                 record.json = n;
6150                 records[i] = record;
6151             }
6152             return {
6153                 success : success,
6154                 records : records,
6155                 totalRecords : totalRecords
6156             };
6157     }
6158 });/*
6159  * Based on:
6160  * Ext JS Library 1.1.1
6161  * Copyright(c) 2006-2007, Ext JS, LLC.
6162  *
6163  * Originally Released Under LGPL - original licence link has changed is not relivant.
6164  *
6165  * Fork - LGPL
6166  * <script type="text/javascript">
6167  */
6168
6169 /**
6170  * @class Roo.data.XmlReader
6171  * @extends Roo.data.DataReader
6172  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
6173  * based on mappings in a provided Roo.data.Record constructor.<br><br>
6174  * <p>
6175  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
6176  * header in the HTTP response must be set to "text/xml".</em>
6177  * <p>
6178  * Example code:
6179  * <pre><code>
6180 var RecordDef = Roo.data.Record.create([
6181    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6182    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6183 ]);
6184 var myReader = new Roo.data.XmlReader({
6185    totalRecords: "results", // The element which contains the total dataset size (optional)
6186    record: "row",           // The repeated element which contains row information
6187    id: "id"                 // The element within the row that provides an ID for the record (optional)
6188 }, RecordDef);
6189 </code></pre>
6190  * <p>
6191  * This would consume an XML file like this:
6192  * <pre><code>
6193 &lt;?xml?>
6194 &lt;dataset>
6195  &lt;results>2&lt;/results>
6196  &lt;row>
6197    &lt;id>1&lt;/id>
6198    &lt;name>Bill&lt;/name>
6199    &lt;occupation>Gardener&lt;/occupation>
6200  &lt;/row>
6201  &lt;row>
6202    &lt;id>2&lt;/id>
6203    &lt;name>Ben&lt;/name>
6204    &lt;occupation>Horticulturalist&lt;/occupation>
6205  &lt;/row>
6206 &lt;/dataset>
6207 </code></pre>
6208  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
6209  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6210  * paged from the remote server.
6211  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
6212  * @cfg {String} success The DomQuery path to the success attribute used by forms.
6213  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
6214  * a record identifier value.
6215  * @constructor
6216  * Create a new XmlReader
6217  * @param {Object} meta Metadata configuration options
6218  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
6219  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
6220  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
6221  */
6222 Roo.data.XmlReader = function(meta, recordType){
6223     meta = meta || {};
6224     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6225 };
6226 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
6227     /**
6228      * This method is only used by a DataProxy which has retrieved data from a remote server.
6229          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
6230          * to contain a method called 'responseXML' that returns an XML document object.
6231      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6232      * a cache of Roo.data.Records.
6233      */
6234     read : function(response){
6235         var doc = response.responseXML;
6236         if(!doc) {
6237             throw {message: "XmlReader.read: XML Document not available"};
6238         }
6239         return this.readRecords(doc);
6240     },
6241
6242     /**
6243      * Create a data block containing Roo.data.Records from an XML document.
6244          * @param {Object} doc A parsed XML document.
6245      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6246      * a cache of Roo.data.Records.
6247      */
6248     readRecords : function(doc){
6249         /**
6250          * After any data loads/reads, the raw XML Document is available for further custom processing.
6251          * @type XMLDocument
6252          */
6253         this.xmlData = doc;
6254         var root = doc.documentElement || doc;
6255         var q = Roo.DomQuery;
6256         var recordType = this.recordType, fields = recordType.prototype.fields;
6257         var sid = this.meta.id;
6258         var totalRecords = 0, success = true;
6259         if(this.meta.totalRecords){
6260             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
6261         }
6262         
6263         if(this.meta.success){
6264             var sv = q.selectValue(this.meta.success, root, true);
6265             success = sv !== false && sv !== 'false';
6266         }
6267         var records = [];
6268         var ns = q.select(this.meta.record, root);
6269         for(var i = 0, len = ns.length; i < len; i++) {
6270                 var n = ns[i];
6271                 var values = {};
6272                 var id = sid ? q.selectValue(sid, n) : undefined;
6273                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6274                     var f = fields.items[j];
6275                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
6276                     v = f.convert(v);
6277                     values[f.name] = v;
6278                 }
6279                 var record = new recordType(values, id);
6280                 record.node = n;
6281                 records[records.length] = record;
6282             }
6283
6284             return {
6285                 success : success,
6286                 records : records,
6287                 totalRecords : totalRecords || records.length
6288             };
6289     }
6290 });/*
6291  * Based on:
6292  * Ext JS Library 1.1.1
6293  * Copyright(c) 2006-2007, Ext JS, LLC.
6294  *
6295  * Originally Released Under LGPL - original licence link has changed is not relivant.
6296  *
6297  * Fork - LGPL
6298  * <script type="text/javascript">
6299  */
6300
6301 /**
6302  * @class Roo.data.ArrayReader
6303  * @extends Roo.data.DataReader
6304  * Data reader class to create an Array of Roo.data.Record objects from an Array.
6305  * Each element of that Array represents a row of data fields. The
6306  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
6307  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
6308  * <p>
6309  * Example code:.
6310  * <pre><code>
6311 var RecordDef = Roo.data.Record.create([
6312     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
6313     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
6314 ]);
6315 var myReader = new Roo.data.ArrayReader({
6316     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
6317 }, RecordDef);
6318 </code></pre>
6319  * <p>
6320  * This would consume an Array like this:
6321  * <pre><code>
6322 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
6323   </code></pre>
6324  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
6325  * @constructor
6326  * Create a new JsonReader
6327  * @param {Object} meta Metadata configuration options.
6328  * @param {Object} recordType Either an Array of field definition objects
6329  * as specified to {@link Roo.data.Record#create},
6330  * or an {@link Roo.data.Record} object
6331  * created using {@link Roo.data.Record#create}.
6332  */
6333 Roo.data.ArrayReader = function(meta, recordType){
6334     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
6335 };
6336
6337 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
6338     /**
6339      * Create a data block containing Roo.data.Records from an XML document.
6340      * @param {Object} o An Array of row objects which represents the dataset.
6341      * @return {Object} data A data block which is used by an Roo.data.Store object as
6342      * a cache of Roo.data.Records.
6343      */
6344     readRecords : function(o){
6345         var sid = this.meta ? this.meta.id : null;
6346         var recordType = this.recordType, fields = recordType.prototype.fields;
6347         var records = [];
6348         var root = o;
6349             for(var i = 0; i < root.length; i++){
6350                     var n = root[i];
6351                 var values = {};
6352                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
6353                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6354                 var f = fields.items[j];
6355                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
6356                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
6357                 v = f.convert(v);
6358                 values[f.name] = v;
6359             }
6360                 var record = new recordType(values, id);
6361                 record.json = n;
6362                 records[records.length] = record;
6363             }
6364             return {
6365                 records : records,
6366                 totalRecords : records.length
6367             };
6368     }
6369 });/*
6370  * Based on:
6371  * Ext JS Library 1.1.1
6372  * Copyright(c) 2006-2007, Ext JS, LLC.
6373  *
6374  * Originally Released Under LGPL - original licence link has changed is not relivant.
6375  *
6376  * Fork - LGPL
6377  * <script type="text/javascript">
6378  */
6379
6380
6381 /**
6382  * @class Roo.data.Tree
6383  * @extends Roo.util.Observable
6384  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
6385  * in the tree have most standard DOM functionality.
6386  * @constructor
6387  * @param {Node} root (optional) The root node
6388  */
6389 Roo.data.Tree = function(root){
6390    this.nodeHash = {};
6391    /**
6392     * The root node for this tree
6393     * @type Node
6394     */
6395    this.root = null;
6396    if(root){
6397        this.setRootNode(root);
6398    }
6399    this.addEvents({
6400        /**
6401         * @event append
6402         * Fires when a new child node is appended to a node in this tree.
6403         * @param {Tree} tree The owner tree
6404         * @param {Node} parent The parent node
6405         * @param {Node} node The newly appended node
6406         * @param {Number} index The index of the newly appended node
6407         */
6408        "append" : true,
6409        /**
6410         * @event remove
6411         * Fires when a child node is removed from a node in this tree.
6412         * @param {Tree} tree The owner tree
6413         * @param {Node} parent The parent node
6414         * @param {Node} node The child node removed
6415         */
6416        "remove" : true,
6417        /**
6418         * @event move
6419         * Fires when a node is moved to a new location in the tree
6420         * @param {Tree} tree The owner tree
6421         * @param {Node} node The node moved
6422         * @param {Node} oldParent The old parent of this node
6423         * @param {Node} newParent The new parent of this node
6424         * @param {Number} index The index it was moved to
6425         */
6426        "move" : true,
6427        /**
6428         * @event insert
6429         * Fires when a new child node is inserted in a node in this tree.
6430         * @param {Tree} tree The owner tree
6431         * @param {Node} parent The parent node
6432         * @param {Node} node The child node inserted
6433         * @param {Node} refNode The child node the node was inserted before
6434         */
6435        "insert" : true,
6436        /**
6437         * @event beforeappend
6438         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
6439         * @param {Tree} tree The owner tree
6440         * @param {Node} parent The parent node
6441         * @param {Node} node The child node to be appended
6442         */
6443        "beforeappend" : true,
6444        /**
6445         * @event beforeremove
6446         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
6447         * @param {Tree} tree The owner tree
6448         * @param {Node} parent The parent node
6449         * @param {Node} node The child node to be removed
6450         */
6451        "beforeremove" : true,
6452        /**
6453         * @event beforemove
6454         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
6455         * @param {Tree} tree The owner tree
6456         * @param {Node} node The node being moved
6457         * @param {Node} oldParent The parent of the node
6458         * @param {Node} newParent The new parent the node is moving to
6459         * @param {Number} index The index it is being moved to
6460         */
6461        "beforemove" : true,
6462        /**
6463         * @event beforeinsert
6464         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
6465         * @param {Tree} tree The owner tree
6466         * @param {Node} parent The parent node
6467         * @param {Node} node The child node to be inserted
6468         * @param {Node} refNode The child node the node is being inserted before
6469         */
6470        "beforeinsert" : true
6471    });
6472
6473     Roo.data.Tree.superclass.constructor.call(this);
6474 };
6475
6476 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
6477     pathSeparator: "/",
6478
6479     proxyNodeEvent : function(){
6480         return this.fireEvent.apply(this, arguments);
6481     },
6482
6483     /**
6484      * Returns the root node for this tree.
6485      * @return {Node}
6486      */
6487     getRootNode : function(){
6488         return this.root;
6489     },
6490
6491     /**
6492      * Sets the root node for this tree.
6493      * @param {Node} node
6494      * @return {Node}
6495      */
6496     setRootNode : function(node){
6497         this.root = node;
6498         node.ownerTree = this;
6499         node.isRoot = true;
6500         this.registerNode(node);
6501         return node;
6502     },
6503
6504     /**
6505      * Gets a node in this tree by its id.
6506      * @param {String} id
6507      * @return {Node}
6508      */
6509     getNodeById : function(id){
6510         return this.nodeHash[id];
6511     },
6512
6513     registerNode : function(node){
6514         this.nodeHash[node.id] = node;
6515     },
6516
6517     unregisterNode : function(node){
6518         delete this.nodeHash[node.id];
6519     },
6520
6521     toString : function(){
6522         return "[Tree"+(this.id?" "+this.id:"")+"]";
6523     }
6524 });
6525
6526 /**
6527  * @class Roo.data.Node
6528  * @extends Roo.util.Observable
6529  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
6530  * @cfg {String} id The id for this node. If one is not specified, one is generated.
6531  * @constructor
6532  * @param {Object} attributes The attributes/config for the node
6533  */
6534 Roo.data.Node = function(attributes){
6535     /**
6536      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
6537      * @type {Object}
6538      */
6539     this.attributes = attributes || {};
6540     this.leaf = this.attributes.leaf;
6541     /**
6542      * The node id. @type String
6543      */
6544     this.id = this.attributes.id;
6545     if(!this.id){
6546         this.id = Roo.id(null, "ynode-");
6547         this.attributes.id = this.id;
6548     }
6549     /**
6550      * All child nodes of this node. @type Array
6551      */
6552     this.childNodes = [];
6553     if(!this.childNodes.indexOf){ // indexOf is a must
6554         this.childNodes.indexOf = function(o){
6555             for(var i = 0, len = this.length; i < len; i++){
6556                 if(this[i] == o) return i;
6557             }
6558             return -1;
6559         };
6560     }
6561     /**
6562      * The parent node for this node. @type Node
6563      */
6564     this.parentNode = null;
6565     /**
6566      * The first direct child node of this node, or null if this node has no child nodes. @type Node
6567      */
6568     this.firstChild = null;
6569     /**
6570      * The last direct child node of this node, or null if this node has no child nodes. @type Node
6571      */
6572     this.lastChild = null;
6573     /**
6574      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
6575      */
6576     this.previousSibling = null;
6577     /**
6578      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
6579      */
6580     this.nextSibling = null;
6581
6582     this.addEvents({
6583        /**
6584         * @event append
6585         * Fires when a new child node is appended
6586         * @param {Tree} tree The owner tree
6587         * @param {Node} this This node
6588         * @param {Node} node The newly appended node
6589         * @param {Number} index The index of the newly appended node
6590         */
6591        "append" : true,
6592        /**
6593         * @event remove
6594         * Fires when a child node is removed
6595         * @param {Tree} tree The owner tree
6596         * @param {Node} this This node
6597         * @param {Node} node The removed node
6598         */
6599        "remove" : true,
6600        /**
6601         * @event move
6602         * Fires when this node is moved to a new location in the tree
6603         * @param {Tree} tree The owner tree
6604         * @param {Node} this This node
6605         * @param {Node} oldParent The old parent of this node
6606         * @param {Node} newParent The new parent of this node
6607         * @param {Number} index The index it was moved to
6608         */
6609        "move" : true,
6610        /**
6611         * @event insert
6612         * Fires when a new child node is inserted.
6613         * @param {Tree} tree The owner tree
6614         * @param {Node} this This node
6615         * @param {Node} node The child node inserted
6616         * @param {Node} refNode The child node the node was inserted before
6617         */
6618        "insert" : true,
6619        /**
6620         * @event beforeappend
6621         * Fires before a new child is appended, return false to cancel the append.
6622         * @param {Tree} tree The owner tree
6623         * @param {Node} this This node
6624         * @param {Node} node The child node to be appended
6625         */
6626        "beforeappend" : true,
6627        /**
6628         * @event beforeremove
6629         * Fires before a child is removed, return false to cancel the remove.
6630         * @param {Tree} tree The owner tree
6631         * @param {Node} this This node
6632         * @param {Node} node The child node to be removed
6633         */
6634        "beforeremove" : true,
6635        /**
6636         * @event beforemove
6637         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
6638         * @param {Tree} tree The owner tree
6639         * @param {Node} this This node
6640         * @param {Node} oldParent The parent of this node
6641         * @param {Node} newParent The new parent this node is moving to
6642         * @param {Number} index The index it is being moved to
6643         */
6644        "beforemove" : true,
6645        /**
6646         * @event beforeinsert
6647         * Fires before a new child is inserted, return false to cancel the insert.
6648         * @param {Tree} tree The owner tree
6649         * @param {Node} this This node
6650         * @param {Node} node The child node to be inserted
6651         * @param {Node} refNode The child node the node is being inserted before
6652         */
6653        "beforeinsert" : true
6654    });
6655     this.listeners = this.attributes.listeners;
6656     Roo.data.Node.superclass.constructor.call(this);
6657 };
6658
6659 Roo.extend(Roo.data.Node, Roo.util.Observable, {
6660     fireEvent : function(evtName){
6661         // first do standard event for this node
6662         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
6663             return false;
6664         }
6665         // then bubble it up to the tree if the event wasn't cancelled
6666         var ot = this.getOwnerTree();
6667         if(ot){
6668             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
6669                 return false;
6670             }
6671         }
6672         return true;
6673     },
6674
6675     /**
6676      * Returns true if this node is a leaf
6677      * @return {Boolean}
6678      */
6679     isLeaf : function(){
6680         return this.leaf === true;
6681     },
6682
6683     // private
6684     setFirstChild : function(node){
6685         this.firstChild = node;
6686     },
6687
6688     //private
6689     setLastChild : function(node){
6690         this.lastChild = node;
6691     },
6692
6693
6694     /**
6695      * Returns true if this node is the last child of its parent
6696      * @return {Boolean}
6697      */
6698     isLast : function(){
6699        return (!this.parentNode ? true : this.parentNode.lastChild == this);
6700     },
6701
6702     /**
6703      * Returns true if this node is the first child of its parent
6704      * @return {Boolean}
6705      */
6706     isFirst : function(){
6707        return (!this.parentNode ? true : this.parentNode.firstChild == this);
6708     },
6709
6710     hasChildNodes : function(){
6711         return !this.isLeaf() && this.childNodes.length > 0;
6712     },
6713
6714     /**
6715      * Insert node(s) as the last child node of this node.
6716      * @param {Node/Array} node The node or Array of nodes to append
6717      * @return {Node} The appended node if single append, or null if an array was passed
6718      */
6719     appendChild : function(node){
6720         var multi = false;
6721         if(node instanceof Array){
6722             multi = node;
6723         }else if(arguments.length > 1){
6724             multi = arguments;
6725         }
6726         // if passed an array or multiple args do them one by one
6727         if(multi){
6728             for(var i = 0, len = multi.length; i < len; i++) {
6729                 this.appendChild(multi[i]);
6730             }
6731         }else{
6732             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
6733                 return false;
6734             }
6735             var index = this.childNodes.length;
6736             var oldParent = node.parentNode;
6737             // it's a move, make sure we move it cleanly
6738             if(oldParent){
6739                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
6740                     return false;
6741                 }
6742                 oldParent.removeChild(node);
6743             }
6744             index = this.childNodes.length;
6745             if(index == 0){
6746                 this.setFirstChild(node);
6747             }
6748             this.childNodes.push(node);
6749             node.parentNode = this;
6750             var ps = this.childNodes[index-1];
6751             if(ps){
6752                 node.previousSibling = ps;
6753                 ps.nextSibling = node;
6754             }else{
6755                 node.previousSibling = null;
6756             }
6757             node.nextSibling = null;
6758             this.setLastChild(node);
6759             node.setOwnerTree(this.getOwnerTree());
6760             this.fireEvent("append", this.ownerTree, this, node, index);
6761             if(oldParent){
6762                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
6763             }
6764             return node;
6765         }
6766     },
6767
6768     /**
6769      * Removes a child node from this node.
6770      * @param {Node} node The node to remove
6771      * @return {Node} The removed node
6772      */
6773     removeChild : function(node){
6774         var index = this.childNodes.indexOf(node);
6775         if(index == -1){
6776             return false;
6777         }
6778         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
6779             return false;
6780         }
6781
6782         // remove it from childNodes collection
6783         this.childNodes.splice(index, 1);
6784
6785         // update siblings
6786         if(node.previousSibling){
6787             node.previousSibling.nextSibling = node.nextSibling;
6788         }
6789         if(node.nextSibling){
6790             node.nextSibling.previousSibling = node.previousSibling;
6791         }
6792
6793         // update child refs
6794         if(this.firstChild == node){
6795             this.setFirstChild(node.nextSibling);
6796         }
6797         if(this.lastChild == node){
6798             this.setLastChild(node.previousSibling);
6799         }
6800
6801         node.setOwnerTree(null);
6802         // clear any references from the node
6803         node.parentNode = null;
6804         node.previousSibling = null;
6805         node.nextSibling = null;
6806         this.fireEvent("remove", this.ownerTree, this, node);
6807         return node;
6808     },
6809
6810     /**
6811      * Inserts the first node before the second node in this nodes childNodes collection.
6812      * @param {Node} node The node to insert
6813      * @param {Node} refNode The node to insert before (if null the node is appended)
6814      * @return {Node} The inserted node
6815      */
6816     insertBefore : function(node, refNode){
6817         if(!refNode){ // like standard Dom, refNode can be null for append
6818             return this.appendChild(node);
6819         }
6820         // nothing to do
6821         if(node == refNode){
6822             return false;
6823         }
6824
6825         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
6826             return false;
6827         }
6828         var index = this.childNodes.indexOf(refNode);
6829         var oldParent = node.parentNode;
6830         var refIndex = index;
6831
6832         // when moving internally, indexes will change after remove
6833         if(oldParent == this && this.childNodes.indexOf(node) < index){
6834             refIndex--;
6835         }
6836
6837         // it's a move, make sure we move it cleanly
6838         if(oldParent){
6839             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
6840                 return false;
6841             }
6842             oldParent.removeChild(node);
6843         }
6844         if(refIndex == 0){
6845             this.setFirstChild(node);
6846         }
6847         this.childNodes.splice(refIndex, 0, node);
6848         node.parentNode = this;
6849         var ps = this.childNodes[refIndex-1];
6850         if(ps){
6851             node.previousSibling = ps;
6852             ps.nextSibling = node;
6853         }else{
6854             node.previousSibling = null;
6855         }
6856         node.nextSibling = refNode;
6857         refNode.previousSibling = node;
6858         node.setOwnerTree(this.getOwnerTree());
6859         this.fireEvent("insert", this.ownerTree, this, node, refNode);
6860         if(oldParent){
6861             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
6862         }
6863         return node;
6864     },
6865
6866     /**
6867      * Returns the child node at the specified index.
6868      * @param {Number} index
6869      * @return {Node}
6870      */
6871     item : function(index){
6872         return this.childNodes[index];
6873     },
6874
6875     /**
6876      * Replaces one child node in this node with another.
6877      * @param {Node} newChild The replacement node
6878      * @param {Node} oldChild The node to replace
6879      * @return {Node} The replaced node
6880      */
6881     replaceChild : function(newChild, oldChild){
6882         this.insertBefore(newChild, oldChild);
6883         this.removeChild(oldChild);
6884         return oldChild;
6885     },
6886
6887     /**
6888      * Returns the index of a child node
6889      * @param {Node} node
6890      * @return {Number} The index of the node or -1 if it was not found
6891      */
6892     indexOf : function(child){
6893         return this.childNodes.indexOf(child);
6894     },
6895
6896     /**
6897      * Returns the tree this node is in.
6898      * @return {Tree}
6899      */
6900     getOwnerTree : function(){
6901         // if it doesn't have one, look for one
6902         if(!this.ownerTree){
6903             var p = this;
6904             while(p){
6905                 if(p.ownerTree){
6906                     this.ownerTree = p.ownerTree;
6907                     break;
6908                 }
6909                 p = p.parentNode;
6910             }
6911         }
6912         return this.ownerTree;
6913     },
6914
6915     /**
6916      * Returns depth of this node (the root node has a depth of 0)
6917      * @return {Number}
6918      */
6919     getDepth : function(){
6920         var depth = 0;
6921         var p = this;
6922         while(p.parentNode){
6923             ++depth;
6924             p = p.parentNode;
6925         }
6926         return depth;
6927     },
6928
6929     // private
6930     setOwnerTree : function(tree){
6931         // if it's move, we need to update everyone
6932         if(tree != this.ownerTree){
6933             if(this.ownerTree){
6934                 this.ownerTree.unregisterNode(this);
6935             }
6936             this.ownerTree = tree;
6937             var cs = this.childNodes;
6938             for(var i = 0, len = cs.length; i < len; i++) {
6939                 cs[i].setOwnerTree(tree);
6940             }
6941             if(tree){
6942                 tree.registerNode(this);
6943             }
6944         }
6945     },
6946
6947     /**
6948      * Returns the path for this node. The path can be used to expand or select this node programmatically.
6949      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
6950      * @return {String} The path
6951      */
6952     getPath : function(attr){
6953         attr = attr || "id";
6954         var p = this.parentNode;
6955         var b = [this.attributes[attr]];
6956         while(p){
6957             b.unshift(p.attributes[attr]);
6958             p = p.parentNode;
6959         }
6960         var sep = this.getOwnerTree().pathSeparator;
6961         return sep + b.join(sep);
6962     },
6963
6964     /**
6965      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
6966      * function call will be the scope provided or the current node. The arguments to the function
6967      * will be the args provided or the current node. If the function returns false at any point,
6968      * the bubble is stopped.
6969      * @param {Function} fn The function to call
6970      * @param {Object} scope (optional) The scope of the function (defaults to current node)
6971      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
6972      */
6973     bubble : function(fn, scope, args){
6974         var p = this;
6975         while(p){
6976             if(fn.call(scope || p, args || p) === false){
6977                 break;
6978             }
6979             p = p.parentNode;
6980         }
6981     },
6982
6983     /**
6984      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
6985      * function call will be the scope provided or the current node. The arguments to the function
6986      * will be the args provided or the current node. If the function returns false at any point,
6987      * the cascade is stopped on that branch.
6988      * @param {Function} fn The function to call
6989      * @param {Object} scope (optional) The scope of the function (defaults to current node)
6990      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
6991      */
6992     cascade : function(fn, scope, args){
6993         if(fn.call(scope || this, args || this) !== false){
6994             var cs = this.childNodes;
6995             for(var i = 0, len = cs.length; i < len; i++) {
6996                 cs[i].cascade(fn, scope, args);
6997             }
6998         }
6999     },
7000
7001     /**
7002      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
7003      * function call will be the scope provided or the current node. The arguments to the function
7004      * will be the args provided or the current node. If the function returns false at any point,
7005      * the iteration stops.
7006      * @param {Function} fn The function to call
7007      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7008      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7009      */
7010     eachChild : function(fn, scope, args){
7011         var cs = this.childNodes;
7012         for(var i = 0, len = cs.length; i < len; i++) {
7013                 if(fn.call(scope || this, args || cs[i]) === false){
7014                     break;
7015                 }
7016         }
7017     },
7018
7019     /**
7020      * Finds the first child that has the attribute with the specified value.
7021      * @param {String} attribute The attribute name
7022      * @param {Mixed} value The value to search for
7023      * @return {Node} The found child or null if none was found
7024      */
7025     findChild : function(attribute, value){
7026         var cs = this.childNodes;
7027         for(var i = 0, len = cs.length; i < len; i++) {
7028                 if(cs[i].attributes[attribute] == value){
7029                     return cs[i];
7030                 }
7031         }
7032         return null;
7033     },
7034
7035     /**
7036      * Finds the first child by a custom function. The child matches if the function passed
7037      * returns true.
7038      * @param {Function} fn
7039      * @param {Object} scope (optional)
7040      * @return {Node} The found child or null if none was found
7041      */
7042     findChildBy : function(fn, scope){
7043         var cs = this.childNodes;
7044         for(var i = 0, len = cs.length; i < len; i++) {
7045                 if(fn.call(scope||cs[i], cs[i]) === true){
7046                     return cs[i];
7047                 }
7048         }
7049         return null;
7050     },
7051
7052     /**
7053      * Sorts this nodes children using the supplied sort function
7054      * @param {Function} fn
7055      * @param {Object} scope (optional)
7056      */
7057     sort : function(fn, scope){
7058         var cs = this.childNodes;
7059         var len = cs.length;
7060         if(len > 0){
7061             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
7062             cs.sort(sortFn);
7063             for(var i = 0; i < len; i++){
7064                 var n = cs[i];
7065                 n.previousSibling = cs[i-1];
7066                 n.nextSibling = cs[i+1];
7067                 if(i == 0){
7068                     this.setFirstChild(n);
7069                 }
7070                 if(i == len-1){
7071                     this.setLastChild(n);
7072                 }
7073             }
7074         }
7075     },
7076
7077     /**
7078      * Returns true if this node is an ancestor (at any point) of the passed node.
7079      * @param {Node} node
7080      * @return {Boolean}
7081      */
7082     contains : function(node){
7083         return node.isAncestor(this);
7084     },
7085
7086     /**
7087      * Returns true if the passed node is an ancestor (at any point) of this node.
7088      * @param {Node} node
7089      * @return {Boolean}
7090      */
7091     isAncestor : function(node){
7092         var p = this.parentNode;
7093         while(p){
7094             if(p == node){
7095                 return true;
7096             }
7097             p = p.parentNode;
7098         }
7099         return false;
7100     },
7101
7102     toString : function(){
7103         return "[Node"+(this.id?" "+this.id:"")+"]";
7104     }
7105 });/*
7106  * Based on:
7107  * Ext JS Library 1.1.1
7108  * Copyright(c) 2006-2007, Ext JS, LLC.
7109  *
7110  * Originally Released Under LGPL - original licence link has changed is not relivant.
7111  *
7112  * Fork - LGPL
7113  * <script type="text/javascript">
7114  */
7115  
7116
7117 /**
7118  * @class Roo.ComponentMgr
7119  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
7120  * @singleton
7121  */
7122 Roo.ComponentMgr = function(){
7123     var all = new Roo.util.MixedCollection();
7124
7125     return {
7126         /**
7127          * Registers a component.
7128          * @param {Roo.Component} c The component
7129          */
7130         register : function(c){
7131             all.add(c);
7132         },
7133
7134         /**
7135          * Unregisters a component.
7136          * @param {Roo.Component} c The component
7137          */
7138         unregister : function(c){
7139             all.remove(c);
7140         },
7141
7142         /**
7143          * Returns a component by id
7144          * @param {String} id The component id
7145          */
7146         get : function(id){
7147             return all.get(id);
7148         },
7149
7150         /**
7151          * Registers a function that will be called when a specified component is added to ComponentMgr
7152          * @param {String} id The component id
7153          * @param {Funtction} fn The callback function
7154          * @param {Object} scope The scope of the callback
7155          */
7156         onAvailable : function(id, fn, scope){
7157             all.on("add", function(index, o){
7158                 if(o.id == id){
7159                     fn.call(scope || o, o);
7160                     all.un("add", fn, scope);
7161                 }
7162             });
7163         }
7164     };
7165 }();/*
7166  * Based on:
7167  * Ext JS Library 1.1.1
7168  * Copyright(c) 2006-2007, Ext JS, LLC.
7169  *
7170  * Originally Released Under LGPL - original licence link has changed is not relivant.
7171  *
7172  * Fork - LGPL
7173  * <script type="text/javascript">
7174  */
7175  
7176 /**
7177  * @class Roo.Component
7178  * @extends Roo.util.Observable
7179  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
7180  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
7181  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
7182  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
7183  * All visual components (widgets) that require rendering into a layout should subclass Component.
7184  * @constructor
7185  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
7186  * 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
7187  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
7188  */
7189 Roo.Component = function(config){
7190     config = config || {};
7191     if(config.tagName || config.dom || typeof config == "string"){ // element object
7192         config = {el: config, id: config.id || config};
7193     }
7194     this.initialConfig = config;
7195
7196     Roo.apply(this, config);
7197     this.addEvents({
7198         /**
7199          * @event disable
7200          * Fires after the component is disabled.
7201              * @param {Roo.Component} this
7202              */
7203         disable : true,
7204         /**
7205          * @event enable
7206          * Fires after the component is enabled.
7207              * @param {Roo.Component} this
7208              */
7209         enable : true,
7210         /**
7211          * @event beforeshow
7212          * Fires before the component is shown.  Return false to stop the show.
7213              * @param {Roo.Component} this
7214              */
7215         beforeshow : true,
7216         /**
7217          * @event show
7218          * Fires after the component is shown.
7219              * @param {Roo.Component} this
7220              */
7221         show : true,
7222         /**
7223          * @event beforehide
7224          * Fires before the component is hidden. Return false to stop the hide.
7225              * @param {Roo.Component} this
7226              */
7227         beforehide : true,
7228         /**
7229          * @event hide
7230          * Fires after the component is hidden.
7231              * @param {Roo.Component} this
7232              */
7233         hide : true,
7234         /**
7235          * @event beforerender
7236          * Fires before the component is rendered. Return false to stop the render.
7237              * @param {Roo.Component} this
7238              */
7239         beforerender : true,
7240         /**
7241          * @event render
7242          * Fires after the component is rendered.
7243              * @param {Roo.Component} this
7244              */
7245         render : true,
7246         /**
7247          * @event beforedestroy
7248          * Fires before the component is destroyed. Return false to stop the destroy.
7249              * @param {Roo.Component} this
7250              */
7251         beforedestroy : true,
7252         /**
7253          * @event destroy
7254          * Fires after the component is destroyed.
7255              * @param {Roo.Component} this
7256              */
7257         destroy : true
7258     });
7259     if(!this.id){
7260         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
7261     }
7262     Roo.ComponentMgr.register(this);
7263     Roo.Component.superclass.constructor.call(this);
7264     this.initComponent();
7265     if(this.renderTo){ // not supported by all components yet. use at your own risk!
7266         this.render(this.renderTo);
7267         delete this.renderTo;
7268     }
7269 };
7270
7271 // private
7272 Roo.Component.AUTO_ID = 1000;
7273
7274 Roo.extend(Roo.Component, Roo.util.Observable, {
7275     /**
7276      * @property {Boolean} hidden
7277      * true if this component is hidden. Read-only.
7278      */
7279     hidden : false,
7280     /**
7281      * true if this component is disabled. Read-only.
7282      */
7283     disabled : false,
7284     /**
7285      * true if this component has been rendered. Read-only.
7286      */
7287     rendered : false,
7288     
7289     /** @cfg {String} disableClass
7290      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
7291      */
7292     disabledClass : "x-item-disabled",
7293         /** @cfg {Boolean} allowDomMove
7294          * Whether the component can move the Dom node when rendering (defaults to true).
7295          */
7296     allowDomMove : true,
7297     /** @cfg {String} hideMode
7298      * How this component should hidden. Supported values are
7299      * "visibility" (css visibility), "offsets" (negative offset position) and
7300      * "display" (css display) - defaults to "display".
7301      */
7302     hideMode: 'display',
7303
7304     // private
7305     ctype : "Roo.Component",
7306
7307     /** @cfg {String} actionMode 
7308      * which property holds the element that used for  hide() / show() / disable() / enable()
7309      * default is 'el' 
7310      */
7311     actionMode : "el",
7312
7313     // private
7314     getActionEl : function(){
7315         return this[this.actionMode];
7316     },
7317
7318     initComponent : Roo.emptyFn,
7319     /**
7320      * If this is a lazy rendering component, render it to its container element.
7321      * @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.
7322      */
7323     render : function(container, position){
7324         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
7325             if(!container && this.el){
7326                 this.el = Roo.get(this.el);
7327                 container = this.el.dom.parentNode;
7328                 this.allowDomMove = false;
7329             }
7330             this.container = Roo.get(container);
7331             this.rendered = true;
7332             if(position !== undefined){
7333                 if(typeof position == 'number'){
7334                     position = this.container.dom.childNodes[position];
7335                 }else{
7336                     position = Roo.getDom(position);
7337                 }
7338             }
7339             this.onRender(this.container, position || null);
7340             if(this.cls){
7341                 this.el.addClass(this.cls);
7342                 delete this.cls;
7343             }
7344             if(this.style){
7345                 this.el.applyStyles(this.style);
7346                 delete this.style;
7347             }
7348             this.fireEvent("render", this);
7349             this.afterRender(this.container);
7350             if(this.hidden){
7351                 this.hide();
7352             }
7353             if(this.disabled){
7354                 this.disable();
7355             }
7356         }
7357         return this;
7358     },
7359
7360     // private
7361     // default function is not really useful
7362     onRender : function(ct, position){
7363         if(this.el){
7364             this.el = Roo.get(this.el);
7365             if(this.allowDomMove !== false){
7366                 ct.dom.insertBefore(this.el.dom, position);
7367             }
7368         }
7369     },
7370
7371     // private
7372     getAutoCreate : function(){
7373         var cfg = typeof this.autoCreate == "object" ?
7374                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
7375         if(this.id && !cfg.id){
7376             cfg.id = this.id;
7377         }
7378         return cfg;
7379     },
7380
7381     // private
7382     afterRender : Roo.emptyFn,
7383
7384     /**
7385      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
7386      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
7387      */
7388     destroy : function(){
7389         if(this.fireEvent("beforedestroy", this) !== false){
7390             this.purgeListeners();
7391             this.beforeDestroy();
7392             if(this.rendered){
7393                 this.el.removeAllListeners();
7394                 this.el.remove();
7395                 if(this.actionMode == "container"){
7396                     this.container.remove();
7397                 }
7398             }
7399             this.onDestroy();
7400             Roo.ComponentMgr.unregister(this);
7401             this.fireEvent("destroy", this);
7402         }
7403     },
7404
7405         // private
7406     beforeDestroy : function(){
7407
7408     },
7409
7410         // private
7411         onDestroy : function(){
7412
7413     },
7414
7415     /**
7416      * Returns the underlying {@link Roo.Element}.
7417      * @return {Roo.Element} The element
7418      */
7419     getEl : function(){
7420         return this.el;
7421     },
7422
7423     /**
7424      * Returns the id of this component.
7425      * @return {String}
7426      */
7427     getId : function(){
7428         return this.id;
7429     },
7430
7431     /**
7432      * Try to focus this component.
7433      * @param {Boolean} selectText True to also select the text in this component (if applicable)
7434      * @return {Roo.Component} this
7435      */
7436     focus : function(selectText){
7437         if(this.rendered){
7438             this.el.focus();
7439             if(selectText === true){
7440                 this.el.dom.select();
7441             }
7442         }
7443         return this;
7444     },
7445
7446     // private
7447     blur : function(){
7448         if(this.rendered){
7449             this.el.blur();
7450         }
7451         return this;
7452     },
7453
7454     /**
7455      * Disable this component.
7456      * @return {Roo.Component} this
7457      */
7458     disable : function(){
7459         if(this.rendered){
7460             this.onDisable();
7461         }
7462         this.disabled = true;
7463         this.fireEvent("disable", this);
7464         return this;
7465     },
7466
7467         // private
7468     onDisable : function(){
7469         this.getActionEl().addClass(this.disabledClass);
7470         this.el.dom.disabled = true;
7471     },
7472
7473     /**
7474      * Enable this component.
7475      * @return {Roo.Component} this
7476      */
7477     enable : function(){
7478         if(this.rendered){
7479             this.onEnable();
7480         }
7481         this.disabled = false;
7482         this.fireEvent("enable", this);
7483         return this;
7484     },
7485
7486         // private
7487     onEnable : function(){
7488         this.getActionEl().removeClass(this.disabledClass);
7489         this.el.dom.disabled = false;
7490     },
7491
7492     /**
7493      * Convenience function for setting disabled/enabled by boolean.
7494      * @param {Boolean} disabled
7495      */
7496     setDisabled : function(disabled){
7497         this[disabled ? "disable" : "enable"]();
7498     },
7499
7500     /**
7501      * Show this component.
7502      * @return {Roo.Component} this
7503      */
7504     show: function(){
7505         if(this.fireEvent("beforeshow", this) !== false){
7506             this.hidden = false;
7507             if(this.rendered){
7508                 this.onShow();
7509             }
7510             this.fireEvent("show", this);
7511         }
7512         return this;
7513     },
7514
7515     // private
7516     onShow : function(){
7517         var ae = this.getActionEl();
7518         if(this.hideMode == 'visibility'){
7519             ae.dom.style.visibility = "visible";
7520         }else if(this.hideMode == 'offsets'){
7521             ae.removeClass('x-hidden');
7522         }else{
7523             ae.dom.style.display = "";
7524         }
7525     },
7526
7527     /**
7528      * Hide this component.
7529      * @return {Roo.Component} this
7530      */
7531     hide: function(){
7532         if(this.fireEvent("beforehide", this) !== false){
7533             this.hidden = true;
7534             if(this.rendered){
7535                 this.onHide();
7536             }
7537             this.fireEvent("hide", this);
7538         }
7539         return this;
7540     },
7541
7542     // private
7543     onHide : function(){
7544         var ae = this.getActionEl();
7545         if(this.hideMode == 'visibility'){
7546             ae.dom.style.visibility = "hidden";
7547         }else if(this.hideMode == 'offsets'){
7548             ae.addClass('x-hidden');
7549         }else{
7550             ae.dom.style.display = "none";
7551         }
7552     },
7553
7554     /**
7555      * Convenience function to hide or show this component by boolean.
7556      * @param {Boolean} visible True to show, false to hide
7557      * @return {Roo.Component} this
7558      */
7559     setVisible: function(visible){
7560         if(visible) {
7561             this.show();
7562         }else{
7563             this.hide();
7564         }
7565         return this;
7566     },
7567
7568     /**
7569      * Returns true if this component is visible.
7570      */
7571     isVisible : function(){
7572         return this.getActionEl().isVisible();
7573     },
7574
7575     cloneConfig : function(overrides){
7576         overrides = overrides || {};
7577         var id = overrides.id || Roo.id();
7578         var cfg = Roo.applyIf(overrides, this.initialConfig);
7579         cfg.id = id; // prevent dup id
7580         return new this.constructor(cfg);
7581     }
7582 });/*
7583  * Based on:
7584  * Ext JS Library 1.1.1
7585  * Copyright(c) 2006-2007, Ext JS, LLC.
7586  *
7587  * Originally Released Under LGPL - original licence link has changed is not relivant.
7588  *
7589  * Fork - LGPL
7590  * <script type="text/javascript">
7591  */
7592  (function(){ 
7593 /**
7594  * @class Roo.Layer
7595  * @extends Roo.Element
7596  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
7597  * automatic maintaining of shadow/shim positions.
7598  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
7599  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
7600  * you can pass a string with a CSS class name. False turns off the shadow.
7601  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
7602  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
7603  * @cfg {String} cls CSS class to add to the element
7604  * @cfg {Number} zindex Starting z-index (defaults to 11000)
7605  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
7606  * @constructor
7607  * @param {Object} config An object with config options.
7608  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
7609  */
7610
7611 Roo.Layer = function(config, existingEl){
7612     config = config || {};
7613     var dh = Roo.DomHelper;
7614     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
7615     if(existingEl){
7616         this.dom = Roo.getDom(existingEl);
7617     }
7618     if(!this.dom){
7619         var o = config.dh || {tag: "div", cls: "x-layer"};
7620         this.dom = dh.append(pel, o);
7621     }
7622     if(config.cls){
7623         this.addClass(config.cls);
7624     }
7625     this.constrain = config.constrain !== false;
7626     this.visibilityMode = Roo.Element.VISIBILITY;
7627     if(config.id){
7628         this.id = this.dom.id = config.id;
7629     }else{
7630         this.id = Roo.id(this.dom);
7631     }
7632     this.zindex = config.zindex || this.getZIndex();
7633     this.position("absolute", this.zindex);
7634     if(config.shadow){
7635         this.shadowOffset = config.shadowOffset || 4;
7636         this.shadow = new Roo.Shadow({
7637             offset : this.shadowOffset,
7638             mode : config.shadow
7639         });
7640     }else{
7641         this.shadowOffset = 0;
7642     }
7643     this.useShim = config.shim !== false && Roo.useShims;
7644     this.useDisplay = config.useDisplay;
7645     this.hide();
7646 };
7647
7648 var supr = Roo.Element.prototype;
7649
7650 // shims are shared among layer to keep from having 100 iframes
7651 var shims = [];
7652
7653 Roo.extend(Roo.Layer, Roo.Element, {
7654
7655     getZIndex : function(){
7656         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
7657     },
7658
7659     getShim : function(){
7660         if(!this.useShim){
7661             return null;
7662         }
7663         if(this.shim){
7664             return this.shim;
7665         }
7666         var shim = shims.shift();
7667         if(!shim){
7668             shim = this.createShim();
7669             shim.enableDisplayMode('block');
7670             shim.dom.style.display = 'none';
7671             shim.dom.style.visibility = 'visible';
7672         }
7673         var pn = this.dom.parentNode;
7674         if(shim.dom.parentNode != pn){
7675             pn.insertBefore(shim.dom, this.dom);
7676         }
7677         shim.setStyle('z-index', this.getZIndex()-2);
7678         this.shim = shim;
7679         return shim;
7680     },
7681
7682     hideShim : function(){
7683         if(this.shim){
7684             this.shim.setDisplayed(false);
7685             shims.push(this.shim);
7686             delete this.shim;
7687         }
7688     },
7689
7690     disableShadow : function(){
7691         if(this.shadow){
7692             this.shadowDisabled = true;
7693             this.shadow.hide();
7694             this.lastShadowOffset = this.shadowOffset;
7695             this.shadowOffset = 0;
7696         }
7697     },
7698
7699     enableShadow : function(show){
7700         if(this.shadow){
7701             this.shadowDisabled = false;
7702             this.shadowOffset = this.lastShadowOffset;
7703             delete this.lastShadowOffset;
7704             if(show){
7705                 this.sync(true);
7706             }
7707         }
7708     },
7709
7710     // private
7711     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
7712     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
7713     sync : function(doShow){
7714         var sw = this.shadow;
7715         if(!this.updating && this.isVisible() && (sw || this.useShim)){
7716             var sh = this.getShim();
7717
7718             var w = this.getWidth(),
7719                 h = this.getHeight();
7720
7721             var l = this.getLeft(true),
7722                 t = this.getTop(true);
7723
7724             if(sw && !this.shadowDisabled){
7725                 if(doShow && !sw.isVisible()){
7726                     sw.show(this);
7727                 }else{
7728                     sw.realign(l, t, w, h);
7729                 }
7730                 if(sh){
7731                     if(doShow){
7732                        sh.show();
7733                     }
7734                     // fit the shim behind the shadow, so it is shimmed too
7735                     var a = sw.adjusts, s = sh.dom.style;
7736                     s.left = (Math.min(l, l+a.l))+"px";
7737                     s.top = (Math.min(t, t+a.t))+"px";
7738                     s.width = (w+a.w)+"px";
7739                     s.height = (h+a.h)+"px";
7740                 }
7741             }else if(sh){
7742                 if(doShow){
7743                    sh.show();
7744                 }
7745                 sh.setSize(w, h);
7746                 sh.setLeftTop(l, t);
7747             }
7748             
7749         }
7750     },
7751
7752     // private
7753     destroy : function(){
7754         this.hideShim();
7755         if(this.shadow){
7756             this.shadow.hide();
7757         }
7758         this.removeAllListeners();
7759         var pn = this.dom.parentNode;
7760         if(pn){
7761             pn.removeChild(this.dom);
7762         }
7763         Roo.Element.uncache(this.id);
7764     },
7765
7766     remove : function(){
7767         this.destroy();
7768     },
7769
7770     // private
7771     beginUpdate : function(){
7772         this.updating = true;
7773     },
7774
7775     // private
7776     endUpdate : function(){
7777         this.updating = false;
7778         this.sync(true);
7779     },
7780
7781     // private
7782     hideUnders : function(negOffset){
7783         if(this.shadow){
7784             this.shadow.hide();
7785         }
7786         this.hideShim();
7787     },
7788
7789     // private
7790     constrainXY : function(){
7791         if(this.constrain){
7792             var vw = Roo.lib.Dom.getViewWidth(),
7793                 vh = Roo.lib.Dom.getViewHeight();
7794             var s = Roo.get(document).getScroll();
7795
7796             var xy = this.getXY();
7797             var x = xy[0], y = xy[1];   
7798             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
7799             // only move it if it needs it
7800             var moved = false;
7801             // first validate right/bottom
7802             if((x + w) > vw+s.left){
7803                 x = vw - w - this.shadowOffset;
7804                 moved = true;
7805             }
7806             if((y + h) > vh+s.top){
7807                 y = vh - h - this.shadowOffset;
7808                 moved = true;
7809             }
7810             // then make sure top/left isn't negative
7811             if(x < s.left){
7812                 x = s.left;
7813                 moved = true;
7814             }
7815             if(y < s.top){
7816                 y = s.top;
7817                 moved = true;
7818             }
7819             if(moved){
7820                 if(this.avoidY){
7821                     var ay = this.avoidY;
7822                     if(y <= ay && (y+h) >= ay){
7823                         y = ay-h-5;   
7824                     }
7825                 }
7826                 xy = [x, y];
7827                 this.storeXY(xy);
7828                 supr.setXY.call(this, xy);
7829                 this.sync();
7830             }
7831         }
7832     },
7833
7834     isVisible : function(){
7835         return this.visible;    
7836     },
7837
7838     // private
7839     showAction : function(){
7840         this.visible = true; // track visibility to prevent getStyle calls
7841         if(this.useDisplay === true){
7842             this.setDisplayed("");
7843         }else if(this.lastXY){
7844             supr.setXY.call(this, this.lastXY);
7845         }else if(this.lastLT){
7846             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
7847         }
7848     },
7849
7850     // private
7851     hideAction : function(){
7852         this.visible = false;
7853         if(this.useDisplay === true){
7854             this.setDisplayed(false);
7855         }else{
7856             this.setLeftTop(-10000,-10000);
7857         }
7858     },
7859
7860     // overridden Element method
7861     setVisible : function(v, a, d, c, e){
7862         if(v){
7863             this.showAction();
7864         }
7865         if(a && v){
7866             var cb = function(){
7867                 this.sync(true);
7868                 if(c){
7869                     c();
7870                 }
7871             }.createDelegate(this);
7872             supr.setVisible.call(this, true, true, d, cb, e);
7873         }else{
7874             if(!v){
7875                 this.hideUnders(true);
7876             }
7877             var cb = c;
7878             if(a){
7879                 cb = function(){
7880                     this.hideAction();
7881                     if(c){
7882                         c();
7883                     }
7884                 }.createDelegate(this);
7885             }
7886             supr.setVisible.call(this, v, a, d, cb, e);
7887             if(v){
7888                 this.sync(true);
7889             }else if(!a){
7890                 this.hideAction();
7891             }
7892         }
7893     },
7894
7895     storeXY : function(xy){
7896         delete this.lastLT;
7897         this.lastXY = xy;
7898     },
7899
7900     storeLeftTop : function(left, top){
7901         delete this.lastXY;
7902         this.lastLT = [left, top];
7903     },
7904
7905     // private
7906     beforeFx : function(){
7907         this.beforeAction();
7908         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
7909     },
7910
7911     // private
7912     afterFx : function(){
7913         Roo.Layer.superclass.afterFx.apply(this, arguments);
7914         this.sync(this.isVisible());
7915     },
7916
7917     // private
7918     beforeAction : function(){
7919         if(!this.updating && this.shadow){
7920             this.shadow.hide();
7921         }
7922     },
7923
7924     // overridden Element method
7925     setLeft : function(left){
7926         this.storeLeftTop(left, this.getTop(true));
7927         supr.setLeft.apply(this, arguments);
7928         this.sync();
7929     },
7930
7931     setTop : function(top){
7932         this.storeLeftTop(this.getLeft(true), top);
7933         supr.setTop.apply(this, arguments);
7934         this.sync();
7935     },
7936
7937     setLeftTop : function(left, top){
7938         this.storeLeftTop(left, top);
7939         supr.setLeftTop.apply(this, arguments);
7940         this.sync();
7941     },
7942
7943     setXY : function(xy, a, d, c, e){
7944         this.fixDisplay();
7945         this.beforeAction();
7946         this.storeXY(xy);
7947         var cb = this.createCB(c);
7948         supr.setXY.call(this, xy, a, d, cb, e);
7949         if(!a){
7950             cb();
7951         }
7952     },
7953
7954     // private
7955     createCB : function(c){
7956         var el = this;
7957         return function(){
7958             el.constrainXY();
7959             el.sync(true);
7960             if(c){
7961                 c();
7962             }
7963         };
7964     },
7965
7966     // overridden Element method
7967     setX : function(x, a, d, c, e){
7968         this.setXY([x, this.getY()], a, d, c, e);
7969     },
7970
7971     // overridden Element method
7972     setY : function(y, a, d, c, e){
7973         this.setXY([this.getX(), y], a, d, c, e);
7974     },
7975
7976     // overridden Element method
7977     setSize : function(w, h, a, d, c, e){
7978         this.beforeAction();
7979         var cb = this.createCB(c);
7980         supr.setSize.call(this, w, h, a, d, cb, e);
7981         if(!a){
7982             cb();
7983         }
7984     },
7985
7986     // overridden Element method
7987     setWidth : function(w, a, d, c, e){
7988         this.beforeAction();
7989         var cb = this.createCB(c);
7990         supr.setWidth.call(this, w, a, d, cb, e);
7991         if(!a){
7992             cb();
7993         }
7994     },
7995
7996     // overridden Element method
7997     setHeight : function(h, a, d, c, e){
7998         this.beforeAction();
7999         var cb = this.createCB(c);
8000         supr.setHeight.call(this, h, a, d, cb, e);
8001         if(!a){
8002             cb();
8003         }
8004     },
8005
8006     // overridden Element method
8007     setBounds : function(x, y, w, h, a, d, c, e){
8008         this.beforeAction();
8009         var cb = this.createCB(c);
8010         if(!a){
8011             this.storeXY([x, y]);
8012             supr.setXY.call(this, [x, y]);
8013             supr.setSize.call(this, w, h, a, d, cb, e);
8014             cb();
8015         }else{
8016             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
8017         }
8018         return this;
8019     },
8020     
8021     /**
8022      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
8023      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
8024      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
8025      * @param {Number} zindex The new z-index to set
8026      * @return {this} The Layer
8027      */
8028     setZIndex : function(zindex){
8029         this.zindex = zindex;
8030         this.setStyle("z-index", zindex + 2);
8031         if(this.shadow){
8032             this.shadow.setZIndex(zindex + 1);
8033         }
8034         if(this.shim){
8035             this.shim.setStyle("z-index", zindex);
8036         }
8037     }
8038 });
8039 })();/*
8040  * Based on:
8041  * Ext JS Library 1.1.1
8042  * Copyright(c) 2006-2007, Ext JS, LLC.
8043  *
8044  * Originally Released Under LGPL - original licence link has changed is not relivant.
8045  *
8046  * Fork - LGPL
8047  * <script type="text/javascript">
8048  */
8049
8050
8051 /**
8052  * @class Roo.Shadow
8053  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
8054  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
8055  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
8056  * @constructor
8057  * Create a new Shadow
8058  * @param {Object} config The config object
8059  */
8060 Roo.Shadow = function(config){
8061     Roo.apply(this, config);
8062     if(typeof this.mode != "string"){
8063         this.mode = this.defaultMode;
8064     }
8065     var o = this.offset, a = {h: 0};
8066     var rad = Math.floor(this.offset/2);
8067     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
8068         case "drop":
8069             a.w = 0;
8070             a.l = a.t = o;
8071             a.t -= 1;
8072             if(Roo.isIE){
8073                 a.l -= this.offset + rad;
8074                 a.t -= this.offset + rad;
8075                 a.w -= rad;
8076                 a.h -= rad;
8077                 a.t += 1;
8078             }
8079         break;
8080         case "sides":
8081             a.w = (o*2);
8082             a.l = -o;
8083             a.t = o-1;
8084             if(Roo.isIE){
8085                 a.l -= (this.offset - rad);
8086                 a.t -= this.offset + rad;
8087                 a.l += 1;
8088                 a.w -= (this.offset - rad)*2;
8089                 a.w -= rad + 1;
8090                 a.h -= 1;
8091             }
8092         break;
8093         case "frame":
8094             a.w = a.h = (o*2);
8095             a.l = a.t = -o;
8096             a.t += 1;
8097             a.h -= 2;
8098             if(Roo.isIE){
8099                 a.l -= (this.offset - rad);
8100                 a.t -= (this.offset - rad);
8101                 a.l += 1;
8102                 a.w -= (this.offset + rad + 1);
8103                 a.h -= (this.offset + rad);
8104                 a.h += 1;
8105             }
8106         break;
8107     };
8108
8109     this.adjusts = a;
8110 };
8111
8112 Roo.Shadow.prototype = {
8113     /**
8114      * @cfg {String} mode
8115      * The shadow display mode.  Supports the following options:<br />
8116      * sides: Shadow displays on both sides and bottom only<br />
8117      * frame: Shadow displays equally on all four sides<br />
8118      * drop: Traditional bottom-right drop shadow (default)
8119      */
8120     /**
8121      * @cfg {String} offset
8122      * The number of pixels to offset the shadow from the element (defaults to 4)
8123      */
8124     offset: 4,
8125
8126     // private
8127     defaultMode: "drop",
8128
8129     /**
8130      * Displays the shadow under the target element
8131      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
8132      */
8133     show : function(target){
8134         target = Roo.get(target);
8135         if(!this.el){
8136             this.el = Roo.Shadow.Pool.pull();
8137             if(this.el.dom.nextSibling != target.dom){
8138                 this.el.insertBefore(target);
8139             }
8140         }
8141         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
8142         if(Roo.isIE){
8143             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
8144         }
8145         this.realign(
8146             target.getLeft(true),
8147             target.getTop(true),
8148             target.getWidth(),
8149             target.getHeight()
8150         );
8151         this.el.dom.style.display = "block";
8152     },
8153
8154     /**
8155      * Returns true if the shadow is visible, else false
8156      */
8157     isVisible : function(){
8158         return this.el ? true : false;  
8159     },
8160
8161     /**
8162      * Direct alignment when values are already available. Show must be called at least once before
8163      * calling this method to ensure it is initialized.
8164      * @param {Number} left The target element left position
8165      * @param {Number} top The target element top position
8166      * @param {Number} width The target element width
8167      * @param {Number} height The target element height
8168      */
8169     realign : function(l, t, w, h){
8170         if(!this.el){
8171             return;
8172         }
8173         var a = this.adjusts, d = this.el.dom, s = d.style;
8174         var iea = 0;
8175         s.left = (l+a.l)+"px";
8176         s.top = (t+a.t)+"px";
8177         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
8178         if(s.width != sws || s.height != shs){
8179             s.width = sws;
8180             s.height = shs;
8181             if(!Roo.isIE){
8182                 var cn = d.childNodes;
8183                 var sww = Math.max(0, (sw-12))+"px";
8184                 cn[0].childNodes[1].style.width = sww;
8185                 cn[1].childNodes[1].style.width = sww;
8186                 cn[2].childNodes[1].style.width = sww;
8187                 cn[1].style.height = Math.max(0, (sh-12))+"px";
8188             }
8189         }
8190     },
8191
8192     /**
8193      * Hides this shadow
8194      */
8195     hide : function(){
8196         if(this.el){
8197             this.el.dom.style.display = "none";
8198             Roo.Shadow.Pool.push(this.el);
8199             delete this.el;
8200         }
8201     },
8202
8203     /**
8204      * Adjust the z-index of this shadow
8205      * @param {Number} zindex The new z-index
8206      */
8207     setZIndex : function(z){
8208         this.zIndex = z;
8209         if(this.el){
8210             this.el.setStyle("z-index", z);
8211         }
8212     }
8213 };
8214
8215 // Private utility class that manages the internal Shadow cache
8216 Roo.Shadow.Pool = function(){
8217     var p = [];
8218     var markup = Roo.isIE ?
8219                  '<div class="x-ie-shadow"></div>' :
8220                  '<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>';
8221     return {
8222         pull : function(){
8223             var sh = p.shift();
8224             if(!sh){
8225                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
8226                 sh.autoBoxAdjust = false;
8227             }
8228             return sh;
8229         },
8230
8231         push : function(sh){
8232             p.push(sh);
8233         }
8234     };
8235 }();/*
8236  * Based on:
8237  * Ext JS Library 1.1.1
8238  * Copyright(c) 2006-2007, Ext JS, LLC.
8239  *
8240  * Originally Released Under LGPL - original licence link has changed is not relivant.
8241  *
8242  * Fork - LGPL
8243  * <script type="text/javascript">
8244  */
8245
8246 /**
8247  * @class Roo.BoxComponent
8248  * @extends Roo.Component
8249  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
8250  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
8251  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
8252  * layout containers.
8253  * @constructor
8254  * @param {Roo.Element/String/Object} config The configuration options.
8255  */
8256 Roo.BoxComponent = function(config){
8257     Roo.Component.call(this, config);
8258     this.addEvents({
8259         /**
8260          * @event resize
8261          * Fires after the component is resized.
8262              * @param {Roo.Component} this
8263              * @param {Number} adjWidth The box-adjusted width that was set
8264              * @param {Number} adjHeight The box-adjusted height that was set
8265              * @param {Number} rawWidth The width that was originally specified
8266              * @param {Number} rawHeight The height that was originally specified
8267              */
8268         resize : true,
8269         /**
8270          * @event move
8271          * Fires after the component is moved.
8272              * @param {Roo.Component} this
8273              * @param {Number} x The new x position
8274              * @param {Number} y The new y position
8275              */
8276         move : true
8277     });
8278 };
8279
8280 Roo.extend(Roo.BoxComponent, Roo.Component, {
8281     // private, set in afterRender to signify that the component has been rendered
8282     boxReady : false,
8283     // private, used to defer height settings to subclasses
8284     deferHeight: false,
8285     /** @cfg {Number} width
8286      * width (optional) size of component
8287      */
8288      /** @cfg {Number} height
8289      * height (optional) size of component
8290      */
8291      
8292     /**
8293      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
8294      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
8295      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
8296      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
8297      * @return {Roo.BoxComponent} this
8298      */
8299     setSize : function(w, h){
8300         // support for standard size objects
8301         if(typeof w == 'object'){
8302             h = w.height;
8303             w = w.width;
8304         }
8305         // not rendered
8306         if(!this.boxReady){
8307             this.width = w;
8308             this.height = h;
8309             return this;
8310         }
8311
8312         // prevent recalcs when not needed
8313         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
8314             return this;
8315         }
8316         this.lastSize = {width: w, height: h};
8317
8318         var adj = this.adjustSize(w, h);
8319         var aw = adj.width, ah = adj.height;
8320         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
8321             var rz = this.getResizeEl();
8322             if(!this.deferHeight && aw !== undefined && ah !== undefined){
8323                 rz.setSize(aw, ah);
8324             }else if(!this.deferHeight && ah !== undefined){
8325                 rz.setHeight(ah);
8326             }else if(aw !== undefined){
8327                 rz.setWidth(aw);
8328             }
8329             this.onResize(aw, ah, w, h);
8330             this.fireEvent('resize', this, aw, ah, w, h);
8331         }
8332         return this;
8333     },
8334
8335     /**
8336      * Gets the current size of the component's underlying element.
8337      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8338      */
8339     getSize : function(){
8340         return this.el.getSize();
8341     },
8342
8343     /**
8344      * Gets the current XY position of the component's underlying element.
8345      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8346      * @return {Array} The XY position of the element (e.g., [100, 200])
8347      */
8348     getPosition : function(local){
8349         if(local === true){
8350             return [this.el.getLeft(true), this.el.getTop(true)];
8351         }
8352         return this.xy || this.el.getXY();
8353     },
8354
8355     /**
8356      * Gets the current box measurements of the component's underlying element.
8357      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8358      * @returns {Object} box An object in the format {x, y, width, height}
8359      */
8360     getBox : function(local){
8361         var s = this.el.getSize();
8362         if(local){
8363             s.x = this.el.getLeft(true);
8364             s.y = this.el.getTop(true);
8365         }else{
8366             var xy = this.xy || this.el.getXY();
8367             s.x = xy[0];
8368             s.y = xy[1];
8369         }
8370         return s;
8371     },
8372
8373     /**
8374      * Sets the current box measurements of the component's underlying element.
8375      * @param {Object} box An object in the format {x, y, width, height}
8376      * @returns {Roo.BoxComponent} this
8377      */
8378     updateBox : function(box){
8379         this.setSize(box.width, box.height);
8380         this.setPagePosition(box.x, box.y);
8381         return this;
8382     },
8383
8384     // protected
8385     getResizeEl : function(){
8386         return this.resizeEl || this.el;
8387     },
8388
8389     // protected
8390     getPositionEl : function(){
8391         return this.positionEl || this.el;
8392     },
8393
8394     /**
8395      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
8396      * This method fires the move event.
8397      * @param {Number} left The new left
8398      * @param {Number} top The new top
8399      * @returns {Roo.BoxComponent} this
8400      */
8401     setPosition : function(x, y){
8402         this.x = x;
8403         this.y = y;
8404         if(!this.boxReady){
8405             return this;
8406         }
8407         var adj = this.adjustPosition(x, y);
8408         var ax = adj.x, ay = adj.y;
8409
8410         var el = this.getPositionEl();
8411         if(ax !== undefined || ay !== undefined){
8412             if(ax !== undefined && ay !== undefined){
8413                 el.setLeftTop(ax, ay);
8414             }else if(ax !== undefined){
8415                 el.setLeft(ax);
8416             }else if(ay !== undefined){
8417                 el.setTop(ay);
8418             }
8419             this.onPosition(ax, ay);
8420             this.fireEvent('move', this, ax, ay);
8421         }
8422         return this;
8423     },
8424
8425     /**
8426      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
8427      * This method fires the move event.
8428      * @param {Number} x The new x position
8429      * @param {Number} y The new y position
8430      * @returns {Roo.BoxComponent} this
8431      */
8432     setPagePosition : function(x, y){
8433         this.pageX = x;
8434         this.pageY = y;
8435         if(!this.boxReady){
8436             return;
8437         }
8438         if(x === undefined || y === undefined){ // cannot translate undefined points
8439             return;
8440         }
8441         var p = this.el.translatePoints(x, y);
8442         this.setPosition(p.left, p.top);
8443         return this;
8444     },
8445
8446     // private
8447     onRender : function(ct, position){
8448         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
8449         if(this.resizeEl){
8450             this.resizeEl = Roo.get(this.resizeEl);
8451         }
8452         if(this.positionEl){
8453             this.positionEl = Roo.get(this.positionEl);
8454         }
8455     },
8456
8457     // private
8458     afterRender : function(){
8459         Roo.BoxComponent.superclass.afterRender.call(this);
8460         this.boxReady = true;
8461         this.setSize(this.width, this.height);
8462         if(this.x || this.y){
8463             this.setPosition(this.x, this.y);
8464         }
8465         if(this.pageX || this.pageY){
8466             this.setPagePosition(this.pageX, this.pageY);
8467         }
8468     },
8469
8470     /**
8471      * Force the component's size to recalculate based on the underlying element's current height and width.
8472      * @returns {Roo.BoxComponent} this
8473      */
8474     syncSize : function(){
8475         delete this.lastSize;
8476         this.setSize(this.el.getWidth(), this.el.getHeight());
8477         return this;
8478     },
8479
8480     /**
8481      * Called after the component is resized, this method is empty by default but can be implemented by any
8482      * subclass that needs to perform custom logic after a resize occurs.
8483      * @param {Number} adjWidth The box-adjusted width that was set
8484      * @param {Number} adjHeight The box-adjusted height that was set
8485      * @param {Number} rawWidth The width that was originally specified
8486      * @param {Number} rawHeight The height that was originally specified
8487      */
8488     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
8489
8490     },
8491
8492     /**
8493      * Called after the component is moved, this method is empty by default but can be implemented by any
8494      * subclass that needs to perform custom logic after a move occurs.
8495      * @param {Number} x The new x position
8496      * @param {Number} y The new y position
8497      */
8498     onPosition : function(x, y){
8499
8500     },
8501
8502     // private
8503     adjustSize : function(w, h){
8504         if(this.autoWidth){
8505             w = 'auto';
8506         }
8507         if(this.autoHeight){
8508             h = 'auto';
8509         }
8510         return {width : w, height: h};
8511     },
8512
8513     // private
8514     adjustPosition : function(x, y){
8515         return {x : x, y: y};
8516     }
8517 });/*
8518  * Based on:
8519  * Ext JS Library 1.1.1
8520  * Copyright(c) 2006-2007, Ext JS, LLC.
8521  *
8522  * Originally Released Under LGPL - original licence link has changed is not relivant.
8523  *
8524  * Fork - LGPL
8525  * <script type="text/javascript">
8526  */
8527
8528
8529 /**
8530  * @class Roo.SplitBar
8531  * @extends Roo.util.Observable
8532  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
8533  * <br><br>
8534  * Usage:
8535  * <pre><code>
8536 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
8537                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
8538 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
8539 split.minSize = 100;
8540 split.maxSize = 600;
8541 split.animate = true;
8542 split.on('moved', splitterMoved);
8543 </code></pre>
8544  * @constructor
8545  * Create a new SplitBar
8546  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
8547  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
8548  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8549  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
8550                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
8551                         position of the SplitBar).
8552  */
8553 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
8554     
8555     /** @private */
8556     this.el = Roo.get(dragElement, true);
8557     this.el.dom.unselectable = "on";
8558     /** @private */
8559     this.resizingEl = Roo.get(resizingElement, true);
8560
8561     /**
8562      * @private
8563      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8564      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
8565      * @type Number
8566      */
8567     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
8568     
8569     /**
8570      * The minimum size of the resizing element. (Defaults to 0)
8571      * @type Number
8572      */
8573     this.minSize = 0;
8574     
8575     /**
8576      * The maximum size of the resizing element. (Defaults to 2000)
8577      * @type Number
8578      */
8579     this.maxSize = 2000;
8580     
8581     /**
8582      * Whether to animate the transition to the new size
8583      * @type Boolean
8584      */
8585     this.animate = false;
8586     
8587     /**
8588      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
8589      * @type Boolean
8590      */
8591     this.useShim = false;
8592     
8593     /** @private */
8594     this.shim = null;
8595     
8596     if(!existingProxy){
8597         /** @private */
8598         this.proxy = Roo.SplitBar.createProxy(this.orientation);
8599     }else{
8600         this.proxy = Roo.get(existingProxy).dom;
8601     }
8602     /** @private */
8603     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
8604     
8605     /** @private */
8606     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
8607     
8608     /** @private */
8609     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
8610     
8611     /** @private */
8612     this.dragSpecs = {};
8613     
8614     /**
8615      * @private The adapter to use to positon and resize elements
8616      */
8617     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
8618     this.adapter.init(this);
8619     
8620     if(this.orientation == Roo.SplitBar.HORIZONTAL){
8621         /** @private */
8622         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
8623         this.el.addClass("x-splitbar-h");
8624     }else{
8625         /** @private */
8626         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
8627         this.el.addClass("x-splitbar-v");
8628     }
8629     
8630     this.addEvents({
8631         /**
8632          * @event resize
8633          * Fires when the splitter is moved (alias for {@link #event-moved})
8634          * @param {Roo.SplitBar} this
8635          * @param {Number} newSize the new width or height
8636          */
8637         "resize" : true,
8638         /**
8639          * @event moved
8640          * Fires when the splitter is moved
8641          * @param {Roo.SplitBar} this
8642          * @param {Number} newSize the new width or height
8643          */
8644         "moved" : true,
8645         /**
8646          * @event beforeresize
8647          * Fires before the splitter is dragged
8648          * @param {Roo.SplitBar} this
8649          */
8650         "beforeresize" : true,
8651
8652         "beforeapply" : true
8653     });
8654
8655     Roo.util.Observable.call(this);
8656 };
8657
8658 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
8659     onStartProxyDrag : function(x, y){
8660         this.fireEvent("beforeresize", this);
8661         if(!this.overlay){
8662             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
8663             o.unselectable();
8664             o.enableDisplayMode("block");
8665             // all splitbars share the same overlay
8666             Roo.SplitBar.prototype.overlay = o;
8667         }
8668         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8669         this.overlay.show();
8670         Roo.get(this.proxy).setDisplayed("block");
8671         var size = this.adapter.getElementSize(this);
8672         this.activeMinSize = this.getMinimumSize();;
8673         this.activeMaxSize = this.getMaximumSize();;
8674         var c1 = size - this.activeMinSize;
8675         var c2 = Math.max(this.activeMaxSize - size, 0);
8676         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8677             this.dd.resetConstraints();
8678             this.dd.setXConstraint(
8679                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
8680                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
8681             );
8682             this.dd.setYConstraint(0, 0);
8683         }else{
8684             this.dd.resetConstraints();
8685             this.dd.setXConstraint(0, 0);
8686             this.dd.setYConstraint(
8687                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
8688                 this.placement == Roo.SplitBar.TOP ? c2 : c1
8689             );
8690          }
8691         this.dragSpecs.startSize = size;
8692         this.dragSpecs.startPoint = [x, y];
8693         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
8694     },
8695     
8696     /** 
8697      * @private Called after the drag operation by the DDProxy
8698      */
8699     onEndProxyDrag : function(e){
8700         Roo.get(this.proxy).setDisplayed(false);
8701         var endPoint = Roo.lib.Event.getXY(e);
8702         if(this.overlay){
8703             this.overlay.hide();
8704         }
8705         var newSize;
8706         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8707             newSize = this.dragSpecs.startSize + 
8708                 (this.placement == Roo.SplitBar.LEFT ?
8709                     endPoint[0] - this.dragSpecs.startPoint[0] :
8710                     this.dragSpecs.startPoint[0] - endPoint[0]
8711                 );
8712         }else{
8713             newSize = this.dragSpecs.startSize + 
8714                 (this.placement == Roo.SplitBar.TOP ?
8715                     endPoint[1] - this.dragSpecs.startPoint[1] :
8716                     this.dragSpecs.startPoint[1] - endPoint[1]
8717                 );
8718         }
8719         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
8720         if(newSize != this.dragSpecs.startSize){
8721             if(this.fireEvent('beforeapply', this, newSize) !== false){
8722                 this.adapter.setElementSize(this, newSize);
8723                 this.fireEvent("moved", this, newSize);
8724                 this.fireEvent("resize", this, newSize);
8725             }
8726         }
8727     },
8728     
8729     /**
8730      * Get the adapter this SplitBar uses
8731      * @return The adapter object
8732      */
8733     getAdapter : function(){
8734         return this.adapter;
8735     },
8736     
8737     /**
8738      * Set the adapter this SplitBar uses
8739      * @param {Object} adapter A SplitBar adapter object
8740      */
8741     setAdapter : function(adapter){
8742         this.adapter = adapter;
8743         this.adapter.init(this);
8744     },
8745     
8746     /**
8747      * Gets the minimum size for the resizing element
8748      * @return {Number} The minimum size
8749      */
8750     getMinimumSize : function(){
8751         return this.minSize;
8752     },
8753     
8754     /**
8755      * Sets the minimum size for the resizing element
8756      * @param {Number} minSize The minimum size
8757      */
8758     setMinimumSize : function(minSize){
8759         this.minSize = minSize;
8760     },
8761     
8762     /**
8763      * Gets the maximum size for the resizing element
8764      * @return {Number} The maximum size
8765      */
8766     getMaximumSize : function(){
8767         return this.maxSize;
8768     },
8769     
8770     /**
8771      * Sets the maximum size for the resizing element
8772      * @param {Number} maxSize The maximum size
8773      */
8774     setMaximumSize : function(maxSize){
8775         this.maxSize = maxSize;
8776     },
8777     
8778     /**
8779      * Sets the initialize size for the resizing element
8780      * @param {Number} size The initial size
8781      */
8782     setCurrentSize : function(size){
8783         var oldAnimate = this.animate;
8784         this.animate = false;
8785         this.adapter.setElementSize(this, size);
8786         this.animate = oldAnimate;
8787     },
8788     
8789     /**
8790      * Destroy this splitbar. 
8791      * @param {Boolean} removeEl True to remove the element
8792      */
8793     destroy : function(removeEl){
8794         if(this.shim){
8795             this.shim.remove();
8796         }
8797         this.dd.unreg();
8798         this.proxy.parentNode.removeChild(this.proxy);
8799         if(removeEl){
8800             this.el.remove();
8801         }
8802     }
8803 });
8804
8805 /**
8806  * @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.
8807  */
8808 Roo.SplitBar.createProxy = function(dir){
8809     var proxy = new Roo.Element(document.createElement("div"));
8810     proxy.unselectable();
8811     var cls = 'x-splitbar-proxy';
8812     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
8813     document.body.appendChild(proxy.dom);
8814     return proxy.dom;
8815 };
8816
8817 /** 
8818  * @class Roo.SplitBar.BasicLayoutAdapter
8819  * Default Adapter. It assumes the splitter and resizing element are not positioned
8820  * elements and only gets/sets the width of the element. Generally used for table based layouts.
8821  */
8822 Roo.SplitBar.BasicLayoutAdapter = function(){
8823 };
8824
8825 Roo.SplitBar.BasicLayoutAdapter.prototype = {
8826     // do nothing for now
8827     init : function(s){
8828     
8829     },
8830     /**
8831      * Called before drag operations to get the current size of the resizing element. 
8832      * @param {Roo.SplitBar} s The SplitBar using this adapter
8833      */
8834      getElementSize : function(s){
8835         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8836             return s.resizingEl.getWidth();
8837         }else{
8838             return s.resizingEl.getHeight();
8839         }
8840     },
8841     
8842     /**
8843      * Called after drag operations to set the size of the resizing element.
8844      * @param {Roo.SplitBar} s The SplitBar using this adapter
8845      * @param {Number} newSize The new size to set
8846      * @param {Function} onComplete A function to be invoked when resizing is complete
8847      */
8848     setElementSize : function(s, newSize, onComplete){
8849         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8850             if(!s.animate){
8851                 s.resizingEl.setWidth(newSize);
8852                 if(onComplete){
8853                     onComplete(s, newSize);
8854                 }
8855             }else{
8856                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
8857             }
8858         }else{
8859             
8860             if(!s.animate){
8861                 s.resizingEl.setHeight(newSize);
8862                 if(onComplete){
8863                     onComplete(s, newSize);
8864                 }
8865             }else{
8866                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
8867             }
8868         }
8869     }
8870 };
8871
8872 /** 
8873  *@class Roo.SplitBar.AbsoluteLayoutAdapter
8874  * @extends Roo.SplitBar.BasicLayoutAdapter
8875  * Adapter that  moves the splitter element to align with the resized sizing element. 
8876  * Used with an absolute positioned SplitBar.
8877  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
8878  * document.body, make sure you assign an id to the body element.
8879  */
8880 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
8881     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
8882     this.container = Roo.get(container);
8883 };
8884
8885 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
8886     init : function(s){
8887         this.basic.init(s);
8888     },
8889     
8890     getElementSize : function(s){
8891         return this.basic.getElementSize(s);
8892     },
8893     
8894     setElementSize : function(s, newSize, onComplete){
8895         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
8896     },
8897     
8898     moveSplitter : function(s){
8899         var yes = Roo.SplitBar;
8900         switch(s.placement){
8901             case yes.LEFT:
8902                 s.el.setX(s.resizingEl.getRight());
8903                 break;
8904             case yes.RIGHT:
8905                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
8906                 break;
8907             case yes.TOP:
8908                 s.el.setY(s.resizingEl.getBottom());
8909                 break;
8910             case yes.BOTTOM:
8911                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
8912                 break;
8913         }
8914     }
8915 };
8916
8917 /**
8918  * Orientation constant - Create a vertical SplitBar
8919  * @static
8920  * @type Number
8921  */
8922 Roo.SplitBar.VERTICAL = 1;
8923
8924 /**
8925  * Orientation constant - Create a horizontal SplitBar
8926  * @static
8927  * @type Number
8928  */
8929 Roo.SplitBar.HORIZONTAL = 2;
8930
8931 /**
8932  * Placement constant - The resizing element is to the left of the splitter element
8933  * @static
8934  * @type Number
8935  */
8936 Roo.SplitBar.LEFT = 1;
8937
8938 /**
8939  * Placement constant - The resizing element is to the right of the splitter element
8940  * @static
8941  * @type Number
8942  */
8943 Roo.SplitBar.RIGHT = 2;
8944
8945 /**
8946  * Placement constant - The resizing element is positioned above the splitter element
8947  * @static
8948  * @type Number
8949  */
8950 Roo.SplitBar.TOP = 3;
8951
8952 /**
8953  * Placement constant - The resizing element is positioned under splitter element
8954  * @static
8955  * @type Number
8956  */
8957 Roo.SplitBar.BOTTOM = 4;
8958 /*
8959  * Based on:
8960  * Ext JS Library 1.1.1
8961  * Copyright(c) 2006-2007, Ext JS, LLC.
8962  *
8963  * Originally Released Under LGPL - original licence link has changed is not relivant.
8964  *
8965  * Fork - LGPL
8966  * <script type="text/javascript">
8967  */
8968
8969 /**
8970  * @class Roo.View
8971  * @extends Roo.util.Observable
8972  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
8973  * This class also supports single and multi selection modes. <br>
8974  * Create a data model bound view:
8975  <pre><code>
8976  var store = new Roo.data.Store(...);
8977
8978  var view = new Roo.View({
8979     el : "my-element",
8980     template : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
8981  
8982     singleSelect: true,
8983     selectedClass: "ydataview-selected",
8984     store: store
8985  });
8986
8987  // listen for node click?
8988  view.on("click", function(vw, index, node, e){
8989  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
8990  });
8991
8992  // load XML data
8993  dataModel.load("foobar.xml");
8994  </code></pre>
8995  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
8996  * <br><br>
8997  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
8998  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
8999  * 
9000  * Note: old style constructor is still suported (container, template, config)
9001  * 
9002  * @constructor
9003  * Create a new View
9004  * @param {Object} config The config object
9005  * 
9006  */
9007 Roo.View = function(config, depreciated_tpl, depreciated_config){
9008     
9009     if (typeof(depreciated_tpl) == 'undefined') {
9010         // new way.. - universal constructor.
9011         Roo.apply(this, config);
9012         this.el  = Roo.get(this.el);
9013     } else {
9014         // old format..
9015         this.el  = Roo.get(config);
9016         this.tpl = depreciated_tpl;
9017         Roo.apply(this, depreciated_config);
9018     }
9019      
9020     
9021     if(typeof(this.tpl) == "string"){
9022         this.tpl = new Roo.Template(this.tpl);
9023     } 
9024     
9025     
9026     this.tpl.compile();
9027    
9028
9029      
9030     /** @private */
9031     this.addEvents({
9032     /**
9033      * @event beforeclick
9034      * Fires before a click is processed. Returns false to cancel the default action.
9035      * @param {Roo.View} this
9036      * @param {Number} index The index of the target node
9037      * @param {HTMLElement} node The target node
9038      * @param {Roo.EventObject} e The raw event object
9039      */
9040         "beforeclick" : true,
9041     /**
9042      * @event click
9043      * Fires when a template node is clicked.
9044      * @param {Roo.View} this
9045      * @param {Number} index The index of the target node
9046      * @param {HTMLElement} node The target node
9047      * @param {Roo.EventObject} e The raw event object
9048      */
9049         "click" : true,
9050     /**
9051      * @event dblclick
9052      * Fires when a template node is double clicked.
9053      * @param {Roo.View} this
9054      * @param {Number} index The index of the target node
9055      * @param {HTMLElement} node The target node
9056      * @param {Roo.EventObject} e The raw event object
9057      */
9058         "dblclick" : true,
9059     /**
9060      * @event contextmenu
9061      * Fires when a template node is right clicked.
9062      * @param {Roo.View} this
9063      * @param {Number} index The index of the target node
9064      * @param {HTMLElement} node The target node
9065      * @param {Roo.EventObject} e The raw event object
9066      */
9067         "contextmenu" : true,
9068     /**
9069      * @event selectionchange
9070      * Fires when the selected nodes change.
9071      * @param {Roo.View} this
9072      * @param {Array} selections Array of the selected nodes
9073      */
9074         "selectionchange" : true,
9075
9076     /**
9077      * @event beforeselect
9078      * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9079      * @param {Roo.View} this
9080      * @param {HTMLElement} node The node to be selected
9081      * @param {Array} selections Array of currently selected nodes
9082      */
9083         "beforeselect" : true
9084     });
9085
9086     this.el.on({
9087         "click": this.onClick,
9088         "dblclick": this.onDblClick,
9089         "contextmenu": this.onContextMenu,
9090         scope:this
9091     });
9092
9093     this.selections = [];
9094     this.nodes = [];
9095     this.cmp = new Roo.CompositeElementLite([]);
9096     if(this.store){
9097         this.store = Roo.factory(this.store, Roo.data);
9098         this.setStore(this.store, true);
9099     }
9100     Roo.View.superclass.constructor.call(this);
9101 };
9102
9103 Roo.extend(Roo.View, Roo.util.Observable, {
9104     
9105      /**
9106      * @cfg {Roo.data.Store} store Data store to load data from.
9107      */
9108     store : false,
9109     
9110     /**
9111      * @cfg {String|Roo.Element} el The container element.
9112      */
9113     el : '',
9114     
9115     /**
9116      * @cfg {String|Roo.Template} tpl The template used by this View 
9117      */
9118     tpl : false,
9119     
9120     /**
9121      * @cfg {String} selectedClass The css class to add to selected nodes
9122      */
9123     selectedClass : "x-view-selected",
9124      /**
9125      * @cfg {String} emptyText The empty text to show when nothing is loaded.
9126      */
9127     emptyText : "",
9128     /**
9129      * Returns the element this view is bound to.
9130      * @return {Roo.Element}
9131      */
9132     getEl : function(){
9133         return this.el;
9134     },
9135
9136     /**
9137      * Refreshes the view.
9138      */
9139     refresh : function(){
9140         var t = this.tpl;
9141         this.clearSelections();
9142         this.el.update("");
9143         var html = [];
9144         var records = this.store.getRange();
9145         if(records.length < 1){
9146             this.el.update(this.emptyText);
9147             return;
9148         }
9149         for(var i = 0, len = records.length; i < len; i++){
9150             var data = this.prepareData(records[i].data, i, records[i]);
9151             html[html.length] = t.apply(data);
9152         }
9153         this.el.update(html.join(""));
9154         this.nodes = this.el.dom.childNodes;
9155         this.updateIndexes(0);
9156     },
9157
9158     /**
9159      * Function to override to reformat the data that is sent to
9160      * the template for each node.
9161      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9162      * a JSON object for an UpdateManager bound view).
9163      */
9164     prepareData : function(data){
9165         return data;
9166     },
9167
9168     onUpdate : function(ds, record){
9169         this.clearSelections();
9170         var index = this.store.indexOf(record);
9171         var n = this.nodes[index];
9172         this.tpl.insertBefore(n, this.prepareData(record.data));
9173         n.parentNode.removeChild(n);
9174         this.updateIndexes(index, index);
9175     },
9176
9177     onAdd : function(ds, records, index){
9178         this.clearSelections();
9179         if(this.nodes.length == 0){
9180             this.refresh();
9181             return;
9182         }
9183         var n = this.nodes[index];
9184         for(var i = 0, len = records.length; i < len; i++){
9185             var d = this.prepareData(records[i].data);
9186             if(n){
9187                 this.tpl.insertBefore(n, d);
9188             }else{
9189                 this.tpl.append(this.el, d);
9190             }
9191         }
9192         this.updateIndexes(index);
9193     },
9194
9195     onRemove : function(ds, record, index){
9196         this.clearSelections();
9197         this.el.dom.removeChild(this.nodes[index]);
9198         this.updateIndexes(index);
9199     },
9200
9201     /**
9202      * Refresh an individual node.
9203      * @param {Number} index
9204      */
9205     refreshNode : function(index){
9206         this.onUpdate(this.store, this.store.getAt(index));
9207     },
9208
9209     updateIndexes : function(startIndex, endIndex){
9210         var ns = this.nodes;
9211         startIndex = startIndex || 0;
9212         endIndex = endIndex || ns.length - 1;
9213         for(var i = startIndex; i <= endIndex; i++){
9214             ns[i].nodeIndex = i;
9215         }
9216     },
9217
9218     /**
9219      * Changes the data store this view uses and refresh the view.
9220      * @param {Store} store
9221      */
9222     setStore : function(store, initial){
9223         if(!initial && this.store){
9224             this.store.un("datachanged", this.refresh);
9225             this.store.un("add", this.onAdd);
9226             this.store.un("remove", this.onRemove);
9227             this.store.un("update", this.onUpdate);
9228             this.store.un("clear", this.refresh);
9229         }
9230         if(store){
9231           
9232             store.on("datachanged", this.refresh, this);
9233             store.on("add", this.onAdd, this);
9234             store.on("remove", this.onRemove, this);
9235             store.on("update", this.onUpdate, this);
9236             store.on("clear", this.refresh, this);
9237         }
9238         
9239         if(store){
9240             this.refresh();
9241         }
9242     },
9243
9244     /**
9245      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9246      * @param {HTMLElement} node
9247      * @return {HTMLElement} The template node
9248      */
9249     findItemFromChild : function(node){
9250         var el = this.el.dom;
9251         if(!node || node.parentNode == el){
9252                     return node;
9253             }
9254             var p = node.parentNode;
9255             while(p && p != el){
9256             if(p.parentNode == el){
9257                 return p;
9258             }
9259             p = p.parentNode;
9260         }
9261             return null;
9262     },
9263
9264     /** @ignore */
9265     onClick : function(e){
9266         var item = this.findItemFromChild(e.getTarget());
9267         if(item){
9268             var index = this.indexOf(item);
9269             if(this.onItemClick(item, index, e) !== false){
9270                 this.fireEvent("click", this, index, item, e);
9271             }
9272         }else{
9273             this.clearSelections();
9274         }
9275     },
9276
9277     /** @ignore */
9278     onContextMenu : function(e){
9279         var item = this.findItemFromChild(e.getTarget());
9280         if(item){
9281             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9282         }
9283     },
9284
9285     /** @ignore */
9286     onDblClick : function(e){
9287         var item = this.findItemFromChild(e.getTarget());
9288         if(item){
9289             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9290         }
9291     },
9292
9293     onItemClick : function(item, index, e){
9294         if(this.fireEvent("beforeclick", this, index, item, e) === false){
9295             return false;
9296         }
9297         if(this.multiSelect || this.singleSelect){
9298             if(this.multiSelect && e.shiftKey && this.lastSelection){
9299                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9300             }else{
9301                 this.select(item, this.multiSelect && e.ctrlKey);
9302                 this.lastSelection = item;
9303             }
9304             e.preventDefault();
9305         }
9306         return true;
9307     },
9308
9309     /**
9310      * Get the number of selected nodes.
9311      * @return {Number}
9312      */
9313     getSelectionCount : function(){
9314         return this.selections.length;
9315     },
9316
9317     /**
9318      * Get the currently selected nodes.
9319      * @return {Array} An array of HTMLElements
9320      */
9321     getSelectedNodes : function(){
9322         return this.selections;
9323     },
9324
9325     /**
9326      * Get the indexes of the selected nodes.
9327      * @return {Array}
9328      */
9329     getSelectedIndexes : function(){
9330         var indexes = [], s = this.selections;
9331         for(var i = 0, len = s.length; i < len; i++){
9332             indexes.push(s[i].nodeIndex);
9333         }
9334         return indexes;
9335     },
9336
9337     /**
9338      * Clear all selections
9339      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9340      */
9341     clearSelections : function(suppressEvent){
9342         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9343             this.cmp.elements = this.selections;
9344             this.cmp.removeClass(this.selectedClass);
9345             this.selections = [];
9346             if(!suppressEvent){
9347                 this.fireEvent("selectionchange", this, this.selections);
9348             }
9349         }
9350     },
9351
9352     /**
9353      * Returns true if the passed node is selected
9354      * @param {HTMLElement/Number} node The node or node index
9355      * @return {Boolean}
9356      */
9357     isSelected : function(node){
9358         var s = this.selections;
9359         if(s.length < 1){
9360             return false;
9361         }
9362         node = this.getNode(node);
9363         return s.indexOf(node) !== -1;
9364     },
9365
9366     /**
9367      * Selects nodes.
9368      * @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
9369      * @param {Boolean} keepExisting (optional) true to keep existing selections
9370      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9371      */
9372     select : function(nodeInfo, keepExisting, suppressEvent){
9373         if(nodeInfo instanceof Array){
9374             if(!keepExisting){
9375                 this.clearSelections(true);
9376             }
9377             for(var i = 0, len = nodeInfo.length; i < len; i++){
9378                 this.select(nodeInfo[i], true, true);
9379             }
9380         } else{
9381             var node = this.getNode(nodeInfo);
9382             if(node && !this.isSelected(node)){
9383                 if(!keepExisting){
9384                     this.clearSelections(true);
9385                 }
9386                 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9387                     Roo.fly(node).addClass(this.selectedClass);
9388                     this.selections.push(node);
9389                     if(!suppressEvent){
9390                         this.fireEvent("selectionchange", this, this.selections);
9391                     }
9392                 }
9393             }
9394         }
9395     },
9396
9397     /**
9398      * Gets a template node.
9399      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9400      * @return {HTMLElement} The node or null if it wasn't found
9401      */
9402     getNode : function(nodeInfo){
9403         if(typeof nodeInfo == "string"){
9404             return document.getElementById(nodeInfo);
9405         }else if(typeof nodeInfo == "number"){
9406             return this.nodes[nodeInfo];
9407         }
9408         return nodeInfo;
9409     },
9410
9411     /**
9412      * Gets a range template nodes.
9413      * @param {Number} startIndex
9414      * @param {Number} endIndex
9415      * @return {Array} An array of nodes
9416      */
9417     getNodes : function(start, end){
9418         var ns = this.nodes;
9419         start = start || 0;
9420         end = typeof end == "undefined" ? ns.length - 1 : end;
9421         var nodes = [];
9422         if(start <= end){
9423             for(var i = start; i <= end; i++){
9424                 nodes.push(ns[i]);
9425             }
9426         } else{
9427             for(var i = start; i >= end; i--){
9428                 nodes.push(ns[i]);
9429             }
9430         }
9431         return nodes;
9432     },
9433
9434     /**
9435      * Finds the index of the passed node
9436      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9437      * @return {Number} The index of the node or -1
9438      */
9439     indexOf : function(node){
9440         node = this.getNode(node);
9441         if(typeof node.nodeIndex == "number"){
9442             return node.nodeIndex;
9443         }
9444         var ns = this.nodes;
9445         for(var i = 0, len = ns.length; i < len; i++){
9446             if(ns[i] == node){
9447                 return i;
9448             }
9449         }
9450         return -1;
9451     }
9452 });
9453 /*
9454  * Based on:
9455  * Ext JS Library 1.1.1
9456  * Copyright(c) 2006-2007, Ext JS, LLC.
9457  *
9458  * Originally Released Under LGPL - original licence link has changed is not relivant.
9459  *
9460  * Fork - LGPL
9461  * <script type="text/javascript">
9462  */
9463
9464 /**
9465  * @class Roo.JsonView
9466  * @extends Roo.View
9467  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
9468 <pre><code>
9469 var view = new Roo.JsonView({
9470     container: "my-element",
9471     template: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
9472     multiSelect: true, 
9473     jsonRoot: "data" 
9474 });
9475
9476 // listen for node click?
9477 view.on("click", function(vw, index, node, e){
9478     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9479 });
9480
9481 // direct load of JSON data
9482 view.load("foobar.php");
9483
9484 // Example from my blog list
9485 var tpl = new Roo.Template(
9486     '&lt;div class="entry"&gt;' +
9487     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
9488     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
9489     "&lt;/div&gt;&lt;hr /&gt;"
9490 );
9491
9492 var moreView = new Roo.JsonView({
9493     container :  "entry-list", 
9494     template : tpl,
9495     jsonRoot: "posts"
9496 });
9497 moreView.on("beforerender", this.sortEntries, this);
9498 moreView.load({
9499     url: "/blog/get-posts.php",
9500     params: "allposts=true",
9501     text: "Loading Blog Entries..."
9502 });
9503 </code></pre>
9504
9505 * Note: old code is supported with arguments : (container, template, config)
9506
9507
9508  * @constructor
9509  * Create a new JsonView
9510  * 
9511  * @param {Object} config The config object
9512  * 
9513  */
9514 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
9515     
9516     
9517     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
9518
9519     var um = this.el.getUpdateManager();
9520     um.setRenderer(this);
9521     um.on("update", this.onLoad, this);
9522     um.on("failure", this.onLoadException, this);
9523
9524     /**
9525      * @event beforerender
9526      * Fires before rendering of the downloaded JSON data.
9527      * @param {Roo.JsonView} this
9528      * @param {Object} data The JSON data loaded
9529      */
9530     /**
9531      * @event load
9532      * Fires when data is loaded.
9533      * @param {Roo.JsonView} this
9534      * @param {Object} data The JSON data loaded
9535      * @param {Object} response The raw Connect response object
9536      */
9537     /**
9538      * @event loadexception
9539      * Fires when loading fails.
9540      * @param {Roo.JsonView} this
9541      * @param {Object} response The raw Connect response object
9542      */
9543     this.addEvents({
9544         'beforerender' : true,
9545         'load' : true,
9546         'loadexception' : true
9547     });
9548 };
9549 Roo.extend(Roo.JsonView, Roo.View, {
9550     /**
9551      * 
9552      * @cfg {String} The root property in the loaded JSON object that contains the data
9553      */
9554     jsonRoot : "",
9555
9556     /**
9557      * Refreshes the view.
9558      */
9559     refresh : function(){
9560         this.clearSelections();
9561         this.el.update("");
9562         var html = [];
9563         var o = this.jsonData;
9564         if(o && o.length > 0){
9565             for(var i = 0, len = o.length; i < len; i++){
9566                 var data = this.prepareData(o[i], i, o);
9567                 html[html.length] = this.tpl.apply(data);
9568             }
9569         }else{
9570             html.push(this.emptyText);
9571         }
9572         this.el.update(html.join(""));
9573         this.nodes = this.el.dom.childNodes;
9574         this.updateIndexes(0);
9575     },
9576
9577     /**
9578      * 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.
9579      * @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:
9580      <pre><code>
9581      view.load({
9582          url: "your-url.php",
9583          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
9584          callback: yourFunction,
9585          scope: yourObject, //(optional scope)
9586          discardUrl: false,
9587          nocache: false,
9588          text: "Loading...",
9589          timeout: 30,
9590          scripts: false
9591      });
9592      </code></pre>
9593      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
9594      * 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.
9595      * @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}
9596      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9597      * @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.
9598      */
9599     load : function(){
9600         var um = this.el.getUpdateManager();
9601         um.update.apply(um, arguments);
9602     },
9603
9604     render : function(el, response){
9605         this.clearSelections();
9606         this.el.update("");
9607         var o;
9608         try{
9609             o = Roo.util.JSON.decode(response.responseText);
9610             if(this.jsonRoot){
9611                 
9612                 o = /** eval:var:o */ eval("o." + this.jsonRoot);
9613             }
9614         } catch(e){
9615         }
9616         /**
9617          * The current JSON data or null
9618          */
9619         this.jsonData = o;
9620         this.beforeRender();
9621         this.refresh();
9622     },
9623
9624 /**
9625  * Get the number of records in the current JSON dataset
9626  * @return {Number}
9627  */
9628     getCount : function(){
9629         return this.jsonData ? this.jsonData.length : 0;
9630     },
9631
9632 /**
9633  * Returns the JSON object for the specified node(s)
9634  * @param {HTMLElement/Array} node The node or an array of nodes
9635  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
9636  * you get the JSON object for the node
9637  */
9638     getNodeData : function(node){
9639         if(node instanceof Array){
9640             var data = [];
9641             for(var i = 0, len = node.length; i < len; i++){
9642                 data.push(this.getNodeData(node[i]));
9643             }
9644             return data;
9645         }
9646         return this.jsonData[this.indexOf(node)] || null;
9647     },
9648
9649     beforeRender : function(){
9650         this.snapshot = this.jsonData;
9651         if(this.sortInfo){
9652             this.sort.apply(this, this.sortInfo);
9653         }
9654         this.fireEvent("beforerender", this, this.jsonData);
9655     },
9656
9657     onLoad : function(el, o){
9658         this.fireEvent("load", this, this.jsonData, o);
9659     },
9660
9661     onLoadException : function(el, o){
9662         this.fireEvent("loadexception", this, o);
9663     },
9664
9665 /**
9666  * Filter the data by a specific property.
9667  * @param {String} property A property on your JSON objects
9668  * @param {String/RegExp} value Either string that the property values
9669  * should start with, or a RegExp to test against the property
9670  */
9671     filter : function(property, value){
9672         if(this.jsonData){
9673             var data = [];
9674             var ss = this.snapshot;
9675             if(typeof value == "string"){
9676                 var vlen = value.length;
9677                 if(vlen == 0){
9678                     this.clearFilter();
9679                     return;
9680                 }
9681                 value = value.toLowerCase();
9682                 for(var i = 0, len = ss.length; i < len; i++){
9683                     var o = ss[i];
9684                     if(o[property].substr(0, vlen).toLowerCase() == value){
9685                         data.push(o);
9686                     }
9687                 }
9688             } else if(value.exec){ // regex?
9689                 for(var i = 0, len = ss.length; i < len; i++){
9690                     var o = ss[i];
9691                     if(value.test(o[property])){
9692                         data.push(o);
9693                     }
9694                 }
9695             } else{
9696                 return;
9697             }
9698             this.jsonData = data;
9699             this.refresh();
9700         }
9701     },
9702
9703 /**
9704  * Filter by a function. The passed function will be called with each
9705  * object in the current dataset. If the function returns true the value is kept,
9706  * otherwise it is filtered.
9707  * @param {Function} fn
9708  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
9709  */
9710     filterBy : function(fn, scope){
9711         if(this.jsonData){
9712             var data = [];
9713             var ss = this.snapshot;
9714             for(var i = 0, len = ss.length; i < len; i++){
9715                 var o = ss[i];
9716                 if(fn.call(scope || this, o)){
9717                     data.push(o);
9718                 }
9719             }
9720             this.jsonData = data;
9721             this.refresh();
9722         }
9723     },
9724
9725 /**
9726  * Clears the current filter.
9727  */
9728     clearFilter : function(){
9729         if(this.snapshot && this.jsonData != this.snapshot){
9730             this.jsonData = this.snapshot;
9731             this.refresh();
9732         }
9733     },
9734
9735
9736 /**
9737  * Sorts the data for this view and refreshes it.
9738  * @param {String} property A property on your JSON objects to sort on
9739  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
9740  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
9741  */
9742     sort : function(property, dir, sortType){
9743         this.sortInfo = Array.prototype.slice.call(arguments, 0);
9744         if(this.jsonData){
9745             var p = property;
9746             var dsc = dir && dir.toLowerCase() == "desc";
9747             var f = function(o1, o2){
9748                 var v1 = sortType ? sortType(o1[p]) : o1[p];
9749                 var v2 = sortType ? sortType(o2[p]) : o2[p];
9750                 ;
9751                 if(v1 < v2){
9752                     return dsc ? +1 : -1;
9753                 } else if(v1 > v2){
9754                     return dsc ? -1 : +1;
9755                 } else{
9756                     return 0;
9757                 }
9758             };
9759             this.jsonData.sort(f);
9760             this.refresh();
9761             if(this.jsonData != this.snapshot){
9762                 this.snapshot.sort(f);
9763             }
9764         }
9765     }
9766 });/*
9767  * Based on:
9768  * Ext JS Library 1.1.1
9769  * Copyright(c) 2006-2007, Ext JS, LLC.
9770  *
9771  * Originally Released Under LGPL - original licence link has changed is not relivant.
9772  *
9773  * Fork - LGPL
9774  * <script type="text/javascript">
9775  */
9776  
9777
9778 /**
9779  * @class Roo.ColorPalette
9780  * @extends Roo.Component
9781  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
9782  * Here's an example of typical usage:
9783  * <pre><code>
9784 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
9785 cp.render('my-div');
9786
9787 cp.on('select', function(palette, selColor){
9788     // do something with selColor
9789 });
9790 </code></pre>
9791  * @constructor
9792  * Create a new ColorPalette
9793  * @param {Object} config The config object
9794  */
9795 Roo.ColorPalette = function(config){
9796     Roo.ColorPalette.superclass.constructor.call(this, config);
9797     this.addEvents({
9798         /**
9799              * @event select
9800              * Fires when a color is selected
9801              * @param {ColorPalette} this
9802              * @param {String} color The 6-digit color hex code (without the # symbol)
9803              */
9804         select: true
9805     });
9806
9807     if(this.handler){
9808         this.on("select", this.handler, this.scope, true);
9809     }
9810 };
9811 Roo.extend(Roo.ColorPalette, Roo.Component, {
9812     /**
9813      * @cfg {String} itemCls
9814      * The CSS class to apply to the containing element (defaults to "x-color-palette")
9815      */
9816     itemCls : "x-color-palette",
9817     /**
9818      * @cfg {String} value
9819      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
9820      * the hex codes are case-sensitive.
9821      */
9822     value : null,
9823     clickEvent:'click',
9824     // private
9825     ctype: "Roo.ColorPalette",
9826
9827     /**
9828      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
9829      */
9830     allowReselect : false,
9831
9832     /**
9833      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
9834      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
9835      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
9836      * of colors with the width setting until the box is symmetrical.</p>
9837      * <p>You can override individual colors if needed:</p>
9838      * <pre><code>
9839 var cp = new Roo.ColorPalette();
9840 cp.colors[0] = "FF0000";  // change the first box to red
9841 </code></pre>
9842
9843 Or you can provide a custom array of your own for complete control:
9844 <pre><code>
9845 var cp = new Roo.ColorPalette();
9846 cp.colors = ["000000", "993300", "333300"];
9847 </code></pre>
9848      * @type Array
9849      */
9850     colors : [
9851         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
9852         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
9853         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
9854         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
9855         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
9856     ],
9857
9858     // private
9859     onRender : function(container, position){
9860         var t = new Roo.MasterTemplate(
9861             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
9862         );
9863         var c = this.colors;
9864         for(var i = 0, len = c.length; i < len; i++){
9865             t.add([c[i]]);
9866         }
9867         var el = document.createElement("div");
9868         el.className = this.itemCls;
9869         t.overwrite(el);
9870         container.dom.insertBefore(el, position);
9871         this.el = Roo.get(el);
9872         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
9873         if(this.clickEvent != 'click'){
9874             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
9875         }
9876     },
9877
9878     // private
9879     afterRender : function(){
9880         Roo.ColorPalette.superclass.afterRender.call(this);
9881         if(this.value){
9882             var s = this.value;
9883             this.value = null;
9884             this.select(s);
9885         }
9886     },
9887
9888     // private
9889     handleClick : function(e, t){
9890         e.preventDefault();
9891         if(!this.disabled){
9892             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
9893             this.select(c.toUpperCase());
9894         }
9895     },
9896
9897     /**
9898      * Selects the specified color in the palette (fires the select event)
9899      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
9900      */
9901     select : function(color){
9902         color = color.replace("#", "");
9903         if(color != this.value || this.allowReselect){
9904             var el = this.el;
9905             if(this.value){
9906                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
9907             }
9908             el.child("a.color-"+color).addClass("x-color-palette-sel");
9909             this.value = color;
9910             this.fireEvent("select", this, color);
9911         }
9912     }
9913 });/*
9914  * Based on:
9915  * Ext JS Library 1.1.1
9916  * Copyright(c) 2006-2007, Ext JS, LLC.
9917  *
9918  * Originally Released Under LGPL - original licence link has changed is not relivant.
9919  *
9920  * Fork - LGPL
9921  * <script type="text/javascript">
9922  */
9923  
9924 /**
9925  * @class Roo.DatePicker
9926  * @extends Roo.Component
9927  * Simple date picker class.
9928  * @constructor
9929  * Create a new DatePicker
9930  * @param {Object} config The config object
9931  */
9932 Roo.DatePicker = function(config){
9933     Roo.DatePicker.superclass.constructor.call(this, config);
9934
9935     this.value = config && config.value ?
9936                  config.value.clearTime() : new Date().clearTime();
9937
9938     this.addEvents({
9939         /**
9940              * @event select
9941              * Fires when a date is selected
9942              * @param {DatePicker} this
9943              * @param {Date} date The selected date
9944              */
9945         select: true
9946     });
9947
9948     if(this.handler){
9949         this.on("select", this.handler,  this.scope || this);
9950     }
9951     // build the disabledDatesRE
9952     if(!this.disabledDatesRE && this.disabledDates){
9953         var dd = this.disabledDates;
9954         var re = "(?:";
9955         for(var i = 0; i < dd.length; i++){
9956             re += dd[i];
9957             if(i != dd.length-1) re += "|";
9958         }
9959         this.disabledDatesRE = new RegExp(re + ")");
9960     }
9961 };
9962
9963 Roo.extend(Roo.DatePicker, Roo.Component, {
9964     /**
9965      * @cfg {String} todayText
9966      * The text to display on the button that selects the current date (defaults to "Today")
9967      */
9968     todayText : "Today",
9969     /**
9970      * @cfg {String} okText
9971      * The text to display on the ok button
9972      */
9973     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
9974     /**
9975      * @cfg {String} cancelText
9976      * The text to display on the cancel button
9977      */
9978     cancelText : "Cancel",
9979     /**
9980      * @cfg {String} todayTip
9981      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
9982      */
9983     todayTip : "{0} (Spacebar)",
9984     /**
9985      * @cfg {Date} minDate
9986      * Minimum allowable date (JavaScript date object, defaults to null)
9987      */
9988     minDate : null,
9989     /**
9990      * @cfg {Date} maxDate
9991      * Maximum allowable date (JavaScript date object, defaults to null)
9992      */
9993     maxDate : null,
9994     /**
9995      * @cfg {String} minText
9996      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
9997      */
9998     minText : "This date is before the minimum date",
9999     /**
10000      * @cfg {String} maxText
10001      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
10002      */
10003     maxText : "This date is after the maximum date",
10004     /**
10005      * @cfg {String} format
10006      * The default date format string which can be overriden for localization support.  The format must be
10007      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
10008      */
10009     format : "m/d/y",
10010     /**
10011      * @cfg {Array} disabledDays
10012      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
10013      */
10014     disabledDays : null,
10015     /**
10016      * @cfg {String} disabledDaysText
10017      * The tooltip to display when the date falls on a disabled day (defaults to "")
10018      */
10019     disabledDaysText : "",
10020     /**
10021      * @cfg {RegExp} disabledDatesRE
10022      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
10023      */
10024     disabledDatesRE : null,
10025     /**
10026      * @cfg {String} disabledDatesText
10027      * The tooltip text to display when the date falls on a disabled date (defaults to "")
10028      */
10029     disabledDatesText : "",
10030     /**
10031      * @cfg {Boolean} constrainToViewport
10032      * True to constrain the date picker to the viewport (defaults to true)
10033      */
10034     constrainToViewport : true,
10035     /**
10036      * @cfg {Array} monthNames
10037      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
10038      */
10039     monthNames : Date.monthNames,
10040     /**
10041      * @cfg {Array} dayNames
10042      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
10043      */
10044     dayNames : Date.dayNames,
10045     /**
10046      * @cfg {String} nextText
10047      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
10048      */
10049     nextText: 'Next Month (Control+Right)',
10050     /**
10051      * @cfg {String} prevText
10052      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
10053      */
10054     prevText: 'Previous Month (Control+Left)',
10055     /**
10056      * @cfg {String} monthYearText
10057      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
10058      */
10059     monthYearText: 'Choose a month (Control+Up/Down to move years)',
10060     /**
10061      * @cfg {Number} startDay
10062      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
10063      */
10064     startDay : 0,
10065     /**
10066      * @cfg {Bool} showClear
10067      * Show a clear button (usefull for date form elements that can be blank.)
10068      */
10069     
10070     showClear: false,
10071     
10072     /**
10073      * Sets the value of the date field
10074      * @param {Date} value The date to set
10075      */
10076     setValue : function(value){
10077         var old = this.value;
10078         this.value = value.clearTime(true);
10079         if(this.el){
10080             this.update(this.value);
10081         }
10082     },
10083
10084     /**
10085      * Gets the current selected value of the date field
10086      * @return {Date} The selected date
10087      */
10088     getValue : function(){
10089         return this.value;
10090     },
10091
10092     // private
10093     focus : function(){
10094         if(this.el){
10095             this.update(this.activeDate);
10096         }
10097     },
10098
10099     // private
10100     onRender : function(container, position){
10101         var m = [
10102              '<table cellspacing="0">',
10103                 '<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>',
10104                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
10105         var dn = this.dayNames;
10106         for(var i = 0; i < 7; i++){
10107             var d = this.startDay+i;
10108             if(d > 6){
10109                 d = d-7;
10110             }
10111             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
10112         }
10113         m[m.length] = "</tr></thead><tbody><tr>";
10114         for(var i = 0; i < 42; i++) {
10115             if(i % 7 == 0 && i != 0){
10116                 m[m.length] = "</tr><tr>";
10117             }
10118             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
10119         }
10120         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
10121             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
10122
10123         var el = document.createElement("div");
10124         el.className = "x-date-picker";
10125         el.innerHTML = m.join("");
10126
10127         container.dom.insertBefore(el, position);
10128
10129         this.el = Roo.get(el);
10130         this.eventEl = Roo.get(el.firstChild);
10131
10132         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
10133             handler: this.showPrevMonth,
10134             scope: this,
10135             preventDefault:true,
10136             stopDefault:true
10137         });
10138
10139         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
10140             handler: this.showNextMonth,
10141             scope: this,
10142             preventDefault:true,
10143             stopDefault:true
10144         });
10145
10146         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
10147
10148         this.monthPicker = this.el.down('div.x-date-mp');
10149         this.monthPicker.enableDisplayMode('block');
10150         
10151         var kn = new Roo.KeyNav(this.eventEl, {
10152             "left" : function(e){
10153                 e.ctrlKey ?
10154                     this.showPrevMonth() :
10155                     this.update(this.activeDate.add("d", -1));
10156             },
10157
10158             "right" : function(e){
10159                 e.ctrlKey ?
10160                     this.showNextMonth() :
10161                     this.update(this.activeDate.add("d", 1));
10162             },
10163
10164             "up" : function(e){
10165                 e.ctrlKey ?
10166                     this.showNextYear() :
10167                     this.update(this.activeDate.add("d", -7));
10168             },
10169
10170             "down" : function(e){
10171                 e.ctrlKey ?
10172                     this.showPrevYear() :
10173                     this.update(this.activeDate.add("d", 7));
10174             },
10175
10176             "pageUp" : function(e){
10177                 this.showNextMonth();
10178             },
10179
10180             "pageDown" : function(e){
10181                 this.showPrevMonth();
10182             },
10183
10184             "enter" : function(e){
10185                 e.stopPropagation();
10186                 return true;
10187             },
10188
10189             scope : this
10190         });
10191
10192         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
10193
10194         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
10195
10196         this.el.unselectable();
10197         
10198         this.cells = this.el.select("table.x-date-inner tbody td");
10199         this.textNodes = this.el.query("table.x-date-inner tbody span");
10200
10201         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
10202             text: "&#160;",
10203             tooltip: this.monthYearText
10204         });
10205
10206         this.mbtn.on('click', this.showMonthPicker, this);
10207         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
10208
10209
10210         var today = (new Date()).dateFormat(this.format);
10211         
10212         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
10213         baseTb.add({
10214             text: String.format(this.todayText, today),
10215             tooltip: String.format(this.todayTip, today),
10216             handler: this.selectToday,
10217             scope: this
10218         });
10219         
10220         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
10221             
10222         //});
10223         if (this.showClear) {
10224             
10225             baseTb.add( new Roo.Toolbar.Fill());
10226             baseTb.add({
10227                 text: '&#160;',
10228                 cls: 'x-btn-icon x-btn-clear',
10229                 handler: function() {
10230                     //this.value = '';
10231                     this.fireEvent("select", this, '');
10232                 },
10233                 scope: this
10234             });
10235         }
10236         
10237         
10238         if(Roo.isIE){
10239             this.el.repaint();
10240         }
10241         this.update(this.value);
10242     },
10243
10244     createMonthPicker : function(){
10245         if(!this.monthPicker.dom.firstChild){
10246             var buf = ['<table border="0" cellspacing="0">'];
10247             for(var i = 0; i < 6; i++){
10248                 buf.push(
10249                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
10250                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
10251                     i == 0 ?
10252                     '<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>' :
10253                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
10254                 );
10255             }
10256             buf.push(
10257                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
10258                     this.okText,
10259                     '</button><button type="button" class="x-date-mp-cancel">',
10260                     this.cancelText,
10261                     '</button></td></tr>',
10262                 '</table>'
10263             );
10264             this.monthPicker.update(buf.join(''));
10265             this.monthPicker.on('click', this.onMonthClick, this);
10266             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
10267
10268             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
10269             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
10270
10271             this.mpMonths.each(function(m, a, i){
10272                 i += 1;
10273                 if((i%2) == 0){
10274                     m.dom.xmonth = 5 + Math.round(i * .5);
10275                 }else{
10276                     m.dom.xmonth = Math.round((i-1) * .5);
10277                 }
10278             });
10279         }
10280     },
10281
10282     showMonthPicker : function(){
10283         this.createMonthPicker();
10284         var size = this.el.getSize();
10285         this.monthPicker.setSize(size);
10286         this.monthPicker.child('table').setSize(size);
10287
10288         this.mpSelMonth = (this.activeDate || this.value).getMonth();
10289         this.updateMPMonth(this.mpSelMonth);
10290         this.mpSelYear = (this.activeDate || this.value).getFullYear();
10291         this.updateMPYear(this.mpSelYear);
10292
10293         this.monthPicker.slideIn('t', {duration:.2});
10294     },
10295
10296     updateMPYear : function(y){
10297         this.mpyear = y;
10298         var ys = this.mpYears.elements;
10299         for(var i = 1; i <= 10; i++){
10300             var td = ys[i-1], y2;
10301             if((i%2) == 0){
10302                 y2 = y + Math.round(i * .5);
10303                 td.firstChild.innerHTML = y2;
10304                 td.xyear = y2;
10305             }else{
10306                 y2 = y - (5-Math.round(i * .5));
10307                 td.firstChild.innerHTML = y2;
10308                 td.xyear = y2;
10309             }
10310             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
10311         }
10312     },
10313
10314     updateMPMonth : function(sm){
10315         this.mpMonths.each(function(m, a, i){
10316             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
10317         });
10318     },
10319
10320     selectMPMonth: function(m){
10321         
10322     },
10323
10324     onMonthClick : function(e, t){
10325         e.stopEvent();
10326         var el = new Roo.Element(t), pn;
10327         if(el.is('button.x-date-mp-cancel')){
10328             this.hideMonthPicker();
10329         }
10330         else if(el.is('button.x-date-mp-ok')){
10331             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10332             this.hideMonthPicker();
10333         }
10334         else if(pn = el.up('td.x-date-mp-month', 2)){
10335             this.mpMonths.removeClass('x-date-mp-sel');
10336             pn.addClass('x-date-mp-sel');
10337             this.mpSelMonth = pn.dom.xmonth;
10338         }
10339         else if(pn = el.up('td.x-date-mp-year', 2)){
10340             this.mpYears.removeClass('x-date-mp-sel');
10341             pn.addClass('x-date-mp-sel');
10342             this.mpSelYear = pn.dom.xyear;
10343         }
10344         else if(el.is('a.x-date-mp-prev')){
10345             this.updateMPYear(this.mpyear-10);
10346         }
10347         else if(el.is('a.x-date-mp-next')){
10348             this.updateMPYear(this.mpyear+10);
10349         }
10350     },
10351
10352     onMonthDblClick : function(e, t){
10353         e.stopEvent();
10354         var el = new Roo.Element(t), pn;
10355         if(pn = el.up('td.x-date-mp-month', 2)){
10356             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
10357             this.hideMonthPicker();
10358         }
10359         else if(pn = el.up('td.x-date-mp-year', 2)){
10360             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10361             this.hideMonthPicker();
10362         }
10363     },
10364
10365     hideMonthPicker : function(disableAnim){
10366         if(this.monthPicker){
10367             if(disableAnim === true){
10368                 this.monthPicker.hide();
10369             }else{
10370                 this.monthPicker.slideOut('t', {duration:.2});
10371             }
10372         }
10373     },
10374
10375     // private
10376     showPrevMonth : function(e){
10377         this.update(this.activeDate.add("mo", -1));
10378     },
10379
10380     // private
10381     showNextMonth : function(e){
10382         this.update(this.activeDate.add("mo", 1));
10383     },
10384
10385     // private
10386     showPrevYear : function(){
10387         this.update(this.activeDate.add("y", -1));
10388     },
10389
10390     // private
10391     showNextYear : function(){
10392         this.update(this.activeDate.add("y", 1));
10393     },
10394
10395     // private
10396     handleMouseWheel : function(e){
10397         var delta = e.getWheelDelta();
10398         if(delta > 0){
10399             this.showPrevMonth();
10400             e.stopEvent();
10401         } else if(delta < 0){
10402             this.showNextMonth();
10403             e.stopEvent();
10404         }
10405     },
10406
10407     // private
10408     handleDateClick : function(e, t){
10409         e.stopEvent();
10410         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
10411             this.setValue(new Date(t.dateValue));
10412             this.fireEvent("select", this, this.value);
10413         }
10414     },
10415
10416     // private
10417     selectToday : function(){
10418         this.setValue(new Date().clearTime());
10419         this.fireEvent("select", this, this.value);
10420     },
10421
10422     // private
10423     update : function(date){
10424         var vd = this.activeDate;
10425         this.activeDate = date;
10426         if(vd && this.el){
10427             var t = date.getTime();
10428             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10429                 this.cells.removeClass("x-date-selected");
10430                 this.cells.each(function(c){
10431                    if(c.dom.firstChild.dateValue == t){
10432                        c.addClass("x-date-selected");
10433                        setTimeout(function(){
10434                             try{c.dom.firstChild.focus();}catch(e){}
10435                        }, 50);
10436                        return false;
10437                    }
10438                 });
10439                 return;
10440             }
10441         }
10442         var days = date.getDaysInMonth();
10443         var firstOfMonth = date.getFirstDateOfMonth();
10444         var startingPos = firstOfMonth.getDay()-this.startDay;
10445
10446         if(startingPos <= this.startDay){
10447             startingPos += 7;
10448         }
10449
10450         var pm = date.add("mo", -1);
10451         var prevStart = pm.getDaysInMonth()-startingPos;
10452
10453         var cells = this.cells.elements;
10454         var textEls = this.textNodes;
10455         days += startingPos;
10456
10457         // convert everything to numbers so it's fast
10458         var day = 86400000;
10459         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10460         var today = new Date().clearTime().getTime();
10461         var sel = date.clearTime().getTime();
10462         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10463         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10464         var ddMatch = this.disabledDatesRE;
10465         var ddText = this.disabledDatesText;
10466         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10467         var ddaysText = this.disabledDaysText;
10468         var format = this.format;
10469
10470         var setCellClass = function(cal, cell){
10471             cell.title = "";
10472             var t = d.getTime();
10473             cell.firstChild.dateValue = t;
10474             if(t == today){
10475                 cell.className += " x-date-today";
10476                 cell.title = cal.todayText;
10477             }
10478             if(t == sel){
10479                 cell.className += " x-date-selected";
10480                 setTimeout(function(){
10481                     try{cell.firstChild.focus();}catch(e){}
10482                 }, 50);
10483             }
10484             // disabling
10485             if(t < min) {
10486                 cell.className = " x-date-disabled";
10487                 cell.title = cal.minText;
10488                 return;
10489             }
10490             if(t > max) {
10491                 cell.className = " x-date-disabled";
10492                 cell.title = cal.maxText;
10493                 return;
10494             }
10495             if(ddays){
10496                 if(ddays.indexOf(d.getDay()) != -1){
10497                     cell.title = ddaysText;
10498                     cell.className = " x-date-disabled";
10499                 }
10500             }
10501             if(ddMatch && format){
10502                 var fvalue = d.dateFormat(format);
10503                 if(ddMatch.test(fvalue)){
10504                     cell.title = ddText.replace("%0", fvalue);
10505                     cell.className = " x-date-disabled";
10506                 }
10507             }
10508         };
10509
10510         var i = 0;
10511         for(; i < startingPos; i++) {
10512             textEls[i].innerHTML = (++prevStart);
10513             d.setDate(d.getDate()+1);
10514             cells[i].className = "x-date-prevday";
10515             setCellClass(this, cells[i]);
10516         }
10517         for(; i < days; i++){
10518             intDay = i - startingPos + 1;
10519             textEls[i].innerHTML = (intDay);
10520             d.setDate(d.getDate()+1);
10521             cells[i].className = "x-date-active";
10522             setCellClass(this, cells[i]);
10523         }
10524         var extraDays = 0;
10525         for(; i < 42; i++) {
10526              textEls[i].innerHTML = (++extraDays);
10527              d.setDate(d.getDate()+1);
10528              cells[i].className = "x-date-nextday";
10529              setCellClass(this, cells[i]);
10530         }
10531
10532         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
10533
10534         if(!this.internalRender){
10535             var main = this.el.dom.firstChild;
10536             var w = main.offsetWidth;
10537             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10538             Roo.fly(main).setWidth(w);
10539             this.internalRender = true;
10540             // opera does not respect the auto grow header center column
10541             // then, after it gets a width opera refuses to recalculate
10542             // without a second pass
10543             if(Roo.isOpera && !this.secondPass){
10544                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10545                 this.secondPass = true;
10546                 this.update.defer(10, this, [date]);
10547             }
10548         }
10549     }
10550 });/*
10551  * Based on:
10552  * Ext JS Library 1.1.1
10553  * Copyright(c) 2006-2007, Ext JS, LLC.
10554  *
10555  * Originally Released Under LGPL - original licence link has changed is not relivant.
10556  *
10557  * Fork - LGPL
10558  * <script type="text/javascript">
10559  */
10560 /**
10561  * @class Roo.TabPanel
10562  * @extends Roo.util.Observable
10563  * A lightweight tab container.
10564  * <br><br>
10565  * Usage:
10566  * <pre><code>
10567 // basic tabs 1, built from existing content
10568 var tabs = new Roo.TabPanel("tabs1");
10569 tabs.addTab("script", "View Script");
10570 tabs.addTab("markup", "View Markup");
10571 tabs.activate("script");
10572
10573 // more advanced tabs, built from javascript
10574 var jtabs = new Roo.TabPanel("jtabs");
10575 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
10576
10577 // set up the UpdateManager
10578 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
10579 var updater = tab2.getUpdateManager();
10580 updater.setDefaultUrl("ajax1.htm");
10581 tab2.on('activate', updater.refresh, updater, true);
10582
10583 // Use setUrl for Ajax loading
10584 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
10585 tab3.setUrl("ajax2.htm", null, true);
10586
10587 // Disabled tab
10588 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
10589 tab4.disable();
10590
10591 jtabs.activate("jtabs-1");
10592  * </code></pre>
10593  * @constructor
10594  * Create a new TabPanel.
10595  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
10596  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
10597  */
10598 Roo.TabPanel = function(container, config){
10599     /**
10600     * The container element for this TabPanel.
10601     * @type Roo.Element
10602     */
10603     this.el = Roo.get(container, true);
10604     if(config){
10605         if(typeof config == "boolean"){
10606             this.tabPosition = config ? "bottom" : "top";
10607         }else{
10608             Roo.apply(this, config);
10609         }
10610     }
10611     if(this.tabPosition == "bottom"){
10612         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10613         this.el.addClass("x-tabs-bottom");
10614     }
10615     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
10616     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
10617     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
10618     if(Roo.isIE){
10619         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
10620     }
10621     if(this.tabPosition != "bottom"){
10622     /** The body element that contains {@link Roo.TabPanelItem} bodies.
10623      * @type Roo.Element
10624      */
10625       this.bodyEl = Roo.get(this.createBody(this.el.dom));
10626       this.el.addClass("x-tabs-top");
10627     }
10628     this.items = [];
10629
10630     this.bodyEl.setStyle("position", "relative");
10631
10632     this.active = null;
10633     this.activateDelegate = this.activate.createDelegate(this);
10634
10635     this.addEvents({
10636         /**
10637          * @event tabchange
10638          * Fires when the active tab changes
10639          * @param {Roo.TabPanel} this
10640          * @param {Roo.TabPanelItem} activePanel The new active tab
10641          */
10642         "tabchange": true,
10643         /**
10644          * @event beforetabchange
10645          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
10646          * @param {Roo.TabPanel} this
10647          * @param {Object} e Set cancel to true on this object to cancel the tab change
10648          * @param {Roo.TabPanelItem} tab The tab being changed to
10649          */
10650         "beforetabchange" : true
10651     });
10652
10653     Roo.EventManager.onWindowResize(this.onResize, this);
10654     this.cpad = this.el.getPadding("lr");
10655     this.hiddenCount = 0;
10656
10657     Roo.TabPanel.superclass.constructor.call(this);
10658 };
10659
10660 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
10661         /*
10662          *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
10663          */
10664     tabPosition : "top",
10665         /*
10666          *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
10667          */
10668     currentTabWidth : 0,
10669         /*
10670          *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
10671          */
10672     minTabWidth : 40,
10673         /*
10674          *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
10675          */
10676     maxTabWidth : 250,
10677         /*
10678          *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
10679          */
10680     preferredTabWidth : 175,
10681         /*
10682          *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
10683          */
10684     resizeTabs : false,
10685         /*
10686          *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
10687          */
10688     monitorResize : true,
10689
10690     /**
10691      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
10692      * @param {String} id The id of the div to use <b>or create</b>
10693      * @param {String} text The text for the tab
10694      * @param {String} content (optional) Content to put in the TabPanelItem body
10695      * @param {Boolean} closable (optional) True to create a close icon on the tab
10696      * @return {Roo.TabPanelItem} The created TabPanelItem
10697      */
10698     addTab : function(id, text, content, closable){
10699         var item = new Roo.TabPanelItem(this, id, text, closable);
10700         this.addTabItem(item);
10701         if(content){
10702             item.setContent(content);
10703         }
10704         return item;
10705     },
10706
10707     /**
10708      * Returns the {@link Roo.TabPanelItem} with the specified id/index
10709      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
10710      * @return {Roo.TabPanelItem}
10711      */
10712     getTab : function(id){
10713         return this.items[id];
10714     },
10715
10716     /**
10717      * Hides the {@link Roo.TabPanelItem} with the specified id/index
10718      * @param {String/Number} id The id or index of the TabPanelItem to hide.
10719      */
10720     hideTab : function(id){
10721         var t = this.items[id];
10722         if(!t.isHidden()){
10723            t.setHidden(true);
10724            this.hiddenCount++;
10725            this.autoSizeTabs();
10726         }
10727     },
10728
10729     /**
10730      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
10731      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
10732      */
10733     unhideTab : function(id){
10734         var t = this.items[id];
10735         if(t.isHidden()){
10736            t.setHidden(false);
10737            this.hiddenCount--;
10738            this.autoSizeTabs();
10739         }
10740     },
10741
10742     /**
10743      * Adds an existing {@link Roo.TabPanelItem}.
10744      * @param {Roo.TabPanelItem} item The TabPanelItem to add
10745      */
10746     addTabItem : function(item){
10747         this.items[item.id] = item;
10748         this.items.push(item);
10749         if(this.resizeTabs){
10750            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
10751            this.autoSizeTabs();
10752         }else{
10753             item.autoSize();
10754         }
10755     },
10756
10757     /**
10758      * Removes a {@link Roo.TabPanelItem}.
10759      * @param {String/Number} id The id or index of the TabPanelItem to remove.
10760      */
10761     removeTab : function(id){
10762         var items = this.items;
10763         var tab = items[id];
10764         if(!tab) return;
10765         var index = items.indexOf(tab);
10766         if(this.active == tab && items.length > 1){
10767             var newTab = this.getNextAvailable(index);
10768             if(newTab)newTab.activate();
10769         }
10770         this.stripEl.dom.removeChild(tab.pnode.dom);
10771         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
10772             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
10773         }
10774         items.splice(index, 1);
10775         delete this.items[tab.id];
10776         tab.fireEvent("close", tab);
10777         tab.purgeListeners();
10778         this.autoSizeTabs();
10779     },
10780
10781     getNextAvailable : function(start){
10782         var items = this.items;
10783         var index = start;
10784         // look for a next tab that will slide over to
10785         // replace the one being removed
10786         while(index < items.length){
10787             var item = items[++index];
10788             if(item && !item.isHidden()){
10789                 return item;
10790             }
10791         }
10792         // if one isn't found select the previous tab (on the left)
10793         index = start;
10794         while(index >= 0){
10795             var item = items[--index];
10796             if(item && !item.isHidden()){
10797                 return item;
10798             }
10799         }
10800         return null;
10801     },
10802
10803     /**
10804      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
10805      * @param {String/Number} id The id or index of the TabPanelItem to disable.
10806      */
10807     disableTab : function(id){
10808         var tab = this.items[id];
10809         if(tab && this.active != tab){
10810             tab.disable();
10811         }
10812     },
10813
10814     /**
10815      * Enables a {@link Roo.TabPanelItem} that is disabled.
10816      * @param {String/Number} id The id or index of the TabPanelItem to enable.
10817      */
10818     enableTab : function(id){
10819         var tab = this.items[id];
10820         tab.enable();
10821     },
10822
10823     /**
10824      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
10825      * @param {String/Number} id The id or index of the TabPanelItem to activate.
10826      * @return {Roo.TabPanelItem} The TabPanelItem.
10827      */
10828     activate : function(id){
10829         var tab = this.items[id];
10830         if(!tab){
10831             return null;
10832         }
10833         if(tab == this.active || tab.disabled){
10834             return tab;
10835         }
10836         var e = {};
10837         this.fireEvent("beforetabchange", this, e, tab);
10838         if(e.cancel !== true && !tab.disabled){
10839             if(this.active){
10840                 this.active.hide();
10841             }
10842             this.active = this.items[id];
10843             this.active.show();
10844             this.fireEvent("tabchange", this, this.active);
10845         }
10846         return tab;
10847     },
10848
10849     /**
10850      * Gets the active {@link Roo.TabPanelItem}.
10851      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
10852      */
10853     getActiveTab : function(){
10854         return this.active;
10855     },
10856
10857     /**
10858      * Updates the tab body element to fit the height of the container element
10859      * for overflow scrolling
10860      * @param {Number} targetHeight (optional) Override the starting height from the elements height
10861      */
10862     syncHeight : function(targetHeight){
10863         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
10864         var bm = this.bodyEl.getMargins();
10865         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
10866         this.bodyEl.setHeight(newHeight);
10867         return newHeight;
10868     },
10869
10870     onResize : function(){
10871         if(this.monitorResize){
10872             this.autoSizeTabs();
10873         }
10874     },
10875
10876     /**
10877      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
10878      */
10879     beginUpdate : function(){
10880         this.updating = true;
10881     },
10882
10883     /**
10884      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
10885      */
10886     endUpdate : function(){
10887         this.updating = false;
10888         this.autoSizeTabs();
10889     },
10890
10891     /**
10892      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
10893      */
10894     autoSizeTabs : function(){
10895         var count = this.items.length;
10896         var vcount = count - this.hiddenCount;
10897         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
10898         var w = Math.max(this.el.getWidth() - this.cpad, 10);
10899         var availWidth = Math.floor(w / vcount);
10900         var b = this.stripBody;
10901         if(b.getWidth() > w){
10902             var tabs = this.items;
10903             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
10904             if(availWidth < this.minTabWidth){
10905                 /*if(!this.sleft){    // incomplete scrolling code
10906                     this.createScrollButtons();
10907                 }
10908                 this.showScroll();
10909                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
10910             }
10911         }else{
10912             if(this.currentTabWidth < this.preferredTabWidth){
10913                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
10914             }
10915         }
10916     },
10917
10918     /**
10919      * Returns the number of tabs in this TabPanel.
10920      * @return {Number}
10921      */
10922      getCount : function(){
10923          return this.items.length;
10924      },
10925
10926     /**
10927      * Resizes all the tabs to the passed width
10928      * @param {Number} The new width
10929      */
10930     setTabWidth : function(width){
10931         this.currentTabWidth = width;
10932         for(var i = 0, len = this.items.length; i < len; i++) {
10933                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
10934         }
10935     },
10936
10937     /**
10938      * Destroys this TabPanel
10939      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
10940      */
10941     destroy : function(removeEl){
10942         Roo.EventManager.removeResizeListener(this.onResize, this);
10943         for(var i = 0, len = this.items.length; i < len; i++){
10944             this.items[i].purgeListeners();
10945         }
10946         if(removeEl === true){
10947             this.el.update("");
10948             this.el.remove();
10949         }
10950     }
10951 });
10952
10953 /**
10954  * @class Roo.TabPanelItem
10955  * @extends Roo.util.Observable
10956  * Represents an individual item (tab plus body) in a TabPanel.
10957  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
10958  * @param {String} id The id of this TabPanelItem
10959  * @param {String} text The text for the tab of this TabPanelItem
10960  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
10961  */
10962 Roo.TabPanelItem = function(tabPanel, id, text, closable){
10963     /**
10964      * The {@link Roo.TabPanel} this TabPanelItem belongs to
10965      * @type Roo.TabPanel
10966      */
10967     this.tabPanel = tabPanel;
10968     /**
10969      * The id for this TabPanelItem
10970      * @type String
10971      */
10972     this.id = id;
10973     /** @private */
10974     this.disabled = false;
10975     /** @private */
10976     this.text = text;
10977     /** @private */
10978     this.loaded = false;
10979     this.closable = closable;
10980
10981     /**
10982      * The body element for this TabPanelItem.
10983      * @type Roo.Element
10984      */
10985     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
10986     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
10987     this.bodyEl.setStyle("display", "block");
10988     this.bodyEl.setStyle("zoom", "1");
10989     this.hideAction();
10990
10991     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
10992     /** @private */
10993     this.el = Roo.get(els.el, true);
10994     this.inner = Roo.get(els.inner, true);
10995     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
10996     this.pnode = Roo.get(els.el.parentNode, true);
10997     this.el.on("mousedown", this.onTabMouseDown, this);
10998     this.el.on("click", this.onTabClick, this);
10999     /** @private */
11000     if(closable){
11001         var c = Roo.get(els.close, true);
11002         c.dom.title = this.closeText;
11003         c.addClassOnOver("close-over");
11004         c.on("click", this.closeClick, this);
11005      }
11006
11007     this.addEvents({
11008          /**
11009          * @event activate
11010          * Fires when this tab becomes the active tab.
11011          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11012          * @param {Roo.TabPanelItem} this
11013          */
11014         "activate": true,
11015         /**
11016          * @event beforeclose
11017          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
11018          * @param {Roo.TabPanelItem} this
11019          * @param {Object} e Set cancel to true on this object to cancel the close.
11020          */
11021         "beforeclose": true,
11022         /**
11023          * @event close
11024          * Fires when this tab is closed.
11025          * @param {Roo.TabPanelItem} this
11026          */
11027          "close": true,
11028         /**
11029          * @event deactivate
11030          * Fires when this tab is no longer the active tab.
11031          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11032          * @param {Roo.TabPanelItem} this
11033          */
11034          "deactivate" : true
11035     });
11036     this.hidden = false;
11037
11038     Roo.TabPanelItem.superclass.constructor.call(this);
11039 };
11040
11041 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
11042     purgeListeners : function(){
11043        Roo.util.Observable.prototype.purgeListeners.call(this);
11044        this.el.removeAllListeners();
11045     },
11046     /**
11047      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
11048      */
11049     show : function(){
11050         this.pnode.addClass("on");
11051         this.showAction();
11052         if(Roo.isOpera){
11053             this.tabPanel.stripWrap.repaint();
11054         }
11055         this.fireEvent("activate", this.tabPanel, this);
11056     },
11057
11058     /**
11059      * Returns true if this tab is the active tab.
11060      * @return {Boolean}
11061      */
11062     isActive : function(){
11063         return this.tabPanel.getActiveTab() == this;
11064     },
11065
11066     /**
11067      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
11068      */
11069     hide : function(){
11070         this.pnode.removeClass("on");
11071         this.hideAction();
11072         this.fireEvent("deactivate", this.tabPanel, this);
11073     },
11074
11075     hideAction : function(){
11076         this.bodyEl.hide();
11077         this.bodyEl.setStyle("position", "absolute");
11078         this.bodyEl.setLeft("-20000px");
11079         this.bodyEl.setTop("-20000px");
11080     },
11081
11082     showAction : function(){
11083         this.bodyEl.setStyle("position", "relative");
11084         this.bodyEl.setTop("");
11085         this.bodyEl.setLeft("");
11086         this.bodyEl.show();
11087     },
11088
11089     /**
11090      * Set the tooltip for the tab.
11091      * @param {String} tooltip The tab's tooltip
11092      */
11093     setTooltip : function(text){
11094         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
11095             this.textEl.dom.qtip = text;
11096             this.textEl.dom.removeAttribute('title');
11097         }else{
11098             this.textEl.dom.title = text;
11099         }
11100     },
11101
11102     onTabClick : function(e){
11103         e.preventDefault();
11104         this.tabPanel.activate(this.id);
11105     },
11106
11107     onTabMouseDown : function(e){
11108         e.preventDefault();
11109         this.tabPanel.activate(this.id);
11110     },
11111
11112     getWidth : function(){
11113         return this.inner.getWidth();
11114     },
11115
11116     setWidth : function(width){
11117         var iwidth = width - this.pnode.getPadding("lr");
11118         this.inner.setWidth(iwidth);
11119         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
11120         this.pnode.setWidth(width);
11121     },
11122
11123     /**
11124      * Show or hide the tab
11125      * @param {Boolean} hidden True to hide or false to show.
11126      */
11127     setHidden : function(hidden){
11128         this.hidden = hidden;
11129         this.pnode.setStyle("display", hidden ? "none" : "");
11130     },
11131
11132     /**
11133      * Returns true if this tab is "hidden"
11134      * @return {Boolean}
11135      */
11136     isHidden : function(){
11137         return this.hidden;
11138     },
11139
11140     /**
11141      * Returns the text for this tab
11142      * @return {String}
11143      */
11144     getText : function(){
11145         return this.text;
11146     },
11147
11148     autoSize : function(){
11149         //this.el.beginMeasure();
11150         this.textEl.setWidth(1);
11151         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
11152         //this.el.endMeasure();
11153     },
11154
11155     /**
11156      * Sets the text for the tab (Note: this also sets the tooltip text)
11157      * @param {String} text The tab's text and tooltip
11158      */
11159     setText : function(text){
11160         this.text = text;
11161         this.textEl.update(text);
11162         this.setTooltip(text);
11163         if(!this.tabPanel.resizeTabs){
11164             this.autoSize();
11165         }
11166     },
11167     /**
11168      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
11169      */
11170     activate : function(){
11171         this.tabPanel.activate(this.id);
11172     },
11173
11174     /**
11175      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
11176      */
11177     disable : function(){
11178         if(this.tabPanel.active != this){
11179             this.disabled = true;
11180             this.pnode.addClass("disabled");
11181         }
11182     },
11183
11184     /**
11185      * Enables this TabPanelItem if it was previously disabled.
11186      */
11187     enable : function(){
11188         this.disabled = false;
11189         this.pnode.removeClass("disabled");
11190     },
11191
11192     /**
11193      * Sets the content for this TabPanelItem.
11194      * @param {String} content The content
11195      * @param {Boolean} loadScripts true to look for and load scripts
11196      */
11197     setContent : function(content, loadScripts){
11198         this.bodyEl.update(content, loadScripts);
11199     },
11200
11201     /**
11202      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
11203      * @return {Roo.UpdateManager} The UpdateManager
11204      */
11205     getUpdateManager : function(){
11206         return this.bodyEl.getUpdateManager();
11207     },
11208
11209     /**
11210      * Set a URL to be used to load the content for this TabPanelItem.
11211      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
11212      * @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)
11213      * @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)
11214      * @return {Roo.UpdateManager} The UpdateManager
11215      */
11216     setUrl : function(url, params, loadOnce){
11217         if(this.refreshDelegate){
11218             this.un('activate', this.refreshDelegate);
11219         }
11220         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
11221         this.on("activate", this.refreshDelegate);
11222         return this.bodyEl.getUpdateManager();
11223     },
11224
11225     /** @private */
11226     _handleRefresh : function(url, params, loadOnce){
11227         if(!loadOnce || !this.loaded){
11228             var updater = this.bodyEl.getUpdateManager();
11229             updater.update(url, params, this._setLoaded.createDelegate(this));
11230         }
11231     },
11232
11233     /**
11234      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
11235      *   Will fail silently if the setUrl method has not been called.
11236      *   This does not activate the panel, just updates its content.
11237      */
11238     refresh : function(){
11239         if(this.refreshDelegate){
11240            this.loaded = false;
11241            this.refreshDelegate();
11242         }
11243     },
11244
11245     /** @private */
11246     _setLoaded : function(){
11247         this.loaded = true;
11248     },
11249
11250     /** @private */
11251     closeClick : function(e){
11252         var o = {};
11253         e.stopEvent();
11254         this.fireEvent("beforeclose", this, o);
11255         if(o.cancel !== true){
11256             this.tabPanel.removeTab(this.id);
11257         }
11258     },
11259     /**
11260      * The text displayed in the tooltip for the close icon.
11261      * @type String
11262      */
11263     closeText : "Close this tab"
11264 });
11265
11266 /** @private */
11267 Roo.TabPanel.prototype.createStrip = function(container){
11268     var strip = document.createElement("div");
11269     strip.className = "x-tabs-wrap";
11270     container.appendChild(strip);
11271     return strip;
11272 };
11273 /** @private */
11274 Roo.TabPanel.prototype.createStripList = function(strip){
11275     // div wrapper for retard IE
11276     strip.innerHTML = '<div class="x-tabs-strip-wrap"><table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr></tr></tbody></table></div>';
11277     return strip.firstChild.firstChild.firstChild.firstChild;
11278 };
11279 /** @private */
11280 Roo.TabPanel.prototype.createBody = function(container){
11281     var body = document.createElement("div");
11282     Roo.id(body, "tab-body");
11283     Roo.fly(body).addClass("x-tabs-body");
11284     container.appendChild(body);
11285     return body;
11286 };
11287 /** @private */
11288 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
11289     var body = Roo.getDom(id);
11290     if(!body){
11291         body = document.createElement("div");
11292         body.id = id;
11293     }
11294     Roo.fly(body).addClass("x-tabs-item-body");
11295     bodyEl.insertBefore(body, bodyEl.firstChild);
11296     return body;
11297 };
11298 /** @private */
11299 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
11300     var td = document.createElement("td");
11301     stripEl.appendChild(td);
11302     if(closable){
11303         td.className = "x-tabs-closable";
11304         if(!this.closeTpl){
11305             this.closeTpl = new Roo.Template(
11306                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11307                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
11308                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
11309             );
11310         }
11311         var el = this.closeTpl.overwrite(td, {"text": text});
11312         var close = el.getElementsByTagName("div")[0];
11313         var inner = el.getElementsByTagName("em")[0];
11314         return {"el": el, "close": close, "inner": inner};
11315     } else {
11316         if(!this.tabTpl){
11317             this.tabTpl = new Roo.Template(
11318                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11319                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
11320             );
11321         }
11322         var el = this.tabTpl.overwrite(td, {"text": text});
11323         var inner = el.getElementsByTagName("em")[0];
11324         return {"el": el, "inner": inner};
11325     }
11326 };/*
11327  * Based on:
11328  * Ext JS Library 1.1.1
11329  * Copyright(c) 2006-2007, Ext JS, LLC.
11330  *
11331  * Originally Released Under LGPL - original licence link has changed is not relivant.
11332  *
11333  * Fork - LGPL
11334  * <script type="text/javascript">
11335  */
11336
11337 /**
11338  * @class Roo.Button
11339  * @extends Roo.util.Observable
11340  * Simple Button class
11341  * @cfg {String} text The button text
11342  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
11343  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
11344  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
11345  * @cfg {Object} scope The scope of the handler
11346  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
11347  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
11348  * @cfg {Boolean} hidden True to start hidden (defaults to false)
11349  * @cfg {Boolean} disabled True to start disabled (defaults to false)
11350  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
11351  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
11352    applies if enableToggle = true)
11353  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
11354  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
11355   an {@link Roo.util.ClickRepeater} config object (defaults to false).
11356  * @constructor
11357  * Create a new button
11358  * @param {Object} config The config object
11359  */
11360 Roo.Button = function(renderTo, config)
11361 {
11362     if (!config) {
11363         config = renderTo;
11364         renderTo = config.renderTo || false;
11365     }
11366     
11367     Roo.apply(this, config);
11368     this.addEvents({
11369         /**
11370              * @event click
11371              * Fires when this button is clicked
11372              * @param {Button} this
11373              * @param {EventObject} e The click event
11374              */
11375             "click" : true,
11376         /**
11377              * @event toggle
11378              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
11379              * @param {Button} this
11380              * @param {Boolean} pressed
11381              */
11382             "toggle" : true,
11383         /**
11384              * @event mouseover
11385              * Fires when the mouse hovers over the button
11386              * @param {Button} this
11387              * @param {Event} e The event object
11388              */
11389         'mouseover' : true,
11390         /**
11391              * @event mouseout
11392              * Fires when the mouse exits the button
11393              * @param {Button} this
11394              * @param {Event} e The event object
11395              */
11396         'mouseout': true,
11397          /**
11398              * @event render
11399              * Fires when the button is rendered
11400              * @param {Button} this
11401              */
11402         'render': true
11403     });
11404     if(this.menu){
11405         this.menu = Roo.menu.MenuMgr.get(this.menu);
11406     }
11407     if(renderTo){
11408         this.render(renderTo);
11409     }
11410     
11411     Roo.util.Observable.call(this);
11412 };
11413
11414 Roo.extend(Roo.Button, Roo.util.Observable, {
11415     /**
11416      * 
11417      */
11418     
11419     /**
11420      * Read-only. True if this button is hidden
11421      * @type Boolean
11422      */
11423     hidden : false,
11424     /**
11425      * Read-only. True if this button is disabled
11426      * @type Boolean
11427      */
11428     disabled : false,
11429     /**
11430      * Read-only. True if this button is pressed (only if enableToggle = true)
11431      * @type Boolean
11432      */
11433     pressed : false,
11434
11435     /**
11436      * @cfg {Number} tabIndex 
11437      * The DOM tabIndex for this button (defaults to undefined)
11438      */
11439     tabIndex : undefined,
11440
11441     /**
11442      * @cfg {Boolean} enableToggle
11443      * True to enable pressed/not pressed toggling (defaults to false)
11444      */
11445     enableToggle: false,
11446     /**
11447      * @cfg {Mixed} menu
11448      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
11449      */
11450     menu : undefined,
11451     /**
11452      * @cfg {String} menuAlign
11453      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
11454      */
11455     menuAlign : "tl-bl?",
11456
11457     /**
11458      * @cfg {String} iconCls
11459      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
11460      */
11461     iconCls : undefined,
11462     /**
11463      * @cfg {String} type
11464      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
11465      */
11466     type : 'button',
11467
11468     // private
11469     menuClassTarget: 'tr',
11470
11471     /**
11472      * @cfg {String} clickEvent
11473      * The type of event to map to the button's event handler (defaults to 'click')
11474      */
11475     clickEvent : 'click',
11476
11477     /**
11478      * @cfg {Boolean} handleMouseEvents
11479      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
11480      */
11481     handleMouseEvents : true,
11482
11483     /**
11484      * @cfg {String} tooltipType
11485      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
11486      */
11487     tooltipType : 'qtip',
11488
11489     /**
11490      * @cfg {String} cls
11491      * A CSS class to apply to the button's main element.
11492      */
11493     
11494     /**
11495      * @cfg {Roo.Template} template (Optional)
11496      * An {@link Roo.Template} with which to create the Button's main element. This Template must
11497      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
11498      * require code modifications if required elements (e.g. a button) aren't present.
11499      */
11500
11501     // private
11502     render : function(renderTo){
11503         var btn;
11504         if(this.hideParent){
11505             this.parentEl = Roo.get(renderTo);
11506         }
11507         if(!this.dhconfig){
11508             if(!this.template){
11509                 if(!Roo.Button.buttonTemplate){
11510                     // hideous table template
11511                     Roo.Button.buttonTemplate = new Roo.Template(
11512                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
11513                         '<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>',
11514                         "</tr></tbody></table>");
11515                 }
11516                 this.template = Roo.Button.buttonTemplate;
11517             }
11518             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
11519             var btnEl = btn.child("button:first");
11520             btnEl.on('focus', this.onFocus, this);
11521             btnEl.on('blur', this.onBlur, this);
11522             if(this.cls){
11523                 btn.addClass(this.cls);
11524             }
11525             if(this.icon){
11526                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
11527             }
11528             if(this.iconCls){
11529                 btnEl.addClass(this.iconCls);
11530                 if(!this.cls){
11531                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11532                 }
11533             }
11534             if(this.tabIndex !== undefined){
11535                 btnEl.dom.tabIndex = this.tabIndex;
11536             }
11537             if(this.tooltip){
11538                 if(typeof this.tooltip == 'object'){
11539                     Roo.QuickTips.tips(Roo.apply({
11540                           target: btnEl.id
11541                     }, this.tooltip));
11542                 } else {
11543                     btnEl.dom[this.tooltipType] = this.tooltip;
11544                 }
11545             }
11546         }else{
11547             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
11548         }
11549         this.el = btn;
11550         if(this.id){
11551             this.el.dom.id = this.el.id = this.id;
11552         }
11553         if(this.menu){
11554             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
11555             this.menu.on("show", this.onMenuShow, this);
11556             this.menu.on("hide", this.onMenuHide, this);
11557         }
11558         btn.addClass("x-btn");
11559         if(Roo.isIE && !Roo.isIE7){
11560             this.autoWidth.defer(1, this);
11561         }else{
11562             this.autoWidth();
11563         }
11564         if(this.handleMouseEvents){
11565             btn.on("mouseover", this.onMouseOver, this);
11566             btn.on("mouseout", this.onMouseOut, this);
11567             btn.on("mousedown", this.onMouseDown, this);
11568         }
11569         btn.on(this.clickEvent, this.onClick, this);
11570         //btn.on("mouseup", this.onMouseUp, this);
11571         if(this.hidden){
11572             this.hide();
11573         }
11574         if(this.disabled){
11575             this.disable();
11576         }
11577         Roo.ButtonToggleMgr.register(this);
11578         if(this.pressed){
11579             this.el.addClass("x-btn-pressed");
11580         }
11581         if(this.repeat){
11582             var repeater = new Roo.util.ClickRepeater(btn,
11583                 typeof this.repeat == "object" ? this.repeat : {}
11584             );
11585             repeater.on("click", this.onClick,  this);
11586         }
11587         this.fireEvent('render', this);
11588         
11589     },
11590     /**
11591      * Returns the button's underlying element
11592      * @return {Roo.Element} The element
11593      */
11594     getEl : function(){
11595         return this.el;  
11596     },
11597     
11598     /**
11599      * Destroys this Button and removes any listeners.
11600      */
11601     destroy : function(){
11602         Roo.ButtonToggleMgr.unregister(this);
11603         this.el.removeAllListeners();
11604         this.purgeListeners();
11605         this.el.remove();
11606     },
11607
11608     // private
11609     autoWidth : function(){
11610         if(this.el){
11611             this.el.setWidth("auto");
11612             if(Roo.isIE7 && Roo.isStrict){
11613                 var ib = this.el.child('button');
11614                 if(ib && ib.getWidth() > 20){
11615                     ib.clip();
11616                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11617                 }
11618             }
11619             if(this.minWidth){
11620                 if(this.hidden){
11621                     this.el.beginMeasure();
11622                 }
11623                 if(this.el.getWidth() < this.minWidth){
11624                     this.el.setWidth(this.minWidth);
11625                 }
11626                 if(this.hidden){
11627                     this.el.endMeasure();
11628                 }
11629             }
11630         }
11631     },
11632
11633     /**
11634      * Assigns this button's click handler
11635      * @param {Function} handler The function to call when the button is clicked
11636      * @param {Object} scope (optional) Scope for the function passed in
11637      */
11638     setHandler : function(handler, scope){
11639         this.handler = handler;
11640         this.scope = scope;  
11641     },
11642     
11643     /**
11644      * Sets this button's text
11645      * @param {String} text The button text
11646      */
11647     setText : function(text){
11648         this.text = text;
11649         if(this.el){
11650             this.el.child("td.x-btn-center button.x-btn-text").update(text);
11651         }
11652         this.autoWidth();
11653     },
11654     
11655     /**
11656      * Gets the text for this button
11657      * @return {String} The button text
11658      */
11659     getText : function(){
11660         return this.text;  
11661     },
11662     
11663     /**
11664      * Show this button
11665      */
11666     show: function(){
11667         this.hidden = false;
11668         if(this.el){
11669             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
11670         }
11671     },
11672     
11673     /**
11674      * Hide this button
11675      */
11676     hide: function(){
11677         this.hidden = true;
11678         if(this.el){
11679             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
11680         }
11681     },
11682     
11683     /**
11684      * Convenience function for boolean show/hide
11685      * @param {Boolean} visible True to show, false to hide
11686      */
11687     setVisible: function(visible){
11688         if(visible) {
11689             this.show();
11690         }else{
11691             this.hide();
11692         }
11693     },
11694     
11695     /**
11696      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
11697      * @param {Boolean} state (optional) Force a particular state
11698      */
11699     toggle : function(state){
11700         state = state === undefined ? !this.pressed : state;
11701         if(state != this.pressed){
11702             if(state){
11703                 this.el.addClass("x-btn-pressed");
11704                 this.pressed = true;
11705                 this.fireEvent("toggle", this, true);
11706             }else{
11707                 this.el.removeClass("x-btn-pressed");
11708                 this.pressed = false;
11709                 this.fireEvent("toggle", this, false);
11710             }
11711             if(this.toggleHandler){
11712                 this.toggleHandler.call(this.scope || this, this, state);
11713             }
11714         }
11715     },
11716     
11717     /**
11718      * Focus the button
11719      */
11720     focus : function(){
11721         this.el.child('button:first').focus();
11722     },
11723     
11724     /**
11725      * Disable this button
11726      */
11727     disable : function(){
11728         if(this.el){
11729             this.el.addClass("x-btn-disabled");
11730         }
11731         this.disabled = true;
11732     },
11733     
11734     /**
11735      * Enable this button
11736      */
11737     enable : function(){
11738         if(this.el){
11739             this.el.removeClass("x-btn-disabled");
11740         }
11741         this.disabled = false;
11742     },
11743
11744     /**
11745      * Convenience function for boolean enable/disable
11746      * @param {Boolean} enabled True to enable, false to disable
11747      */
11748     setDisabled : function(v){
11749         this[v !== true ? "enable" : "disable"]();
11750     },
11751
11752     // private
11753     onClick : function(e){
11754         if(e){
11755             e.preventDefault();
11756         }
11757         if(e.button != 0){
11758             return;
11759         }
11760         if(!this.disabled){
11761             if(this.enableToggle){
11762                 this.toggle();
11763             }
11764             if(this.menu && !this.menu.isVisible()){
11765                 this.menu.show(this.el, this.menuAlign);
11766             }
11767             this.fireEvent("click", this, e);
11768             if(this.handler){
11769                 this.el.removeClass("x-btn-over");
11770                 this.handler.call(this.scope || this, this, e);
11771             }
11772         }
11773     },
11774     // private
11775     onMouseOver : function(e){
11776         if(!this.disabled){
11777             this.el.addClass("x-btn-over");
11778             this.fireEvent('mouseover', this, e);
11779         }
11780     },
11781     // private
11782     onMouseOut : function(e){
11783         if(!e.within(this.el,  true)){
11784             this.el.removeClass("x-btn-over");
11785             this.fireEvent('mouseout', this, e);
11786         }
11787     },
11788     // private
11789     onFocus : function(e){
11790         if(!this.disabled){
11791             this.el.addClass("x-btn-focus");
11792         }
11793     },
11794     // private
11795     onBlur : function(e){
11796         this.el.removeClass("x-btn-focus");
11797     },
11798     // private
11799     onMouseDown : function(e){
11800         if(!this.disabled && e.button == 0){
11801             this.el.addClass("x-btn-click");
11802             Roo.get(document).on('mouseup', this.onMouseUp, this);
11803         }
11804     },
11805     // private
11806     onMouseUp : function(e){
11807         if(e.button == 0){
11808             this.el.removeClass("x-btn-click");
11809             Roo.get(document).un('mouseup', this.onMouseUp, this);
11810         }
11811     },
11812     // private
11813     onMenuShow : function(e){
11814         this.el.addClass("x-btn-menu-active");
11815     },
11816     // private
11817     onMenuHide : function(e){
11818         this.el.removeClass("x-btn-menu-active");
11819     }   
11820 });
11821
11822 // Private utility class used by Button
11823 Roo.ButtonToggleMgr = function(){
11824    var groups = {};
11825    
11826    function toggleGroup(btn, state){
11827        if(state){
11828            var g = groups[btn.toggleGroup];
11829            for(var i = 0, l = g.length; i < l; i++){
11830                if(g[i] != btn){
11831                    g[i].toggle(false);
11832                }
11833            }
11834        }
11835    }
11836    
11837    return {
11838        register : function(btn){
11839            if(!btn.toggleGroup){
11840                return;
11841            }
11842            var g = groups[btn.toggleGroup];
11843            if(!g){
11844                g = groups[btn.toggleGroup] = [];
11845            }
11846            g.push(btn);
11847            btn.on("toggle", toggleGroup);
11848        },
11849        
11850        unregister : function(btn){
11851            if(!btn.toggleGroup){
11852                return;
11853            }
11854            var g = groups[btn.toggleGroup];
11855            if(g){
11856                g.remove(btn);
11857                btn.un("toggle", toggleGroup);
11858            }
11859        }
11860    };
11861 }();/*
11862  * Based on:
11863  * Ext JS Library 1.1.1
11864  * Copyright(c) 2006-2007, Ext JS, LLC.
11865  *
11866  * Originally Released Under LGPL - original licence link has changed is not relivant.
11867  *
11868  * Fork - LGPL
11869  * <script type="text/javascript">
11870  */
11871  
11872 /**
11873  * @class Roo.SplitButton
11874  * @extends Roo.Button
11875  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
11876  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
11877  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
11878  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
11879  * @cfg {String} arrowTooltip The title attribute of the arrow
11880  * @constructor
11881  * Create a new menu button
11882  * @param {String/HTMLElement/Element} renderTo The element to append the button to
11883  * @param {Object} config The config object
11884  */
11885 Roo.SplitButton = function(renderTo, config){
11886     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
11887     /**
11888      * @event arrowclick
11889      * Fires when this button's arrow is clicked
11890      * @param {SplitButton} this
11891      * @param {EventObject} e The click event
11892      */
11893     this.addEvents({"arrowclick":true});
11894 };
11895
11896 Roo.extend(Roo.SplitButton, Roo.Button, {
11897     render : function(renderTo){
11898         // this is one sweet looking template!
11899         var tpl = new Roo.Template(
11900             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
11901             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
11902             '<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>',
11903             "</tbody></table></td><td>",
11904             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
11905             '<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>',
11906             "</tbody></table></td></tr></table>"
11907         );
11908         var btn = tpl.append(renderTo, [this.text, this.type], true);
11909         var btnEl = btn.child("button");
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         this.el = btn;
11923         if(this.handleMouseEvents){
11924             btn.on("mouseover", this.onMouseOver, this);
11925             btn.on("mouseout", this.onMouseOut, this);
11926             btn.on("mousedown", this.onMouseDown, this);
11927             btn.on("mouseup", this.onMouseUp, this);
11928         }
11929         btn.on(this.clickEvent, this.onClick, this);
11930         if(this.tooltip){
11931             if(typeof this.tooltip == 'object'){
11932                 Roo.QuickTips.tips(Roo.apply({
11933                       target: btnEl.id
11934                 }, this.tooltip));
11935             } else {
11936                 btnEl.dom[this.tooltipType] = this.tooltip;
11937             }
11938         }
11939         if(this.arrowTooltip){
11940             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
11941         }
11942         if(this.hidden){
11943             this.hide();
11944         }
11945         if(this.disabled){
11946             this.disable();
11947         }
11948         if(this.pressed){
11949             this.el.addClass("x-btn-pressed");
11950         }
11951         if(Roo.isIE && !Roo.isIE7){
11952             this.autoWidth.defer(1, this);
11953         }else{
11954             this.autoWidth();
11955         }
11956         if(this.menu){
11957             this.menu.on("show", this.onMenuShow, this);
11958             this.menu.on("hide", this.onMenuHide, this);
11959         }
11960         this.fireEvent('render', this);
11961     },
11962
11963     // private
11964     autoWidth : function(){
11965         if(this.el){
11966             var tbl = this.el.child("table:first");
11967             var tbl2 = this.el.child("table:last");
11968             this.el.setWidth("auto");
11969             tbl.setWidth("auto");
11970             if(Roo.isIE7 && Roo.isStrict){
11971                 var ib = this.el.child('button:first');
11972                 if(ib && ib.getWidth() > 20){
11973                     ib.clip();
11974                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11975                 }
11976             }
11977             if(this.minWidth){
11978                 if(this.hidden){
11979                     this.el.beginMeasure();
11980                 }
11981                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
11982                     tbl.setWidth(this.minWidth-tbl2.getWidth());
11983                 }
11984                 if(this.hidden){
11985                     this.el.endMeasure();
11986                 }
11987             }
11988             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
11989         } 
11990     },
11991     /**
11992      * Sets this button's click handler
11993      * @param {Function} handler The function to call when the button is clicked
11994      * @param {Object} scope (optional) Scope for the function passed above
11995      */
11996     setHandler : function(handler, scope){
11997         this.handler = handler;
11998         this.scope = scope;  
11999     },
12000     
12001     /**
12002      * Sets this button's arrow click handler
12003      * @param {Function} handler The function to call when the arrow is clicked
12004      * @param {Object} scope (optional) Scope for the function passed above
12005      */
12006     setArrowHandler : function(handler, scope){
12007         this.arrowHandler = handler;
12008         this.scope = scope;  
12009     },
12010     
12011     /**
12012      * Focus the button
12013      */
12014     focus : function(){
12015         if(this.el){
12016             this.el.child("button:first").focus();
12017         }
12018     },
12019
12020     // private
12021     onClick : function(e){
12022         e.preventDefault();
12023         if(!this.disabled){
12024             if(e.getTarget(".x-btn-menu-arrow-wrap")){
12025                 if(this.menu && !this.menu.isVisible()){
12026                     this.menu.show(this.el, this.menuAlign);
12027                 }
12028                 this.fireEvent("arrowclick", this, e);
12029                 if(this.arrowHandler){
12030                     this.arrowHandler.call(this.scope || this, this, e);
12031                 }
12032             }else{
12033                 this.fireEvent("click", this, e);
12034                 if(this.handler){
12035                     this.handler.call(this.scope || this, this, e);
12036                 }
12037             }
12038         }
12039     },
12040     // private
12041     onMouseDown : function(e){
12042         if(!this.disabled){
12043             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
12044         }
12045     },
12046     // private
12047     onMouseUp : function(e){
12048         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
12049     }   
12050 });
12051
12052
12053 // backwards compat
12054 Roo.MenuButton = Roo.SplitButton;/*
12055  * Based on:
12056  * Ext JS Library 1.1.1
12057  * Copyright(c) 2006-2007, Ext JS, LLC.
12058  *
12059  * Originally Released Under LGPL - original licence link has changed is not relivant.
12060  *
12061  * Fork - LGPL
12062  * <script type="text/javascript">
12063  */
12064
12065 /**
12066  * @class Roo.Toolbar
12067  * Basic Toolbar class.
12068  * @constructor
12069  * Creates a new Toolbar
12070  * @param {Object} config The config object
12071  */ 
12072 Roo.Toolbar = function(container, buttons, config)
12073 {
12074     /// old consturctor format still supported..
12075     if(container instanceof Array){ // omit the container for later rendering
12076         buttons = container;
12077         config = buttons;
12078         container = null;
12079     }
12080     if (typeof(container) == 'object' && container.xtype) {
12081         config = container;
12082         container = config.container;
12083         buttons = config.buttons; // not really - use items!!
12084     }
12085     var xitems = [];
12086     if (config && config.items) {
12087         xitems = config.items;
12088         delete config.items;
12089     }
12090     Roo.apply(this, config);
12091     this.buttons = buttons;
12092     
12093     if(container){
12094         this.render(container);
12095     }
12096     Roo.each(xitems, function(b) {
12097         this.add(b);
12098     }, this);
12099     
12100 };
12101
12102 Roo.Toolbar.prototype = {
12103     /**
12104      * @cfg {Roo.data.Store} items
12105      * array of button configs or elements to add
12106      */
12107     
12108     /**
12109      * @cfg {String/HTMLElement/Element} container
12110      * The id or element that will contain the toolbar
12111      */
12112     // private
12113     render : function(ct){
12114         this.el = Roo.get(ct);
12115         if(this.cls){
12116             this.el.addClass(this.cls);
12117         }
12118         // using a table allows for vertical alignment
12119         // 100% width is needed by Safari...
12120         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
12121         this.tr = this.el.child("tr", true);
12122         var autoId = 0;
12123         this.items = new Roo.util.MixedCollection(false, function(o){
12124             return o.id || ("item" + (++autoId));
12125         });
12126         if(this.buttons){
12127             this.add.apply(this, this.buttons);
12128             delete this.buttons;
12129         }
12130     },
12131
12132     /**
12133      * Adds element(s) to the toolbar -- this function takes a variable number of 
12134      * arguments of mixed type and adds them to the toolbar.
12135      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
12136      * <ul>
12137      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
12138      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
12139      * <li>Field: Any form field (equivalent to {@link #addField})</li>
12140      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
12141      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
12142      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
12143      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
12144      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
12145      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
12146      * </ul>
12147      * @param {Mixed} arg2
12148      * @param {Mixed} etc.
12149      */
12150     add : function(){
12151         var a = arguments, l = a.length;
12152         for(var i = 0; i < l; i++){
12153             this._add(a[i]);
12154         }
12155     },
12156     // private..
12157     _add : function(el) {
12158         
12159         if (el.xtype) {
12160             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
12161         }
12162         
12163         if (el.applyTo){ // some kind of form field
12164             return this.addField(el);
12165         } 
12166         if (el.render){ // some kind of Toolbar.Item
12167             return this.addItem(el);
12168         }
12169         if (typeof el == "string"){ // string
12170             if(el == "separator" || el == "-"){
12171                 return this.addSeparator();
12172             }
12173             if (el == " "){
12174                 return this.addSpacer();
12175             }
12176             if(el == "->"){
12177                 return this.addFill();
12178             }
12179             return this.addText(el);
12180             
12181         }
12182         if(el.tagName){ // element
12183             return this.addElement(el);
12184         }
12185         if(typeof el == "object"){ // must be button config?
12186             return this.addButton(el);
12187         }
12188         // and now what?!?!
12189         return false;
12190         
12191     },
12192     
12193     /**
12194      * Add an Xtype element
12195      * @param {Object} xtype Xtype Object
12196      * @return {Object} created Object
12197      */
12198     addxtype : function(e){
12199         return this.add(e);  
12200     },
12201     
12202     /**
12203      * Returns the Element for this toolbar.
12204      * @return {Roo.Element}
12205      */
12206     getEl : function(){
12207         return this.el;  
12208     },
12209     
12210     /**
12211      * Adds a separator
12212      * @return {Roo.Toolbar.Item} The separator item
12213      */
12214     addSeparator : function(){
12215         return this.addItem(new Roo.Toolbar.Separator());
12216     },
12217
12218     /**
12219      * Adds a spacer element
12220      * @return {Roo.Toolbar.Spacer} The spacer item
12221      */
12222     addSpacer : function(){
12223         return this.addItem(new Roo.Toolbar.Spacer());
12224     },
12225
12226     /**
12227      * Adds a fill element that forces subsequent additions to the right side of the toolbar
12228      * @return {Roo.Toolbar.Fill} The fill item
12229      */
12230     addFill : function(){
12231         return this.addItem(new Roo.Toolbar.Fill());
12232     },
12233
12234     /**
12235      * Adds any standard HTML element to the toolbar
12236      * @param {String/HTMLElement/Element} el The element or id of the element to add
12237      * @return {Roo.Toolbar.Item} The element's item
12238      */
12239     addElement : function(el){
12240         return this.addItem(new Roo.Toolbar.Item(el));
12241     },
12242     /**
12243      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
12244      * @type Roo.util.MixedCollection  
12245      */
12246     items : false,
12247      
12248     /**
12249      * Adds any Toolbar.Item or subclass
12250      * @param {Roo.Toolbar.Item} item
12251      * @return {Roo.Toolbar.Item} The item
12252      */
12253     addItem : function(item){
12254         var td = this.nextBlock();
12255         item.render(td);
12256         this.items.add(item);
12257         return item;
12258     },
12259     
12260     /**
12261      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
12262      * @param {Object/Array} config A button config or array of configs
12263      * @return {Roo.Toolbar.Button/Array}
12264      */
12265     addButton : function(config){
12266         if(config instanceof Array){
12267             var buttons = [];
12268             for(var i = 0, len = config.length; i < len; i++) {
12269                 buttons.push(this.addButton(config[i]));
12270             }
12271             return buttons;
12272         }
12273         var b = config;
12274         if(!(config instanceof Roo.Toolbar.Button)){
12275             b = config.split ?
12276                 new Roo.Toolbar.SplitButton(config) :
12277                 new Roo.Toolbar.Button(config);
12278         }
12279         var td = this.nextBlock();
12280         b.render(td);
12281         this.items.add(b);
12282         return b;
12283     },
12284     
12285     /**
12286      * Adds text to the toolbar
12287      * @param {String} text The text to add
12288      * @return {Roo.Toolbar.Item} The element's item
12289      */
12290     addText : function(text){
12291         return this.addItem(new Roo.Toolbar.TextItem(text));
12292     },
12293     
12294     /**
12295      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
12296      * @param {Number} index The index where the item is to be inserted
12297      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
12298      * @return {Roo.Toolbar.Button/Item}
12299      */
12300     insertButton : function(index, item){
12301         if(item instanceof Array){
12302             var buttons = [];
12303             for(var i = 0, len = item.length; i < len; i++) {
12304                buttons.push(this.insertButton(index + i, item[i]));
12305             }
12306             return buttons;
12307         }
12308         if (!(item instanceof Roo.Toolbar.Button)){
12309            item = new Roo.Toolbar.Button(item);
12310         }
12311         var td = document.createElement("td");
12312         this.tr.insertBefore(td, this.tr.childNodes[index]);
12313         item.render(td);
12314         this.items.insert(index, item);
12315         return item;
12316     },
12317     
12318     /**
12319      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
12320      * @param {Object} config
12321      * @return {Roo.Toolbar.Item} The element's item
12322      */
12323     addDom : function(config, returnEl){
12324         var td = this.nextBlock();
12325         Roo.DomHelper.overwrite(td, config);
12326         var ti = new Roo.Toolbar.Item(td.firstChild);
12327         ti.render(td);
12328         this.items.add(ti);
12329         return ti;
12330     },
12331
12332     /**
12333      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
12334      * @type Roo.util.MixedCollection  
12335      */
12336     fields : false,
12337     
12338     /**
12339      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc). Note: the field should not have
12340      * been rendered yet. For a field that has already been rendered, use {@link #addElement}.
12341      * @param {Roo.form.Field} field
12342      * @return {Roo.ToolbarItem}
12343      */
12344      
12345       
12346     addField : function(field) {
12347         if (!this.fields) {
12348             var autoId = 0;
12349             this.fields = new Roo.util.MixedCollection(false, function(o){
12350                 return o.id || ("item" + (++autoId));
12351             });
12352
12353         }
12354         
12355         var td = this.nextBlock();
12356         field.render(td);
12357         var ti = new Roo.Toolbar.Item(td.firstChild);
12358         ti.render(td);
12359         this.items.add(ti);
12360         this.fields.add(field);
12361         return ti;
12362     },
12363     /**
12364      * Hide the toolbar
12365      * @method hide
12366      */
12367      
12368       
12369     hide : function()
12370     {
12371         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
12372         this.el.child('div').hide();
12373     },
12374     /**
12375      * Show the toolbar
12376      * @method show
12377      */
12378     show : function()
12379     {
12380         this.el.child('div').show();
12381     },
12382       
12383     // private
12384     nextBlock : function(){
12385         var td = document.createElement("td");
12386         this.tr.appendChild(td);
12387         return td;
12388     },
12389
12390     // private
12391     destroy : function(){
12392         if(this.items){ // rendered?
12393             Roo.destroy.apply(Roo, this.items.items);
12394         }
12395         if(this.fields){ // rendered?
12396             Roo.destroy.apply(Roo, this.fields.items);
12397         }
12398         Roo.Element.uncache(this.el, this.tr);
12399     }
12400 };
12401
12402 /**
12403  * @class Roo.Toolbar.Item
12404  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
12405  * @constructor
12406  * Creates a new Item
12407  * @param {HTMLElement} el 
12408  */
12409 Roo.Toolbar.Item = function(el){
12410     this.el = Roo.getDom(el);
12411     this.id = Roo.id(this.el);
12412     this.hidden = false;
12413 };
12414
12415 Roo.Toolbar.Item.prototype = {
12416     
12417     /**
12418      * Get this item's HTML Element
12419      * @return {HTMLElement}
12420      */
12421     getEl : function(){
12422        return this.el;  
12423     },
12424
12425     // private
12426     render : function(td){
12427         this.td = td;
12428         td.appendChild(this.el);
12429     },
12430     
12431     /**
12432      * Removes and destroys this item.
12433      */
12434     destroy : function(){
12435         this.td.parentNode.removeChild(this.td);
12436     },
12437     
12438     /**
12439      * Shows this item.
12440      */
12441     show: function(){
12442         this.hidden = false;
12443         this.td.style.display = "";
12444     },
12445     
12446     /**
12447      * Hides this item.
12448      */
12449     hide: function(){
12450         this.hidden = true;
12451         this.td.style.display = "none";
12452     },
12453     
12454     /**
12455      * Convenience function for boolean show/hide.
12456      * @param {Boolean} visible true to show/false to hide
12457      */
12458     setVisible: function(visible){
12459         if(visible) {
12460             this.show();
12461         }else{
12462             this.hide();
12463         }
12464     },
12465     
12466     /**
12467      * Try to focus this item.
12468      */
12469     focus : function(){
12470         Roo.fly(this.el).focus();
12471     },
12472     
12473     /**
12474      * Disables this item.
12475      */
12476     disable : function(){
12477         Roo.fly(this.td).addClass("x-item-disabled");
12478         this.disabled = true;
12479         this.el.disabled = true;
12480     },
12481     
12482     /**
12483      * Enables this item.
12484      */
12485     enable : function(){
12486         Roo.fly(this.td).removeClass("x-item-disabled");
12487         this.disabled = false;
12488         this.el.disabled = false;
12489     }
12490 };
12491
12492
12493 /**
12494  * @class Roo.Toolbar.Separator
12495  * @extends Roo.Toolbar.Item
12496  * A simple toolbar separator class
12497  * @constructor
12498  * Creates a new Separator
12499  */
12500 Roo.Toolbar.Separator = function(){
12501     var s = document.createElement("span");
12502     s.className = "ytb-sep";
12503     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
12504 };
12505 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
12506     enable:Roo.emptyFn,
12507     disable:Roo.emptyFn,
12508     focus:Roo.emptyFn
12509 });
12510
12511 /**
12512  * @class Roo.Toolbar.Spacer
12513  * @extends Roo.Toolbar.Item
12514  * A simple element that adds extra horizontal space to a toolbar.
12515  * @constructor
12516  * Creates a new Spacer
12517  */
12518 Roo.Toolbar.Spacer = function(){
12519     var s = document.createElement("div");
12520     s.className = "ytb-spacer";
12521     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
12522 };
12523 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
12524     enable:Roo.emptyFn,
12525     disable:Roo.emptyFn,
12526     focus:Roo.emptyFn
12527 });
12528
12529 /**
12530  * @class Roo.Toolbar.Fill
12531  * @extends Roo.Toolbar.Spacer
12532  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
12533  * @constructor
12534  * Creates a new Spacer
12535  */
12536 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
12537     // private
12538     render : function(td){
12539         td.style.width = '100%';
12540         Roo.Toolbar.Fill.superclass.render.call(this, td);
12541     }
12542 });
12543
12544 /**
12545  * @class Roo.Toolbar.TextItem
12546  * @extends Roo.Toolbar.Item
12547  * A simple class that renders text directly into a toolbar.
12548  * @constructor
12549  * Creates a new TextItem
12550  * @param {String} text
12551  */
12552 Roo.Toolbar.TextItem = function(text){
12553     if (typeof(text) == 'object') {
12554         text = text.text;
12555     }
12556     var s = document.createElement("span");
12557     s.className = "ytb-text";
12558     s.innerHTML = text;
12559     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
12560 };
12561 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
12562     enable:Roo.emptyFn,
12563     disable:Roo.emptyFn,
12564     focus:Roo.emptyFn
12565 });
12566
12567 /**
12568  * @class Roo.Toolbar.Button
12569  * @extends Roo.Button
12570  * A button that renders into a toolbar.
12571  * @constructor
12572  * Creates a new Button
12573  * @param {Object} config A standard {@link Roo.Button} config object
12574  */
12575 Roo.Toolbar.Button = function(config){
12576     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
12577 };
12578 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
12579     render : function(td){
12580         this.td = td;
12581         Roo.Toolbar.Button.superclass.render.call(this, td);
12582     },
12583     
12584     /**
12585      * Removes and destroys this button
12586      */
12587     destroy : function(){
12588         Roo.Toolbar.Button.superclass.destroy.call(this);
12589         this.td.parentNode.removeChild(this.td);
12590     },
12591     
12592     /**
12593      * Shows this button
12594      */
12595     show: function(){
12596         this.hidden = false;
12597         this.td.style.display = "";
12598     },
12599     
12600     /**
12601      * Hides this button
12602      */
12603     hide: function(){
12604         this.hidden = true;
12605         this.td.style.display = "none";
12606     },
12607
12608     /**
12609      * Disables this item
12610      */
12611     disable : function(){
12612         Roo.fly(this.td).addClass("x-item-disabled");
12613         this.disabled = true;
12614     },
12615
12616     /**
12617      * Enables this item
12618      */
12619     enable : function(){
12620         Roo.fly(this.td).removeClass("x-item-disabled");
12621         this.disabled = false;
12622     }
12623 });
12624 // backwards compat
12625 Roo.ToolbarButton = Roo.Toolbar.Button;
12626
12627 /**
12628  * @class Roo.Toolbar.SplitButton
12629  * @extends Roo.SplitButton
12630  * A menu button that renders into a toolbar.
12631  * @constructor
12632  * Creates a new SplitButton
12633  * @param {Object} config A standard {@link Roo.SplitButton} config object
12634  */
12635 Roo.Toolbar.SplitButton = function(config){
12636     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
12637 };
12638 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
12639     render : function(td){
12640         this.td = td;
12641         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
12642     },
12643     
12644     /**
12645      * Removes and destroys this button
12646      */
12647     destroy : function(){
12648         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
12649         this.td.parentNode.removeChild(this.td);
12650     },
12651     
12652     /**
12653      * Shows this button
12654      */
12655     show: function(){
12656         this.hidden = false;
12657         this.td.style.display = "";
12658     },
12659     
12660     /**
12661      * Hides this button
12662      */
12663     hide: function(){
12664         this.hidden = true;
12665         this.td.style.display = "none";
12666     }
12667 });
12668
12669 // backwards compat
12670 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
12671  * Based on:
12672  * Ext JS Library 1.1.1
12673  * Copyright(c) 2006-2007, Ext JS, LLC.
12674  *
12675  * Originally Released Under LGPL - original licence link has changed is not relivant.
12676  *
12677  * Fork - LGPL
12678  * <script type="text/javascript">
12679  */
12680  
12681 /**
12682  * @class Roo.PagingToolbar
12683  * @extends Roo.Toolbar
12684  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
12685  * @constructor
12686  * Create a new PagingToolbar
12687  * @param {Object} config The config object
12688  */
12689 Roo.PagingToolbar = function(el, ds, config)
12690 {
12691     // old args format still supported... - xtype is prefered..
12692     if (typeof(el) == 'object' && el.xtype) {
12693         // created from xtype...
12694         config = el;
12695         ds = el.dataSource;
12696         el = config.container;
12697     }
12698     
12699     
12700     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
12701     this.ds = ds;
12702     this.cursor = 0;
12703     this.renderButtons(this.el);
12704     this.bind(ds);
12705 };
12706
12707 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
12708     /**
12709      * @cfg {Roo.data.Store} dataSource
12710      * The underlying data store providing the paged data
12711      */
12712     /**
12713      * @cfg {String/HTMLElement/Element} container
12714      * container The id or element that will contain the toolbar
12715      */
12716     /**
12717      * @cfg {Boolean} displayInfo
12718      * True to display the displayMsg (defaults to false)
12719      */
12720     /**
12721      * @cfg {Number} pageSize
12722      * The number of records to display per page (defaults to 20)
12723      */
12724     pageSize: 20,
12725     /**
12726      * @cfg {String} displayMsg
12727      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
12728      */
12729     displayMsg : 'Displaying {0} - {1} of {2}',
12730     /**
12731      * @cfg {String} emptyMsg
12732      * The message to display when no records are found (defaults to "No data to display")
12733      */
12734     emptyMsg : 'No data to display',
12735     /**
12736      * Customizable piece of the default paging text (defaults to "Page")
12737      * @type String
12738      */
12739     beforePageText : "Page",
12740     /**
12741      * Customizable piece of the default paging text (defaults to "of %0")
12742      * @type String
12743      */
12744     afterPageText : "of {0}",
12745     /**
12746      * Customizable piece of the default paging text (defaults to "First Page")
12747      * @type String
12748      */
12749     firstText : "First Page",
12750     /**
12751      * Customizable piece of the default paging text (defaults to "Previous Page")
12752      * @type String
12753      */
12754     prevText : "Previous Page",
12755     /**
12756      * Customizable piece of the default paging text (defaults to "Next Page")
12757      * @type String
12758      */
12759     nextText : "Next Page",
12760     /**
12761      * Customizable piece of the default paging text (defaults to "Last Page")
12762      * @type String
12763      */
12764     lastText : "Last Page",
12765     /**
12766      * Customizable piece of the default paging text (defaults to "Refresh")
12767      * @type String
12768      */
12769     refreshText : "Refresh",
12770
12771     // private
12772     renderButtons : function(el){
12773         Roo.PagingToolbar.superclass.render.call(this, el);
12774         this.first = this.addButton({
12775             tooltip: this.firstText,
12776             cls: "x-btn-icon x-grid-page-first",
12777             disabled: true,
12778             handler: this.onClick.createDelegate(this, ["first"])
12779         });
12780         this.prev = this.addButton({
12781             tooltip: this.prevText,
12782             cls: "x-btn-icon x-grid-page-prev",
12783             disabled: true,
12784             handler: this.onClick.createDelegate(this, ["prev"])
12785         });
12786         this.addSeparator();
12787         this.add(this.beforePageText);
12788         this.field = Roo.get(this.addDom({
12789            tag: "input",
12790            type: "text",
12791            size: "3",
12792            value: "1",
12793            cls: "x-grid-page-number"
12794         }).el);
12795         this.field.on("keydown", this.onPagingKeydown, this);
12796         this.field.on("focus", function(){this.dom.select();});
12797         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
12798         this.field.setHeight(18);
12799         this.addSeparator();
12800         this.next = this.addButton({
12801             tooltip: this.nextText,
12802             cls: "x-btn-icon x-grid-page-next",
12803             disabled: true,
12804             handler: this.onClick.createDelegate(this, ["next"])
12805         });
12806         this.last = this.addButton({
12807             tooltip: this.lastText,
12808             cls: "x-btn-icon x-grid-page-last",
12809             disabled: true,
12810             handler: this.onClick.createDelegate(this, ["last"])
12811         });
12812         this.addSeparator();
12813         this.loading = this.addButton({
12814             tooltip: this.refreshText,
12815             cls: "x-btn-icon x-grid-loading",
12816             handler: this.onClick.createDelegate(this, ["refresh"])
12817         });
12818
12819         if(this.displayInfo){
12820             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
12821         }
12822     },
12823
12824     // private
12825     updateInfo : function(){
12826         if(this.displayEl){
12827             var count = this.ds.getCount();
12828             var msg = count == 0 ?
12829                 this.emptyMsg :
12830                 String.format(
12831                     this.displayMsg,
12832                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
12833                 );
12834             this.displayEl.update(msg);
12835         }
12836     },
12837
12838     // private
12839     onLoad : function(ds, r, o){
12840        this.cursor = o.params ? o.params.start : 0;
12841        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
12842
12843        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
12844        this.field.dom.value = ap;
12845        this.first.setDisabled(ap == 1);
12846        this.prev.setDisabled(ap == 1);
12847        this.next.setDisabled(ap == ps);
12848        this.last.setDisabled(ap == ps);
12849        this.loading.enable();
12850        this.updateInfo();
12851     },
12852
12853     // private
12854     getPageData : function(){
12855         var total = this.ds.getTotalCount();
12856         return {
12857             total : total,
12858             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
12859             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
12860         };
12861     },
12862
12863     // private
12864     onLoadError : function(){
12865         this.loading.enable();
12866     },
12867
12868     // private
12869     onPagingKeydown : function(e){
12870         var k = e.getKey();
12871         var d = this.getPageData();
12872         if(k == e.RETURN){
12873             var v = this.field.dom.value, pageNum;
12874             if(!v || isNaN(pageNum = parseInt(v, 10))){
12875                 this.field.dom.value = d.activePage;
12876                 return;
12877             }
12878             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
12879             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
12880             e.stopEvent();
12881         }
12882         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))
12883         {
12884           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
12885           this.field.dom.value = pageNum;
12886           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
12887           e.stopEvent();
12888         }
12889         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
12890         {
12891           var v = this.field.dom.value, pageNum; 
12892           var increment = (e.shiftKey) ? 10 : 1;
12893           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
12894             increment *= -1;
12895           if(!v || isNaN(pageNum = parseInt(v, 10))) {
12896             this.field.dom.value = d.activePage;
12897             return;
12898           }
12899           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
12900           {
12901             this.field.dom.value = parseInt(v, 10) + increment;
12902             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
12903             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
12904           }
12905           e.stopEvent();
12906         }
12907     },
12908
12909     // private
12910     beforeLoad : function(){
12911         if(this.loading){
12912             this.loading.disable();
12913         }
12914     },
12915
12916     // private
12917     onClick : function(which){
12918         var ds = this.ds;
12919         switch(which){
12920             case "first":
12921                 ds.load({params:{start: 0, limit: this.pageSize}});
12922             break;
12923             case "prev":
12924                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
12925             break;
12926             case "next":
12927                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
12928             break;
12929             case "last":
12930                 var total = ds.getTotalCount();
12931                 var extra = total % this.pageSize;
12932                 var lastStart = extra ? (total - extra) : total-this.pageSize;
12933                 ds.load({params:{start: lastStart, limit: this.pageSize}});
12934             break;
12935             case "refresh":
12936                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
12937             break;
12938         }
12939     },
12940
12941     /**
12942      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
12943      * @param {Roo.data.Store} store The data store to unbind
12944      */
12945     unbind : function(ds){
12946         ds.un("beforeload", this.beforeLoad, this);
12947         ds.un("load", this.onLoad, this);
12948         ds.un("loadexception", this.onLoadError, this);
12949         ds.un("remove", this.updateInfo, this);
12950         ds.un("add", this.updateInfo, this);
12951         this.ds = undefined;
12952     },
12953
12954     /**
12955      * Binds the paging toolbar to the specified {@link Roo.data.Store}
12956      * @param {Roo.data.Store} store The data store to bind
12957      */
12958     bind : function(ds){
12959         ds.on("beforeload", this.beforeLoad, this);
12960         ds.on("load", this.onLoad, this);
12961         ds.on("loadexception", this.onLoadError, this);
12962         ds.on("remove", this.updateInfo, this);
12963         ds.on("add", this.updateInfo, this);
12964         this.ds = ds;
12965     }
12966 });/*
12967  * Based on:
12968  * Ext JS Library 1.1.1
12969  * Copyright(c) 2006-2007, Ext JS, LLC.
12970  *
12971  * Originally Released Under LGPL - original licence link has changed is not relivant.
12972  *
12973  * Fork - LGPL
12974  * <script type="text/javascript">
12975  */
12976
12977 /**
12978  * @class Roo.Resizable
12979  * @extends Roo.util.Observable
12980  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
12981  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
12982  * 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
12983  * the element will be wrapped for you automatically.</p>
12984  * <p>Here is the list of valid resize handles:</p>
12985  * <pre>
12986 Value   Description
12987 ------  -------------------
12988  'n'     north
12989  's'     south
12990  'e'     east
12991  'w'     west
12992  'nw'    northwest
12993  'sw'    southwest
12994  'se'    southeast
12995  'ne'    northeast
12996  'all'   all
12997 </pre>
12998  * <p>Here's an example showing the creation of a typical Resizable:</p>
12999  * <pre><code>
13000 var resizer = new Roo.Resizable("element-id", {
13001     handles: 'all',
13002     minWidth: 200,
13003     minHeight: 100,
13004     maxWidth: 500,
13005     maxHeight: 400,
13006     pinned: true
13007 });
13008 resizer.on("resize", myHandler);
13009 </code></pre>
13010  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
13011  * resizer.east.setDisplayed(false);</p>
13012  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
13013  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
13014  * resize operation's new size (defaults to [0, 0])
13015  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
13016  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
13017  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
13018  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
13019  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
13020  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
13021  * @cfg {Number} width The width of the element in pixels (defaults to null)
13022  * @cfg {Number} height The height of the element in pixels (defaults to null)
13023  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
13024  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
13025  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
13026  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
13027  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
13028  * in favor of the handles config option (defaults to false)
13029  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
13030  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
13031  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
13032  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
13033  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
13034  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
13035  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
13036  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
13037  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
13038  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
13039  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
13040  * @constructor
13041  * Create a new resizable component
13042  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
13043  * @param {Object} config configuration options
13044   */
13045 Roo.Resizable = function(el, config){
13046     this.el = Roo.get(el);
13047
13048     if(config && config.wrap){
13049         config.resizeChild = this.el;
13050         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
13051         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
13052         this.el.setStyle("overflow", "hidden");
13053         this.el.setPositioning(config.resizeChild.getPositioning());
13054         config.resizeChild.clearPositioning();
13055         if(!config.width || !config.height){
13056             var csize = config.resizeChild.getSize();
13057             this.el.setSize(csize.width, csize.height);
13058         }
13059         if(config.pinned && !config.adjustments){
13060             config.adjustments = "auto";
13061         }
13062     }
13063
13064     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
13065     this.proxy.unselectable();
13066     this.proxy.enableDisplayMode('block');
13067
13068     Roo.apply(this, config);
13069
13070     if(this.pinned){
13071         this.disableTrackOver = true;
13072         this.el.addClass("x-resizable-pinned");
13073     }
13074     // if the element isn't positioned, make it relative
13075     var position = this.el.getStyle("position");
13076     if(position != "absolute" && position != "fixed"){
13077         this.el.setStyle("position", "relative");
13078     }
13079     if(!this.handles){ // no handles passed, must be legacy style
13080         this.handles = 's,e,se';
13081         if(this.multiDirectional){
13082             this.handles += ',n,w';
13083         }
13084     }
13085     if(this.handles == "all"){
13086         this.handles = "n s e w ne nw se sw";
13087     }
13088     var hs = this.handles.split(/\s*?[,;]\s*?| /);
13089     var ps = Roo.Resizable.positions;
13090     for(var i = 0, len = hs.length; i < len; i++){
13091         if(hs[i] && ps[hs[i]]){
13092             var pos = ps[hs[i]];
13093             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
13094         }
13095     }
13096     // legacy
13097     this.corner = this.southeast;
13098
13099     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1){
13100         this.updateBox = true;
13101     }
13102
13103     this.activeHandle = null;
13104
13105     if(this.resizeChild){
13106         if(typeof this.resizeChild == "boolean"){
13107             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
13108         }else{
13109             this.resizeChild = Roo.get(this.resizeChild, true);
13110         }
13111     }
13112
13113     if(this.adjustments == "auto"){
13114         var rc = this.resizeChild;
13115         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
13116         if(rc && (hw || hn)){
13117             rc.position("relative");
13118             rc.setLeft(hw ? hw.el.getWidth() : 0);
13119             rc.setTop(hn ? hn.el.getHeight() : 0);
13120         }
13121         this.adjustments = [
13122             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
13123             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
13124         ];
13125     }
13126
13127     if(this.draggable){
13128         this.dd = this.dynamic ?
13129             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
13130         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
13131     }
13132
13133     // public events
13134     this.addEvents({
13135         /**
13136          * @event beforeresize
13137          * Fired before resize is allowed. Set enabled to false to cancel resize.
13138          * @param {Roo.Resizable} this
13139          * @param {Roo.EventObject} e The mousedown event
13140          */
13141         "beforeresize" : true,
13142         /**
13143          * @event resize
13144          * Fired after a resize.
13145          * @param {Roo.Resizable} this
13146          * @param {Number} width The new width
13147          * @param {Number} height The new height
13148          * @param {Roo.EventObject} e The mouseup event
13149          */
13150         "resize" : true
13151     });
13152
13153     if(this.width !== null && this.height !== null){
13154         this.resizeTo(this.width, this.height);
13155     }else{
13156         this.updateChildSize();
13157     }
13158     if(Roo.isIE){
13159         this.el.dom.style.zoom = 1;
13160     }
13161     Roo.Resizable.superclass.constructor.call(this);
13162 };
13163
13164 Roo.extend(Roo.Resizable, Roo.util.Observable, {
13165         resizeChild : false,
13166         adjustments : [0, 0],
13167         minWidth : 5,
13168         minHeight : 5,
13169         maxWidth : 10000,
13170         maxHeight : 10000,
13171         enabled : true,
13172         animate : false,
13173         duration : .35,
13174         dynamic : false,
13175         handles : false,
13176         multiDirectional : false,
13177         disableTrackOver : false,
13178         easing : 'easeOutStrong',
13179         widthIncrement : 0,
13180         heightIncrement : 0,
13181         pinned : false,
13182         width : null,
13183         height : null,
13184         preserveRatio : false,
13185         transparent: false,
13186         minX: 0,
13187         minY: 0,
13188         draggable: false,
13189
13190         /**
13191          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
13192          */
13193         constrainTo: undefined,
13194         /**
13195          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
13196          */
13197         resizeRegion: undefined,
13198
13199
13200     /**
13201      * Perform a manual resize
13202      * @param {Number} width
13203      * @param {Number} height
13204      */
13205     resizeTo : function(width, height){
13206         this.el.setSize(width, height);
13207         this.updateChildSize();
13208         this.fireEvent("resize", this, width, height, null);
13209     },
13210
13211     // private
13212     startSizing : function(e, handle){
13213         this.fireEvent("beforeresize", this, e);
13214         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
13215
13216             if(!this.overlay){
13217                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
13218                 this.overlay.unselectable();
13219                 this.overlay.enableDisplayMode("block");
13220                 this.overlay.on("mousemove", this.onMouseMove, this);
13221                 this.overlay.on("mouseup", this.onMouseUp, this);
13222             }
13223             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
13224
13225             this.resizing = true;
13226             this.startBox = this.el.getBox();
13227             this.startPoint = e.getXY();
13228             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
13229                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
13230
13231             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13232             this.overlay.show();
13233
13234             if(this.constrainTo) {
13235                 var ct = Roo.get(this.constrainTo);
13236                 this.resizeRegion = ct.getRegion().adjust(
13237                     ct.getFrameWidth('t'),
13238                     ct.getFrameWidth('l'),
13239                     -ct.getFrameWidth('b'),
13240                     -ct.getFrameWidth('r')
13241                 );
13242             }
13243
13244             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
13245             this.proxy.show();
13246             this.proxy.setBox(this.startBox);
13247             if(!this.dynamic){
13248                 this.proxy.setStyle('visibility', 'visible');
13249             }
13250         }
13251     },
13252
13253     // private
13254     onMouseDown : function(handle, e){
13255         if(this.enabled){
13256             e.stopEvent();
13257             this.activeHandle = handle;
13258             this.startSizing(e, handle);
13259         }
13260     },
13261
13262     // private
13263     onMouseUp : function(e){
13264         var size = this.resizeElement();
13265         this.resizing = false;
13266         this.handleOut();
13267         this.overlay.hide();
13268         this.proxy.hide();
13269         this.fireEvent("resize", this, size.width, size.height, e);
13270     },
13271
13272     // private
13273     updateChildSize : function(){
13274         if(this.resizeChild){
13275             var el = this.el;
13276             var child = this.resizeChild;
13277             var adj = this.adjustments;
13278             if(el.dom.offsetWidth){
13279                 var b = el.getSize(true);
13280                 child.setSize(b.width+adj[0], b.height+adj[1]);
13281             }
13282             // Second call here for IE
13283             // The first call enables instant resizing and
13284             // the second call corrects scroll bars if they
13285             // exist
13286             if(Roo.isIE){
13287                 setTimeout(function(){
13288                     if(el.dom.offsetWidth){
13289                         var b = el.getSize(true);
13290                         child.setSize(b.width+adj[0], b.height+adj[1]);
13291                     }
13292                 }, 10);
13293             }
13294         }
13295     },
13296
13297     // private
13298     snap : function(value, inc, min){
13299         if(!inc || !value) return value;
13300         var newValue = value;
13301         var m = value % inc;
13302         if(m > 0){
13303             if(m > (inc/2)){
13304                 newValue = value + (inc-m);
13305             }else{
13306                 newValue = value - m;
13307             }
13308         }
13309         return Math.max(min, newValue);
13310     },
13311
13312     // private
13313     resizeElement : function(){
13314         var box = this.proxy.getBox();
13315         if(this.updateBox){
13316             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
13317         }else{
13318             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
13319         }
13320         this.updateChildSize();
13321         if(!this.dynamic){
13322             this.proxy.hide();
13323         }
13324         return box;
13325     },
13326
13327     // private
13328     constrain : function(v, diff, m, mx){
13329         if(v - diff < m){
13330             diff = v - m;
13331         }else if(v - diff > mx){
13332             diff = mx - v;
13333         }
13334         return diff;
13335     },
13336
13337     // private
13338     onMouseMove : function(e){
13339         if(this.enabled){
13340             try{// try catch so if something goes wrong the user doesn't get hung
13341
13342             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
13343                 return;
13344             }
13345
13346             //var curXY = this.startPoint;
13347             var curSize = this.curSize || this.startBox;
13348             var x = this.startBox.x, y = this.startBox.y;
13349             var ox = x, oy = y;
13350             var w = curSize.width, h = curSize.height;
13351             var ow = w, oh = h;
13352             var mw = this.minWidth, mh = this.minHeight;
13353             var mxw = this.maxWidth, mxh = this.maxHeight;
13354             var wi = this.widthIncrement;
13355             var hi = this.heightIncrement;
13356
13357             var eventXY = e.getXY();
13358             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
13359             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
13360
13361             var pos = this.activeHandle.position;
13362
13363             switch(pos){
13364                 case "east":
13365                     w += diffX;
13366                     w = Math.min(Math.max(mw, w), mxw);
13367                     break;
13368                 case "south":
13369                     h += diffY;
13370                     h = Math.min(Math.max(mh, h), mxh);
13371                     break;
13372                 case "southeast":
13373                     w += diffX;
13374                     h += diffY;
13375                     w = Math.min(Math.max(mw, w), mxw);
13376                     h = Math.min(Math.max(mh, h), mxh);
13377                     break;
13378                 case "north":
13379                     diffY = this.constrain(h, diffY, mh, mxh);
13380                     y += diffY;
13381                     h -= diffY;
13382                     break;
13383                 case "west":
13384                     diffX = this.constrain(w, diffX, mw, mxw);
13385                     x += diffX;
13386                     w -= diffX;
13387                     break;
13388                 case "northeast":
13389                     w += diffX;
13390                     w = Math.min(Math.max(mw, w), mxw);
13391                     diffY = this.constrain(h, diffY, mh, mxh);
13392                     y += diffY;
13393                     h -= diffY;
13394                     break;
13395                 case "northwest":
13396                     diffX = this.constrain(w, diffX, mw, mxw);
13397                     diffY = this.constrain(h, diffY, mh, mxh);
13398                     y += diffY;
13399                     h -= diffY;
13400                     x += diffX;
13401                     w -= diffX;
13402                     break;
13403                case "southwest":
13404                     diffX = this.constrain(w, diffX, mw, mxw);
13405                     h += diffY;
13406                     h = Math.min(Math.max(mh, h), mxh);
13407                     x += diffX;
13408                     w -= diffX;
13409                     break;
13410             }
13411
13412             var sw = this.snap(w, wi, mw);
13413             var sh = this.snap(h, hi, mh);
13414             if(sw != w || sh != h){
13415                 switch(pos){
13416                     case "northeast":
13417                         y -= sh - h;
13418                     break;
13419                     case "north":
13420                         y -= sh - h;
13421                         break;
13422                     case "southwest":
13423                         x -= sw - w;
13424                     break;
13425                     case "west":
13426                         x -= sw - w;
13427                         break;
13428                     case "northwest":
13429                         x -= sw - w;
13430                         y -= sh - h;
13431                     break;
13432                 }
13433                 w = sw;
13434                 h = sh;
13435             }
13436
13437             if(this.preserveRatio){
13438                 switch(pos){
13439                     case "southeast":
13440                     case "east":
13441                         h = oh * (w/ow);
13442                         h = Math.min(Math.max(mh, h), mxh);
13443                         w = ow * (h/oh);
13444                        break;
13445                     case "south":
13446                         w = ow * (h/oh);
13447                         w = Math.min(Math.max(mw, w), mxw);
13448                         h = oh * (w/ow);
13449                         break;
13450                     case "northeast":
13451                         w = ow * (h/oh);
13452                         w = Math.min(Math.max(mw, w), mxw);
13453                         h = oh * (w/ow);
13454                     break;
13455                     case "north":
13456                         var tw = w;
13457                         w = ow * (h/oh);
13458                         w = Math.min(Math.max(mw, w), mxw);
13459                         h = oh * (w/ow);
13460                         x += (tw - w) / 2;
13461                         break;
13462                     case "southwest":
13463                         h = oh * (w/ow);
13464                         h = Math.min(Math.max(mh, h), mxh);
13465                         var tw = w;
13466                         w = ow * (h/oh);
13467                         x += tw - w;
13468                         break;
13469                     case "west":
13470                         var th = h;
13471                         h = oh * (w/ow);
13472                         h = Math.min(Math.max(mh, h), mxh);
13473                         y += (th - h) / 2;
13474                         var tw = w;
13475                         w = ow * (h/oh);
13476                         x += tw - w;
13477                        break;
13478                     case "northwest":
13479                         var tw = w;
13480                         var th = h;
13481                         h = oh * (w/ow);
13482                         h = Math.min(Math.max(mh, h), mxh);
13483                         w = ow * (h/oh);
13484                         y += th - h;
13485                          x += tw - w;
13486                        break;
13487
13488                 }
13489             }
13490             this.proxy.setBounds(x, y, w, h);
13491             if(this.dynamic){
13492                 this.resizeElement();
13493             }
13494             }catch(e){}
13495         }
13496     },
13497
13498     // private
13499     handleOver : function(){
13500         if(this.enabled){
13501             this.el.addClass("x-resizable-over");
13502         }
13503     },
13504
13505     // private
13506     handleOut : function(){
13507         if(!this.resizing){
13508             this.el.removeClass("x-resizable-over");
13509         }
13510     },
13511
13512     /**
13513      * Returns the element this component is bound to.
13514      * @return {Roo.Element}
13515      */
13516     getEl : function(){
13517         return this.el;
13518     },
13519
13520     /**
13521      * Returns the resizeChild element (or null).
13522      * @return {Roo.Element}
13523      */
13524     getResizeChild : function(){
13525         return this.resizeChild;
13526     },
13527
13528     /**
13529      * Destroys this resizable. If the element was wrapped and
13530      * removeEl is not true then the element remains.
13531      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
13532      */
13533     destroy : function(removeEl){
13534         this.proxy.remove();
13535         if(this.overlay){
13536             this.overlay.removeAllListeners();
13537             this.overlay.remove();
13538         }
13539         var ps = Roo.Resizable.positions;
13540         for(var k in ps){
13541             if(typeof ps[k] != "function" && this[ps[k]]){
13542                 var h = this[ps[k]];
13543                 h.el.removeAllListeners();
13544                 h.el.remove();
13545             }
13546         }
13547         if(removeEl){
13548             this.el.update("");
13549             this.el.remove();
13550         }
13551     }
13552 });
13553
13554 // private
13555 // hash to map config positions to true positions
13556 Roo.Resizable.positions = {
13557     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast"
13558 };
13559
13560 // private
13561 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
13562     if(!this.tpl){
13563         // only initialize the template if resizable is used
13564         var tpl = Roo.DomHelper.createTemplate(
13565             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
13566         );
13567         tpl.compile();
13568         Roo.Resizable.Handle.prototype.tpl = tpl;
13569     }
13570     this.position = pos;
13571     this.rz = rz;
13572     this.el = this.tpl.append(rz.el.dom, [this.position], true);
13573     this.el.unselectable();
13574     if(transparent){
13575         this.el.setOpacity(0);
13576     }
13577     this.el.on("mousedown", this.onMouseDown, this);
13578     if(!disableTrackOver){
13579         this.el.on("mouseover", this.onMouseOver, this);
13580         this.el.on("mouseout", this.onMouseOut, this);
13581     }
13582 };
13583
13584 // private
13585 Roo.Resizable.Handle.prototype = {
13586     afterResize : function(rz){
13587         // do nothing
13588     },
13589     // private
13590     onMouseDown : function(e){
13591         this.rz.onMouseDown(this, e);
13592     },
13593     // private
13594     onMouseOver : function(e){
13595         this.rz.handleOver(this, e);
13596     },
13597     // private
13598     onMouseOut : function(e){
13599         this.rz.handleOut(this, e);
13600     }
13601 };/*
13602  * Based on:
13603  * Ext JS Library 1.1.1
13604  * Copyright(c) 2006-2007, Ext JS, LLC.
13605  *
13606  * Originally Released Under LGPL - original licence link has changed is not relivant.
13607  *
13608  * Fork - LGPL
13609  * <script type="text/javascript">
13610  */
13611
13612 /**
13613  * @class Roo.Editor
13614  * @extends Roo.Component
13615  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
13616  * @constructor
13617  * Create a new Editor
13618  * @param {Roo.form.Field} field The Field object (or descendant)
13619  * @param {Object} config The config object
13620  */
13621 Roo.Editor = function(field, config){
13622     Roo.Editor.superclass.constructor.call(this, config);
13623     this.field = field;
13624     this.addEvents({
13625         /**
13626              * @event beforestartedit
13627              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
13628              * false from the handler of this event.
13629              * @param {Editor} this
13630              * @param {Roo.Element} boundEl The underlying element bound to this editor
13631              * @param {Mixed} value The field value being set
13632              */
13633         "beforestartedit" : true,
13634         /**
13635              * @event startedit
13636              * Fires when this editor is displayed
13637              * @param {Roo.Element} boundEl The underlying element bound to this editor
13638              * @param {Mixed} value The starting field value
13639              */
13640         "startedit" : true,
13641         /**
13642              * @event beforecomplete
13643              * Fires after a change has been made to the field, but before the change is reflected in the underlying
13644              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
13645              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
13646              * event will not fire since no edit actually occurred.
13647              * @param {Editor} this
13648              * @param {Mixed} value The current field value
13649              * @param {Mixed} startValue The original field value
13650              */
13651         "beforecomplete" : true,
13652         /**
13653              * @event complete
13654              * Fires after editing is complete and any changed value has been written to the underlying field.
13655              * @param {Editor} this
13656              * @param {Mixed} value The current field value
13657              * @param {Mixed} startValue The original field value
13658              */
13659         "complete" : true,
13660         /**
13661          * @event specialkey
13662          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
13663          * {@link Roo.EventObject#getKey} to determine which key was pressed.
13664          * @param {Roo.form.Field} this
13665          * @param {Roo.EventObject} e The event object
13666          */
13667         "specialkey" : true
13668     });
13669 };
13670
13671 Roo.extend(Roo.Editor, Roo.Component, {
13672     /**
13673      * @cfg {Boolean/String} autosize
13674      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
13675      * or "height" to adopt the height only (defaults to false)
13676      */
13677     /**
13678      * @cfg {Boolean} revertInvalid
13679      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
13680      * validation fails (defaults to true)
13681      */
13682     /**
13683      * @cfg {Boolean} ignoreNoChange
13684      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
13685      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
13686      * will never be ignored.
13687      */
13688     /**
13689      * @cfg {Boolean} hideEl
13690      * False to keep the bound element visible while the editor is displayed (defaults to true)
13691      */
13692     /**
13693      * @cfg {Mixed} value
13694      * The data value of the underlying field (defaults to "")
13695      */
13696     value : "",
13697     /**
13698      * @cfg {String} alignment
13699      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
13700      */
13701     alignment: "c-c?",
13702     /**
13703      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
13704      * for bottom-right shadow (defaults to "frame")
13705      */
13706     shadow : "frame",
13707     /**
13708      * @cfg {Boolean} constrain True to constrain the editor to the viewport
13709      */
13710     constrain : false,
13711     /**
13712      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
13713      */
13714     completeOnEnter : false,
13715     /**
13716      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
13717      */
13718     cancelOnEsc : false,
13719     /**
13720      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
13721      */
13722     updateEl : false,
13723
13724     // private
13725     onRender : function(ct, position){
13726         this.el = new Roo.Layer({
13727             shadow: this.shadow,
13728             cls: "x-editor",
13729             parentEl : ct,
13730             shim : this.shim,
13731             shadowOffset:4,
13732             id: this.id,
13733             constrain: this.constrain
13734         });
13735         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
13736         if(this.field.msgTarget != 'title'){
13737             this.field.msgTarget = 'qtip';
13738         }
13739         this.field.render(this.el);
13740         if(Roo.isGecko){
13741             this.field.el.dom.setAttribute('autocomplete', 'off');
13742         }
13743         this.field.on("specialkey", this.onSpecialKey, this);
13744         if(this.swallowKeys){
13745             this.field.el.swallowEvent(['keydown','keypress']);
13746         }
13747         this.field.show();
13748         this.field.on("blur", this.onBlur, this);
13749         if(this.field.grow){
13750             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
13751         }
13752     },
13753
13754     onSpecialKey : function(field, e){
13755         if(this.completeOnEnter && e.getKey() == e.ENTER){
13756             e.stopEvent();
13757             this.completeEdit();
13758         }else if(this.cancelOnEsc && e.getKey() == e.ESC){
13759             this.cancelEdit();
13760         }else{
13761             this.fireEvent('specialkey', field, e);
13762         }
13763     },
13764
13765     /**
13766      * Starts the editing process and shows the editor.
13767      * @param {String/HTMLElement/Element} el The element to edit
13768      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
13769       * to the innerHTML of el.
13770      */
13771     startEdit : function(el, value){
13772         if(this.editing){
13773             this.completeEdit();
13774         }
13775         this.boundEl = Roo.get(el);
13776         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
13777         if(!this.rendered){
13778             this.render(this.parentEl || document.body);
13779         }
13780         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
13781             return;
13782         }
13783         this.startValue = v;
13784         this.field.setValue(v);
13785         if(this.autoSize){
13786             var sz = this.boundEl.getSize();
13787             switch(this.autoSize){
13788                 case "width":
13789                 this.setSize(sz.width,  "");
13790                 break;
13791                 case "height":
13792                 this.setSize("",  sz.height);
13793                 break;
13794                 default:
13795                 this.setSize(sz.width,  sz.height);
13796             }
13797         }
13798         this.el.alignTo(this.boundEl, this.alignment);
13799         this.editing = true;
13800         if(Roo.QuickTips){
13801             Roo.QuickTips.disable();
13802         }
13803         this.show();
13804     },
13805
13806     /**
13807      * Sets the height and width of this editor.
13808      * @param {Number} width The new width
13809      * @param {Number} height The new height
13810      */
13811     setSize : function(w, h){
13812         this.field.setSize(w, h);
13813         if(this.el){
13814             this.el.sync();
13815         }
13816     },
13817
13818     /**
13819      * Realigns the editor to the bound field based on the current alignment config value.
13820      */
13821     realign : function(){
13822         this.el.alignTo(this.boundEl, this.alignment);
13823     },
13824
13825     /**
13826      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
13827      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
13828      */
13829     completeEdit : function(remainVisible){
13830         if(!this.editing){
13831             return;
13832         }
13833         var v = this.getValue();
13834         if(this.revertInvalid !== false && !this.field.isValid()){
13835             v = this.startValue;
13836             this.cancelEdit(true);
13837         }
13838         if(String(v) === String(this.startValue) && this.ignoreNoChange){
13839             this.editing = false;
13840             this.hide();
13841             return;
13842         }
13843         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
13844             this.editing = false;
13845             if(this.updateEl && this.boundEl){
13846                 this.boundEl.update(v);
13847             }
13848             if(remainVisible !== true){
13849                 this.hide();
13850             }
13851             this.fireEvent("complete", this, v, this.startValue);
13852         }
13853     },
13854
13855     // private
13856     onShow : function(){
13857         this.el.show();
13858         if(this.hideEl !== false){
13859             this.boundEl.hide();
13860         }
13861         this.field.show();
13862         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
13863             this.fixIEFocus = true;
13864             this.deferredFocus.defer(50, this);
13865         }else{
13866             this.field.focus();
13867         }
13868         this.fireEvent("startedit", this.boundEl, this.startValue);
13869     },
13870
13871     deferredFocus : function(){
13872         if(this.editing){
13873             this.field.focus();
13874         }
13875     },
13876
13877     /**
13878      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
13879      * reverted to the original starting value.
13880      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
13881      * cancel (defaults to false)
13882      */
13883     cancelEdit : function(remainVisible){
13884         if(this.editing){
13885             this.setValue(this.startValue);
13886             if(remainVisible !== true){
13887                 this.hide();
13888             }
13889         }
13890     },
13891
13892     // private
13893     onBlur : function(){
13894         if(this.allowBlur !== true && this.editing){
13895             this.completeEdit();
13896         }
13897     },
13898
13899     // private
13900     onHide : function(){
13901         if(this.editing){
13902             this.completeEdit();
13903             return;
13904         }
13905         this.field.blur();
13906         if(this.field.collapse){
13907             this.field.collapse();
13908         }
13909         this.el.hide();
13910         if(this.hideEl !== false){
13911             this.boundEl.show();
13912         }
13913         if(Roo.QuickTips){
13914             Roo.QuickTips.enable();
13915         }
13916     },
13917
13918     /**
13919      * Sets the data value of the editor
13920      * @param {Mixed} value Any valid value supported by the underlying field
13921      */
13922     setValue : function(v){
13923         this.field.setValue(v);
13924     },
13925
13926     /**
13927      * Gets the data value of the editor
13928      * @return {Mixed} The data value
13929      */
13930     getValue : function(){
13931         return this.field.getValue();
13932     }
13933 });/*
13934  * Based on:
13935  * Ext JS Library 1.1.1
13936  * Copyright(c) 2006-2007, Ext JS, LLC.
13937  *
13938  * Originally Released Under LGPL - original licence link has changed is not relivant.
13939  *
13940  * Fork - LGPL
13941  * <script type="text/javascript">
13942  */
13943  
13944 /**
13945  * @class Roo.BasicDialog
13946  * @extends Roo.util.Observable
13947  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
13948  * <pre><code>
13949 var dlg = new Roo.BasicDialog("my-dlg", {
13950     height: 200,
13951     width: 300,
13952     minHeight: 100,
13953     minWidth: 150,
13954     modal: true,
13955     proxyDrag: true,
13956     shadow: true
13957 });
13958 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
13959 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
13960 dlg.addButton('Cancel', dlg.hide, dlg);
13961 dlg.show();
13962 </code></pre>
13963   <b>A Dialog should always be a direct child of the body element.</b>
13964  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
13965  * @cfg {String} title Default text to display in the title bar (defaults to null)
13966  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
13967  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
13968  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
13969  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
13970  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
13971  * (defaults to null with no animation)
13972  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
13973  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
13974  * property for valid values (defaults to 'all')
13975  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
13976  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
13977  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
13978  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
13979  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
13980  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
13981  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
13982  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
13983  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
13984  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
13985  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
13986  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
13987  * draggable = true (defaults to false)
13988  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
13989  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13990  * shadow (defaults to false)
13991  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
13992  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
13993  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
13994  * @cfg {Array} buttons Array of buttons
13995  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
13996  * @constructor
13997  * Create a new BasicDialog.
13998  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
13999  * @param {Object} config Configuration options
14000  */
14001 Roo.BasicDialog = function(el, config){
14002     this.el = Roo.get(el);
14003     var dh = Roo.DomHelper;
14004     if(!this.el && config && config.autoCreate){
14005         if(typeof config.autoCreate == "object"){
14006             if(!config.autoCreate.id){
14007                 config.autoCreate.id = el;
14008             }
14009             this.el = dh.append(document.body,
14010                         config.autoCreate, true);
14011         }else{
14012             this.el = dh.append(document.body,
14013                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
14014         }
14015     }
14016     el = this.el;
14017     el.setDisplayed(true);
14018     el.hide = this.hideAction;
14019     this.id = el.id;
14020     el.addClass("x-dlg");
14021
14022     Roo.apply(this, config);
14023
14024     this.proxy = el.createProxy("x-dlg-proxy");
14025     this.proxy.hide = this.hideAction;
14026     this.proxy.setOpacity(.5);
14027     this.proxy.hide();
14028
14029     if(config.width){
14030         el.setWidth(config.width);
14031     }
14032     if(config.height){
14033         el.setHeight(config.height);
14034     }
14035     this.size = el.getSize();
14036     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
14037         this.xy = [config.x,config.y];
14038     }else{
14039         this.xy = el.getCenterXY(true);
14040     }
14041     /** The header element @type Roo.Element */
14042     this.header = el.child("> .x-dlg-hd");
14043     /** The body element @type Roo.Element */
14044     this.body = el.child("> .x-dlg-bd");
14045     /** The footer element @type Roo.Element */
14046     this.footer = el.child("> .x-dlg-ft");
14047
14048     if(!this.header){
14049         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
14050     }
14051     if(!this.body){
14052         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
14053     }
14054
14055     this.header.unselectable();
14056     if(this.title){
14057         this.header.update(this.title);
14058     }
14059     // this element allows the dialog to be focused for keyboard event
14060     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
14061     this.focusEl.swallowEvent("click", true);
14062
14063     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
14064
14065     // wrap the body and footer for special rendering
14066     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
14067     if(this.footer){
14068         this.bwrap.dom.appendChild(this.footer.dom);
14069     }
14070
14071     this.bg = this.el.createChild({
14072         tag: "div", cls:"x-dlg-bg",
14073         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
14074     });
14075     this.centerBg = this.bg.child("div.x-dlg-bg-center");
14076
14077
14078     if(this.autoScroll !== false && !this.autoTabs){
14079         this.body.setStyle("overflow", "auto");
14080     }
14081
14082     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
14083
14084     if(this.closable !== false){
14085         this.el.addClass("x-dlg-closable");
14086         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
14087         this.close.on("click", this.closeClick, this);
14088         this.close.addClassOnOver("x-dlg-close-over");
14089     }
14090     if(this.collapsible !== false){
14091         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
14092         this.collapseBtn.on("click", this.collapseClick, this);
14093         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
14094         this.header.on("dblclick", this.collapseClick, this);
14095     }
14096     if(this.resizable !== false){
14097         this.el.addClass("x-dlg-resizable");
14098         this.resizer = new Roo.Resizable(el, {
14099             minWidth: this.minWidth || 80,
14100             minHeight:this.minHeight || 80,
14101             handles: this.resizeHandles || "all",
14102             pinned: true
14103         });
14104         this.resizer.on("beforeresize", this.beforeResize, this);
14105         this.resizer.on("resize", this.onResize, this);
14106     }
14107     if(this.draggable !== false){
14108         el.addClass("x-dlg-draggable");
14109         if (!this.proxyDrag) {
14110             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
14111         }
14112         else {
14113             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
14114         }
14115         dd.setHandleElId(this.header.id);
14116         dd.endDrag = this.endMove.createDelegate(this);
14117         dd.startDrag = this.startMove.createDelegate(this);
14118         dd.onDrag = this.onDrag.createDelegate(this);
14119         dd.scroll = false;
14120         this.dd = dd;
14121     }
14122     if(this.modal){
14123         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
14124         this.mask.enableDisplayMode("block");
14125         this.mask.hide();
14126         this.el.addClass("x-dlg-modal");
14127     }
14128     if(this.shadow){
14129         this.shadow = new Roo.Shadow({
14130             mode : typeof this.shadow == "string" ? this.shadow : "sides",
14131             offset : this.shadowOffset
14132         });
14133     }else{
14134         this.shadowOffset = 0;
14135     }
14136     if(Roo.useShims && this.shim !== false){
14137         this.shim = this.el.createShim();
14138         this.shim.hide = this.hideAction;
14139         this.shim.hide();
14140     }else{
14141         this.shim = false;
14142     }
14143     if(this.autoTabs){
14144         this.initTabs();
14145     }
14146     if (this.buttons) { 
14147         var bts= this.buttons;
14148         this.buttons = [];
14149         Roo.each(bts, function(b) {
14150             this.addButton(b);
14151         }, this);
14152     }
14153     
14154     
14155     this.addEvents({
14156         /**
14157          * @event keydown
14158          * Fires when a key is pressed
14159          * @param {Roo.BasicDialog} this
14160          * @param {Roo.EventObject} e
14161          */
14162         "keydown" : true,
14163         /**
14164          * @event move
14165          * Fires when this dialog is moved by the user.
14166          * @param {Roo.BasicDialog} this
14167          * @param {Number} x The new page X
14168          * @param {Number} y The new page Y
14169          */
14170         "move" : true,
14171         /**
14172          * @event resize
14173          * Fires when this dialog is resized by the user.
14174          * @param {Roo.BasicDialog} this
14175          * @param {Number} width The new width
14176          * @param {Number} height The new height
14177          */
14178         "resize" : true,
14179         /**
14180          * @event beforehide
14181          * Fires before this dialog is hidden.
14182          * @param {Roo.BasicDialog} this
14183          */
14184         "beforehide" : true,
14185         /**
14186          * @event hide
14187          * Fires when this dialog is hidden.
14188          * @param {Roo.BasicDialog} this
14189          */
14190         "hide" : true,
14191         /**
14192          * @event beforeshow
14193          * Fires before this dialog is shown.
14194          * @param {Roo.BasicDialog} this
14195          */
14196         "beforeshow" : true,
14197         /**
14198          * @event show
14199          * Fires when this dialog is shown.
14200          * @param {Roo.BasicDialog} this
14201          */
14202         "show" : true
14203     });
14204     el.on("keydown", this.onKeyDown, this);
14205     el.on("mousedown", this.toFront, this);
14206     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
14207     this.el.hide();
14208     Roo.DialogManager.register(this);
14209     Roo.BasicDialog.superclass.constructor.call(this);
14210 };
14211
14212 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
14213     shadowOffset: Roo.isIE ? 6 : 5,
14214     minHeight: 80,
14215     minWidth: 200,
14216     minButtonWidth: 75,
14217     defaultButton: null,
14218     buttonAlign: "right",
14219     tabTag: 'div',
14220     firstShow: true,
14221
14222     /**
14223      * Sets the dialog title text
14224      * @param {String} text The title text to display
14225      * @return {Roo.BasicDialog} this
14226      */
14227     setTitle : function(text){
14228         this.header.update(text);
14229         return this;
14230     },
14231
14232     // private
14233     closeClick : function(){
14234         this.hide();
14235     },
14236
14237     // private
14238     collapseClick : function(){
14239         this[this.collapsed ? "expand" : "collapse"]();
14240     },
14241
14242     /**
14243      * Collapses the dialog to its minimized state (only the title bar is visible).
14244      * Equivalent to the user clicking the collapse dialog button.
14245      */
14246     collapse : function(){
14247         if(!this.collapsed){
14248             this.collapsed = true;
14249             this.el.addClass("x-dlg-collapsed");
14250             this.restoreHeight = this.el.getHeight();
14251             this.resizeTo(this.el.getWidth(), this.header.getHeight());
14252         }
14253     },
14254
14255     /**
14256      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
14257      * clicking the expand dialog button.
14258      */
14259     expand : function(){
14260         if(this.collapsed){
14261             this.collapsed = false;
14262             this.el.removeClass("x-dlg-collapsed");
14263             this.resizeTo(this.el.getWidth(), this.restoreHeight);
14264         }
14265     },
14266
14267     /**
14268      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
14269      * @return {Roo.TabPanel} The tabs component
14270      */
14271     initTabs : function(){
14272         var tabs = this.getTabs();
14273         while(tabs.getTab(0)){
14274             tabs.removeTab(0);
14275         }
14276         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
14277             var dom = el.dom;
14278             tabs.addTab(Roo.id(dom), dom.title);
14279             dom.title = "";
14280         });
14281         tabs.activate(0);
14282         return tabs;
14283     },
14284
14285     // private
14286     beforeResize : function(){
14287         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
14288     },
14289
14290     // private
14291     onResize : function(){
14292         this.refreshSize();
14293         this.syncBodyHeight();
14294         this.adjustAssets();
14295         this.focus();
14296         this.fireEvent("resize", this, this.size.width, this.size.height);
14297     },
14298
14299     // private
14300     onKeyDown : function(e){
14301         if(this.isVisible()){
14302             this.fireEvent("keydown", this, e);
14303         }
14304     },
14305
14306     /**
14307      * Resizes the dialog.
14308      * @param {Number} width
14309      * @param {Number} height
14310      * @return {Roo.BasicDialog} this
14311      */
14312     resizeTo : function(width, height){
14313         this.el.setSize(width, height);
14314         this.size = {width: width, height: height};
14315         this.syncBodyHeight();
14316         if(this.fixedcenter){
14317             this.center();
14318         }
14319         if(this.isVisible()){
14320             this.constrainXY();
14321             this.adjustAssets();
14322         }
14323         this.fireEvent("resize", this, width, height);
14324         return this;
14325     },
14326
14327
14328     /**
14329      * Resizes the dialog to fit the specified content size.
14330      * @param {Number} width
14331      * @param {Number} height
14332      * @return {Roo.BasicDialog} this
14333      */
14334     setContentSize : function(w, h){
14335         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
14336         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
14337         //if(!this.el.isBorderBox()){
14338             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
14339             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
14340         //}
14341         if(this.tabs){
14342             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
14343             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
14344         }
14345         this.resizeTo(w, h);
14346         return this;
14347     },
14348
14349     /**
14350      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
14351      * executed in response to a particular key being pressed while the dialog is active.
14352      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
14353      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14354      * @param {Function} fn The function to call
14355      * @param {Object} scope (optional) The scope of the function
14356      * @return {Roo.BasicDialog} this
14357      */
14358     addKeyListener : function(key, fn, scope){
14359         var keyCode, shift, ctrl, alt;
14360         if(typeof key == "object" && !(key instanceof Array)){
14361             keyCode = key["key"];
14362             shift = key["shift"];
14363             ctrl = key["ctrl"];
14364             alt = key["alt"];
14365         }else{
14366             keyCode = key;
14367         }
14368         var handler = function(dlg, e){
14369             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14370                 var k = e.getKey();
14371                 if(keyCode instanceof Array){
14372                     for(var i = 0, len = keyCode.length; i < len; i++){
14373                         if(keyCode[i] == k){
14374                           fn.call(scope || window, dlg, k, e);
14375                           return;
14376                         }
14377                     }
14378                 }else{
14379                     if(k == keyCode){
14380                         fn.call(scope || window, dlg, k, e);
14381                     }
14382                 }
14383             }
14384         };
14385         this.on("keydown", handler);
14386         return this;
14387     },
14388
14389     /**
14390      * Returns the TabPanel component (creates it if it doesn't exist).
14391      * Note: If you wish to simply check for the existence of tabs without creating them,
14392      * check for a null 'tabs' property.
14393      * @return {Roo.TabPanel} The tabs component
14394      */
14395     getTabs : function(){
14396         if(!this.tabs){
14397             this.el.addClass("x-dlg-auto-tabs");
14398             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
14399             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
14400         }
14401         return this.tabs;
14402     },
14403
14404     /**
14405      * Adds a button to the footer section of the dialog.
14406      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
14407      * object or a valid Roo.DomHelper element config
14408      * @param {Function} handler The function called when the button is clicked
14409      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
14410      * @return {Roo.Button} The new button
14411      */
14412     addButton : function(config, handler, scope){
14413         var dh = Roo.DomHelper;
14414         if(!this.footer){
14415             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
14416         }
14417         if(!this.btnContainer){
14418             var tb = this.footer.createChild({
14419
14420                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
14421                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
14422             }, null, true);
14423             this.btnContainer = tb.firstChild.firstChild.firstChild;
14424         }
14425         var bconfig = {
14426             handler: handler,
14427             scope: scope,
14428             minWidth: this.minButtonWidth,
14429             hideParent:true
14430         };
14431         if(typeof config == "string"){
14432             bconfig.text = config;
14433         }else{
14434             if(config.tag){
14435                 bconfig.dhconfig = config;
14436             }else{
14437                 Roo.apply(bconfig, config);
14438             }
14439         }
14440         var fc = false;
14441         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
14442             bconfig.position = Math.max(0, bconfig.position);
14443             fc = this.btnContainer.childNodes[bconfig.position];
14444         }
14445          
14446         var btn = new Roo.Button(
14447             fc ? 
14448                 this.btnContainer.insertBefore(document.createElement("td"),fc)
14449                 : this.btnContainer.appendChild(document.createElement("td")),
14450             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
14451             bconfig
14452         );
14453         this.syncBodyHeight();
14454         if(!this.buttons){
14455             /**
14456              * Array of all the buttons that have been added to this dialog via addButton
14457              * @type Array
14458              */
14459             this.buttons = [];
14460         }
14461         this.buttons.push(btn);
14462         return btn;
14463     },
14464
14465     /**
14466      * Sets the default button to be focused when the dialog is displayed.
14467      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
14468      * @return {Roo.BasicDialog} this
14469      */
14470     setDefaultButton : function(btn){
14471         this.defaultButton = btn;
14472         return this;
14473     },
14474
14475     // private
14476     getHeaderFooterHeight : function(safe){
14477         var height = 0;
14478         if(this.header){
14479            height += this.header.getHeight();
14480         }
14481         if(this.footer){
14482            var fm = this.footer.getMargins();
14483             height += (this.footer.getHeight()+fm.top+fm.bottom);
14484         }
14485         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
14486         height += this.centerBg.getPadding("tb");
14487         return height;
14488     },
14489
14490     // private
14491     syncBodyHeight : function(){
14492         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
14493         var height = this.size.height - this.getHeaderFooterHeight(false);
14494         bd.setHeight(height-bd.getMargins("tb"));
14495         var hh = this.header.getHeight();
14496         var h = this.size.height-hh;
14497         cb.setHeight(h);
14498         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
14499         bw.setHeight(h-cb.getPadding("tb"));
14500         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
14501         bd.setWidth(bw.getWidth(true));
14502         if(this.tabs){
14503             this.tabs.syncHeight();
14504             if(Roo.isIE){
14505                 this.tabs.el.repaint();
14506             }
14507         }
14508     },
14509
14510     /**
14511      * Restores the previous state of the dialog if Roo.state is configured.
14512      * @return {Roo.BasicDialog} this
14513      */
14514     restoreState : function(){
14515         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
14516         if(box && box.width){
14517             this.xy = [box.x, box.y];
14518             this.resizeTo(box.width, box.height);
14519         }
14520         return this;
14521     },
14522
14523     // private
14524     beforeShow : function(){
14525         this.expand();
14526         if(this.fixedcenter){
14527             this.xy = this.el.getCenterXY(true);
14528         }
14529         if(this.modal){
14530             Roo.get(document.body).addClass("x-body-masked");
14531             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14532             this.mask.show();
14533         }
14534         this.constrainXY();
14535     },
14536
14537     // private
14538     animShow : function(){
14539         var b = Roo.get(this.animateTarget, true).getBox();
14540         this.proxy.setSize(b.width, b.height);
14541         this.proxy.setLocation(b.x, b.y);
14542         this.proxy.show();
14543         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
14544                     true, .35, this.showEl.createDelegate(this));
14545     },
14546
14547     /**
14548      * Shows the dialog.
14549      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
14550      * @return {Roo.BasicDialog} this
14551      */
14552     show : function(animateTarget){
14553         if (this.fireEvent("beforeshow", this) === false){
14554             return;
14555         }
14556         if(this.syncHeightBeforeShow){
14557             this.syncBodyHeight();
14558         }else if(this.firstShow){
14559             this.firstShow = false;
14560             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
14561         }
14562         this.animateTarget = animateTarget || this.animateTarget;
14563         if(!this.el.isVisible()){
14564             this.beforeShow();
14565             if(this.animateTarget){
14566                 this.animShow();
14567             }else{
14568                 this.showEl();
14569             }
14570         }
14571         return this;
14572     },
14573
14574     // private
14575     showEl : function(){
14576         this.proxy.hide();
14577         this.el.setXY(this.xy);
14578         this.el.show();
14579         this.adjustAssets(true);
14580         this.toFront();
14581         this.focus();
14582         // IE peekaboo bug - fix found by Dave Fenwick
14583         if(Roo.isIE){
14584             this.el.repaint();
14585         }
14586         this.fireEvent("show", this);
14587     },
14588
14589     /**
14590      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
14591      * dialog itself will receive focus.
14592      */
14593     focus : function(){
14594         if(this.defaultButton){
14595             this.defaultButton.focus();
14596         }else{
14597             this.focusEl.focus();
14598         }
14599     },
14600
14601     // private
14602     constrainXY : function(){
14603         if(this.constraintoviewport !== false){
14604             if(!this.viewSize){
14605                 if(this.container){
14606                     var s = this.container.getSize();
14607                     this.viewSize = [s.width, s.height];
14608                 }else{
14609                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
14610                 }
14611             }
14612             var s = Roo.get(this.container||document).getScroll();
14613
14614             var x = this.xy[0], y = this.xy[1];
14615             var w = this.size.width, h = this.size.height;
14616             var vw = this.viewSize[0], vh = this.viewSize[1];
14617             // only move it if it needs it
14618             var moved = false;
14619             // first validate right/bottom
14620             if(x + w > vw+s.left){
14621                 x = vw - w;
14622                 moved = true;
14623             }
14624             if(y + h > vh+s.top){
14625                 y = vh - h;
14626                 moved = true;
14627             }
14628             // then make sure top/left isn't negative
14629             if(x < s.left){
14630                 x = s.left;
14631                 moved = true;
14632             }
14633             if(y < s.top){
14634                 y = s.top;
14635                 moved = true;
14636             }
14637             if(moved){
14638                 // cache xy
14639                 this.xy = [x, y];
14640                 if(this.isVisible()){
14641                     this.el.setLocation(x, y);
14642                     this.adjustAssets();
14643                 }
14644             }
14645         }
14646     },
14647
14648     // private
14649     onDrag : function(){
14650         if(!this.proxyDrag){
14651             this.xy = this.el.getXY();
14652             this.adjustAssets();
14653         }
14654     },
14655
14656     // private
14657     adjustAssets : function(doShow){
14658         var x = this.xy[0], y = this.xy[1];
14659         var w = this.size.width, h = this.size.height;
14660         if(doShow === true){
14661             if(this.shadow){
14662                 this.shadow.show(this.el);
14663             }
14664             if(this.shim){
14665                 this.shim.show();
14666             }
14667         }
14668         if(this.shadow && this.shadow.isVisible()){
14669             this.shadow.show(this.el);
14670         }
14671         if(this.shim && this.shim.isVisible()){
14672             this.shim.setBounds(x, y, w, h);
14673         }
14674     },
14675
14676     // private
14677     adjustViewport : function(w, h){
14678         if(!w || !h){
14679             w = Roo.lib.Dom.getViewWidth();
14680             h = Roo.lib.Dom.getViewHeight();
14681         }
14682         // cache the size
14683         this.viewSize = [w, h];
14684         if(this.modal && this.mask.isVisible()){
14685             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
14686             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14687         }
14688         if(this.isVisible()){
14689             this.constrainXY();
14690         }
14691     },
14692
14693     /**
14694      * Destroys this dialog and all its supporting elements (including any tabs, shim,
14695      * shadow, proxy, mask, etc.)  Also removes all event listeners.
14696      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
14697      */
14698     destroy : function(removeEl){
14699         if(this.isVisible()){
14700             this.animateTarget = null;
14701             this.hide();
14702         }
14703         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
14704         if(this.tabs){
14705             this.tabs.destroy(removeEl);
14706         }
14707         Roo.destroy(
14708              this.shim,
14709              this.proxy,
14710              this.resizer,
14711              this.close,
14712              this.mask
14713         );
14714         if(this.dd){
14715             this.dd.unreg();
14716         }
14717         if(this.buttons){
14718            for(var i = 0, len = this.buttons.length; i < len; i++){
14719                this.buttons[i].destroy();
14720            }
14721         }
14722         this.el.removeAllListeners();
14723         if(removeEl === true){
14724             this.el.update("");
14725             this.el.remove();
14726         }
14727         Roo.DialogManager.unregister(this);
14728     },
14729
14730     // private
14731     startMove : function(){
14732         if(this.proxyDrag){
14733             this.proxy.show();
14734         }
14735         if(this.constraintoviewport !== false){
14736             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
14737         }
14738     },
14739
14740     // private
14741     endMove : function(){
14742         if(!this.proxyDrag){
14743             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
14744         }else{
14745             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
14746             this.proxy.hide();
14747         }
14748         this.refreshSize();
14749         this.adjustAssets();
14750         this.focus();
14751         this.fireEvent("move", this, this.xy[0], this.xy[1]);
14752     },
14753
14754     /**
14755      * Brings this dialog to the front of any other visible dialogs
14756      * @return {Roo.BasicDialog} this
14757      */
14758     toFront : function(){
14759         Roo.DialogManager.bringToFront(this);
14760         return this;
14761     },
14762
14763     /**
14764      * Sends this dialog to the back (under) of any other visible dialogs
14765      * @return {Roo.BasicDialog} this
14766      */
14767     toBack : function(){
14768         Roo.DialogManager.sendToBack(this);
14769         return this;
14770     },
14771
14772     /**
14773      * Centers this dialog in the viewport
14774      * @return {Roo.BasicDialog} this
14775      */
14776     center : function(){
14777         var xy = this.el.getCenterXY(true);
14778         this.moveTo(xy[0], xy[1]);
14779         return this;
14780     },
14781
14782     /**
14783      * Moves the dialog's top-left corner to the specified point
14784      * @param {Number} x
14785      * @param {Number} y
14786      * @return {Roo.BasicDialog} this
14787      */
14788     moveTo : function(x, y){
14789         this.xy = [x,y];
14790         if(this.isVisible()){
14791             this.el.setXY(this.xy);
14792             this.adjustAssets();
14793         }
14794         return this;
14795     },
14796
14797     /**
14798      * Aligns the dialog to the specified element
14799      * @param {String/HTMLElement/Roo.Element} element The element to align to.
14800      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
14801      * @param {Array} offsets (optional) Offset the positioning by [x, y]
14802      * @return {Roo.BasicDialog} this
14803      */
14804     alignTo : function(element, position, offsets){
14805         this.xy = this.el.getAlignToXY(element, position, offsets);
14806         if(this.isVisible()){
14807             this.el.setXY(this.xy);
14808             this.adjustAssets();
14809         }
14810         return this;
14811     },
14812
14813     /**
14814      * Anchors an element to another element and realigns it when the window is resized.
14815      * @param {String/HTMLElement/Roo.Element} element The element to align to.
14816      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
14817      * @param {Array} offsets (optional) Offset the positioning by [x, y]
14818      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
14819      * is a number, it is used as the buffer delay (defaults to 50ms).
14820      * @return {Roo.BasicDialog} this
14821      */
14822     anchorTo : function(el, alignment, offsets, monitorScroll){
14823         var action = function(){
14824             this.alignTo(el, alignment, offsets);
14825         };
14826         Roo.EventManager.onWindowResize(action, this);
14827         var tm = typeof monitorScroll;
14828         if(tm != 'undefined'){
14829             Roo.EventManager.on(window, 'scroll', action, this,
14830                 {buffer: tm == 'number' ? monitorScroll : 50});
14831         }
14832         action.call(this);
14833         return this;
14834     },
14835
14836     /**
14837      * Returns true if the dialog is visible
14838      * @return {Boolean}
14839      */
14840     isVisible : function(){
14841         return this.el.isVisible();
14842     },
14843
14844     // private
14845     animHide : function(callback){
14846         var b = Roo.get(this.animateTarget).getBox();
14847         this.proxy.show();
14848         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
14849         this.el.hide();
14850         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
14851                     this.hideEl.createDelegate(this, [callback]));
14852     },
14853
14854     /**
14855      * Hides the dialog.
14856      * @param {Function} callback (optional) Function to call when the dialog is hidden
14857      * @return {Roo.BasicDialog} this
14858      */
14859     hide : function(callback){
14860         if (this.fireEvent("beforehide", this) === false){
14861             return;
14862         }
14863         if(this.shadow){
14864             this.shadow.hide();
14865         }
14866         if(this.shim) {
14867           this.shim.hide();
14868         }
14869         if(this.animateTarget){
14870            this.animHide(callback);
14871         }else{
14872             this.el.hide();
14873             this.hideEl(callback);
14874         }
14875         return this;
14876     },
14877
14878     // private
14879     hideEl : function(callback){
14880         this.proxy.hide();
14881         if(this.modal){
14882             this.mask.hide();
14883             Roo.get(document.body).removeClass("x-body-masked");
14884         }
14885         this.fireEvent("hide", this);
14886         if(typeof callback == "function"){
14887             callback();
14888         }
14889     },
14890
14891     // private
14892     hideAction : function(){
14893         this.setLeft("-10000px");
14894         this.setTop("-10000px");
14895         this.setStyle("visibility", "hidden");
14896     },
14897
14898     // private
14899     refreshSize : function(){
14900         this.size = this.el.getSize();
14901         this.xy = this.el.getXY();
14902         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
14903     },
14904
14905     // private
14906     // z-index is managed by the DialogManager and may be overwritten at any time
14907     setZIndex : function(index){
14908         if(this.modal){
14909             this.mask.setStyle("z-index", index);
14910         }
14911         if(this.shim){
14912             this.shim.setStyle("z-index", ++index);
14913         }
14914         if(this.shadow){
14915             this.shadow.setZIndex(++index);
14916         }
14917         this.el.setStyle("z-index", ++index);
14918         if(this.proxy){
14919             this.proxy.setStyle("z-index", ++index);
14920         }
14921         if(this.resizer){
14922             this.resizer.proxy.setStyle("z-index", ++index);
14923         }
14924
14925         this.lastZIndex = index;
14926     },
14927
14928     /**
14929      * Returns the element for this dialog
14930      * @return {Roo.Element} The underlying dialog Element
14931      */
14932     getEl : function(){
14933         return this.el;
14934     }
14935 });
14936
14937 /**
14938  * @class Roo.DialogManager
14939  * Provides global access to BasicDialogs that have been created and
14940  * support for z-indexing (layering) multiple open dialogs.
14941  */
14942 Roo.DialogManager = function(){
14943     var list = {};
14944     var accessList = [];
14945     var front = null;
14946
14947     // private
14948     var sortDialogs = function(d1, d2){
14949         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
14950     };
14951
14952     // private
14953     var orderDialogs = function(){
14954         accessList.sort(sortDialogs);
14955         var seed = Roo.DialogManager.zseed;
14956         for(var i = 0, len = accessList.length; i < len; i++){
14957             var dlg = accessList[i];
14958             if(dlg){
14959                 dlg.setZIndex(seed + (i*10));
14960             }
14961         }
14962     };
14963
14964     return {
14965         /**
14966          * The starting z-index for BasicDialogs (defaults to 9000)
14967          * @type Number The z-index value
14968          */
14969         zseed : 9000,
14970
14971         // private
14972         register : function(dlg){
14973             list[dlg.id] = dlg;
14974             accessList.push(dlg);
14975         },
14976
14977         // private
14978         unregister : function(dlg){
14979             delete list[dlg.id];
14980             var i=0;
14981             var len=0;
14982             if(!accessList.indexOf){
14983                 for(  i = 0, len = accessList.length; i < len; i++){
14984                     if(accessList[i] == dlg){
14985                         accessList.splice(i, 1);
14986                         return;
14987                     }
14988                 }
14989             }else{
14990                  i = accessList.indexOf(dlg);
14991                 if(i != -1){
14992                     accessList.splice(i, 1);
14993                 }
14994             }
14995         },
14996
14997         /**
14998          * Gets a registered dialog by id
14999          * @param {String/Object} id The id of the dialog or a dialog
15000          * @return {Roo.BasicDialog} this
15001          */
15002         get : function(id){
15003             return typeof id == "object" ? id : list[id];
15004         },
15005
15006         /**
15007          * Brings the specified dialog to the front
15008          * @param {String/Object} dlg The id of the dialog or a dialog
15009          * @return {Roo.BasicDialog} this
15010          */
15011         bringToFront : function(dlg){
15012             dlg = this.get(dlg);
15013             if(dlg != front){
15014                 front = dlg;
15015                 dlg._lastAccess = new Date().getTime();
15016                 orderDialogs();
15017             }
15018             return dlg;
15019         },
15020
15021         /**
15022          * Sends the specified dialog to the back
15023          * @param {String/Object} dlg The id of the dialog or a dialog
15024          * @return {Roo.BasicDialog} this
15025          */
15026         sendToBack : function(dlg){
15027             dlg = this.get(dlg);
15028             dlg._lastAccess = -(new Date().getTime());
15029             orderDialogs();
15030             return dlg;
15031         },
15032
15033         /**
15034          * Hides all dialogs
15035          */
15036         hideAll : function(){
15037             for(var id in list){
15038                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
15039                     list[id].hide();
15040                 }
15041             }
15042         }
15043     };
15044 }();
15045
15046 /**
15047  * @class Roo.LayoutDialog
15048  * @extends Roo.BasicDialog
15049  * Dialog which provides adjustments for working with a layout in a Dialog.
15050  * Add your necessary layout config options to the dialog's config.<br>
15051  * Example usage (including a nested layout):
15052  * <pre><code>
15053 if(!dialog){
15054     dialog = new Roo.LayoutDialog("download-dlg", {
15055         modal: true,
15056         width:600,
15057         height:450,
15058         shadow:true,
15059         minWidth:500,
15060         minHeight:350,
15061         autoTabs:true,
15062         proxyDrag:true,
15063         // layout config merges with the dialog config
15064         center:{
15065             tabPosition: "top",
15066             alwaysShowTabs: true
15067         }
15068     });
15069     dialog.addKeyListener(27, dialog.hide, dialog);
15070     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
15071     dialog.addButton("Build It!", this.getDownload, this);
15072
15073     // we can even add nested layouts
15074     var innerLayout = new Roo.BorderLayout("dl-inner", {
15075         east: {
15076             initialSize: 200,
15077             autoScroll:true,
15078             split:true
15079         },
15080         center: {
15081             autoScroll:true
15082         }
15083     });
15084     innerLayout.beginUpdate();
15085     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
15086     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
15087     innerLayout.endUpdate(true);
15088
15089     var layout = dialog.getLayout();
15090     layout.beginUpdate();
15091     layout.add("center", new Roo.ContentPanel("standard-panel",
15092                         {title: "Download the Source", fitToFrame:true}));
15093     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
15094                {title: "Build your own roo.js"}));
15095     layout.getRegion("center").showPanel(sp);
15096     layout.endUpdate();
15097 }
15098 </code></pre>
15099     * @constructor
15100     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
15101     * @param {Object} config configuration options
15102   */
15103 Roo.LayoutDialog = function(el, cfg){
15104     
15105     var config=  cfg;
15106     if (typeof(cfg) == 'undefined') {
15107         config = Roo.apply({}, el);
15108         el = Roo.get( document.documentElement || document.body).createChild();
15109         //config.autoCreate = true;
15110     }
15111     
15112     
15113     config.autoTabs = false;
15114     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
15115     this.body.setStyle({overflow:"hidden", position:"relative"});
15116     this.layout = new Roo.BorderLayout(this.body.dom, config);
15117     this.layout.monitorWindowResize = false;
15118     this.el.addClass("x-dlg-auto-layout");
15119     // fix case when center region overwrites center function
15120     this.center = Roo.BasicDialog.prototype.center;
15121     this.on("show", this.layout.layout, this.layout, true);
15122     if (config.items) {
15123         var xitems = config.items;
15124         delete config.items;
15125         Roo.each(xitems, this.addxtype, this);
15126     }
15127     
15128     
15129 };
15130 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
15131     /**
15132      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
15133      * @deprecated
15134      */
15135     endUpdate : function(){
15136         this.layout.endUpdate();
15137     },
15138
15139     /**
15140      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
15141      *  @deprecated
15142      */
15143     beginUpdate : function(){
15144         this.layout.beginUpdate();
15145     },
15146
15147     /**
15148      * Get the BorderLayout for this dialog
15149      * @return {Roo.BorderLayout}
15150      */
15151     getLayout : function(){
15152         return this.layout;
15153     },
15154
15155     showEl : function(){
15156         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
15157         if(Roo.isIE7){
15158             this.layout.layout();
15159         }
15160     },
15161
15162     // private
15163     // Use the syncHeightBeforeShow config option to control this automatically
15164     syncBodyHeight : function(){
15165         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
15166         if(this.layout){this.layout.layout();}
15167     },
15168     
15169       /**
15170      * Add an xtype element (actually adds to the layout.)
15171      * @return {Object} xdata xtype object data.
15172      */
15173     
15174     addxtype : function(c) {
15175         return this.layout.addxtype(c);
15176     }
15177 });/*
15178  * Based on:
15179  * Ext JS Library 1.1.1
15180  * Copyright(c) 2006-2007, Ext JS, LLC.
15181  *
15182  * Originally Released Under LGPL - original licence link has changed is not relivant.
15183  *
15184  * Fork - LGPL
15185  * <script type="text/javascript">
15186  */
15187  
15188 /**
15189  * @class Roo.MessageBox
15190  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
15191  * Example usage:
15192  *<pre><code>
15193 // Basic alert:
15194 Roo.Msg.alert('Status', 'Changes saved successfully.');
15195
15196 // Prompt for user data:
15197 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
15198     if (btn == 'ok'){
15199         // process text value...
15200     }
15201 });
15202
15203 // Show a dialog using config options:
15204 Roo.Msg.show({
15205    title:'Save Changes?',
15206    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
15207    buttons: Roo.Msg.YESNOCANCEL,
15208    fn: processResult,
15209    animEl: 'elId'
15210 });
15211 </code></pre>
15212  * @singleton
15213  */
15214 Roo.MessageBox = function(){
15215     var dlg, opt, mask, waitTimer;
15216     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
15217     var buttons, activeTextEl, bwidth;
15218
15219     // private
15220     var handleButton = function(button){
15221         dlg.hide();
15222         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
15223     };
15224
15225     // private
15226     var handleHide = function(){
15227         if(opt && opt.cls){
15228             dlg.el.removeClass(opt.cls);
15229         }
15230         if(waitTimer){
15231             Roo.TaskMgr.stop(waitTimer);
15232             waitTimer = null;
15233         }
15234     };
15235
15236     // private
15237     var updateButtons = function(b){
15238         var width = 0;
15239         if(!b){
15240             buttons["ok"].hide();
15241             buttons["cancel"].hide();
15242             buttons["yes"].hide();
15243             buttons["no"].hide();
15244             dlg.footer.dom.style.display = 'none';
15245             return width;
15246         }
15247         dlg.footer.dom.style.display = '';
15248         for(var k in buttons){
15249             if(typeof buttons[k] != "function"){
15250                 if(b[k]){
15251                     buttons[k].show();
15252                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
15253                     width += buttons[k].el.getWidth()+15;
15254                 }else{
15255                     buttons[k].hide();
15256                 }
15257             }
15258         }
15259         return width;
15260     };
15261
15262     // private
15263     var handleEsc = function(d, k, e){
15264         if(opt && opt.closable !== false){
15265             dlg.hide();
15266         }
15267         if(e){
15268             e.stopEvent();
15269         }
15270     };
15271
15272     return {
15273         /**
15274          * Returns a reference to the underlying {@link Roo.BasicDialog} element
15275          * @return {Roo.BasicDialog} The BasicDialog element
15276          */
15277         getDialog : function(){
15278            if(!dlg){
15279                 dlg = new Roo.BasicDialog("x-msg-box", {
15280                     autoCreate : true,
15281                     shadow: true,
15282                     draggable: true,
15283                     resizable:false,
15284                     constraintoviewport:false,
15285                     fixedcenter:true,
15286                     collapsible : false,
15287                     shim:true,
15288                     modal: true,
15289                     width:400, height:100,
15290                     buttonAlign:"center",
15291                     closeClick : function(){
15292                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
15293                             handleButton("no");
15294                         }else{
15295                             handleButton("cancel");
15296                         }
15297                     }
15298                 });
15299                 dlg.on("hide", handleHide);
15300                 mask = dlg.mask;
15301                 dlg.addKeyListener(27, handleEsc);
15302                 buttons = {};
15303                 var bt = this.buttonText;
15304                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
15305                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
15306                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
15307                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
15308                 bodyEl = dlg.body.createChild({
15309
15310                     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>'
15311                 });
15312                 msgEl = bodyEl.dom.firstChild;
15313                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
15314                 textboxEl.enableDisplayMode();
15315                 textboxEl.addKeyListener([10,13], function(){
15316                     if(dlg.isVisible() && opt && opt.buttons){
15317                         if(opt.buttons.ok){
15318                             handleButton("ok");
15319                         }else if(opt.buttons.yes){
15320                             handleButton("yes");
15321                         }
15322                     }
15323                 });
15324                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
15325                 textareaEl.enableDisplayMode();
15326                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
15327                 progressEl.enableDisplayMode();
15328                 var pf = progressEl.dom.firstChild;
15329                 if (pf) {
15330                     pp = Roo.get(pf.firstChild);
15331                     pp.setHeight(pf.offsetHeight);
15332                 }
15333                 
15334             }
15335             return dlg;
15336         },
15337
15338         /**
15339          * Updates the message box body text
15340          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
15341          * the XHTML-compliant non-breaking space character '&amp;#160;')
15342          * @return {Roo.MessageBox} This message box
15343          */
15344         updateText : function(text){
15345             if(!dlg.isVisible() && !opt.width){
15346                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
15347             }
15348             msgEl.innerHTML = text || '&#160;';
15349             var w = Math.max(Math.min(opt.width || msgEl.offsetWidth, this.maxWidth), 
15350                         Math.max(opt.minWidth || this.minWidth, bwidth));
15351             if(opt.prompt){
15352                 activeTextEl.setWidth(w);
15353             }
15354             if(dlg.isVisible()){
15355                 dlg.fixedcenter = false;
15356             }
15357             dlg.setContentSize(w, bodyEl.getHeight());
15358             if(dlg.isVisible()){
15359                 dlg.fixedcenter = true;
15360             }
15361             return this;
15362         },
15363
15364         /**
15365          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
15366          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
15367          * @param {Number} value Any number between 0 and 1 (e.g., .5)
15368          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
15369          * @return {Roo.MessageBox} This message box
15370          */
15371         updateProgress : function(value, text){
15372             if(text){
15373                 this.updateText(text);
15374             }
15375             if (pp) { // weird bug on my firefox - for some reason this is not defined
15376                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
15377             }
15378             return this;
15379         },        
15380
15381         /**
15382          * Returns true if the message box is currently displayed
15383          * @return {Boolean} True if the message box is visible, else false
15384          */
15385         isVisible : function(){
15386             return dlg && dlg.isVisible();  
15387         },
15388
15389         /**
15390          * Hides the message box if it is displayed
15391          */
15392         hide : function(){
15393             if(this.isVisible()){
15394                 dlg.hide();
15395             }  
15396         },
15397
15398         /**
15399          * Displays a new message box, or reinitializes an existing message box, based on the config options
15400          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
15401          * The following config object properties are supported:
15402          * <pre>
15403 Property    Type             Description
15404 ----------  ---------------  ------------------------------------------------------------------------------------
15405 animEl            String/Element   An id or Element from which the message box should animate as it opens and
15406                                    closes (defaults to undefined)
15407 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
15408                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
15409 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
15410                                    progress and wait dialogs will ignore this property and always hide the
15411                                    close button as they can only be closed programmatically.
15412 cls               String           A custom CSS class to apply to the message box element
15413 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
15414                                    displayed (defaults to 75)
15415 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
15416                                    function will be btn (the name of the button that was clicked, if applicable,
15417                                    e.g. "ok"), and text (the value of the active text field, if applicable).
15418                                    Progress and wait dialogs will ignore this option since they do not respond to
15419                                    user actions and can only be closed programmatically, so any required function
15420                                    should be called by the same code after it closes the dialog.
15421 icon              String           A CSS class that provides a background image to be used as an icon for
15422                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
15423 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
15424 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
15425 modal             Boolean          False to allow user interaction with the page while the message box is
15426                                    displayed (defaults to true)
15427 msg               String           A string that will replace the existing message box body text (defaults
15428                                    to the XHTML-compliant non-breaking space character '&#160;')
15429 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
15430 progress          Boolean          True to display a progress bar (defaults to false)
15431 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
15432 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
15433 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
15434 title             String           The title text
15435 value             String           The string value to set into the active textbox element if displayed
15436 wait              Boolean          True to display a progress bar (defaults to false)
15437 width             Number           The width of the dialog in pixels
15438 </pre>
15439          *
15440          * Example usage:
15441          * <pre><code>
15442 Roo.Msg.show({
15443    title: 'Address',
15444    msg: 'Please enter your address:',
15445    width: 300,
15446    buttons: Roo.MessageBox.OKCANCEL,
15447    multiline: true,
15448    fn: saveAddress,
15449    animEl: 'addAddressBtn'
15450 });
15451 </code></pre>
15452          * @param {Object} config Configuration options
15453          * @return {Roo.MessageBox} This message box
15454          */
15455         show : function(options){
15456             if(this.isVisible()){
15457                 this.hide();
15458             }
15459             var d = this.getDialog();
15460             opt = options;
15461             d.setTitle(opt.title || "&#160;");
15462             d.close.setDisplayed(opt.closable !== false);
15463             activeTextEl = textboxEl;
15464             opt.prompt = opt.prompt || (opt.multiline ? true : false);
15465             if(opt.prompt){
15466                 if(opt.multiline){
15467                     textboxEl.hide();
15468                     textareaEl.show();
15469                     textareaEl.setHeight(typeof opt.multiline == "number" ?
15470                         opt.multiline : this.defaultTextHeight);
15471                     activeTextEl = textareaEl;
15472                 }else{
15473                     textboxEl.show();
15474                     textareaEl.hide();
15475                 }
15476             }else{
15477                 textboxEl.hide();
15478                 textareaEl.hide();
15479             }
15480             progressEl.setDisplayed(opt.progress === true);
15481             this.updateProgress(0);
15482             activeTextEl.dom.value = opt.value || "";
15483             if(opt.prompt){
15484                 dlg.setDefaultButton(activeTextEl);
15485             }else{
15486                 var bs = opt.buttons;
15487                 var db = null;
15488                 if(bs && bs.ok){
15489                     db = buttons["ok"];
15490                 }else if(bs && bs.yes){
15491                     db = buttons["yes"];
15492                 }
15493                 dlg.setDefaultButton(db);
15494             }
15495             bwidth = updateButtons(opt.buttons);
15496             this.updateText(opt.msg);
15497             if(opt.cls){
15498                 d.el.addClass(opt.cls);
15499             }
15500             d.proxyDrag = opt.proxyDrag === true;
15501             d.modal = opt.modal !== false;
15502             d.mask = opt.modal !== false ? mask : false;
15503             if(!d.isVisible()){
15504                 // force it to the end of the z-index stack so it gets a cursor in FF
15505                 document.body.appendChild(dlg.el.dom);
15506                 d.animateTarget = null;
15507                 d.show(options.animEl);
15508             }
15509             return this;
15510         },
15511
15512         /**
15513          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
15514          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
15515          * and closing the message box when the process is complete.
15516          * @param {String} title The title bar text
15517          * @param {String} msg The message box body text
15518          * @return {Roo.MessageBox} This message box
15519          */
15520         progress : function(title, msg){
15521             this.show({
15522                 title : title,
15523                 msg : msg,
15524                 buttons: false,
15525                 progress:true,
15526                 closable:false,
15527                 minWidth: this.minProgressWidth,
15528                 modal : true
15529             });
15530             return this;
15531         },
15532
15533         /**
15534          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
15535          * If a callback function is passed it will be called after the user clicks the button, and the
15536          * id of the button that was clicked will be passed as the only parameter to the callback
15537          * (could also be the top-right close button).
15538          * @param {String} title The title bar text
15539          * @param {String} msg The message box body text
15540          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15541          * @param {Object} scope (optional) The scope of the callback function
15542          * @return {Roo.MessageBox} This message box
15543          */
15544         alert : function(title, msg, fn, scope){
15545             this.show({
15546                 title : title,
15547                 msg : msg,
15548                 buttons: this.OK,
15549                 fn: fn,
15550                 scope : scope,
15551                 modal : true
15552             });
15553             return this;
15554         },
15555
15556         /**
15557          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
15558          * interaction while waiting for a long-running process to complete that does not have defined intervals.
15559          * You are responsible for closing the message box when the process is complete.
15560          * @param {String} msg The message box body text
15561          * @param {String} title (optional) The title bar text
15562          * @return {Roo.MessageBox} This message box
15563          */
15564         wait : function(msg, title){
15565             this.show({
15566                 title : title,
15567                 msg : msg,
15568                 buttons: false,
15569                 closable:false,
15570                 progress:true,
15571                 modal:true,
15572                 width:300,
15573                 wait:true
15574             });
15575             waitTimer = Roo.TaskMgr.start({
15576                 run: function(i){
15577                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
15578                 },
15579                 interval: 1000
15580             });
15581             return this;
15582         },
15583
15584         /**
15585          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
15586          * If a callback function is passed it will be called after the user clicks either button, and the id of the
15587          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
15588          * @param {String} title The title bar text
15589          * @param {String} msg The message box body text
15590          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15591          * @param {Object} scope (optional) The scope of the callback function
15592          * @return {Roo.MessageBox} This message box
15593          */
15594         confirm : function(title, msg, fn, scope){
15595             this.show({
15596                 title : title,
15597                 msg : msg,
15598                 buttons: this.YESNO,
15599                 fn: fn,
15600                 scope : scope,
15601                 modal : true
15602             });
15603             return this;
15604         },
15605
15606         /**
15607          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
15608          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
15609          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
15610          * (could also be the top-right close button) and the text that was entered will be passed as the two
15611          * parameters to the callback.
15612          * @param {String} title The title bar text
15613          * @param {String} msg The message box body text
15614          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15615          * @param {Object} scope (optional) The scope of the callback function
15616          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
15617          * property, or the height in pixels to create the textbox (defaults to false / single-line)
15618          * @return {Roo.MessageBox} This message box
15619          */
15620         prompt : function(title, msg, fn, scope, multiline){
15621             this.show({
15622                 title : title,
15623                 msg : msg,
15624                 buttons: this.OKCANCEL,
15625                 fn: fn,
15626                 minWidth:250,
15627                 scope : scope,
15628                 prompt:true,
15629                 multiline: multiline,
15630                 modal : true
15631             });
15632             return this;
15633         },
15634
15635         /**
15636          * Button config that displays a single OK button
15637          * @type Object
15638          */
15639         OK : {ok:true},
15640         /**
15641          * Button config that displays Yes and No buttons
15642          * @type Object
15643          */
15644         YESNO : {yes:true, no:true},
15645         /**
15646          * Button config that displays OK and Cancel buttons
15647          * @type Object
15648          */
15649         OKCANCEL : {ok:true, cancel:true},
15650         /**
15651          * Button config that displays Yes, No and Cancel buttons
15652          * @type Object
15653          */
15654         YESNOCANCEL : {yes:true, no:true, cancel:true},
15655
15656         /**
15657          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
15658          * @type Number
15659          */
15660         defaultTextHeight : 75,
15661         /**
15662          * The maximum width in pixels of the message box (defaults to 600)
15663          * @type Number
15664          */
15665         maxWidth : 600,
15666         /**
15667          * The minimum width in pixels of the message box (defaults to 100)
15668          * @type Number
15669          */
15670         minWidth : 100,
15671         /**
15672          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
15673          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
15674          * @type Number
15675          */
15676         minProgressWidth : 250,
15677         /**
15678          * An object containing the default button text strings that can be overriden for localized language support.
15679          * Supported properties are: ok, cancel, yes and no.
15680          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
15681          * @type Object
15682          */
15683         buttonText : {
15684             ok : "OK",
15685             cancel : "Cancel",
15686             yes : "Yes",
15687             no : "No"
15688         }
15689     };
15690 }();
15691
15692 /**
15693  * Shorthand for {@link Roo.MessageBox}
15694  */
15695 Roo.Msg = Roo.MessageBox;/*
15696  * Based on:
15697  * Ext JS Library 1.1.1
15698  * Copyright(c) 2006-2007, Ext JS, LLC.
15699  *
15700  * Originally Released Under LGPL - original licence link has changed is not relivant.
15701  *
15702  * Fork - LGPL
15703  * <script type="text/javascript">
15704  */
15705 /**
15706  * @class Roo.QuickTips
15707  * Provides attractive and customizable tooltips for any element.
15708  * @singleton
15709  */
15710 Roo.QuickTips = function(){
15711     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
15712     var ce, bd, xy, dd;
15713     var visible = false, disabled = true, inited = false;
15714     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
15715     
15716     var onOver = function(e){
15717         if(disabled){
15718             return;
15719         }
15720         var t = e.getTarget();
15721         if(!t || t.nodeType !== 1 || t == document || t == document.body){
15722             return;
15723         }
15724         if(ce && t == ce.el){
15725             clearTimeout(hideProc);
15726             return;
15727         }
15728         if(t && tagEls[t.id]){
15729             tagEls[t.id].el = t;
15730             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
15731             return;
15732         }
15733         var ttp, et = Roo.fly(t);
15734         var ns = cfg.namespace;
15735         if(tm.interceptTitles && t.title){
15736             ttp = t.title;
15737             t.qtip = ttp;
15738             t.removeAttribute("title");
15739             e.preventDefault();
15740         }else{
15741             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
15742         }
15743         if(ttp){
15744             showProc = show.defer(tm.showDelay, tm, [{
15745                 el: t, 
15746                 text: ttp, 
15747                 width: et.getAttributeNS(ns, cfg.width),
15748                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
15749                 title: et.getAttributeNS(ns, cfg.title),
15750                     cls: et.getAttributeNS(ns, cfg.cls)
15751             }]);
15752         }
15753     };
15754     
15755     var onOut = function(e){
15756         clearTimeout(showProc);
15757         var t = e.getTarget();
15758         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
15759             hideProc = setTimeout(hide, tm.hideDelay);
15760         }
15761     };
15762     
15763     var onMove = function(e){
15764         if(disabled){
15765             return;
15766         }
15767         xy = e.getXY();
15768         xy[1] += 18;
15769         if(tm.trackMouse && ce){
15770             el.setXY(xy);
15771         }
15772     };
15773     
15774     var onDown = function(e){
15775         clearTimeout(showProc);
15776         clearTimeout(hideProc);
15777         if(!e.within(el)){
15778             if(tm.hideOnClick){
15779                 hide();
15780                 tm.disable();
15781                 tm.enable.defer(100, tm);
15782             }
15783         }
15784     };
15785     
15786     var getPad = function(){
15787         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
15788     };
15789
15790     var show = function(o){
15791         if(disabled){
15792             return;
15793         }
15794         clearTimeout(dismissProc);
15795         ce = o;
15796         if(removeCls){ // in case manually hidden
15797             el.removeClass(removeCls);
15798             removeCls = null;
15799         }
15800         if(ce.cls){
15801             el.addClass(ce.cls);
15802             removeCls = ce.cls;
15803         }
15804         if(ce.title){
15805             tipTitle.update(ce.title);
15806             tipTitle.show();
15807         }else{
15808             tipTitle.update('');
15809             tipTitle.hide();
15810         }
15811         el.dom.style.width  = tm.maxWidth+'px';
15812         //tipBody.dom.style.width = '';
15813         tipBodyText.update(o.text);
15814         var p = getPad(), w = ce.width;
15815         if(!w){
15816             var td = tipBodyText.dom;
15817             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
15818             if(aw > tm.maxWidth){
15819                 w = tm.maxWidth;
15820             }else if(aw < tm.minWidth){
15821                 w = tm.minWidth;
15822             }else{
15823                 w = aw;
15824             }
15825         }
15826         //tipBody.setWidth(w);
15827         el.setWidth(parseInt(w, 10) + p);
15828         if(ce.autoHide === false){
15829             close.setDisplayed(true);
15830             if(dd){
15831                 dd.unlock();
15832             }
15833         }else{
15834             close.setDisplayed(false);
15835             if(dd){
15836                 dd.lock();
15837             }
15838         }
15839         if(xy){
15840             el.avoidY = xy[1]-18;
15841             el.setXY(xy);
15842         }
15843         if(tm.animate){
15844             el.setOpacity(.1);
15845             el.setStyle("visibility", "visible");
15846             el.fadeIn({callback: afterShow});
15847         }else{
15848             afterShow();
15849         }
15850     };
15851     
15852     var afterShow = function(){
15853         if(ce){
15854             el.show();
15855             esc.enable();
15856             if(tm.autoDismiss && ce.autoHide !== false){
15857                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
15858             }
15859         }
15860     };
15861     
15862     var hide = function(noanim){
15863         clearTimeout(dismissProc);
15864         clearTimeout(hideProc);
15865         ce = null;
15866         if(el.isVisible()){
15867             esc.disable();
15868             if(noanim !== true && tm.animate){
15869                 el.fadeOut({callback: afterHide});
15870             }else{
15871                 afterHide();
15872             } 
15873         }
15874     };
15875     
15876     var afterHide = function(){
15877         el.hide();
15878         if(removeCls){
15879             el.removeClass(removeCls);
15880             removeCls = null;
15881         }
15882     };
15883     
15884     return {
15885         /**
15886         * @cfg {Number} minWidth
15887         * The minimum width of the quick tip (defaults to 40)
15888         */
15889        minWidth : 40,
15890         /**
15891         * @cfg {Number} maxWidth
15892         * The maximum width of the quick tip (defaults to 300)
15893         */
15894        maxWidth : 300,
15895         /**
15896         * @cfg {Boolean} interceptTitles
15897         * True to automatically use the element's DOM title value if available (defaults to false)
15898         */
15899        interceptTitles : false,
15900         /**
15901         * @cfg {Boolean} trackMouse
15902         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
15903         */
15904        trackMouse : false,
15905         /**
15906         * @cfg {Boolean} hideOnClick
15907         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
15908         */
15909        hideOnClick : true,
15910         /**
15911         * @cfg {Number} showDelay
15912         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
15913         */
15914        showDelay : 500,
15915         /**
15916         * @cfg {Number} hideDelay
15917         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
15918         */
15919        hideDelay : 200,
15920         /**
15921         * @cfg {Boolean} autoHide
15922         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
15923         * Used in conjunction with hideDelay.
15924         */
15925        autoHide : true,
15926         /**
15927         * @cfg {Boolean}
15928         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
15929         * (defaults to true).  Used in conjunction with autoDismissDelay.
15930         */
15931        autoDismiss : true,
15932         /**
15933         * @cfg {Number}
15934         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
15935         */
15936        autoDismissDelay : 5000,
15937        /**
15938         * @cfg {Boolean} animate
15939         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
15940         */
15941        animate : false,
15942
15943        /**
15944         * @cfg {String} title
15945         * Title text to display (defaults to '').  This can be any valid HTML markup.
15946         */
15947         title: '',
15948        /**
15949         * @cfg {String} text
15950         * Body text to display (defaults to '').  This can be any valid HTML markup.
15951         */
15952         text : '',
15953        /**
15954         * @cfg {String} cls
15955         * A CSS class to apply to the base quick tip element (defaults to '').
15956         */
15957         cls : '',
15958        /**
15959         * @cfg {Number} width
15960         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
15961         * minWidth or maxWidth.
15962         */
15963         width : null,
15964
15965     /**
15966      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
15967      * or display QuickTips in a page.
15968      */
15969        init : function(){
15970           tm = Roo.QuickTips;
15971           cfg = tm.tagConfig;
15972           if(!inited){
15973               if(!Roo.isReady){ // allow calling of init() before onReady
15974                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
15975                   return;
15976               }
15977               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
15978               el.fxDefaults = {stopFx: true};
15979               // maximum custom styling
15980               //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>');
15981               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>');              
15982               tipTitle = el.child('h3');
15983               tipTitle.enableDisplayMode("block");
15984               tipBody = el.child('div.x-tip-bd');
15985               tipBodyText = el.child('div.x-tip-bd-inner');
15986               //bdLeft = el.child('div.x-tip-bd-left');
15987               //bdRight = el.child('div.x-tip-bd-right');
15988               close = el.child('div.x-tip-close');
15989               close.enableDisplayMode("block");
15990               close.on("click", hide);
15991               var d = Roo.get(document);
15992               d.on("mousedown", onDown);
15993               d.on("mouseover", onOver);
15994               d.on("mouseout", onOut);
15995               d.on("mousemove", onMove);
15996               esc = d.addKeyListener(27, hide);
15997               esc.disable();
15998               if(Roo.dd.DD){
15999                   dd = el.initDD("default", null, {
16000                       onDrag : function(){
16001                           el.sync();  
16002                       }
16003                   });
16004                   dd.setHandleElId(tipTitle.id);
16005                   dd.lock();
16006               }
16007               inited = true;
16008           }
16009           this.enable(); 
16010        },
16011
16012     /**
16013      * Configures a new quick tip instance and assigns it to a target element.  The following config options
16014      * are supported:
16015      * <pre>
16016 Property    Type                   Description
16017 ----------  ---------------------  ------------------------------------------------------------------------
16018 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
16019      * </ul>
16020      * @param {Object} config The config object
16021      */
16022        register : function(config){
16023            var cs = config instanceof Array ? config : arguments;
16024            for(var i = 0, len = cs.length; i < len; i++) {
16025                var c = cs[i];
16026                var target = c.target;
16027                if(target){
16028                    if(target instanceof Array){
16029                        for(var j = 0, jlen = target.length; j < jlen; j++){
16030                            tagEls[target[j]] = c;
16031                        }
16032                    }else{
16033                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
16034                    }
16035                }
16036            }
16037        },
16038
16039     /**
16040      * Removes this quick tip from its element and destroys it.
16041      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
16042      */
16043        unregister : function(el){
16044            delete tagEls[Roo.id(el)];
16045        },
16046
16047     /**
16048      * Enable this quick tip.
16049      */
16050        enable : function(){
16051            if(inited && disabled){
16052                locks.pop();
16053                if(locks.length < 1){
16054                    disabled = false;
16055                }
16056            }
16057        },
16058
16059     /**
16060      * Disable this quick tip.
16061      */
16062        disable : function(){
16063           disabled = true;
16064           clearTimeout(showProc);
16065           clearTimeout(hideProc);
16066           clearTimeout(dismissProc);
16067           if(ce){
16068               hide(true);
16069           }
16070           locks.push(1);
16071        },
16072
16073     /**
16074      * Returns true if the quick tip is enabled, else false.
16075      */
16076        isEnabled : function(){
16077             return !disabled;
16078        },
16079
16080         // private
16081        tagConfig : {
16082            namespace : "ext",
16083            attribute : "qtip",
16084            width : "width",
16085            target : "target",
16086            title : "qtitle",
16087            hide : "hide",
16088            cls : "qclass"
16089        }
16090    };
16091 }();
16092
16093 // backwards compat
16094 Roo.QuickTips.tips = Roo.QuickTips.register;/*
16095  * Based on:
16096  * Ext JS Library 1.1.1
16097  * Copyright(c) 2006-2007, Ext JS, LLC.
16098  *
16099  * Originally Released Under LGPL - original licence link has changed is not relivant.
16100  *
16101  * Fork - LGPL
16102  * <script type="text/javascript">
16103  */
16104  
16105
16106 /**
16107  * @class Roo.tree.TreePanel
16108  * @extends Roo.data.Tree
16109
16110  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
16111  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
16112  * @cfg {Boolean} enableDD true to enable drag and drop
16113  * @cfg {Boolean} enableDrag true to enable just drag
16114  * @cfg {Boolean} enableDrop true to enable just drop
16115  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
16116  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
16117  * @cfg {String} ddGroup The DD group this TreePanel belongs to
16118  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
16119  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
16120  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
16121  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
16122  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
16123  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
16124  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
16125  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
16126  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
16127  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
16128  * @cfg {Function} renderer 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>
16129  * @cfg {Function} rendererTip 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>
16130  * 
16131  * @constructor
16132  * @param {String/HTMLElement/Element} el The container element
16133  * @param {Object} config
16134  */
16135 Roo.tree.TreePanel = function(el, config){
16136     var root = false;
16137     var loader = false;
16138     if (config.root) {
16139         root = config.root;
16140         delete config.root;
16141     }
16142     if (config.loader) {
16143         loader = config.loader;
16144         delete config.loader;
16145     }
16146     
16147     Roo.apply(this, config);
16148     Roo.tree.TreePanel.superclass.constructor.call(this);
16149     this.el = Roo.get(el);
16150     this.el.addClass('x-tree');
16151     //console.log(root);
16152     if (root) {
16153         this.setRootNode( Roo.factory(root, Roo.tree));
16154     }
16155     if (loader) {
16156         this.loader = Roo.factory(loader, Roo.tree);
16157     }
16158    /**
16159     * Read-only. The id of the container element becomes this TreePanel's id.
16160     */
16161    this.id = this.el.id;
16162    this.addEvents({
16163         /**
16164         * @event beforeload
16165         * Fires before a node is loaded, return false to cancel
16166         * @param {Node} node The node being loaded
16167         */
16168         "beforeload" : true,
16169         /**
16170         * @event load
16171         * Fires when a node is loaded
16172         * @param {Node} node The node that was loaded
16173         */
16174         "load" : true,
16175         /**
16176         * @event textchange
16177         * Fires when the text for a node is changed
16178         * @param {Node} node The node
16179         * @param {String} text The new text
16180         * @param {String} oldText The old text
16181         */
16182         "textchange" : true,
16183         /**
16184         * @event beforeexpand
16185         * Fires before a node is expanded, return false to cancel.
16186         * @param {Node} node The node
16187         * @param {Boolean} deep
16188         * @param {Boolean} anim
16189         */
16190         "beforeexpand" : true,
16191         /**
16192         * @event beforecollapse
16193         * Fires before a node is collapsed, return false to cancel.
16194         * @param {Node} node The node
16195         * @param {Boolean} deep
16196         * @param {Boolean} anim
16197         */
16198         "beforecollapse" : true,
16199         /**
16200         * @event expand
16201         * Fires when a node is expanded
16202         * @param {Node} node The node
16203         */
16204         "expand" : true,
16205         /**
16206         * @event disabledchange
16207         * Fires when the disabled status of a node changes
16208         * @param {Node} node The node
16209         * @param {Boolean} disabled
16210         */
16211         "disabledchange" : true,
16212         /**
16213         * @event collapse
16214         * Fires when a node is collapsed
16215         * @param {Node} node The node
16216         */
16217         "collapse" : true,
16218         /**
16219         * @event beforeclick
16220         * Fires before click processing on a node. Return false to cancel the default action.
16221         * @param {Node} node The node
16222         * @param {Roo.EventObject} e The event object
16223         */
16224         "beforeclick":true,
16225         /**
16226         * @event checkchange
16227         * Fires when a node with a checkbox's checked property changes
16228         * @param {Node} this This node
16229         * @param {Boolean} checked
16230         */
16231         "checkchange":true,
16232         /**
16233         * @event click
16234         * Fires when a node is clicked
16235         * @param {Node} node The node
16236         * @param {Roo.EventObject} e The event object
16237         */
16238         "click":true,
16239         /**
16240         * @event dblclick
16241         * Fires when a node is double clicked
16242         * @param {Node} node The node
16243         * @param {Roo.EventObject} e The event object
16244         */
16245         "dblclick":true,
16246         /**
16247         * @event contextmenu
16248         * Fires when a node is right clicked
16249         * @param {Node} node The node
16250         * @param {Roo.EventObject} e The event object
16251         */
16252         "contextmenu":true,
16253         /**
16254         * @event beforechildrenrendered
16255         * Fires right before the child nodes for a node are rendered
16256         * @param {Node} node The node
16257         */
16258         "beforechildrenrendered":true,
16259        /**
16260              * @event startdrag
16261              * Fires when a node starts being dragged
16262              * @param {Roo.tree.TreePanel} this
16263              * @param {Roo.tree.TreeNode} node
16264              * @param {event} e The raw browser event
16265              */ 
16266             "startdrag" : true,
16267             /**
16268              * @event enddrag
16269              * Fires when a drag operation is complete
16270              * @param {Roo.tree.TreePanel} this
16271              * @param {Roo.tree.TreeNode} node
16272              * @param {event} e The raw browser event
16273              */
16274             "enddrag" : true,
16275             /**
16276              * @event dragdrop
16277              * Fires when a dragged node is dropped on a valid DD target
16278              * @param {Roo.tree.TreePanel} this
16279              * @param {Roo.tree.TreeNode} node
16280              * @param {DD} dd The dd it was dropped on
16281              * @param {event} e The raw browser event
16282              */
16283             "dragdrop" : true,
16284             /**
16285              * @event beforenodedrop
16286              * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
16287              * passed to handlers has the following properties:<br />
16288              * <ul style="padding:5px;padding-left:16px;">
16289              * <li>tree - The TreePanel</li>
16290              * <li>target - The node being targeted for the drop</li>
16291              * <li>data - The drag data from the drag source</li>
16292              * <li>point - The point of the drop - append, above or below</li>
16293              * <li>source - The drag source</li>
16294              * <li>rawEvent - Raw mouse event</li>
16295              * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
16296              * to be inserted by setting them on this object.</li>
16297              * <li>cancel - Set this to true to cancel the drop.</li>
16298              * </ul>
16299              * @param {Object} dropEvent
16300              */
16301             "beforenodedrop" : true,
16302             /**
16303              * @event nodedrop
16304              * Fires after a DD object is dropped on a node in this tree. The dropEvent
16305              * passed to handlers has the following properties:<br />
16306              * <ul style="padding:5px;padding-left:16px;">
16307              * <li>tree - The TreePanel</li>
16308              * <li>target - The node being targeted for the drop</li>
16309              * <li>data - The drag data from the drag source</li>
16310              * <li>point - The point of the drop - append, above or below</li>
16311              * <li>source - The drag source</li>
16312              * <li>rawEvent - Raw mouse event</li>
16313              * <li>dropNode - Dropped node(s).</li>
16314              * </ul>
16315              * @param {Object} dropEvent
16316              */
16317             "nodedrop" : true,
16318              /**
16319              * @event nodedragover
16320              * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
16321              * passed to handlers has the following properties:<br />
16322              * <ul style="padding:5px;padding-left:16px;">
16323              * <li>tree - The TreePanel</li>
16324              * <li>target - The node being targeted for the drop</li>
16325              * <li>data - The drag data from the drag source</li>
16326              * <li>point - The point of the drop - append, above or below</li>
16327              * <li>source - The drag source</li>
16328              * <li>rawEvent - Raw mouse event</li>
16329              * <li>dropNode - Drop node(s) provided by the source.</li>
16330              * <li>cancel - Set this to true to signal drop not allowed.</li>
16331              * </ul>
16332              * @param {Object} dragOverEvent
16333              */
16334             "nodedragover" : true
16335         
16336    });
16337    if(this.singleExpand){
16338        this.on("beforeexpand", this.restrictExpand, this);
16339    }
16340 };
16341 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
16342     rootVisible : true,
16343     animate: Roo.enableFx,
16344     lines : true,
16345     enableDD : false,
16346     hlDrop : Roo.enableFx,
16347   
16348     renderer: false,
16349     
16350     rendererTip: false,
16351     // private
16352     restrictExpand : function(node){
16353         var p = node.parentNode;
16354         if(p){
16355             if(p.expandedChild && p.expandedChild.parentNode == p){
16356                 p.expandedChild.collapse();
16357             }
16358             p.expandedChild = node;
16359         }
16360     },
16361
16362     // private override
16363     setRootNode : function(node){
16364         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
16365         if(!this.rootVisible){
16366             node.ui = new Roo.tree.RootTreeNodeUI(node);
16367         }
16368         return node;
16369     },
16370
16371     /**
16372      * Returns the container element for this TreePanel
16373      */
16374     getEl : function(){
16375         return this.el;
16376     },
16377
16378     /**
16379      * Returns the default TreeLoader for this TreePanel
16380      */
16381     getLoader : function(){
16382         return this.loader;
16383     },
16384
16385     /**
16386      * Expand all nodes
16387      */
16388     expandAll : function(){
16389         this.root.expand(true);
16390     },
16391
16392     /**
16393      * Collapse all nodes
16394      */
16395     collapseAll : function(){
16396         this.root.collapse(true);
16397     },
16398
16399     /**
16400      * Returns the selection model used by this TreePanel
16401      */
16402     getSelectionModel : function(){
16403         if(!this.selModel){
16404             this.selModel = new Roo.tree.DefaultSelectionModel();
16405         }
16406         return this.selModel;
16407     },
16408
16409     /**
16410      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
16411      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
16412      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
16413      * @return {Array}
16414      */
16415     getChecked : function(a, startNode){
16416         startNode = startNode || this.root;
16417         var r = [];
16418         var f = function(){
16419             if(this.attributes.checked){
16420                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
16421             }
16422         }
16423         startNode.cascade(f);
16424         return r;
16425     },
16426
16427     /**
16428      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16429      * @param {String} path
16430      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16431      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
16432      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
16433      */
16434     expandPath : function(path, attr, callback){
16435         attr = attr || "id";
16436         var keys = path.split(this.pathSeparator);
16437         var curNode = this.root;
16438         if(curNode.attributes[attr] != keys[1]){ // invalid root
16439             if(callback){
16440                 callback(false, null);
16441             }
16442             return;
16443         }
16444         var index = 1;
16445         var f = function(){
16446             if(++index == keys.length){
16447                 if(callback){
16448                     callback(true, curNode);
16449                 }
16450                 return;
16451             }
16452             var c = curNode.findChild(attr, keys[index]);
16453             if(!c){
16454                 if(callback){
16455                     callback(false, curNode);
16456                 }
16457                 return;
16458             }
16459             curNode = c;
16460             c.expand(false, false, f);
16461         };
16462         curNode.expand(false, false, f);
16463     },
16464
16465     /**
16466      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16467      * @param {String} path
16468      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16469      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
16470      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
16471      */
16472     selectPath : function(path, attr, callback){
16473         attr = attr || "id";
16474         var keys = path.split(this.pathSeparator);
16475         var v = keys.pop();
16476         if(keys.length > 0){
16477             var f = function(success, node){
16478                 if(success && node){
16479                     var n = node.findChild(attr, v);
16480                     if(n){
16481                         n.select();
16482                         if(callback){
16483                             callback(true, n);
16484                         }
16485                     }else if(callback){
16486                         callback(false, n);
16487                     }
16488                 }else{
16489                     if(callback){
16490                         callback(false, n);
16491                     }
16492                 }
16493             };
16494             this.expandPath(keys.join(this.pathSeparator), attr, f);
16495         }else{
16496             this.root.select();
16497             if(callback){
16498                 callback(true, this.root);
16499             }
16500         }
16501     },
16502
16503     getTreeEl : function(){
16504         return this.el;
16505     },
16506
16507     /**
16508      * Trigger rendering of this TreePanel
16509      */
16510     render : function(){
16511         if (this.innerCt) {
16512             return this; // stop it rendering more than once!!
16513         }
16514         
16515         this.innerCt = this.el.createChild({tag:"ul",
16516                cls:"x-tree-root-ct " +
16517                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
16518
16519         if(this.containerScroll){
16520             Roo.dd.ScrollManager.register(this.el);
16521         }
16522         if((this.enableDD || this.enableDrop) && !this.dropZone){
16523            /**
16524             * The dropZone used by this tree if drop is enabled
16525             * @type Roo.tree.TreeDropZone
16526             */
16527              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
16528                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
16529            });
16530         }
16531         if((this.enableDD || this.enableDrag) && !this.dragZone){
16532            /**
16533             * The dragZone used by this tree if drag is enabled
16534             * @type Roo.tree.TreeDragZone
16535             */
16536             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
16537                ddGroup: this.ddGroup || "TreeDD",
16538                scroll: this.ddScroll
16539            });
16540         }
16541         this.getSelectionModel().init(this);
16542         if (!this.root) {
16543             console.log("ROOT not set in tree");
16544             return;
16545         }
16546         this.root.render();
16547         if(!this.rootVisible){
16548             this.root.renderChildren();
16549         }
16550         return this;
16551     }
16552 });/*
16553  * Based on:
16554  * Ext JS Library 1.1.1
16555  * Copyright(c) 2006-2007, Ext JS, LLC.
16556  *
16557  * Originally Released Under LGPL - original licence link has changed is not relivant.
16558  *
16559  * Fork - LGPL
16560  * <script type="text/javascript">
16561  */
16562  
16563
16564 /**
16565  * @class Roo.tree.DefaultSelectionModel
16566  * @extends Roo.util.Observable
16567  * The default single selection for a TreePanel.
16568  */
16569 Roo.tree.DefaultSelectionModel = function(){
16570    this.selNode = null;
16571    
16572    this.addEvents({
16573        /**
16574         * @event selectionchange
16575         * Fires when the selected node changes
16576         * @param {DefaultSelectionModel} this
16577         * @param {TreeNode} node the new selection
16578         */
16579        "selectionchange" : true,
16580
16581        /**
16582         * @event beforeselect
16583         * Fires before the selected node changes, return false to cancel the change
16584         * @param {DefaultSelectionModel} this
16585         * @param {TreeNode} node the new selection
16586         * @param {TreeNode} node the old selection
16587         */
16588        "beforeselect" : true
16589    });
16590 };
16591
16592 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
16593     init : function(tree){
16594         this.tree = tree;
16595         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16596         tree.on("click", this.onNodeClick, this);
16597     },
16598     
16599     onNodeClick : function(node, e){
16600         if (e.ctrlKey && this.selNode == node)  {
16601             this.unselect(node);
16602             return;
16603         }
16604         this.select(node);
16605     },
16606     
16607     /**
16608      * Select a node.
16609      * @param {TreeNode} node The node to select
16610      * @return {TreeNode} The selected node
16611      */
16612     select : function(node){
16613         var last = this.selNode;
16614         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
16615             if(last){
16616                 last.ui.onSelectedChange(false);
16617             }
16618             this.selNode = node;
16619             node.ui.onSelectedChange(true);
16620             this.fireEvent("selectionchange", this, node, last);
16621         }
16622         return node;
16623     },
16624     
16625     /**
16626      * Deselect a node.
16627      * @param {TreeNode} node The node to unselect
16628      */
16629     unselect : function(node){
16630         if(this.selNode == node){
16631             this.clearSelections();
16632         }    
16633     },
16634     
16635     /**
16636      * Clear all selections
16637      */
16638     clearSelections : function(){
16639         var n = this.selNode;
16640         if(n){
16641             n.ui.onSelectedChange(false);
16642             this.selNode = null;
16643             this.fireEvent("selectionchange", this, null);
16644         }
16645         return n;
16646     },
16647     
16648     /**
16649      * Get the selected node
16650      * @return {TreeNode} The selected node
16651      */
16652     getSelectedNode : function(){
16653         return this.selNode;    
16654     },
16655     
16656     /**
16657      * Returns true if the node is selected
16658      * @param {TreeNode} node The node to check
16659      * @return {Boolean}
16660      */
16661     isSelected : function(node){
16662         return this.selNode == node;  
16663     },
16664
16665     /**
16666      * Selects the node above the selected node in the tree, intelligently walking the nodes
16667      * @return TreeNode The new selection
16668      */
16669     selectPrevious : function(){
16670         var s = this.selNode || this.lastSelNode;
16671         if(!s){
16672             return null;
16673         }
16674         var ps = s.previousSibling;
16675         if(ps){
16676             if(!ps.isExpanded() || ps.childNodes.length < 1){
16677                 return this.select(ps);
16678             } else{
16679                 var lc = ps.lastChild;
16680                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
16681                     lc = lc.lastChild;
16682                 }
16683                 return this.select(lc);
16684             }
16685         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
16686             return this.select(s.parentNode);
16687         }
16688         return null;
16689     },
16690
16691     /**
16692      * Selects the node above the selected node in the tree, intelligently walking the nodes
16693      * @return TreeNode The new selection
16694      */
16695     selectNext : function(){
16696         var s = this.selNode || this.lastSelNode;
16697         if(!s){
16698             return null;
16699         }
16700         if(s.firstChild && s.isExpanded()){
16701              return this.select(s.firstChild);
16702          }else if(s.nextSibling){
16703              return this.select(s.nextSibling);
16704          }else if(s.parentNode){
16705             var newS = null;
16706             s.parentNode.bubble(function(){
16707                 if(this.nextSibling){
16708                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
16709                     return false;
16710                 }
16711             });
16712             return newS;
16713          }
16714         return null;
16715     },
16716
16717     onKeyDown : function(e){
16718         var s = this.selNode || this.lastSelNode;
16719         // undesirable, but required
16720         var sm = this;
16721         if(!s){
16722             return;
16723         }
16724         var k = e.getKey();
16725         switch(k){
16726              case e.DOWN:
16727                  e.stopEvent();
16728                  this.selectNext();
16729              break;
16730              case e.UP:
16731                  e.stopEvent();
16732                  this.selectPrevious();
16733              break;
16734              case e.RIGHT:
16735                  e.preventDefault();
16736                  if(s.hasChildNodes()){
16737                      if(!s.isExpanded()){
16738                          s.expand();
16739                      }else if(s.firstChild){
16740                          this.select(s.firstChild, e);
16741                      }
16742                  }
16743              break;
16744              case e.LEFT:
16745                  e.preventDefault();
16746                  if(s.hasChildNodes() && s.isExpanded()){
16747                      s.collapse();
16748                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
16749                      this.select(s.parentNode, e);
16750                  }
16751              break;
16752         };
16753     }
16754 });
16755
16756 /**
16757  * @class Roo.tree.MultiSelectionModel
16758  * @extends Roo.util.Observable
16759  * Multi selection for a TreePanel.
16760  */
16761 Roo.tree.MultiSelectionModel = function(){
16762    this.selNodes = [];
16763    this.selMap = {};
16764    this.addEvents({
16765        /**
16766         * @event selectionchange
16767         * Fires when the selected nodes change
16768         * @param {MultiSelectionModel} this
16769         * @param {Array} nodes Array of the selected nodes
16770         */
16771        "selectionchange" : true
16772    });
16773 };
16774
16775 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
16776     init : function(tree){
16777         this.tree = tree;
16778         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16779         tree.on("click", this.onNodeClick, this);
16780     },
16781     
16782     onNodeClick : function(node, e){
16783         this.select(node, e, e.ctrlKey);
16784     },
16785     
16786     /**
16787      * Select a node.
16788      * @param {TreeNode} node The node to select
16789      * @param {EventObject} e (optional) An event associated with the selection
16790      * @param {Boolean} keepExisting True to retain existing selections
16791      * @return {TreeNode} The selected node
16792      */
16793     select : function(node, e, keepExisting){
16794         if(keepExisting !== true){
16795             this.clearSelections(true);
16796         }
16797         if(this.isSelected(node)){
16798             this.lastSelNode = node;
16799             return node;
16800         }
16801         this.selNodes.push(node);
16802         this.selMap[node.id] = node;
16803         this.lastSelNode = node;
16804         node.ui.onSelectedChange(true);
16805         this.fireEvent("selectionchange", this, this.selNodes);
16806         return node;
16807     },
16808     
16809     /**
16810      * Deselect a node.
16811      * @param {TreeNode} node The node to unselect
16812      */
16813     unselect : function(node){
16814         if(this.selMap[node.id]){
16815             node.ui.onSelectedChange(false);
16816             var sn = this.selNodes;
16817             var index = -1;
16818             if(sn.indexOf){
16819                 index = sn.indexOf(node);
16820             }else{
16821                 for(var i = 0, len = sn.length; i < len; i++){
16822                     if(sn[i] == node){
16823                         index = i;
16824                         break;
16825                     }
16826                 }
16827             }
16828             if(index != -1){
16829                 this.selNodes.splice(index, 1);
16830             }
16831             delete this.selMap[node.id];
16832             this.fireEvent("selectionchange", this, this.selNodes);
16833         }
16834     },
16835     
16836     /**
16837      * Clear all selections
16838      */
16839     clearSelections : function(suppressEvent){
16840         var sn = this.selNodes;
16841         if(sn.length > 0){
16842             for(var i = 0, len = sn.length; i < len; i++){
16843                 sn[i].ui.onSelectedChange(false);
16844             }
16845             this.selNodes = [];
16846             this.selMap = {};
16847             if(suppressEvent !== true){
16848                 this.fireEvent("selectionchange", this, this.selNodes);
16849             }
16850         }
16851     },
16852     
16853     /**
16854      * Returns true if the node is selected
16855      * @param {TreeNode} node The node to check
16856      * @return {Boolean}
16857      */
16858     isSelected : function(node){
16859         return this.selMap[node.id] ? true : false;  
16860     },
16861     
16862     /**
16863      * Returns an array of the selected nodes
16864      * @return {Array}
16865      */
16866     getSelectedNodes : function(){
16867         return this.selNodes;    
16868     },
16869
16870     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
16871
16872     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
16873
16874     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
16875 });/*
16876  * Based on:
16877  * Ext JS Library 1.1.1
16878  * Copyright(c) 2006-2007, Ext JS, LLC.
16879  *
16880  * Originally Released Under LGPL - original licence link has changed is not relivant.
16881  *
16882  * Fork - LGPL
16883  * <script type="text/javascript">
16884  */
16885  
16886 /**
16887  * @class Roo.tree.TreeNode
16888  * @extends Roo.data.Node
16889  * @cfg {String} text The text for this node
16890  * @cfg {Boolean} expanded true to start the node expanded
16891  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
16892  * @cfg {Boolean} allowDrop false if this node cannot be drop on
16893  * @cfg {Boolean} disabled true to start the node disabled
16894  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
16895  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
16896  * @cfg {String} cls A css class to be added to the node
16897  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
16898  * @cfg {String} href URL of the link used for the node (defaults to #)
16899  * @cfg {String} hrefTarget target frame for the link
16900  * @cfg {String} qtip An Ext QuickTip for the node
16901  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
16902  * @cfg {Boolean} singleClickExpand True for single click expand on this node
16903  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
16904  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
16905  * (defaults to undefined with no checkbox rendered)
16906  * @constructor
16907  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
16908  */
16909 Roo.tree.TreeNode = function(attributes){
16910     attributes = attributes || {};
16911     if(typeof attributes == "string"){
16912         attributes = {text: attributes};
16913     }
16914     this.childrenRendered = false;
16915     this.rendered = false;
16916     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
16917     this.expanded = attributes.expanded === true;
16918     this.isTarget = attributes.isTarget !== false;
16919     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
16920     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
16921
16922     /**
16923      * Read-only. The text for this node. To change it use setText().
16924      * @type String
16925      */
16926     this.text = attributes.text;
16927     /**
16928      * True if this node is disabled.
16929      * @type Boolean
16930      */
16931     this.disabled = attributes.disabled === true;
16932
16933     this.addEvents({
16934         /**
16935         * @event textchange
16936         * Fires when the text for this node is changed
16937         * @param {Node} this This node
16938         * @param {String} text The new text
16939         * @param {String} oldText The old text
16940         */
16941         "textchange" : true,
16942         /**
16943         * @event beforeexpand
16944         * Fires before this node is expanded, return false to cancel.
16945         * @param {Node} this This node
16946         * @param {Boolean} deep
16947         * @param {Boolean} anim
16948         */
16949         "beforeexpand" : true,
16950         /**
16951         * @event beforecollapse
16952         * Fires before this node is collapsed, return false to cancel.
16953         * @param {Node} this This node
16954         * @param {Boolean} deep
16955         * @param {Boolean} anim
16956         */
16957         "beforecollapse" : true,
16958         /**
16959         * @event expand
16960         * Fires when this node is expanded
16961         * @param {Node} this This node
16962         */
16963         "expand" : true,
16964         /**
16965         * @event disabledchange
16966         * Fires when the disabled status of this node changes
16967         * @param {Node} this This node
16968         * @param {Boolean} disabled
16969         */
16970         "disabledchange" : true,
16971         /**
16972         * @event collapse
16973         * Fires when this node is collapsed
16974         * @param {Node} this This node
16975         */
16976         "collapse" : true,
16977         /**
16978         * @event beforeclick
16979         * Fires before click processing. Return false to cancel the default action.
16980         * @param {Node} this This node
16981         * @param {Roo.EventObject} e The event object
16982         */
16983         "beforeclick":true,
16984         /**
16985         * @event checkchange
16986         * Fires when a node with a checkbox's checked property changes
16987         * @param {Node} this This node
16988         * @param {Boolean} checked
16989         */
16990         "checkchange":true,
16991         /**
16992         * @event click
16993         * Fires when this node is clicked
16994         * @param {Node} this This node
16995         * @param {Roo.EventObject} e The event object
16996         */
16997         "click":true,
16998         /**
16999         * @event dblclick
17000         * Fires when this node is double clicked
17001         * @param {Node} this This node
17002         * @param {Roo.EventObject} e The event object
17003         */
17004         "dblclick":true,
17005         /**
17006         * @event contextmenu
17007         * Fires when this node is right clicked
17008         * @param {Node} this This node
17009         * @param {Roo.EventObject} e The event object
17010         */
17011         "contextmenu":true,
17012         /**
17013         * @event beforechildrenrendered
17014         * Fires right before the child nodes for this node are rendered
17015         * @param {Node} this This node
17016         */
17017         "beforechildrenrendered":true
17018     });
17019
17020     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
17021
17022     /**
17023      * Read-only. The UI for this node
17024      * @type TreeNodeUI
17025      */
17026     this.ui = new uiClass(this);
17027 };
17028 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
17029     preventHScroll: true,
17030     /**
17031      * Returns true if this node is expanded
17032      * @return {Boolean}
17033      */
17034     isExpanded : function(){
17035         return this.expanded;
17036     },
17037
17038     /**
17039      * Returns the UI object for this node
17040      * @return {TreeNodeUI}
17041      */
17042     getUI : function(){
17043         return this.ui;
17044     },
17045
17046     // private override
17047     setFirstChild : function(node){
17048         var of = this.firstChild;
17049         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
17050         if(this.childrenRendered && of && node != of){
17051             of.renderIndent(true, true);
17052         }
17053         if(this.rendered){
17054             this.renderIndent(true, true);
17055         }
17056     },
17057
17058     // private override
17059     setLastChild : function(node){
17060         var ol = this.lastChild;
17061         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
17062         if(this.childrenRendered && ol && node != ol){
17063             ol.renderIndent(true, true);
17064         }
17065         if(this.rendered){
17066             this.renderIndent(true, true);
17067         }
17068     },
17069
17070     // these methods are overridden to provide lazy rendering support
17071     // private override
17072     appendChild : function(){
17073         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
17074         if(node && this.childrenRendered){
17075             node.render();
17076         }
17077         this.ui.updateExpandIcon();
17078         return node;
17079     },
17080
17081     // private override
17082     removeChild : function(node){
17083         this.ownerTree.getSelectionModel().unselect(node);
17084         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
17085         // if it's been rendered remove dom node
17086         if(this.childrenRendered){
17087             node.ui.remove();
17088         }
17089         if(this.childNodes.length < 1){
17090             this.collapse(false, false);
17091         }else{
17092             this.ui.updateExpandIcon();
17093         }
17094         if(!this.firstChild) {
17095             this.childrenRendered = false;
17096         }
17097         return node;
17098     },
17099
17100     // private override
17101     insertBefore : function(node, refNode){
17102         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
17103         if(newNode && refNode && this.childrenRendered){
17104             node.render();
17105         }
17106         this.ui.updateExpandIcon();
17107         return newNode;
17108     },
17109
17110     /**
17111      * Sets the text for this node
17112      * @param {String} text
17113      */
17114     setText : function(text){
17115         var oldText = this.text;
17116         this.text = text;
17117         this.attributes.text = text;
17118         if(this.rendered){ // event without subscribing
17119             this.ui.onTextChange(this, text, oldText);
17120         }
17121         this.fireEvent("textchange", this, text, oldText);
17122     },
17123
17124     /**
17125      * Triggers selection of this node
17126      */
17127     select : function(){
17128         this.getOwnerTree().getSelectionModel().select(this);
17129     },
17130
17131     /**
17132      * Triggers deselection of this node
17133      */
17134     unselect : function(){
17135         this.getOwnerTree().getSelectionModel().unselect(this);
17136     },
17137
17138     /**
17139      * Returns true if this node is selected
17140      * @return {Boolean}
17141      */
17142     isSelected : function(){
17143         return this.getOwnerTree().getSelectionModel().isSelected(this);
17144     },
17145
17146     /**
17147      * Expand this node.
17148      * @param {Boolean} deep (optional) True to expand all children as well
17149      * @param {Boolean} anim (optional) false to cancel the default animation
17150      * @param {Function} callback (optional) A callback to be called when
17151      * expanding this node completes (does not wait for deep expand to complete).
17152      * Called with 1 parameter, this node.
17153      */
17154     expand : function(deep, anim, callback){
17155         if(!this.expanded){
17156             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
17157                 return;
17158             }
17159             if(!this.childrenRendered){
17160                 this.renderChildren();
17161             }
17162             this.expanded = true;
17163             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
17164                 this.ui.animExpand(function(){
17165                     this.fireEvent("expand", this);
17166                     if(typeof callback == "function"){
17167                         callback(this);
17168                     }
17169                     if(deep === true){
17170                         this.expandChildNodes(true);
17171                     }
17172                 }.createDelegate(this));
17173                 return;
17174             }else{
17175                 this.ui.expand();
17176                 this.fireEvent("expand", this);
17177                 if(typeof callback == "function"){
17178                     callback(this);
17179                 }
17180             }
17181         }else{
17182            if(typeof callback == "function"){
17183                callback(this);
17184            }
17185         }
17186         if(deep === true){
17187             this.expandChildNodes(true);
17188         }
17189     },
17190
17191     isHiddenRoot : function(){
17192         return this.isRoot && !this.getOwnerTree().rootVisible;
17193     },
17194
17195     /**
17196      * Collapse this node.
17197      * @param {Boolean} deep (optional) True to collapse all children as well
17198      * @param {Boolean} anim (optional) false to cancel the default animation
17199      */
17200     collapse : function(deep, anim){
17201         if(this.expanded && !this.isHiddenRoot()){
17202             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
17203                 return;
17204             }
17205             this.expanded = false;
17206             if((this.getOwnerTree().animate && anim !== false) || anim){
17207                 this.ui.animCollapse(function(){
17208                     this.fireEvent("collapse", this);
17209                     if(deep === true){
17210                         this.collapseChildNodes(true);
17211                     }
17212                 }.createDelegate(this));
17213                 return;
17214             }else{
17215                 this.ui.collapse();
17216                 this.fireEvent("collapse", this);
17217             }
17218         }
17219         if(deep === true){
17220             var cs = this.childNodes;
17221             for(var i = 0, len = cs.length; i < len; i++) {
17222                 cs[i].collapse(true, false);
17223             }
17224         }
17225     },
17226
17227     // private
17228     delayedExpand : function(delay){
17229         if(!this.expandProcId){
17230             this.expandProcId = this.expand.defer(delay, this);
17231         }
17232     },
17233
17234     // private
17235     cancelExpand : function(){
17236         if(this.expandProcId){
17237             clearTimeout(this.expandProcId);
17238         }
17239         this.expandProcId = false;
17240     },
17241
17242     /**
17243      * Toggles expanded/collapsed state of the node
17244      */
17245     toggle : function(){
17246         if(this.expanded){
17247             this.collapse();
17248         }else{
17249             this.expand();
17250         }
17251     },
17252
17253     /**
17254      * Ensures all parent nodes are expanded
17255      */
17256     ensureVisible : function(callback){
17257         var tree = this.getOwnerTree();
17258         tree.expandPath(this.parentNode.getPath(), false, function(){
17259             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
17260             Roo.callback(callback);
17261         }.createDelegate(this));
17262     },
17263
17264     /**
17265      * Expand all child nodes
17266      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
17267      */
17268     expandChildNodes : function(deep){
17269         var cs = this.childNodes;
17270         for(var i = 0, len = cs.length; i < len; i++) {
17271                 cs[i].expand(deep);
17272         }
17273     },
17274
17275     /**
17276      * Collapse all child nodes
17277      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
17278      */
17279     collapseChildNodes : function(deep){
17280         var cs = this.childNodes;
17281         for(var i = 0, len = cs.length; i < len; i++) {
17282                 cs[i].collapse(deep);
17283         }
17284     },
17285
17286     /**
17287      * Disables this node
17288      */
17289     disable : function(){
17290         this.disabled = true;
17291         this.unselect();
17292         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17293             this.ui.onDisableChange(this, true);
17294         }
17295         this.fireEvent("disabledchange", this, true);
17296     },
17297
17298     /**
17299      * Enables this node
17300      */
17301     enable : function(){
17302         this.disabled = false;
17303         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17304             this.ui.onDisableChange(this, false);
17305         }
17306         this.fireEvent("disabledchange", this, false);
17307     },
17308
17309     // private
17310     renderChildren : function(suppressEvent){
17311         if(suppressEvent !== false){
17312             this.fireEvent("beforechildrenrendered", this);
17313         }
17314         var cs = this.childNodes;
17315         for(var i = 0, len = cs.length; i < len; i++){
17316             cs[i].render(true);
17317         }
17318         this.childrenRendered = true;
17319     },
17320
17321     // private
17322     sort : function(fn, scope){
17323         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
17324         if(this.childrenRendered){
17325             var cs = this.childNodes;
17326             for(var i = 0, len = cs.length; i < len; i++){
17327                 cs[i].render(true);
17328             }
17329         }
17330     },
17331
17332     // private
17333     render : function(bulkRender){
17334         this.ui.render(bulkRender);
17335         if(!this.rendered){
17336             this.rendered = true;
17337             if(this.expanded){
17338                 this.expanded = false;
17339                 this.expand(false, false);
17340             }
17341         }
17342     },
17343
17344     // private
17345     renderIndent : function(deep, refresh){
17346         if(refresh){
17347             this.ui.childIndent = null;
17348         }
17349         this.ui.renderIndent();
17350         if(deep === true && this.childrenRendered){
17351             var cs = this.childNodes;
17352             for(var i = 0, len = cs.length; i < len; i++){
17353                 cs[i].renderIndent(true, refresh);
17354             }
17355         }
17356     }
17357 });/*
17358  * Based on:
17359  * Ext JS Library 1.1.1
17360  * Copyright(c) 2006-2007, Ext JS, LLC.
17361  *
17362  * Originally Released Under LGPL - original licence link has changed is not relivant.
17363  *
17364  * Fork - LGPL
17365  * <script type="text/javascript">
17366  */
17367  
17368 /**
17369  * @class Roo.tree.AsyncTreeNode
17370  * @extends Roo.tree.TreeNode
17371  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
17372  * @constructor
17373  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
17374  */
17375  Roo.tree.AsyncTreeNode = function(config){
17376     this.loaded = false;
17377     this.loading = false;
17378     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
17379     /**
17380     * @event beforeload
17381     * Fires before this node is loaded, return false to cancel
17382     * @param {Node} this This node
17383     */
17384     this.addEvents({'beforeload':true, 'load': true});
17385     /**
17386     * @event load
17387     * Fires when this node is loaded
17388     * @param {Node} this This node
17389     */
17390     /**
17391      * The loader used by this node (defaults to using the tree's defined loader)
17392      * @type TreeLoader
17393      * @property loader
17394      */
17395 };
17396 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
17397     expand : function(deep, anim, callback){
17398         if(this.loading){ // if an async load is already running, waiting til it's done
17399             var timer;
17400             var f = function(){
17401                 if(!this.loading){ // done loading
17402                     clearInterval(timer);
17403                     this.expand(deep, anim, callback);
17404                 }
17405             }.createDelegate(this);
17406             timer = setInterval(f, 200);
17407             return;
17408         }
17409         if(!this.loaded){
17410             if(this.fireEvent("beforeload", this) === false){
17411                 return;
17412             }
17413             this.loading = true;
17414             this.ui.beforeLoad(this);
17415             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
17416             if(loader){
17417                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
17418                 return;
17419             }
17420         }
17421         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
17422     },
17423     
17424     /**
17425      * Returns true if this node is currently loading
17426      * @return {Boolean}
17427      */
17428     isLoading : function(){
17429         return this.loading;  
17430     },
17431     
17432     loadComplete : function(deep, anim, callback){
17433         this.loading = false;
17434         this.loaded = true;
17435         this.ui.afterLoad(this);
17436         this.fireEvent("load", this);
17437         this.expand(deep, anim, callback);
17438     },
17439     
17440     /**
17441      * Returns true if this node has been loaded
17442      * @return {Boolean}
17443      */
17444     isLoaded : function(){
17445         return this.loaded;
17446     },
17447     
17448     hasChildNodes : function(){
17449         if(!this.isLeaf() && !this.loaded){
17450             return true;
17451         }else{
17452             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
17453         }
17454     },
17455
17456     /**
17457      * Trigger a reload for this node
17458      * @param {Function} callback
17459      */
17460     reload : function(callback){
17461         this.collapse(false, false);
17462         while(this.firstChild){
17463             this.removeChild(this.firstChild);
17464         }
17465         this.childrenRendered = false;
17466         this.loaded = false;
17467         if(this.isHiddenRoot()){
17468             this.expanded = false;
17469         }
17470         this.expand(false, false, callback);
17471     }
17472 });/*
17473  * Based on:
17474  * Ext JS Library 1.1.1
17475  * Copyright(c) 2006-2007, Ext JS, LLC.
17476  *
17477  * Originally Released Under LGPL - original licence link has changed is not relivant.
17478  *
17479  * Fork - LGPL
17480  * <script type="text/javascript">
17481  */
17482  
17483 /**
17484  * @class Roo.tree.TreeNodeUI
17485  * @constructor
17486  * @param {Object} node The node to render
17487  * The TreeNode UI implementation is separate from the
17488  * tree implementation. Unless you are customizing the tree UI,
17489  * you should never have to use this directly.
17490  */
17491 Roo.tree.TreeNodeUI = function(node){
17492     this.node = node;
17493     this.rendered = false;
17494     this.animating = false;
17495     this.emptyIcon = Roo.BLANK_IMAGE_URL;
17496 };
17497
17498 Roo.tree.TreeNodeUI.prototype = {
17499     removeChild : function(node){
17500         if(this.rendered){
17501             this.ctNode.removeChild(node.ui.getEl());
17502         }
17503     },
17504
17505     beforeLoad : function(){
17506          this.addClass("x-tree-node-loading");
17507     },
17508
17509     afterLoad : function(){
17510          this.removeClass("x-tree-node-loading");
17511     },
17512
17513     onTextChange : function(node, text, oldText){
17514         if(this.rendered){
17515             this.textNode.innerHTML = text;
17516         }
17517     },
17518
17519     onDisableChange : function(node, state){
17520         this.disabled = state;
17521         if(state){
17522             this.addClass("x-tree-node-disabled");
17523         }else{
17524             this.removeClass("x-tree-node-disabled");
17525         }
17526     },
17527
17528     onSelectedChange : function(state){
17529         if(state){
17530             this.focus();
17531             this.addClass("x-tree-selected");
17532         }else{
17533             //this.blur();
17534             this.removeClass("x-tree-selected");
17535         }
17536     },
17537
17538     onMove : function(tree, node, oldParent, newParent, index, refNode){
17539         this.childIndent = null;
17540         if(this.rendered){
17541             var targetNode = newParent.ui.getContainer();
17542             if(!targetNode){//target not rendered
17543                 this.holder = document.createElement("div");
17544                 this.holder.appendChild(this.wrap);
17545                 return;
17546             }
17547             var insertBefore = refNode ? refNode.ui.getEl() : null;
17548             if(insertBefore){
17549                 targetNode.insertBefore(this.wrap, insertBefore);
17550             }else{
17551                 targetNode.appendChild(this.wrap);
17552             }
17553             this.node.renderIndent(true);
17554         }
17555     },
17556
17557     addClass : function(cls){
17558         if(this.elNode){
17559             Roo.fly(this.elNode).addClass(cls);
17560         }
17561     },
17562
17563     removeClass : function(cls){
17564         if(this.elNode){
17565             Roo.fly(this.elNode).removeClass(cls);
17566         }
17567     },
17568
17569     remove : function(){
17570         if(this.rendered){
17571             this.holder = document.createElement("div");
17572             this.holder.appendChild(this.wrap);
17573         }
17574     },
17575
17576     fireEvent : function(){
17577         return this.node.fireEvent.apply(this.node, arguments);
17578     },
17579
17580     initEvents : function(){
17581         this.node.on("move", this.onMove, this);
17582         var E = Roo.EventManager;
17583         var a = this.anchor;
17584
17585         var el = Roo.fly(a, '_treeui');
17586
17587         if(Roo.isOpera){ // opera render bug ignores the CSS
17588             el.setStyle("text-decoration", "none");
17589         }
17590
17591         el.on("click", this.onClick, this);
17592         el.on("dblclick", this.onDblClick, this);
17593
17594         if(this.checkbox){
17595             Roo.EventManager.on(this.checkbox,
17596                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
17597         }
17598
17599         el.on("contextmenu", this.onContextMenu, this);
17600
17601         var icon = Roo.fly(this.iconNode);
17602         icon.on("click", this.onClick, this);
17603         icon.on("dblclick", this.onDblClick, this);
17604         icon.on("contextmenu", this.onContextMenu, this);
17605         E.on(this.ecNode, "click", this.ecClick, this, true);
17606
17607         if(this.node.disabled){
17608             this.addClass("x-tree-node-disabled");
17609         }
17610         if(this.node.hidden){
17611             this.addClass("x-tree-node-disabled");
17612         }
17613         var ot = this.node.getOwnerTree();
17614         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
17615         if(dd && (!this.node.isRoot || ot.rootVisible)){
17616             Roo.dd.Registry.register(this.elNode, {
17617                 node: this.node,
17618                 handles: this.getDDHandles(),
17619                 isHandle: false
17620             });
17621         }
17622     },
17623
17624     getDDHandles : function(){
17625         return [this.iconNode, this.textNode];
17626     },
17627
17628     hide : function(){
17629         if(this.rendered){
17630             this.wrap.style.display = "none";
17631         }
17632     },
17633
17634     show : function(){
17635         if(this.rendered){
17636             this.wrap.style.display = "";
17637         }
17638     },
17639
17640     onContextMenu : function(e){
17641         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
17642             e.preventDefault();
17643             this.focus();
17644             this.fireEvent("contextmenu", this.node, e);
17645         }
17646     },
17647
17648     onClick : function(e){
17649         if(this.dropping){
17650             e.stopEvent();
17651             return;
17652         }
17653         if(this.fireEvent("beforeclick", this.node, e) !== false){
17654             if(!this.disabled && this.node.attributes.href){
17655                 this.fireEvent("click", this.node, e);
17656                 return;
17657             }
17658             e.preventDefault();
17659             if(this.disabled){
17660                 return;
17661             }
17662
17663             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
17664                 this.node.toggle();
17665             }
17666
17667             this.fireEvent("click", this.node, e);
17668         }else{
17669             e.stopEvent();
17670         }
17671     },
17672
17673     onDblClick : function(e){
17674         e.preventDefault();
17675         if(this.disabled){
17676             return;
17677         }
17678         if(this.checkbox){
17679             this.toggleCheck();
17680         }
17681         if(!this.animating && this.node.hasChildNodes()){
17682             this.node.toggle();
17683         }
17684         this.fireEvent("dblclick", this.node, e);
17685     },
17686
17687     onCheckChange : function(){
17688         var checked = this.checkbox.checked;
17689         this.node.attributes.checked = checked;
17690         this.fireEvent('checkchange', this.node, checked);
17691     },
17692
17693     ecClick : function(e){
17694         if(!this.animating && this.node.hasChildNodes()){
17695             this.node.toggle();
17696         }
17697     },
17698
17699     startDrop : function(){
17700         this.dropping = true;
17701     },
17702
17703     // delayed drop so the click event doesn't get fired on a drop
17704     endDrop : function(){
17705        setTimeout(function(){
17706            this.dropping = false;
17707        }.createDelegate(this), 50);
17708     },
17709
17710     expand : function(){
17711         this.updateExpandIcon();
17712         this.ctNode.style.display = "";
17713     },
17714
17715     focus : function(){
17716         if(!this.node.preventHScroll){
17717             try{this.anchor.focus();
17718             }catch(e){}
17719         }else if(!Roo.isIE){
17720             try{
17721                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
17722                 var l = noscroll.scrollLeft;
17723                 this.anchor.focus();
17724                 noscroll.scrollLeft = l;
17725             }catch(e){}
17726         }
17727     },
17728
17729     toggleCheck : function(value){
17730         var cb = this.checkbox;
17731         if(cb){
17732             cb.checked = (value === undefined ? !cb.checked : value);
17733         }
17734     },
17735
17736     blur : function(){
17737         try{
17738             this.anchor.blur();
17739         }catch(e){}
17740     },
17741
17742     animExpand : function(callback){
17743         var ct = Roo.get(this.ctNode);
17744         ct.stopFx();
17745         if(!this.node.hasChildNodes()){
17746             this.updateExpandIcon();
17747             this.ctNode.style.display = "";
17748             Roo.callback(callback);
17749             return;
17750         }
17751         this.animating = true;
17752         this.updateExpandIcon();
17753
17754         ct.slideIn('t', {
17755            callback : function(){
17756                this.animating = false;
17757                Roo.callback(callback);
17758             },
17759             scope: this,
17760             duration: this.node.ownerTree.duration || .25
17761         });
17762     },
17763
17764     highlight : function(){
17765         var tree = this.node.getOwnerTree();
17766         Roo.fly(this.wrap).highlight(
17767             tree.hlColor || "C3DAF9",
17768             {endColor: tree.hlBaseColor}
17769         );
17770     },
17771
17772     collapse : function(){
17773         this.updateExpandIcon();
17774         this.ctNode.style.display = "none";
17775     },
17776
17777     animCollapse : function(callback){
17778         var ct = Roo.get(this.ctNode);
17779         ct.enableDisplayMode('block');
17780         ct.stopFx();
17781
17782         this.animating = true;
17783         this.updateExpandIcon();
17784
17785         ct.slideOut('t', {
17786             callback : function(){
17787                this.animating = false;
17788                Roo.callback(callback);
17789             },
17790             scope: this,
17791             duration: this.node.ownerTree.duration || .25
17792         });
17793     },
17794
17795     getContainer : function(){
17796         return this.ctNode;
17797     },
17798
17799     getEl : function(){
17800         return this.wrap;
17801     },
17802
17803     appendDDGhost : function(ghostNode){
17804         ghostNode.appendChild(this.elNode.cloneNode(true));
17805     },
17806
17807     getDDRepairXY : function(){
17808         return Roo.lib.Dom.getXY(this.iconNode);
17809     },
17810
17811     onRender : function(){
17812         this.render();
17813     },
17814
17815     render : function(bulkRender){
17816         var n = this.node, a = n.attributes;
17817         var targetNode = n.parentNode ?
17818               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
17819
17820         if(!this.rendered){
17821             this.rendered = true;
17822
17823             this.renderElements(n, a, targetNode, bulkRender);
17824
17825             if(a.qtip){
17826                if(this.textNode.setAttributeNS){
17827                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
17828                    if(a.qtipTitle){
17829                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
17830                    }
17831                }else{
17832                    this.textNode.setAttribute("ext:qtip", a.qtip);
17833                    if(a.qtipTitle){
17834                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
17835                    }
17836                }
17837             }else if(a.qtipCfg){
17838                 a.qtipCfg.target = Roo.id(this.textNode);
17839                 Roo.QuickTips.register(a.qtipCfg);
17840             }
17841             this.initEvents();
17842             if(!this.node.expanded){
17843                 this.updateExpandIcon();
17844             }
17845         }else{
17846             if(bulkRender === true) {
17847                 targetNode.appendChild(this.wrap);
17848             }
17849         }
17850     },
17851
17852     renderElements : function(n, a, targetNode, bulkRender){
17853         // add some indent caching, this helps performance when rendering a large tree
17854         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
17855         var t = n.getOwnerTree();
17856         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
17857         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
17858         var cb = typeof a.checked == 'boolean';
17859         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
17860         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
17861             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
17862             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
17863             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
17864             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
17865             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
17866              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
17867                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
17868             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
17869             "</li>"];
17870
17871         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
17872             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
17873                                 n.nextSibling.ui.getEl(), buf.join(""));
17874         }else{
17875             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
17876         }
17877
17878         this.elNode = this.wrap.childNodes[0];
17879         this.ctNode = this.wrap.childNodes[1];
17880         var cs = this.elNode.childNodes;
17881         this.indentNode = cs[0];
17882         this.ecNode = cs[1];
17883         this.iconNode = cs[2];
17884         var index = 3;
17885         if(cb){
17886             this.checkbox = cs[3];
17887             index++;
17888         }
17889         this.anchor = cs[index];
17890         this.textNode = cs[index].firstChild;
17891     },
17892
17893     getAnchor : function(){
17894         return this.anchor;
17895     },
17896
17897     getTextEl : function(){
17898         return this.textNode;
17899     },
17900
17901     getIconEl : function(){
17902         return this.iconNode;
17903     },
17904
17905     isChecked : function(){
17906         return this.checkbox ? this.checkbox.checked : false;
17907     },
17908
17909     updateExpandIcon : function(){
17910         if(this.rendered){
17911             var n = this.node, c1, c2;
17912             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
17913             var hasChild = n.hasChildNodes();
17914             if(hasChild){
17915                 if(n.expanded){
17916                     cls += "-minus";
17917                     c1 = "x-tree-node-collapsed";
17918                     c2 = "x-tree-node-expanded";
17919                 }else{
17920                     cls += "-plus";
17921                     c1 = "x-tree-node-expanded";
17922                     c2 = "x-tree-node-collapsed";
17923                 }
17924                 if(this.wasLeaf){
17925                     this.removeClass("x-tree-node-leaf");
17926                     this.wasLeaf = false;
17927                 }
17928                 if(this.c1 != c1 || this.c2 != c2){
17929                     Roo.fly(this.elNode).replaceClass(c1, c2);
17930                     this.c1 = c1; this.c2 = c2;
17931                 }
17932             }else{
17933                 if(!this.wasLeaf){
17934                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
17935                     delete this.c1;
17936                     delete this.c2;
17937                     this.wasLeaf = true;
17938                 }
17939             }
17940             var ecc = "x-tree-ec-icon "+cls;
17941             if(this.ecc != ecc){
17942                 this.ecNode.className = ecc;
17943                 this.ecc = ecc;
17944             }
17945         }
17946     },
17947
17948     getChildIndent : function(){
17949         if(!this.childIndent){
17950             var buf = [];
17951             var p = this.node;
17952             while(p){
17953                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
17954                     if(!p.isLast()) {
17955                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
17956                     } else {
17957                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
17958                     }
17959                 }
17960                 p = p.parentNode;
17961             }
17962             this.childIndent = buf.join("");
17963         }
17964         return this.childIndent;
17965     },
17966
17967     renderIndent : function(){
17968         if(this.rendered){
17969             var indent = "";
17970             var p = this.node.parentNode;
17971             if(p){
17972                 indent = p.ui.getChildIndent();
17973             }
17974             if(this.indentMarkup != indent){ // don't rerender if not required
17975                 this.indentNode.innerHTML = indent;
17976                 this.indentMarkup = indent;
17977             }
17978             this.updateExpandIcon();
17979         }
17980     }
17981 };
17982
17983 Roo.tree.RootTreeNodeUI = function(){
17984     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
17985 };
17986 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
17987     render : function(){
17988         if(!this.rendered){
17989             var targetNode = this.node.ownerTree.innerCt.dom;
17990             this.node.expanded = true;
17991             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
17992             this.wrap = this.ctNode = targetNode.firstChild;
17993         }
17994     },
17995     collapse : function(){
17996     },
17997     expand : function(){
17998     }
17999 });/*
18000  * Based on:
18001  * Ext JS Library 1.1.1
18002  * Copyright(c) 2006-2007, Ext JS, LLC.
18003  *
18004  * Originally Released Under LGPL - original licence link has changed is not relivant.
18005  *
18006  * Fork - LGPL
18007  * <script type="text/javascript">
18008  */
18009 /**
18010  * @class Roo.tree.TreeLoader
18011  * @extends Roo.util.Observable
18012  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
18013  * nodes from a specified URL. The response must be a javascript Array definition
18014  * who's elements are node definition objects. eg:
18015  * <pre><code>
18016    [{ 'id': 1, 'text': 'A folder Node', 'leaf': false },
18017     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }]
18018 </code></pre>
18019  * <br><br>
18020  * A server request is sent, and child nodes are loaded only when a node is expanded.
18021  * The loading node's id is passed to the server under the parameter name "node" to
18022  * enable the server to produce the correct child nodes.
18023  * <br><br>
18024  * To pass extra parameters, an event handler may be attached to the "beforeload"
18025  * event, and the parameters specified in the TreeLoader's baseParams property:
18026  * <pre><code>
18027     myTreeLoader.on("beforeload", function(treeLoader, node) {
18028         this.baseParams.category = node.attributes.category;
18029     }, this);
18030 </code></pre><
18031  * This would pass an HTTP parameter called "category" to the server containing
18032  * the value of the Node's "category" attribute.
18033  * @constructor
18034  * Creates a new Treeloader.
18035  * @param {Object} config A config object containing config properties.
18036  */
18037 Roo.tree.TreeLoader = function(config){
18038     this.baseParams = {};
18039     this.requestMethod = "POST";
18040     Roo.apply(this, config);
18041
18042     this.addEvents({
18043     
18044         /**
18045          * @event beforeload
18046          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
18047          * @param {Object} This TreeLoader object.
18048          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18049          * @param {Object} callback The callback function specified in the {@link #load} call.
18050          */
18051         beforeload : true,
18052         /**
18053          * @event load
18054          * Fires when the node has been successfuly loaded.
18055          * @param {Object} This TreeLoader object.
18056          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18057          * @param {Object} response The response object containing the data from the server.
18058          */
18059         load : true,
18060         /**
18061          * @event loadexception
18062          * Fires if the network request failed.
18063          * @param {Object} This TreeLoader object.
18064          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18065          * @param {Object} response The response object containing the data from the server.
18066          */
18067         loadexception : true,
18068         /**
18069          * @event create
18070          * Fires before a node is created, enabling you to return custom Node types 
18071          * @param {Object} This TreeLoader object.
18072          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
18073          */
18074         create : true
18075     });
18076
18077     Roo.tree.TreeLoader.superclass.constructor.call(this);
18078 };
18079
18080 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
18081     /**
18082     * @cfg {String} dataUrl The URL from which to request a Json string which
18083     * specifies an array of node definition object representing the child nodes
18084     * to be loaded.
18085     */
18086     /**
18087     * @cfg {Object} baseParams (optional) An object containing properties which
18088     * specify HTTP parameters to be passed to each request for child nodes.
18089     */
18090     /**
18091     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
18092     * created by this loader. If the attributes sent by the server have an attribute in this object,
18093     * they take priority.
18094     */
18095     /**
18096     * @cfg {Object} uiProviders (optional) An object containing properties which
18097     * 
18098     * DEPRECIATED - use 'create' event handler to modify attributes - which affect creation.
18099     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
18100     * <i>uiProvider</i> attribute of a returned child node is a string rather
18101     * than a reference to a TreeNodeUI implementation, this that string value
18102     * is used as a property name in the uiProviders object. You can define the provider named
18103     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
18104     */
18105     uiProviders : {},
18106
18107     /**
18108     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
18109     * child nodes before loading.
18110     */
18111     clearOnLoad : true,
18112
18113     /**
18114     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
18115     * property on loading, rather than expecting an array. (eg. more compatible to a standard
18116     * Grid query { data : [ .....] }
18117     */
18118     
18119     root : false,
18120      /**
18121     * @cfg {String} queryParam (optional) 
18122     * Name of the query as it will be passed on the querystring (defaults to 'node')
18123     * eg. the request will be ?node=[id]
18124     */
18125     
18126     
18127     queryParam: false,
18128     
18129     /**
18130      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
18131      * This is called automatically when a node is expanded, but may be used to reload
18132      * a node (or append new children if the {@link #clearOnLoad} option is false.)
18133      * @param {Roo.tree.TreeNode} node
18134      * @param {Function} callback
18135      */
18136     load : function(node, callback){
18137         if(this.clearOnLoad){
18138             while(node.firstChild){
18139                 node.removeChild(node.firstChild);
18140             }
18141         }
18142         if(node.attributes.children){ // preloaded json children
18143             var cs = node.attributes.children;
18144             for(var i = 0, len = cs.length; i < len; i++){
18145                 node.appendChild(this.createNode(cs[i]));
18146             }
18147             if(typeof callback == "function"){
18148                 callback();
18149             }
18150         }else if(this.dataUrl){
18151             this.requestData(node, callback);
18152         }
18153     },
18154
18155     getParams: function(node){
18156         var buf = [], bp = this.baseParams;
18157         for(var key in bp){
18158             if(typeof bp[key] != "function"){
18159                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
18160             }
18161         }
18162         var n = this.queryParam === false ? 'node' : this.queryParam;
18163         buf.push(n + "=", encodeURIComponent(node.id));
18164         return buf.join("");
18165     },
18166
18167     requestData : function(node, callback){
18168         if(this.fireEvent("beforeload", this, node, callback) !== false){
18169             this.transId = Roo.Ajax.request({
18170                 method:this.requestMethod,
18171                 url: this.dataUrl||this.url,
18172                 success: this.handleResponse,
18173                 failure: this.handleFailure,
18174                 scope: this,
18175                 argument: {callback: callback, node: node},
18176                 params: this.getParams(node)
18177             });
18178         }else{
18179             // if the load is cancelled, make sure we notify
18180             // the node that we are done
18181             if(typeof callback == "function"){
18182                 callback();
18183             }
18184         }
18185     },
18186
18187     isLoading : function(){
18188         return this.transId ? true : false;
18189     },
18190
18191     abort : function(){
18192         if(this.isLoading()){
18193             Roo.Ajax.abort(this.transId);
18194         }
18195     },
18196
18197     // private
18198     createNode : function(attr){
18199         // apply baseAttrs, nice idea Corey!
18200         if(this.baseAttrs){
18201             Roo.applyIf(attr, this.baseAttrs);
18202         }
18203         if(this.applyLoader !== false){
18204             attr.loader = this;
18205         }
18206         // uiProvider = depreciated..
18207         
18208         if(typeof(attr.uiProvider) == 'string'){
18209            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
18210                 /**  eval:var:attr */ eval(attr.uiProvider);
18211         }
18212         if(typeof(this.uiProviders['default']) != 'undefined') {
18213             attr.uiProvider = this.uiProviders['default'];
18214         }
18215         
18216         this.fireEvent('create', this, attr);
18217         
18218         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
18219         return(attr.leaf ?
18220                         new Roo.tree.TreeNode(attr) :
18221                         new Roo.tree.AsyncTreeNode(attr));
18222     },
18223
18224     processResponse : function(response, node, callback){
18225         var json = response.responseText;
18226         try {
18227             
18228             var o = /**  eval:var:zzzzzzzzzz */ eval("("+json+")");
18229             if (this.root !== false) {
18230                 o = o[this.root];
18231             }
18232             
18233             for(var i = 0, len = o.length; i < len; i++){
18234                 var n = this.createNode(o[i]);
18235                 if(n){
18236                     node.appendChild(n);
18237                 }
18238             }
18239             if(typeof callback == "function"){
18240                 callback(this, node);
18241             }
18242         }catch(e){
18243             this.handleFailure(response);
18244         }
18245     },
18246
18247     handleResponse : function(response){
18248         this.transId = false;
18249         var a = response.argument;
18250         this.processResponse(response, a.node, a.callback);
18251         this.fireEvent("load", this, a.node, response);
18252     },
18253
18254     handleFailure : function(response){
18255         this.transId = false;
18256         var a = response.argument;
18257         this.fireEvent("loadexception", this, a.node, response);
18258         if(typeof a.callback == "function"){
18259             a.callback(this, a.node);
18260         }
18261     }
18262 });/*
18263  * Based on:
18264  * Ext JS Library 1.1.1
18265  * Copyright(c) 2006-2007, Ext JS, LLC.
18266  *
18267  * Originally Released Under LGPL - original licence link has changed is not relivant.
18268  *
18269  * Fork - LGPL
18270  * <script type="text/javascript">
18271  */
18272
18273 /**
18274 * @class Roo.tree.TreeFilter
18275 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
18276 * @param {TreePanel} tree
18277 * @param {Object} config (optional)
18278  */
18279 Roo.tree.TreeFilter = function(tree, config){
18280     this.tree = tree;
18281     this.filtered = {};
18282     Roo.apply(this, config);
18283 };
18284
18285 Roo.tree.TreeFilter.prototype = {
18286     clearBlank:false,
18287     reverse:false,
18288     autoClear:false,
18289     remove:false,
18290
18291      /**
18292      * Filter the data by a specific attribute.
18293      * @param {String/RegExp} value Either string that the attribute value
18294      * should start with or a RegExp to test against the attribute
18295      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
18296      * @param {TreeNode} startNode (optional) The node to start the filter at.
18297      */
18298     filter : function(value, attr, startNode){
18299         attr = attr || "text";
18300         var f;
18301         if(typeof value == "string"){
18302             var vlen = value.length;
18303             // auto clear empty filter
18304             if(vlen == 0 && this.clearBlank){
18305                 this.clear();
18306                 return;
18307             }
18308             value = value.toLowerCase();
18309             f = function(n){
18310                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
18311             };
18312         }else if(value.exec){ // regex?
18313             f = function(n){
18314                 return value.test(n.attributes[attr]);
18315             };
18316         }else{
18317             throw 'Illegal filter type, must be string or regex';
18318         }
18319         this.filterBy(f, null, startNode);
18320         },
18321
18322     /**
18323      * Filter by a function. The passed function will be called with each
18324      * node in the tree (or from the startNode). If the function returns true, the node is kept
18325      * otherwise it is filtered. If a node is filtered, its children are also filtered.
18326      * @param {Function} fn The filter function
18327      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
18328      */
18329     filterBy : function(fn, scope, startNode){
18330         startNode = startNode || this.tree.root;
18331         if(this.autoClear){
18332             this.clear();
18333         }
18334         var af = this.filtered, rv = this.reverse;
18335         var f = function(n){
18336             if(n == startNode){
18337                 return true;
18338             }
18339             if(af[n.id]){
18340                 return false;
18341             }
18342             var m = fn.call(scope || n, n);
18343             if(!m || rv){
18344                 af[n.id] = n;
18345                 n.ui.hide();
18346                 return false;
18347             }
18348             return true;
18349         };
18350         startNode.cascade(f);
18351         if(this.remove){
18352            for(var id in af){
18353                if(typeof id != "function"){
18354                    var n = af[id];
18355                    if(n && n.parentNode){
18356                        n.parentNode.removeChild(n);
18357                    }
18358                }
18359            }
18360         }
18361     },
18362
18363     /**
18364      * Clears the current filter. Note: with the "remove" option
18365      * set a filter cannot be cleared.
18366      */
18367     clear : function(){
18368         var t = this.tree;
18369         var af = this.filtered;
18370         for(var id in af){
18371             if(typeof id != "function"){
18372                 var n = af[id];
18373                 if(n){
18374                     n.ui.show();
18375                 }
18376             }
18377         }
18378         this.filtered = {};
18379     }
18380 };
18381 /*
18382  * Based on:
18383  * Ext JS Library 1.1.1
18384  * Copyright(c) 2006-2007, Ext JS, LLC.
18385  *
18386  * Originally Released Under LGPL - original licence link has changed is not relivant.
18387  *
18388  * Fork - LGPL
18389  * <script type="text/javascript">
18390  */
18391  
18392
18393 /**
18394  * @class Roo.tree.TreeSorter
18395  * Provides sorting of nodes in a TreePanel
18396  * 
18397  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
18398  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
18399  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
18400  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
18401  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
18402  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
18403  * @constructor
18404  * @param {TreePanel} tree
18405  * @param {Object} config
18406  */
18407 Roo.tree.TreeSorter = function(tree, config){
18408     Roo.apply(this, config);
18409     tree.on("beforechildrenrendered", this.doSort, this);
18410     tree.on("append", this.updateSort, this);
18411     tree.on("insert", this.updateSort, this);
18412     
18413     var dsc = this.dir && this.dir.toLowerCase() == "desc";
18414     var p = this.property || "text";
18415     var sortType = this.sortType;
18416     var fs = this.folderSort;
18417     var cs = this.caseSensitive === true;
18418     var leafAttr = this.leafAttr || 'leaf';
18419
18420     this.sortFn = function(n1, n2){
18421         if(fs){
18422             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
18423                 return 1;
18424             }
18425             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
18426                 return -1;
18427             }
18428         }
18429         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
18430         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
18431         if(v1 < v2){
18432                         return dsc ? +1 : -1;
18433                 }else if(v1 > v2){
18434                         return dsc ? -1 : +1;
18435         }else{
18436                 return 0;
18437         }
18438     };
18439 };
18440
18441 Roo.tree.TreeSorter.prototype = {
18442     doSort : function(node){
18443         node.sort(this.sortFn);
18444     },
18445     
18446     compareNodes : function(n1, n2){
18447         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
18448     },
18449     
18450     updateSort : function(tree, node){
18451         if(node.childrenRendered){
18452             this.doSort.defer(1, this, [node]);
18453         }
18454     }
18455 };/*
18456  * Based on:
18457  * Ext JS Library 1.1.1
18458  * Copyright(c) 2006-2007, Ext JS, LLC.
18459  *
18460  * Originally Released Under LGPL - original licence link has changed is not relivant.
18461  *
18462  * Fork - LGPL
18463  * <script type="text/javascript">
18464  */
18465
18466 if(Roo.dd.DropZone){
18467     
18468 Roo.tree.TreeDropZone = function(tree, config){
18469     this.allowParentInsert = false;
18470     this.allowContainerDrop = false;
18471     this.appendOnly = false;
18472     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
18473     this.tree = tree;
18474     this.lastInsertClass = "x-tree-no-status";
18475     this.dragOverData = {};
18476 };
18477
18478 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
18479     ddGroup : "TreeDD",
18480     
18481     expandDelay : 1000,
18482     
18483     expandNode : function(node){
18484         if(node.hasChildNodes() && !node.isExpanded()){
18485             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
18486         }
18487     },
18488     
18489     queueExpand : function(node){
18490         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
18491     },
18492     
18493     cancelExpand : function(){
18494         if(this.expandProcId){
18495             clearTimeout(this.expandProcId);
18496             this.expandProcId = false;
18497         }
18498     },
18499     
18500     isValidDropPoint : function(n, pt, dd, e, data){
18501         if(!n || !data){ return false; }
18502         var targetNode = n.node;
18503         var dropNode = data.node;
18504         // default drop rules
18505         if(!(targetNode && targetNode.isTarget && pt)){
18506             return false;
18507         }
18508         if(pt == "append" && targetNode.allowChildren === false){
18509             return false;
18510         }
18511         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
18512             return false;
18513         }
18514         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
18515             return false;
18516         }
18517         // reuse the object
18518         var overEvent = this.dragOverData;
18519         overEvent.tree = this.tree;
18520         overEvent.target = targetNode;
18521         overEvent.data = data;
18522         overEvent.point = pt;
18523         overEvent.source = dd;
18524         overEvent.rawEvent = e;
18525         overEvent.dropNode = dropNode;
18526         overEvent.cancel = false;  
18527         var result = this.tree.fireEvent("nodedragover", overEvent);
18528         return overEvent.cancel === false && result !== false;
18529     },
18530     
18531     getDropPoint : function(e, n, dd){
18532         var tn = n.node;
18533         if(tn.isRoot){
18534             return tn.allowChildren !== false ? "append" : false; // always append for root
18535         }
18536         var dragEl = n.ddel;
18537         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
18538         var y = Roo.lib.Event.getPageY(e);
18539         var noAppend = tn.allowChildren === false || tn.isLeaf();
18540         if(this.appendOnly || tn.parentNode.allowChildren === false){
18541             return noAppend ? false : "append";
18542         }
18543         var noBelow = false;
18544         if(!this.allowParentInsert){
18545             noBelow = tn.hasChildNodes() && tn.isExpanded();
18546         }
18547         var q = (b - t) / (noAppend ? 2 : 3);
18548         if(y >= t && y < (t + q)){
18549             return "above";
18550         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
18551             return "below";
18552         }else{
18553             return "append";
18554         }
18555     },
18556     
18557     onNodeEnter : function(n, dd, e, data){
18558         this.cancelExpand();
18559     },
18560     
18561     onNodeOver : function(n, dd, e, data){
18562         var pt = this.getDropPoint(e, n, dd);
18563         var node = n.node;
18564         
18565         // auto node expand check
18566         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
18567             this.queueExpand(node);
18568         }else if(pt != "append"){
18569             this.cancelExpand();
18570         }
18571         
18572         // set the insert point style on the target node
18573         var returnCls = this.dropNotAllowed;
18574         if(this.isValidDropPoint(n, pt, dd, e, data)){
18575            if(pt){
18576                var el = n.ddel;
18577                var cls;
18578                if(pt == "above"){
18579                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
18580                    cls = "x-tree-drag-insert-above";
18581                }else if(pt == "below"){
18582                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
18583                    cls = "x-tree-drag-insert-below";
18584                }else{
18585                    returnCls = "x-tree-drop-ok-append";
18586                    cls = "x-tree-drag-append";
18587                }
18588                if(this.lastInsertClass != cls){
18589                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
18590                    this.lastInsertClass = cls;
18591                }
18592            }
18593        }
18594        return returnCls;
18595     },
18596     
18597     onNodeOut : function(n, dd, e, data){
18598         this.cancelExpand();
18599         this.removeDropIndicators(n);
18600     },
18601     
18602     onNodeDrop : function(n, dd, e, data){
18603         var point = this.getDropPoint(e, n, dd);
18604         var targetNode = n.node;
18605         targetNode.ui.startDrop();
18606         if(!this.isValidDropPoint(n, point, dd, e, data)){
18607             targetNode.ui.endDrop();
18608             return false;
18609         }
18610         // first try to find the drop node
18611         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
18612         var dropEvent = {
18613             tree : this.tree,
18614             target: targetNode,
18615             data: data,
18616             point: point,
18617             source: dd,
18618             rawEvent: e,
18619             dropNode: dropNode,
18620             cancel: !dropNode   
18621         };
18622         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
18623         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
18624             targetNode.ui.endDrop();
18625             return false;
18626         }
18627         // allow target changing
18628         targetNode = dropEvent.target;
18629         if(point == "append" && !targetNode.isExpanded()){
18630             targetNode.expand(false, null, function(){
18631                 this.completeDrop(dropEvent);
18632             }.createDelegate(this));
18633         }else{
18634             this.completeDrop(dropEvent);
18635         }
18636         return true;
18637     },
18638     
18639     completeDrop : function(de){
18640         var ns = de.dropNode, p = de.point, t = de.target;
18641         if(!(ns instanceof Array)){
18642             ns = [ns];
18643         }
18644         var n;
18645         for(var i = 0, len = ns.length; i < len; i++){
18646             n = ns[i];
18647             if(p == "above"){
18648                 t.parentNode.insertBefore(n, t);
18649             }else if(p == "below"){
18650                 t.parentNode.insertBefore(n, t.nextSibling);
18651             }else{
18652                 t.appendChild(n);
18653             }
18654         }
18655         n.ui.focus();
18656         if(this.tree.hlDrop){
18657             n.ui.highlight();
18658         }
18659         t.ui.endDrop();
18660         this.tree.fireEvent("nodedrop", de);
18661     },
18662     
18663     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
18664         if(this.tree.hlDrop){
18665             dropNode.ui.focus();
18666             dropNode.ui.highlight();
18667         }
18668         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
18669     },
18670     
18671     getTree : function(){
18672         return this.tree;
18673     },
18674     
18675     removeDropIndicators : function(n){
18676         if(n && n.ddel){
18677             var el = n.ddel;
18678             Roo.fly(el).removeClass([
18679                     "x-tree-drag-insert-above",
18680                     "x-tree-drag-insert-below",
18681                     "x-tree-drag-append"]);
18682             this.lastInsertClass = "_noclass";
18683         }
18684     },
18685     
18686     beforeDragDrop : function(target, e, id){
18687         this.cancelExpand();
18688         return true;
18689     },
18690     
18691     afterRepair : function(data){
18692         if(data && Roo.enableFx){
18693             data.node.ui.highlight();
18694         }
18695         this.hideProxy();
18696     }    
18697 });
18698
18699 }/*
18700  * Based on:
18701  * Ext JS Library 1.1.1
18702  * Copyright(c) 2006-2007, Ext JS, LLC.
18703  *
18704  * Originally Released Under LGPL - original licence link has changed is not relivant.
18705  *
18706  * Fork - LGPL
18707  * <script type="text/javascript">
18708  */
18709  
18710
18711 if(Roo.dd.DragZone){
18712 Roo.tree.TreeDragZone = function(tree, config){
18713     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
18714     this.tree = tree;
18715 };
18716
18717 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
18718     ddGroup : "TreeDD",
18719     
18720     onBeforeDrag : function(data, e){
18721         var n = data.node;
18722         return n && n.draggable && !n.disabled;
18723     },
18724     
18725     onInitDrag : function(e){
18726         var data = this.dragData;
18727         this.tree.getSelectionModel().select(data.node);
18728         this.proxy.update("");
18729         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
18730         this.tree.fireEvent("startdrag", this.tree, data.node, e);
18731     },
18732     
18733     getRepairXY : function(e, data){
18734         return data.node.ui.getDDRepairXY();
18735     },
18736     
18737     onEndDrag : function(data, e){
18738         this.tree.fireEvent("enddrag", this.tree, data.node, e);
18739     },
18740     
18741     onValidDrop : function(dd, e, id){
18742         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
18743         this.hideProxy();
18744     },
18745     
18746     beforeInvalidDrop : function(e, id){
18747         // this scrolls the original position back into view
18748         var sm = this.tree.getSelectionModel();
18749         sm.clearSelections();
18750         sm.select(this.dragData.node);
18751     }
18752 });
18753 }/*
18754  * Based on:
18755  * Ext JS Library 1.1.1
18756  * Copyright(c) 2006-2007, Ext JS, LLC.
18757  *
18758  * Originally Released Under LGPL - original licence link has changed is not relivant.
18759  *
18760  * Fork - LGPL
18761  * <script type="text/javascript">
18762  */
18763 /**
18764  * @class Roo.tree.TreeEditor
18765  * @extends Roo.Editor
18766  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
18767  * as the editor field.
18768  * @constructor
18769  * @param {TreePanel} tree
18770  * @param {Object} config Either a prebuilt {@link Roo.form.Field} instance or a Field config object
18771  */
18772 Roo.tree.TreeEditor = function(tree, config){
18773     config = config || {};
18774     var field = config.events ? config : new Roo.form.TextField(config);
18775     Roo.tree.TreeEditor.superclass.constructor.call(this, field);
18776
18777     this.tree = tree;
18778
18779     tree.on('beforeclick', this.beforeNodeClick, this);
18780     tree.getTreeEl().on('mousedown', this.hide, this);
18781     this.on('complete', this.updateNode, this);
18782     this.on('beforestartedit', this.fitToTree, this);
18783     this.on('startedit', this.bindScroll, this, {delay:10});
18784     this.on('specialkey', this.onSpecialKey, this);
18785 };
18786
18787 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
18788     /**
18789      * @cfg {String} alignment
18790      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
18791      */
18792     alignment: "l-l",
18793     // inherit
18794     autoSize: false,
18795     /**
18796      * @cfg {Boolean} hideEl
18797      * True to hide the bound element while the editor is displayed (defaults to false)
18798      */
18799     hideEl : false,
18800     /**
18801      * @cfg {String} cls
18802      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
18803      */
18804     cls: "x-small-editor x-tree-editor",
18805     /**
18806      * @cfg {Boolean} shim
18807      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
18808      */
18809     shim:false,
18810     // inherit
18811     shadow:"frame",
18812     /**
18813      * @cfg {Number} maxWidth
18814      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
18815      * the containing tree element's size, it will be automatically limited for you to the container width, taking
18816      * scroll and client offsets into account prior to each edit.
18817      */
18818     maxWidth: 250,
18819
18820     editDelay : 350,
18821
18822     // private
18823     fitToTree : function(ed, el){
18824         var td = this.tree.getTreeEl().dom, nd = el.dom;
18825         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
18826             td.scrollLeft = nd.offsetLeft;
18827         }
18828         var w = Math.min(
18829                 this.maxWidth,
18830                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
18831         this.setSize(w, '');
18832     },
18833
18834     // private
18835     triggerEdit : function(node){
18836         this.completeEdit();
18837         this.editNode = node;
18838         this.startEdit(node.ui.textNode, node.text);
18839     },
18840
18841     // private
18842     bindScroll : function(){
18843         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
18844     },
18845
18846     // private
18847     beforeNodeClick : function(node, e){
18848         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
18849         this.lastClick = new Date();
18850         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
18851             e.stopEvent();
18852             this.triggerEdit(node);
18853             return false;
18854         }
18855     },
18856
18857     // private
18858     updateNode : function(ed, value){
18859         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
18860         this.editNode.setText(value);
18861     },
18862
18863     // private
18864     onHide : function(){
18865         Roo.tree.TreeEditor.superclass.onHide.call(this);
18866         if(this.editNode){
18867             this.editNode.ui.focus();
18868         }
18869     },
18870
18871     // private
18872     onSpecialKey : function(field, e){
18873         var k = e.getKey();
18874         if(k == e.ESC){
18875             e.stopEvent();
18876             this.cancelEdit();
18877         }else if(k == e.ENTER && !e.hasModifier()){
18878             e.stopEvent();
18879             this.completeEdit();
18880         }
18881     }
18882 });//<Script type="text/javascript">
18883 /*
18884  * Based on:
18885  * Ext JS Library 1.1.1
18886  * Copyright(c) 2006-2007, Ext JS, LLC.
18887  *
18888  * Originally Released Under LGPL - original licence link has changed is not relivant.
18889  *
18890  * Fork - LGPL
18891  * <script type="text/javascript">
18892  */
18893  
18894 /**
18895  * Not documented??? - probably should be...
18896  */
18897
18898 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
18899     //focus: Roo.emptyFn, // prevent odd scrolling behavior
18900     
18901     renderElements : function(n, a, targetNode, bulkRender){
18902         //consel.log("renderElements?");
18903         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
18904
18905         var t = n.getOwnerTree();
18906         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
18907         
18908         var cols = t.columns;
18909         var bw = t.borderWidth;
18910         var c = cols[0];
18911         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
18912          var cb = typeof a.checked == "boolean";
18913         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
18914         var colcls = 'x-t-' + tid + '-c0';
18915         var buf = [
18916             '<li class="x-tree-node">',
18917             
18918                 
18919                 '<div class="x-tree-node-el ', a.cls,'">',
18920                     // extran...
18921                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
18922                 
18923                 
18924                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
18925                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
18926                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
18927                            (a.icon ? ' x-tree-node-inline-icon' : ''),
18928                            (a.iconCls ? ' '+a.iconCls : ''),
18929                            '" unselectable="on" />',
18930                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
18931                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
18932                              
18933                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
18934                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
18935                             '<span unselectable="on" qtip="' + tx + '">',
18936                              tx,
18937                              '</span></a>' ,
18938                     '</div>',
18939                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
18940                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
18941                  ];
18942         
18943         for(var i = 1, len = cols.length; i < len; i++){
18944             c = cols[i];
18945             colcls = 'x-t-' + tid + '-c' +i;
18946             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
18947             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
18948                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
18949                       "</div>");
18950          }
18951          
18952          buf.push(
18953             '</a>',
18954             '<div class="x-clear"></div></div>',
18955             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
18956             "</li>");
18957         
18958         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
18959             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
18960                                 n.nextSibling.ui.getEl(), buf.join(""));
18961         }else{
18962             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
18963         }
18964         var el = this.wrap.firstChild;
18965         this.elRow = el;
18966         this.elNode = el.firstChild;
18967         this.ranchor = el.childNodes[1];
18968         this.ctNode = this.wrap.childNodes[1];
18969         var cs = el.firstChild.childNodes;
18970         this.indentNode = cs[0];
18971         this.ecNode = cs[1];
18972         this.iconNode = cs[2];
18973         var index = 3;
18974         if(cb){
18975             this.checkbox = cs[3];
18976             index++;
18977         }
18978         this.anchor = cs[index];
18979         
18980         this.textNode = cs[index].firstChild;
18981         
18982         //el.on("click", this.onClick, this);
18983         //el.on("dblclick", this.onDblClick, this);
18984         
18985         
18986        // console.log(this);
18987     },
18988     initEvents : function(){
18989         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
18990         
18991             
18992         var a = this.ranchor;
18993
18994         var el = Roo.get(a);
18995
18996         if(Roo.isOpera){ // opera render bug ignores the CSS
18997             el.setStyle("text-decoration", "none");
18998         }
18999
19000         el.on("click", this.onClick, this);
19001         el.on("dblclick", this.onDblClick, this);
19002         el.on("contextmenu", this.onContextMenu, this);
19003         
19004     },
19005     
19006     /*onSelectedChange : function(state){
19007         if(state){
19008             this.focus();
19009             this.addClass("x-tree-selected");
19010         }else{
19011             //this.blur();
19012             this.removeClass("x-tree-selected");
19013         }
19014     },*/
19015     addClass : function(cls){
19016         if(this.elRow){
19017             Roo.fly(this.elRow).addClass(cls);
19018         }
19019         
19020     },
19021     
19022     
19023     removeClass : function(cls){
19024         if(this.elRow){
19025             Roo.fly(this.elRow).removeClass(cls);
19026         }
19027     }
19028
19029     
19030     
19031 });//<Script type="text/javascript">
19032
19033 /*
19034  * Based on:
19035  * Ext JS Library 1.1.1
19036  * Copyright(c) 2006-2007, Ext JS, LLC.
19037  *
19038  * Originally Released Under LGPL - original licence link has changed is not relivant.
19039  *
19040  * Fork - LGPL
19041  * <script type="text/javascript">
19042  */
19043  
19044
19045 /**
19046  * @class Roo.tree.ColumnTree
19047  * @extends Roo.data.TreePanel
19048  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
19049  * @cfg {int} borderWidth  compined right/left border allowance
19050  * @constructor
19051  * @param {String/HTMLElement/Element} el The container element
19052  * @param {Object} config
19053  */
19054 Roo.tree.ColumnTree =  function(el, config)
19055 {
19056    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
19057    this.addEvents({
19058         /**
19059         * @event resize
19060         * Fire this event on a container when it resizes
19061         * @param {int} w Width
19062         * @param {int} h Height
19063         */
19064        "resize" : true
19065     });
19066     this.on('resize', this.onResize, this);
19067 };
19068
19069 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
19070     //lines:false,
19071     
19072     
19073     borderWidth: Roo.isBorderBox ? 0 : 2, 
19074     headEls : false,
19075     
19076     render : function(){
19077         // add the header.....
19078        
19079         Roo.tree.ColumnTree.superclass.render.apply(this);
19080         
19081         this.el.addClass('x-column-tree');
19082         
19083         this.headers = this.el.createChild(
19084             {cls:'x-tree-headers'},this.innerCt.dom);
19085    
19086         var cols = this.columns, c;
19087         var totalWidth = 0;
19088         this.headEls = [];
19089         var  len = cols.length;
19090         for(var i = 0; i < len; i++){
19091              c = cols[i];
19092              totalWidth += c.width;
19093             this.headEls.push(this.headers.createChild({
19094                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
19095                  cn: {
19096                      cls:'x-tree-hd-text',
19097                      html: c.header
19098                  },
19099                  style:'width:'+(c.width-this.borderWidth)+'px;'
19100              }));
19101         }
19102         this.headers.createChild({cls:'x-clear'});
19103         // prevent floats from wrapping when clipped
19104         this.headers.setWidth(totalWidth);
19105         //this.innerCt.setWidth(totalWidth);
19106         this.innerCt.setStyle({ overflow: 'auto' });
19107         this.onResize(this.width, this.height);
19108              
19109         
19110     },
19111     onResize : function(w,h)
19112     {
19113         this.height = h;
19114         this.width = w;
19115         // resize cols..
19116         this.innerCt.setWidth(this.width);
19117         this.innerCt.setHeight(this.height-20);
19118         
19119         // headers...
19120         var cols = this.columns, c;
19121         var totalWidth = 0;
19122         var expEl = false;
19123         var len = cols.length;
19124         for(var i = 0; i < len; i++){
19125             c = cols[i];
19126             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
19127                 // it's the expander..
19128                 expEl  = this.headEls[i];
19129                 continue;
19130             }
19131             totalWidth += c.width;
19132             
19133         }
19134         if (expEl) {
19135             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
19136         }
19137         this.headers.setWidth(w-20);
19138
19139         
19140         
19141         
19142     }
19143 });
19144 /*
19145  * Based on:
19146  * Ext JS Library 1.1.1
19147  * Copyright(c) 2006-2007, Ext JS, LLC.
19148  *
19149  * Originally Released Under LGPL - original licence link has changed is not relivant.
19150  *
19151  * Fork - LGPL
19152  * <script type="text/javascript">
19153  */
19154  
19155 /**
19156  * @class Roo.menu.Menu
19157  * @extends Roo.util.Observable
19158  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
19159  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
19160  * @constructor
19161  * Creates a new Menu
19162  * @param {Object} config Configuration options
19163  */
19164 Roo.menu.Menu = function(config){
19165     Roo.apply(this, config);
19166     this.id = this.id || Roo.id();
19167     this.addEvents({
19168         /**
19169          * @event beforeshow
19170          * Fires before this menu is displayed
19171          * @param {Roo.menu.Menu} this
19172          */
19173         beforeshow : true,
19174         /**
19175          * @event beforehide
19176          * Fires before this menu is hidden
19177          * @param {Roo.menu.Menu} this
19178          */
19179         beforehide : true,
19180         /**
19181          * @event show
19182          * Fires after this menu is displayed
19183          * @param {Roo.menu.Menu} this
19184          */
19185         show : true,
19186         /**
19187          * @event hide
19188          * Fires after this menu is hidden
19189          * @param {Roo.menu.Menu} this
19190          */
19191         hide : true,
19192         /**
19193          * @event click
19194          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19195          * @param {Roo.menu.Menu} this
19196          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19197          * @param {Roo.EventObject} e
19198          */
19199         click : true,
19200         /**
19201          * @event mouseover
19202          * Fires when the mouse is hovering over this menu
19203          * @param {Roo.menu.Menu} this
19204          * @param {Roo.EventObject} e
19205          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19206          */
19207         mouseover : true,
19208         /**
19209          * @event mouseout
19210          * Fires when the mouse exits this menu
19211          * @param {Roo.menu.Menu} this
19212          * @param {Roo.EventObject} e
19213          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19214          */
19215         mouseout : true,
19216         /**
19217          * @event itemclick
19218          * Fires when a menu item contained in this menu is clicked
19219          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
19220          * @param {Roo.EventObject} e
19221          */
19222         itemclick: true
19223     });
19224     if (this.registerMenu) {
19225         Roo.menu.MenuMgr.register(this);
19226     }
19227     
19228     var mis = this.items;
19229     this.items = new Roo.util.MixedCollection();
19230     if(mis){
19231         this.add.apply(this, mis);
19232     }
19233 };
19234
19235 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
19236     /**
19237      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
19238      */
19239     minWidth : 120,
19240     /**
19241      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
19242      * for bottom-right shadow (defaults to "sides")
19243      */
19244     shadow : "sides",
19245     /**
19246      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
19247      * this menu (defaults to "tl-tr?")
19248      */
19249     subMenuAlign : "tl-tr?",
19250     /**
19251      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
19252      * relative to its element of origin (defaults to "tl-bl?")
19253      */
19254     defaultAlign : "tl-bl?",
19255     /**
19256      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
19257      */
19258     allowOtherMenus : false,
19259     /**
19260      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
19261      */
19262     registerMenu : true,
19263
19264     hidden:true,
19265
19266     // private
19267     render : function(){
19268         if(this.el){
19269             return;
19270         }
19271         var el = this.el = new Roo.Layer({
19272             cls: "x-menu",
19273             shadow:this.shadow,
19274             constrain: false,
19275             parentEl: this.parentEl || document.body,
19276             zindex:15000
19277         });
19278
19279         this.keyNav = new Roo.menu.MenuNav(this);
19280
19281         if(this.plain){
19282             el.addClass("x-menu-plain");
19283         }
19284         if(this.cls){
19285             el.addClass(this.cls);
19286         }
19287         // generic focus element
19288         this.focusEl = el.createChild({
19289             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
19290         });
19291         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
19292         ul.on("click", this.onClick, this);
19293         ul.on("mouseover", this.onMouseOver, this);
19294         ul.on("mouseout", this.onMouseOut, this);
19295         this.items.each(function(item){
19296             var li = document.createElement("li");
19297             li.className = "x-menu-list-item";
19298             ul.dom.appendChild(li);
19299             item.render(li, this);
19300         }, this);
19301         this.ul = ul;
19302         this.autoWidth();
19303     },
19304
19305     // private
19306     autoWidth : function(){
19307         var el = this.el, ul = this.ul;
19308         if(!el){
19309             return;
19310         }
19311         var w = this.width;
19312         if(w){
19313             el.setWidth(w);
19314         }else if(Roo.isIE){
19315             el.setWidth(this.minWidth);
19316             var t = el.dom.offsetWidth; // force recalc
19317             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
19318         }
19319     },
19320
19321     // private
19322     delayAutoWidth : function(){
19323         if(this.rendered){
19324             if(!this.awTask){
19325                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
19326             }
19327             this.awTask.delay(20);
19328         }
19329     },
19330
19331     // private
19332     findTargetItem : function(e){
19333         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
19334         if(t && t.menuItemId){
19335             return this.items.get(t.menuItemId);
19336         }
19337     },
19338
19339     // private
19340     onClick : function(e){
19341         var t;
19342         if(t = this.findTargetItem(e)){
19343             t.onClick(e);
19344             this.fireEvent("click", this, t, e);
19345         }
19346     },
19347
19348     // private
19349     setActiveItem : function(item, autoExpand){
19350         if(item != this.activeItem){
19351             if(this.activeItem){
19352                 this.activeItem.deactivate();
19353             }
19354             this.activeItem = item;
19355             item.activate(autoExpand);
19356         }else if(autoExpand){
19357             item.expandMenu();
19358         }
19359     },
19360
19361     // private
19362     tryActivate : function(start, step){
19363         var items = this.items;
19364         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
19365             var item = items.get(i);
19366             if(!item.disabled && item.canActivate){
19367                 this.setActiveItem(item, false);
19368                 return item;
19369             }
19370         }
19371         return false;
19372     },
19373
19374     // private
19375     onMouseOver : function(e){
19376         var t;
19377         if(t = this.findTargetItem(e)){
19378             if(t.canActivate && !t.disabled){
19379                 this.setActiveItem(t, true);
19380             }
19381         }
19382         this.fireEvent("mouseover", this, e, t);
19383     },
19384
19385     // private
19386     onMouseOut : function(e){
19387         var t;
19388         if(t = this.findTargetItem(e)){
19389             if(t == this.activeItem && t.shouldDeactivate(e)){
19390                 this.activeItem.deactivate();
19391                 delete this.activeItem;
19392             }
19393         }
19394         this.fireEvent("mouseout", this, e, t);
19395     },
19396
19397     /**
19398      * Read-only.  Returns true if the menu is currently displayed, else false.
19399      * @type Boolean
19400      */
19401     isVisible : function(){
19402         return this.el && !this.hidden;
19403     },
19404
19405     /**
19406      * Displays this menu relative to another element
19407      * @param {String/HTMLElement/Roo.Element} element The element to align to
19408      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
19409      * the element (defaults to this.defaultAlign)
19410      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19411      */
19412     show : function(el, pos, parentMenu){
19413         this.parentMenu = parentMenu;
19414         if(!this.el){
19415             this.render();
19416         }
19417         this.fireEvent("beforeshow", this);
19418         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
19419     },
19420
19421     /**
19422      * Displays this menu at a specific xy position
19423      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
19424      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19425      */
19426     showAt : function(xy, parentMenu, /* private: */_e){
19427         this.parentMenu = parentMenu;
19428         if(!this.el){
19429             this.render();
19430         }
19431         if(_e !== false){
19432             this.fireEvent("beforeshow", this);
19433             xy = this.el.adjustForConstraints(xy);
19434         }
19435         this.el.setXY(xy);
19436         this.el.show();
19437         this.hidden = false;
19438         this.focus();
19439         this.fireEvent("show", this);
19440     },
19441
19442     focus : function(){
19443         if(!this.hidden){
19444             this.doFocus.defer(50, this);
19445         }
19446     },
19447
19448     doFocus : function(){
19449         if(!this.hidden){
19450             this.focusEl.focus();
19451         }
19452     },
19453
19454     /**
19455      * Hides this menu and optionally all parent menus
19456      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
19457      */
19458     hide : function(deep){
19459         if(this.el && this.isVisible()){
19460             this.fireEvent("beforehide", this);
19461             if(this.activeItem){
19462                 this.activeItem.deactivate();
19463                 this.activeItem = null;
19464             }
19465             this.el.hide();
19466             this.hidden = true;
19467             this.fireEvent("hide", this);
19468         }
19469         if(deep === true && this.parentMenu){
19470             this.parentMenu.hide(true);
19471         }
19472     },
19473
19474     /**
19475      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
19476      * Any of the following are valid:
19477      * <ul>
19478      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
19479      * <li>An HTMLElement object which will be converted to a menu item</li>
19480      * <li>A menu item config object that will be created as a new menu item</li>
19481      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
19482      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
19483      * </ul>
19484      * Usage:
19485      * <pre><code>
19486 // Create the menu
19487 var menu = new Roo.menu.Menu();
19488
19489 // Create a menu item to add by reference
19490 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
19491
19492 // Add a bunch of items at once using different methods.
19493 // Only the last item added will be returned.
19494 var item = menu.add(
19495     menuItem,                // add existing item by ref
19496     'Dynamic Item',          // new TextItem
19497     '-',                     // new separator
19498     { text: 'Config Item' }  // new item by config
19499 );
19500 </code></pre>
19501      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
19502      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
19503      */
19504     add : function(){
19505         var a = arguments, l = a.length, item;
19506         for(var i = 0; i < l; i++){
19507             var el = a[i];
19508             if(el.render){ // some kind of Item
19509                 item = this.addItem(el);
19510             }else if(typeof el == "string"){ // string
19511                 if(el == "separator" || el == "-"){
19512                     item = this.addSeparator();
19513                 }else{
19514                     item = this.addText(el);
19515                 }
19516             }else if(el.tagName || el.el){ // element
19517                 item = this.addElement(el);
19518             }else if(typeof el == "object"){ // must be menu item config?
19519                 item = this.addMenuItem(el);
19520             }
19521         }
19522         return item;
19523     },
19524
19525     /**
19526      * Returns this menu's underlying {@link Roo.Element} object
19527      * @return {Roo.Element} The element
19528      */
19529     getEl : function(){
19530         if(!this.el){
19531             this.render();
19532         }
19533         return this.el;
19534     },
19535
19536     /**
19537      * Adds a separator bar to the menu
19538      * @return {Roo.menu.Item} The menu item that was added
19539      */
19540     addSeparator : function(){
19541         return this.addItem(new Roo.menu.Separator());
19542     },
19543
19544     /**
19545      * Adds an {@link Roo.Element} object to the menu
19546      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
19547      * @return {Roo.menu.Item} The menu item that was added
19548      */
19549     addElement : function(el){
19550         return this.addItem(new Roo.menu.BaseItem(el));
19551     },
19552
19553     /**
19554      * Adds an existing object based on {@link Roo.menu.Item} to the menu
19555      * @param {Roo.menu.Item} item The menu item to add
19556      * @return {Roo.menu.Item} The menu item that was added
19557      */
19558     addItem : function(item){
19559         this.items.add(item);
19560         if(this.ul){
19561             var li = document.createElement("li");
19562             li.className = "x-menu-list-item";
19563             this.ul.dom.appendChild(li);
19564             item.render(li, this);
19565             this.delayAutoWidth();
19566         }
19567         return item;
19568     },
19569
19570     /**
19571      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
19572      * @param {Object} config A MenuItem config object
19573      * @return {Roo.menu.Item} The menu item that was added
19574      */
19575     addMenuItem : function(config){
19576         if(!(config instanceof Roo.menu.Item)){
19577             if(typeof config.checked == "boolean"){ // must be check menu item config?
19578                 config = new Roo.menu.CheckItem(config);
19579             }else{
19580                 config = new Roo.menu.Item(config);
19581             }
19582         }
19583         return this.addItem(config);
19584     },
19585
19586     /**
19587      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
19588      * @param {String} text The text to display in the menu item
19589      * @return {Roo.menu.Item} The menu item that was added
19590      */
19591     addText : function(text){
19592         return this.addItem(new Roo.menu.TextItem(text));
19593     },
19594
19595     /**
19596      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
19597      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
19598      * @param {Roo.menu.Item} item The menu item to add
19599      * @return {Roo.menu.Item} The menu item that was added
19600      */
19601     insert : function(index, item){
19602         this.items.insert(index, item);
19603         if(this.ul){
19604             var li = document.createElement("li");
19605             li.className = "x-menu-list-item";
19606             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
19607             item.render(li, this);
19608             this.delayAutoWidth();
19609         }
19610         return item;
19611     },
19612
19613     /**
19614      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
19615      * @param {Roo.menu.Item} item The menu item to remove
19616      */
19617     remove : function(item){
19618         this.items.removeKey(item.id);
19619         item.destroy();
19620     },
19621
19622     /**
19623      * Removes and destroys all items in the menu
19624      */
19625     removeAll : function(){
19626         var f;
19627         while(f = this.items.first()){
19628             this.remove(f);
19629         }
19630     }
19631 });
19632
19633 // MenuNav is a private utility class used internally by the Menu
19634 Roo.menu.MenuNav = function(menu){
19635     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
19636     this.scope = this.menu = menu;
19637 };
19638
19639 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
19640     doRelay : function(e, h){
19641         var k = e.getKey();
19642         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
19643             this.menu.tryActivate(0, 1);
19644             return false;
19645         }
19646         return h.call(this.scope || this, e, this.menu);
19647     },
19648
19649     up : function(e, m){
19650         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
19651             m.tryActivate(m.items.length-1, -1);
19652         }
19653     },
19654
19655     down : function(e, m){
19656         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
19657             m.tryActivate(0, 1);
19658         }
19659     },
19660
19661     right : function(e, m){
19662         if(m.activeItem){
19663             m.activeItem.expandMenu(true);
19664         }
19665     },
19666
19667     left : function(e, m){
19668         m.hide();
19669         if(m.parentMenu && m.parentMenu.activeItem){
19670             m.parentMenu.activeItem.activate();
19671         }
19672     },
19673
19674     enter : function(e, m){
19675         if(m.activeItem){
19676             e.stopPropagation();
19677             m.activeItem.onClick(e);
19678             m.fireEvent("click", this, m.activeItem);
19679             return true;
19680         }
19681     }
19682 });/*
19683  * Based on:
19684  * Ext JS Library 1.1.1
19685  * Copyright(c) 2006-2007, Ext JS, LLC.
19686  *
19687  * Originally Released Under LGPL - original licence link has changed is not relivant.
19688  *
19689  * Fork - LGPL
19690  * <script type="text/javascript">
19691  */
19692  
19693 /**
19694  * @class Roo.menu.MenuMgr
19695  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
19696  * @singleton
19697  */
19698 Roo.menu.MenuMgr = function(){
19699    var menus, active, groups = {}, attached = false, lastShow = new Date();
19700
19701    // private - called when first menu is created
19702    function init(){
19703        menus = {};
19704        active = new Roo.util.MixedCollection();
19705        Roo.get(document).addKeyListener(27, function(){
19706            if(active.length > 0){
19707                hideAll();
19708            }
19709        });
19710    }
19711
19712    // private
19713    function hideAll(){
19714        if(active && active.length > 0){
19715            var c = active.clone();
19716            c.each(function(m){
19717                m.hide();
19718            });
19719        }
19720    }
19721
19722    // private
19723    function onHide(m){
19724        active.remove(m);
19725        if(active.length < 1){
19726            Roo.get(document).un("mousedown", onMouseDown);
19727            attached = false;
19728        }
19729    }
19730
19731    // private
19732    function onShow(m){
19733        var last = active.last();
19734        lastShow = new Date();
19735        active.add(m);
19736        if(!attached){
19737            Roo.get(document).on("mousedown", onMouseDown);
19738            attached = true;
19739        }
19740        if(m.parentMenu){
19741           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
19742           m.parentMenu.activeChild = m;
19743        }else if(last && last.isVisible()){
19744           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
19745        }
19746    }
19747
19748    // private
19749    function onBeforeHide(m){
19750        if(m.activeChild){
19751            m.activeChild.hide();
19752        }
19753        if(m.autoHideTimer){
19754            clearTimeout(m.autoHideTimer);
19755            delete m.autoHideTimer;
19756        }
19757    }
19758
19759    // private
19760    function onBeforeShow(m){
19761        var pm = m.parentMenu;
19762        if(!pm && !m.allowOtherMenus){
19763            hideAll();
19764        }else if(pm && pm.activeChild && active != m){
19765            pm.activeChild.hide();
19766        }
19767    }
19768
19769    // private
19770    function onMouseDown(e){
19771        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
19772            hideAll();
19773        }
19774    }
19775
19776    // private
19777    function onBeforeCheck(mi, state){
19778        if(state){
19779            var g = groups[mi.group];
19780            for(var i = 0, l = g.length; i < l; i++){
19781                if(g[i] != mi){
19782                    g[i].setChecked(false);
19783                }
19784            }
19785        }
19786    }
19787
19788    return {
19789
19790        /**
19791         * Hides all menus that are currently visible
19792         */
19793        hideAll : function(){
19794             hideAll();  
19795        },
19796
19797        // private
19798        register : function(menu){
19799            if(!menus){
19800                init();
19801            }
19802            menus[menu.id] = menu;
19803            menu.on("beforehide", onBeforeHide);
19804            menu.on("hide", onHide);
19805            menu.on("beforeshow", onBeforeShow);
19806            menu.on("show", onShow);
19807            var g = menu.group;
19808            if(g && menu.events["checkchange"]){
19809                if(!groups[g]){
19810                    groups[g] = [];
19811                }
19812                groups[g].push(menu);
19813                menu.on("checkchange", onCheck);
19814            }
19815        },
19816
19817         /**
19818          * Returns a {@link Roo.menu.Menu} object
19819          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
19820          * be used to generate and return a new Menu instance.
19821          */
19822        get : function(menu){
19823            if(typeof menu == "string"){ // menu id
19824                return menus[menu];
19825            }else if(menu.events){  // menu instance
19826                return menu;
19827            }else if(typeof menu.length == 'number'){ // array of menu items?
19828                return new Roo.menu.Menu({items:menu});
19829            }else{ // otherwise, must be a config
19830                return new Roo.menu.Menu(menu);
19831            }
19832        },
19833
19834        // private
19835        unregister : function(menu){
19836            delete menus[menu.id];
19837            menu.un("beforehide", onBeforeHide);
19838            menu.un("hide", onHide);
19839            menu.un("beforeshow", onBeforeShow);
19840            menu.un("show", onShow);
19841            var g = menu.group;
19842            if(g && menu.events["checkchange"]){
19843                groups[g].remove(menu);
19844                menu.un("checkchange", onCheck);
19845            }
19846        },
19847
19848        // private
19849        registerCheckable : function(menuItem){
19850            var g = menuItem.group;
19851            if(g){
19852                if(!groups[g]){
19853                    groups[g] = [];
19854                }
19855                groups[g].push(menuItem);
19856                menuItem.on("beforecheckchange", onBeforeCheck);
19857            }
19858        },
19859
19860        // private
19861        unregisterCheckable : function(menuItem){
19862            var g = menuItem.group;
19863            if(g){
19864                groups[g].remove(menuItem);
19865                menuItem.un("beforecheckchange", onBeforeCheck);
19866            }
19867        }
19868    };
19869 }();/*
19870  * Based on:
19871  * Ext JS Library 1.1.1
19872  * Copyright(c) 2006-2007, Ext JS, LLC.
19873  *
19874  * Originally Released Under LGPL - original licence link has changed is not relivant.
19875  *
19876  * Fork - LGPL
19877  * <script type="text/javascript">
19878  */
19879  
19880
19881 /**
19882  * @class Roo.menu.BaseItem
19883  * @extends Roo.Component
19884  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
19885  * management and base configuration options shared by all menu components.
19886  * @constructor
19887  * Creates a new BaseItem
19888  * @param {Object} config Configuration options
19889  */
19890 Roo.menu.BaseItem = function(config){
19891     Roo.menu.BaseItem.superclass.constructor.call(this, config);
19892
19893     this.addEvents({
19894         /**
19895          * @event click
19896          * Fires when this item is clicked
19897          * @param {Roo.menu.BaseItem} this
19898          * @param {Roo.EventObject} e
19899          */
19900         click: true,
19901         /**
19902          * @event activate
19903          * Fires when this item is activated
19904          * @param {Roo.menu.BaseItem} this
19905          */
19906         activate : true,
19907         /**
19908          * @event deactivate
19909          * Fires when this item is deactivated
19910          * @param {Roo.menu.BaseItem} this
19911          */
19912         deactivate : true
19913     });
19914
19915     if(this.handler){
19916         this.on("click", this.handler, this.scope, true);
19917     }
19918 };
19919
19920 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
19921     /**
19922      * @cfg {Function} handler
19923      * A function that will handle the click event of this menu item (defaults to undefined)
19924      */
19925     /**
19926      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
19927      */
19928     canActivate : false,
19929     /**
19930      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
19931      */
19932     activeClass : "x-menu-item-active",
19933     /**
19934      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
19935      */
19936     hideOnClick : true,
19937     /**
19938      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
19939      */
19940     hideDelay : 100,
19941
19942     // private
19943     ctype: "Roo.menu.BaseItem",
19944
19945     // private
19946     actionMode : "container",
19947
19948     // private
19949     render : function(container, parentMenu){
19950         this.parentMenu = parentMenu;
19951         Roo.menu.BaseItem.superclass.render.call(this, container);
19952         this.container.menuItemId = this.id;
19953     },
19954
19955     // private
19956     onRender : function(container, position){
19957         this.el = Roo.get(this.el);
19958         container.dom.appendChild(this.el.dom);
19959     },
19960
19961     // private
19962     onClick : function(e){
19963         if(!this.disabled && this.fireEvent("click", this, e) !== false
19964                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
19965             this.handleClick(e);
19966         }else{
19967             e.stopEvent();
19968         }
19969     },
19970
19971     // private
19972     activate : function(){
19973         if(this.disabled){
19974             return false;
19975         }
19976         var li = this.container;
19977         li.addClass(this.activeClass);
19978         this.region = li.getRegion().adjust(2, 2, -2, -2);
19979         this.fireEvent("activate", this);
19980         return true;
19981     },
19982
19983     // private
19984     deactivate : function(){
19985         this.container.removeClass(this.activeClass);
19986         this.fireEvent("deactivate", this);
19987     },
19988
19989     // private
19990     shouldDeactivate : function(e){
19991         return !this.region || !this.region.contains(e.getPoint());
19992     },
19993
19994     // private
19995     handleClick : function(e){
19996         if(this.hideOnClick){
19997             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
19998         }
19999     },
20000
20001     // private
20002     expandMenu : function(autoActivate){
20003         // do nothing
20004     },
20005
20006     // private
20007     hideMenu : function(){
20008         // do nothing
20009     }
20010 });/*
20011  * Based on:
20012  * Ext JS Library 1.1.1
20013  * Copyright(c) 2006-2007, Ext JS, LLC.
20014  *
20015  * Originally Released Under LGPL - original licence link has changed is not relivant.
20016  *
20017  * Fork - LGPL
20018  * <script type="text/javascript">
20019  */
20020  
20021 /**
20022  * @class Roo.menu.Adapter
20023  * @extends Roo.menu.BaseItem
20024  * 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.
20025  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
20026  * @constructor
20027  * Creates a new Adapter
20028  * @param {Object} config Configuration options
20029  */
20030 Roo.menu.Adapter = function(component, config){
20031     Roo.menu.Adapter.superclass.constructor.call(this, config);
20032     this.component = component;
20033 };
20034 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
20035     // private
20036     canActivate : true,
20037
20038     // private
20039     onRender : function(container, position){
20040         this.component.render(container);
20041         this.el = this.component.getEl();
20042     },
20043
20044     // private
20045     activate : function(){
20046         if(this.disabled){
20047             return false;
20048         }
20049         this.component.focus();
20050         this.fireEvent("activate", this);
20051         return true;
20052     },
20053
20054     // private
20055     deactivate : function(){
20056         this.fireEvent("deactivate", this);
20057     },
20058
20059     // private
20060     disable : function(){
20061         this.component.disable();
20062         Roo.menu.Adapter.superclass.disable.call(this);
20063     },
20064
20065     // private
20066     enable : function(){
20067         this.component.enable();
20068         Roo.menu.Adapter.superclass.enable.call(this);
20069     }
20070 });/*
20071  * Based on:
20072  * Ext JS Library 1.1.1
20073  * Copyright(c) 2006-2007, Ext JS, LLC.
20074  *
20075  * Originally Released Under LGPL - original licence link has changed is not relivant.
20076  *
20077  * Fork - LGPL
20078  * <script type="text/javascript">
20079  */
20080
20081 /**
20082  * @class Roo.menu.TextItem
20083  * @extends Roo.menu.BaseItem
20084  * Adds a static text string to a menu, usually used as either a heading or group separator.
20085  * @constructor
20086  * Creates a new TextItem
20087  * @param {String} text The text to display
20088  */
20089 Roo.menu.TextItem = function(text){
20090     this.text = text;
20091     Roo.menu.TextItem.superclass.constructor.call(this);
20092 };
20093
20094 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
20095     /**
20096      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20097      */
20098     hideOnClick : false,
20099     /**
20100      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
20101      */
20102     itemCls : "x-menu-text",
20103
20104     // private
20105     onRender : function(){
20106         var s = document.createElement("span");
20107         s.className = this.itemCls;
20108         s.innerHTML = this.text;
20109         this.el = s;
20110         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
20111     }
20112 });/*
20113  * Based on:
20114  * Ext JS Library 1.1.1
20115  * Copyright(c) 2006-2007, Ext JS, LLC.
20116  *
20117  * Originally Released Under LGPL - original licence link has changed is not relivant.
20118  *
20119  * Fork - LGPL
20120  * <script type="text/javascript">
20121  */
20122
20123 /**
20124  * @class Roo.menu.Separator
20125  * @extends Roo.menu.BaseItem
20126  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
20127  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
20128  * @constructor
20129  * @param {Object} config Configuration options
20130  */
20131 Roo.menu.Separator = function(config){
20132     Roo.menu.Separator.superclass.constructor.call(this, config);
20133 };
20134
20135 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
20136     /**
20137      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
20138      */
20139     itemCls : "x-menu-sep",
20140     /**
20141      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20142      */
20143     hideOnClick : false,
20144
20145     // private
20146     onRender : function(li){
20147         var s = document.createElement("span");
20148         s.className = this.itemCls;
20149         s.innerHTML = "&#160;";
20150         this.el = s;
20151         li.addClass("x-menu-sep-li");
20152         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
20153     }
20154 });/*
20155  * Based on:
20156  * Ext JS Library 1.1.1
20157  * Copyright(c) 2006-2007, Ext JS, LLC.
20158  *
20159  * Originally Released Under LGPL - original licence link has changed is not relivant.
20160  *
20161  * Fork - LGPL
20162  * <script type="text/javascript">
20163  */
20164 /**
20165  * @class Roo.menu.Item
20166  * @extends Roo.menu.BaseItem
20167  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
20168  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
20169  * activation and click handling.
20170  * @constructor
20171  * Creates a new Item
20172  * @param {Object} config Configuration options
20173  */
20174 Roo.menu.Item = function(config){
20175     Roo.menu.Item.superclass.constructor.call(this, config);
20176     if(this.menu){
20177         this.menu = Roo.menu.MenuMgr.get(this.menu);
20178     }
20179 };
20180 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
20181     /**
20182      * @cfg {String} icon
20183      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
20184      */
20185     /**
20186      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
20187      */
20188     itemCls : "x-menu-item",
20189     /**
20190      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
20191      */
20192     canActivate : true,
20193     /**
20194      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
20195      */
20196     showDelay: 200,
20197     // doc'd in BaseItem
20198     hideDelay: 200,
20199
20200     // private
20201     ctype: "Roo.menu.Item",
20202     
20203     // private
20204     onRender : function(container, position){
20205         var el = document.createElement("a");
20206         el.hideFocus = true;
20207         el.unselectable = "on";
20208         el.href = this.href || "#";
20209         if(this.hrefTarget){
20210             el.target = this.hrefTarget;
20211         }
20212         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
20213         el.innerHTML = String.format(
20214                 '<img src="{0}" class="x-menu-item-icon {2}" />{1}',
20215                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || '');
20216         this.el = el;
20217         Roo.menu.Item.superclass.onRender.call(this, container, position);
20218     },
20219
20220     /**
20221      * Sets the text to display in this menu item
20222      * @param {String} text The text to display
20223      */
20224     setText : function(text){
20225         this.text = text;
20226         if(this.rendered){
20227             this.el.update(String.format(
20228                 '<img src="{0}" class="x-menu-item-icon {2}">{1}',
20229                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
20230             this.parentMenu.autoWidth();
20231         }
20232     },
20233
20234     // private
20235     handleClick : function(e){
20236         if(!this.href){ // if no link defined, stop the event automatically
20237             e.stopEvent();
20238         }
20239         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
20240     },
20241
20242     // private
20243     activate : function(autoExpand){
20244         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
20245             this.focus();
20246             if(autoExpand){
20247                 this.expandMenu();
20248             }
20249         }
20250         return true;
20251     },
20252
20253     // private
20254     shouldDeactivate : function(e){
20255         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
20256             if(this.menu && this.menu.isVisible()){
20257                 return !this.menu.getEl().getRegion().contains(e.getPoint());
20258             }
20259             return true;
20260         }
20261         return false;
20262     },
20263
20264     // private
20265     deactivate : function(){
20266         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
20267         this.hideMenu();
20268     },
20269
20270     // private
20271     expandMenu : function(autoActivate){
20272         if(!this.disabled && this.menu){
20273             clearTimeout(this.hideTimer);
20274             delete this.hideTimer;
20275             if(!this.menu.isVisible() && !this.showTimer){
20276                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
20277             }else if (this.menu.isVisible() && autoActivate){
20278                 this.menu.tryActivate(0, 1);
20279             }
20280         }
20281     },
20282
20283     // private
20284     deferExpand : function(autoActivate){
20285         delete this.showTimer;
20286         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
20287         if(autoActivate){
20288             this.menu.tryActivate(0, 1);
20289         }
20290     },
20291
20292     // private
20293     hideMenu : function(){
20294         clearTimeout(this.showTimer);
20295         delete this.showTimer;
20296         if(!this.hideTimer && this.menu && this.menu.isVisible()){
20297             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
20298         }
20299     },
20300
20301     // private
20302     deferHide : function(){
20303         delete this.hideTimer;
20304         this.menu.hide();
20305     }
20306 });/*
20307  * Based on:
20308  * Ext JS Library 1.1.1
20309  * Copyright(c) 2006-2007, Ext JS, LLC.
20310  *
20311  * Originally Released Under LGPL - original licence link has changed is not relivant.
20312  *
20313  * Fork - LGPL
20314  * <script type="text/javascript">
20315  */
20316  
20317 /**
20318  * @class Roo.menu.CheckItem
20319  * @extends Roo.menu.Item
20320  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
20321  * @constructor
20322  * Creates a new CheckItem
20323  * @param {Object} config Configuration options
20324  */
20325 Roo.menu.CheckItem = function(config){
20326     Roo.menu.CheckItem.superclass.constructor.call(this, config);
20327     this.addEvents({
20328         /**
20329          * @event beforecheckchange
20330          * Fires before the checked value is set, providing an opportunity to cancel if needed
20331          * @param {Roo.menu.CheckItem} this
20332          * @param {Boolean} checked The new checked value that will be set
20333          */
20334         "beforecheckchange" : true,
20335         /**
20336          * @event checkchange
20337          * Fires after the checked value has been set
20338          * @param {Roo.menu.CheckItem} this
20339          * @param {Boolean} checked The checked value that was set
20340          */
20341         "checkchange" : true
20342     });
20343     if(this.checkHandler){
20344         this.on('checkchange', this.checkHandler, this.scope);
20345     }
20346 };
20347 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
20348     /**
20349      * @cfg {String} group
20350      * All check items with the same group name will automatically be grouped into a single-select
20351      * radio button group (defaults to '')
20352      */
20353     /**
20354      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
20355      */
20356     itemCls : "x-menu-item x-menu-check-item",
20357     /**
20358      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
20359      */
20360     groupClass : "x-menu-group-item",
20361
20362     /**
20363      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
20364      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
20365      * initialized with checked = true will be rendered as checked.
20366      */
20367     checked: false,
20368
20369     // private
20370     ctype: "Roo.menu.CheckItem",
20371
20372     // private
20373     onRender : function(c){
20374         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
20375         if(this.group){
20376             this.el.addClass(this.groupClass);
20377         }
20378         Roo.menu.MenuMgr.registerCheckable(this);
20379         if(this.checked){
20380             this.checked = false;
20381             this.setChecked(true, true);
20382         }
20383     },
20384
20385     // private
20386     destroy : function(){
20387         if(this.rendered){
20388             Roo.menu.MenuMgr.unregisterCheckable(this);
20389         }
20390         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
20391     },
20392
20393     /**
20394      * Set the checked state of this item
20395      * @param {Boolean} checked The new checked value
20396      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
20397      */
20398     setChecked : function(state, suppressEvent){
20399         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
20400             if(this.container){
20401                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
20402             }
20403             this.checked = state;
20404             if(suppressEvent !== true){
20405                 this.fireEvent("checkchange", this, state);
20406             }
20407         }
20408     },
20409
20410     // private
20411     handleClick : function(e){
20412        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
20413            this.setChecked(!this.checked);
20414        }
20415        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
20416     }
20417 });/*
20418  * Based on:
20419  * Ext JS Library 1.1.1
20420  * Copyright(c) 2006-2007, Ext JS, LLC.
20421  *
20422  * Originally Released Under LGPL - original licence link has changed is not relivant.
20423  *
20424  * Fork - LGPL
20425  * <script type="text/javascript">
20426  */
20427  
20428 /**
20429  * @class Roo.menu.DateItem
20430  * @extends Roo.menu.Adapter
20431  * A menu item that wraps the {@link Roo.DatPicker} component.
20432  * @constructor
20433  * Creates a new DateItem
20434  * @param {Object} config Configuration options
20435  */
20436 Roo.menu.DateItem = function(config){
20437     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
20438     /** The Roo.DatePicker object @type Roo.DatePicker */
20439     this.picker = this.component;
20440     this.addEvents({select: true});
20441     
20442     this.picker.on("render", function(picker){
20443         picker.getEl().swallowEvent("click");
20444         picker.container.addClass("x-menu-date-item");
20445     });
20446
20447     this.picker.on("select", this.onSelect, this);
20448 };
20449
20450 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
20451     // private
20452     onSelect : function(picker, date){
20453         this.fireEvent("select", this, date, picker);
20454         Roo.menu.DateItem.superclass.handleClick.call(this);
20455     }
20456 });/*
20457  * Based on:
20458  * Ext JS Library 1.1.1
20459  * Copyright(c) 2006-2007, Ext JS, LLC.
20460  *
20461  * Originally Released Under LGPL - original licence link has changed is not relivant.
20462  *
20463  * Fork - LGPL
20464  * <script type="text/javascript">
20465  */
20466  
20467 /**
20468  * @class Roo.menu.ColorItem
20469  * @extends Roo.menu.Adapter
20470  * A menu item that wraps the {@link Roo.ColorPalette} component.
20471  * @constructor
20472  * Creates a new ColorItem
20473  * @param {Object} config Configuration options
20474  */
20475 Roo.menu.ColorItem = function(config){
20476     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
20477     /** The Roo.ColorPalette object @type Roo.ColorPalette */
20478     this.palette = this.component;
20479     this.relayEvents(this.palette, ["select"]);
20480     if(this.selectHandler){
20481         this.on('select', this.selectHandler, this.scope);
20482     }
20483 };
20484 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
20485  * Based on:
20486  * Ext JS Library 1.1.1
20487  * Copyright(c) 2006-2007, Ext JS, LLC.
20488  *
20489  * Originally Released Under LGPL - original licence link has changed is not relivant.
20490  *
20491  * Fork - LGPL
20492  * <script type="text/javascript">
20493  */
20494  
20495
20496 /**
20497  * @class Roo.menu.DateMenu
20498  * @extends Roo.menu.Menu
20499  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
20500  * @constructor
20501  * Creates a new DateMenu
20502  * @param {Object} config Configuration options
20503  */
20504 Roo.menu.DateMenu = function(config){
20505     Roo.menu.DateMenu.superclass.constructor.call(this, config);
20506     this.plain = true;
20507     var di = new Roo.menu.DateItem(config);
20508     this.add(di);
20509     /**
20510      * The {@link Roo.DatePicker} instance for this DateMenu
20511      * @type DatePicker
20512      */
20513     this.picker = di.picker;
20514     /**
20515      * @event select
20516      * @param {DatePicker} picker
20517      * @param {Date} date
20518      */
20519     this.relayEvents(di, ["select"]);
20520
20521     this.on('beforeshow', function(){
20522         if(this.picker){
20523             this.picker.hideMonthPicker(true);
20524         }
20525     }, this);
20526 };
20527 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
20528     cls:'x-date-menu'
20529 });/*
20530  * Based on:
20531  * Ext JS Library 1.1.1
20532  * Copyright(c) 2006-2007, Ext JS, LLC.
20533  *
20534  * Originally Released Under LGPL - original licence link has changed is not relivant.
20535  *
20536  * Fork - LGPL
20537  * <script type="text/javascript">
20538  */
20539  
20540
20541 /**
20542  * @class Roo.menu.ColorMenu
20543  * @extends Roo.menu.Menu
20544  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
20545  * @constructor
20546  * Creates a new ColorMenu
20547  * @param {Object} config Configuration options
20548  */
20549 Roo.menu.ColorMenu = function(config){
20550     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
20551     this.plain = true;
20552     var ci = new Roo.menu.ColorItem(config);
20553     this.add(ci);
20554     /**
20555      * The {@link Roo.ColorPalette} instance for this ColorMenu
20556      * @type ColorPalette
20557      */
20558     this.palette = ci.palette;
20559     /**
20560      * @event select
20561      * @param {ColorPalette} palette
20562      * @param {String} color
20563      */
20564     this.relayEvents(ci, ["select"]);
20565 };
20566 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
20567  * Based on:
20568  * Ext JS Library 1.1.1
20569  * Copyright(c) 2006-2007, Ext JS, LLC.
20570  *
20571  * Originally Released Under LGPL - original licence link has changed is not relivant.
20572  *
20573  * Fork - LGPL
20574  * <script type="text/javascript">
20575  */
20576  
20577 /**
20578  * @class Roo.form.Field
20579  * @extends Roo.BoxComponent
20580  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
20581  * @constructor
20582  * Creates a new Field
20583  * @param {Object} config Configuration options
20584  */
20585 Roo.form.Field = function(config){
20586     Roo.form.Field.superclass.constructor.call(this, config);
20587 };
20588
20589 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
20590     /**
20591      * @cfg {String} fieldLabel Label to use when rendering a form.
20592      */
20593        /**
20594      * @cfg {String} qtip Mouse over tip
20595      */
20596      
20597     /**
20598      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
20599      */
20600     invalidClass : "x-form-invalid",
20601     /**
20602      * @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")
20603      */
20604     invalidText : "The value in this field is invalid",
20605     /**
20606      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
20607      */
20608     focusClass : "x-form-focus",
20609     /**
20610      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
20611       automatic validation (defaults to "keyup").
20612      */
20613     validationEvent : "keyup",
20614     /**
20615      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
20616      */
20617     validateOnBlur : true,
20618     /**
20619      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
20620      */
20621     validationDelay : 250,
20622     /**
20623      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20624      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
20625      */
20626     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
20627     /**
20628      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
20629      */
20630     fieldClass : "x-form-field",
20631     /**
20632      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
20633      *<pre>
20634 Value         Description
20635 -----------   ----------------------------------------------------------------------
20636 qtip          Display a quick tip when the user hovers over the field
20637 title         Display a default browser title attribute popup
20638 under         Add a block div beneath the field containing the error text
20639 side          Add an error icon to the right of the field with a popup on hover
20640 [element id]  Add the error text directly to the innerHTML of the specified element
20641 </pre>
20642      */
20643     msgTarget : 'qtip',
20644     /**
20645      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
20646      */
20647     msgFx : 'normal',
20648
20649     /**
20650      * @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.
20651      */
20652     readOnly : false,
20653
20654     /**
20655      * @cfg {Boolean} disabled True to disable the field (defaults to false).
20656      */
20657     disabled : false,
20658
20659     /**
20660      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
20661      */
20662     inputType : undefined,
20663     
20664     /**
20665      * @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).
20666          */
20667         tabIndex : undefined,
20668         
20669     // private
20670     isFormField : true,
20671
20672     // private
20673     hasFocus : false,
20674     /**
20675      * @property {Roo.Element} fieldEl
20676      * Element Containing the rendered Field (with label etc.)
20677      */
20678     /**
20679      * @cfg {Mixed} value A value to initialize this field with.
20680      */
20681     value : undefined,
20682
20683     /**
20684      * @cfg {String} name The field's HTML name attribute.
20685      */
20686     /**
20687      * @cfg {String} cls A CSS class to apply to the field's underlying element.
20688      */
20689
20690         // private ??
20691         initComponent : function(){
20692         Roo.form.Field.superclass.initComponent.call(this);
20693         this.addEvents({
20694             /**
20695              * @event focus
20696              * Fires when this field receives input focus.
20697              * @param {Roo.form.Field} this
20698              */
20699             focus : true,
20700             /**
20701              * @event blur
20702              * Fires when this field loses input focus.
20703              * @param {Roo.form.Field} this
20704              */
20705             blur : true,
20706             /**
20707              * @event specialkey
20708              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
20709              * {@link Roo.EventObject#getKey} to determine which key was pressed.
20710              * @param {Roo.form.Field} this
20711              * @param {Roo.EventObject} e The event object
20712              */
20713             specialkey : true,
20714             /**
20715              * @event change
20716              * Fires just before the field blurs if the field value has changed.
20717              * @param {Roo.form.Field} this
20718              * @param {Mixed} newValue The new value
20719              * @param {Mixed} oldValue The original value
20720              */
20721             change : true,
20722             /**
20723              * @event invalid
20724              * Fires after the field has been marked as invalid.
20725              * @param {Roo.form.Field} this
20726              * @param {String} msg The validation message
20727              */
20728             invalid : true,
20729             /**
20730              * @event valid
20731              * Fires after the field has been validated with no errors.
20732              * @param {Roo.form.Field} this
20733              */
20734             valid : true
20735         });
20736     },
20737
20738     /**
20739      * Returns the name attribute of the field if available
20740      * @return {String} name The field name
20741      */
20742     getName: function(){
20743          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
20744     },
20745
20746     // private
20747     onRender : function(ct, position){
20748         Roo.form.Field.superclass.onRender.call(this, ct, position);
20749         if(!this.el){
20750             var cfg = this.getAutoCreate();
20751             if(!cfg.name){
20752                 cfg.name = this.name || this.id;
20753             }
20754             if(this.inputType){
20755                 cfg.type = this.inputType;
20756             }
20757             this.el = ct.createChild(cfg, position);
20758         }
20759         var type = this.el.dom.type;
20760         if(type){
20761             if(type == 'password'){
20762                 type = 'text';
20763             }
20764             this.el.addClass('x-form-'+type);
20765         }
20766         if(this.readOnly){
20767             this.el.dom.readOnly = true;
20768         }
20769         if(this.tabIndex !== undefined){
20770             this.el.dom.setAttribute('tabIndex', this.tabIndex);
20771         }
20772
20773         this.el.addClass([this.fieldClass, this.cls]);
20774         this.initValue();
20775     },
20776
20777     /**
20778      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
20779      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
20780      * @return {Roo.form.Field} this
20781      */
20782     applyTo : function(target){
20783         this.allowDomMove = false;
20784         this.el = Roo.get(target);
20785         this.render(this.el.dom.parentNode);
20786         return this;
20787     },
20788
20789     // private
20790     initValue : function(){
20791         if(this.value !== undefined){
20792             this.setValue(this.value);
20793         }else if(this.el.dom.value.length > 0){
20794             this.setValue(this.el.dom.value);
20795         }
20796     },
20797
20798     /**
20799      * Returns true if this field has been changed since it was originally loaded and is not disabled.
20800      */
20801     isDirty : function() {
20802         if(this.disabled) {
20803             return false;
20804         }
20805         return String(this.getValue()) !== String(this.originalValue);
20806     },
20807
20808     // private
20809     afterRender : function(){
20810         Roo.form.Field.superclass.afterRender.call(this);
20811         this.initEvents();
20812     },
20813
20814     // private
20815     fireKey : function(e){
20816         if(e.isNavKeyPress()){
20817             this.fireEvent("specialkey", this, e);
20818         }
20819     },
20820
20821     /**
20822      * Resets the current field value to the originally loaded value and clears any validation messages
20823      */
20824     reset : function(){
20825         this.setValue(this.originalValue);
20826         this.clearInvalid();
20827     },
20828
20829     // private
20830     initEvents : function(){
20831         this.el.on(Roo.isIE ? "keydown" : "keypress", this.fireKey,  this);
20832         this.el.on("focus", this.onFocus,  this);
20833         this.el.on("blur", this.onBlur,  this);
20834
20835         // reference to original value for reset
20836         this.originalValue = this.getValue();
20837     },
20838
20839     // private
20840     onFocus : function(){
20841         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
20842             this.el.addClass(this.focusClass);
20843         }
20844         if(!this.hasFocus){
20845             this.hasFocus = true;
20846             this.startValue = this.getValue();
20847             this.fireEvent("focus", this);
20848         }
20849     },
20850
20851     beforeBlur : Roo.emptyFn,
20852
20853     // private
20854     onBlur : function(){
20855         this.beforeBlur();
20856         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
20857             this.el.removeClass(this.focusClass);
20858         }
20859         this.hasFocus = false;
20860         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
20861             this.validate();
20862         }
20863         var v = this.getValue();
20864         if(String(v) !== String(this.startValue)){
20865             this.fireEvent('change', this, v, this.startValue);
20866         }
20867         this.fireEvent("blur", this);
20868     },
20869
20870     /**
20871      * Returns whether or not the field value is currently valid
20872      * @param {Boolean} preventMark True to disable marking the field invalid
20873      * @return {Boolean} True if the value is valid, else false
20874      */
20875     isValid : function(preventMark){
20876         if(this.disabled){
20877             return true;
20878         }
20879         var restore = this.preventMark;
20880         this.preventMark = preventMark === true;
20881         var v = this.validateValue(this.processValue(this.getRawValue()));
20882         this.preventMark = restore;
20883         return v;
20884     },
20885
20886     /**
20887      * Validates the field value
20888      * @return {Boolean} True if the value is valid, else false
20889      */
20890     validate : function(){
20891         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
20892             this.clearInvalid();
20893             return true;
20894         }
20895         return false;
20896     },
20897
20898     processValue : function(value){
20899         return value;
20900     },
20901
20902     // private
20903     // Subclasses should provide the validation implementation by overriding this
20904     validateValue : function(value){
20905         return true;
20906     },
20907
20908     /**
20909      * Mark this field as invalid
20910      * @param {String} msg The validation message
20911      */
20912     markInvalid : function(msg){
20913         if(!this.rendered || this.preventMark){ // not rendered
20914             return;
20915         }
20916         this.el.addClass(this.invalidClass);
20917         msg = msg || this.invalidText;
20918         switch(this.msgTarget){
20919             case 'qtip':
20920                 this.el.dom.qtip = msg;
20921                 this.el.dom.qclass = 'x-form-invalid-tip';
20922                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
20923                     Roo.QuickTips.enable();
20924                 }
20925                 break;
20926             case 'title':
20927                 this.el.dom.title = msg;
20928                 break;
20929             case 'under':
20930                 if(!this.errorEl){
20931                     var elp = this.el.findParent('.x-form-element', 5, true);
20932                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
20933                     this.errorEl.setWidth(elp.getWidth(true)-20);
20934                 }
20935                 this.errorEl.update(msg);
20936                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
20937                 break;
20938             case 'side':
20939                 if(!this.errorIcon){
20940                     var elp = this.el.findParent('.x-form-element', 5, true);
20941                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
20942                 }
20943                 this.alignErrorIcon();
20944                 this.errorIcon.dom.qtip = msg;
20945                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
20946                 this.errorIcon.show();
20947                 this.on('resize', this.alignErrorIcon, this);
20948                 break;
20949             default:
20950                 var t = Roo.getDom(this.msgTarget);
20951                 t.innerHTML = msg;
20952                 t.style.display = this.msgDisplay;
20953                 break;
20954         }
20955         this.fireEvent('invalid', this, msg);
20956     },
20957
20958     // private
20959     alignErrorIcon : function(){
20960         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
20961     },
20962
20963     /**
20964      * Clear any invalid styles/messages for this field
20965      */
20966     clearInvalid : function(){
20967         if(!this.rendered || this.preventMark){ // not rendered
20968             return;
20969         }
20970         this.el.removeClass(this.invalidClass);
20971         switch(this.msgTarget){
20972             case 'qtip':
20973                 this.el.dom.qtip = '';
20974                 break;
20975             case 'title':
20976                 this.el.dom.title = '';
20977                 break;
20978             case 'under':
20979                 if(this.errorEl){
20980                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
20981                 }
20982                 break;
20983             case 'side':
20984                 if(this.errorIcon){
20985                     this.errorIcon.dom.qtip = '';
20986                     this.errorIcon.hide();
20987                     this.un('resize', this.alignErrorIcon, this);
20988                 }
20989                 break;
20990             default:
20991                 var t = Roo.getDom(this.msgTarget);
20992                 t.innerHTML = '';
20993                 t.style.display = 'none';
20994                 break;
20995         }
20996         this.fireEvent('valid', this);
20997     },
20998
20999     /**
21000      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
21001      * @return {Mixed} value The field value
21002      */
21003     getRawValue : function(){
21004         var v = this.el.getValue();
21005         if(v === this.emptyText){
21006             v = '';
21007         }
21008         return v;
21009     },
21010
21011     /**
21012      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
21013      * @return {Mixed} value The field value
21014      */
21015     getValue : function(){
21016         var v = this.el.getValue();
21017         if(v === this.emptyText || v === undefined){
21018             v = '';
21019         }
21020         return v;
21021     },
21022
21023     /**
21024      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
21025      * @param {Mixed} value The value to set
21026      */
21027     setRawValue : function(v){
21028         return this.el.dom.value = (v === null || v === undefined ? '' : v);
21029     },
21030
21031     /**
21032      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
21033      * @param {Mixed} value The value to set
21034      */
21035     setValue : function(v){
21036         this.value = v;
21037         if(this.rendered){
21038             this.el.dom.value = (v === null || v === undefined ? '' : v);
21039             this.validate();
21040         }
21041     },
21042
21043     adjustSize : function(w, h){
21044         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
21045         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
21046         return s;
21047     },
21048
21049     adjustWidth : function(tag, w){
21050         tag = tag.toLowerCase();
21051         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
21052             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
21053                 if(tag == 'input'){
21054                     return w + 2;
21055                 }
21056                 if(tag = 'textarea'){
21057                     return w-2;
21058                 }
21059             }else if(Roo.isOpera){
21060                 if(tag == 'input'){
21061                     return w + 2;
21062                 }
21063                 if(tag = 'textarea'){
21064                     return w-2;
21065                 }
21066             }
21067         }
21068         return w;
21069     }
21070 });
21071
21072
21073 // anything other than normal should be considered experimental
21074 Roo.form.Field.msgFx = {
21075     normal : {
21076         show: function(msgEl, f){
21077             msgEl.setDisplayed('block');
21078         },
21079
21080         hide : function(msgEl, f){
21081             msgEl.setDisplayed(false).update('');
21082         }
21083     },
21084
21085     slide : {
21086         show: function(msgEl, f){
21087             msgEl.slideIn('t', {stopFx:true});
21088         },
21089
21090         hide : function(msgEl, f){
21091             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
21092         }
21093     },
21094
21095     slideRight : {
21096         show: function(msgEl, f){
21097             msgEl.fixDisplay();
21098             msgEl.alignTo(f.el, 'tl-tr');
21099             msgEl.slideIn('l', {stopFx:true});
21100         },
21101
21102         hide : function(msgEl, f){
21103             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
21104         }
21105     }
21106 };/*
21107  * Based on:
21108  * Ext JS Library 1.1.1
21109  * Copyright(c) 2006-2007, Ext JS, LLC.
21110  *
21111  * Originally Released Under LGPL - original licence link has changed is not relivant.
21112  *
21113  * Fork - LGPL
21114  * <script type="text/javascript">
21115  */
21116  
21117
21118 /**
21119  * @class Roo.form.TextField
21120  * @extends Roo.form.Field
21121  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
21122  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
21123  * @constructor
21124  * Creates a new TextField
21125  * @param {Object} config Configuration options
21126  */
21127 Roo.form.TextField = function(config){
21128     Roo.form.TextField.superclass.constructor.call(this, config);
21129     this.addEvents({
21130         /**
21131          * @event autosize
21132          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
21133          * according to the default logic, but this event provides a hook for the developer to apply additional
21134          * logic at runtime to resize the field if needed.
21135              * @param {Roo.form.Field} this This text field
21136              * @param {Number} width The new field width
21137              */
21138         autosize : true
21139     });
21140 };
21141
21142 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
21143     /**
21144      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
21145      */
21146     grow : false,
21147     /**
21148      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
21149      */
21150     growMin : 30,
21151     /**
21152      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
21153      */
21154     growMax : 800,
21155     /**
21156      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
21157      */
21158     vtype : null,
21159     /**
21160      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
21161      */
21162     maskRe : null,
21163     /**
21164      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
21165      */
21166     disableKeyFilter : false,
21167     /**
21168      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
21169      */
21170     allowBlank : true,
21171     /**
21172      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
21173      */
21174     minLength : 0,
21175     /**
21176      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
21177      */
21178     maxLength : Number.MAX_VALUE,
21179     /**
21180      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
21181      */
21182     minLengthText : "The minimum length for this field is {0}",
21183     /**
21184      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
21185      */
21186     maxLengthText : "The maximum length for this field is {0}",
21187     /**
21188      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
21189      */
21190     selectOnFocus : false,
21191     /**
21192      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
21193      */
21194     blankText : "This field is required",
21195     /**
21196      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
21197      * If available, this function will be called only after the basic validators all return true, and will be passed the
21198      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
21199      */
21200     validator : null,
21201     /**
21202      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
21203      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
21204      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
21205      */
21206     regex : null,
21207     /**
21208      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
21209      */
21210     regexText : "",
21211     /**
21212      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
21213      */
21214     emptyText : null,
21215     /**
21216      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
21217      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
21218      */
21219     emptyClass : 'x-form-empty-field',
21220
21221     // private
21222     initEvents : function(){
21223         Roo.form.TextField.superclass.initEvents.call(this);
21224         if(this.validationEvent == 'keyup'){
21225             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
21226             this.el.on('keyup', this.filterValidation, this);
21227         }
21228         else if(this.validationEvent !== false){
21229             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
21230         }
21231         if(this.selectOnFocus || this.emptyText){
21232             this.on("focus", this.preFocus, this);
21233             if(this.emptyText){
21234                 this.on('blur', this.postBlur, this);
21235                 this.applyEmptyText();
21236             }
21237         }
21238         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
21239             this.el.on("keypress", this.filterKeys, this);
21240         }
21241         if(this.grow){
21242             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
21243             this.el.on("click", this.autoSize,  this);
21244         }
21245     },
21246
21247     processValue : function(value){
21248         if(this.stripCharsRe){
21249             var newValue = value.replace(this.stripCharsRe, '');
21250             if(newValue !== value){
21251                 this.setRawValue(newValue);
21252                 return newValue;
21253             }
21254         }
21255         return value;
21256     },
21257
21258     filterValidation : function(e){
21259         if(!e.isNavKeyPress()){
21260             this.validationTask.delay(this.validationDelay);
21261         }
21262     },
21263
21264     // private
21265     onKeyUp : function(e){
21266         if(!e.isNavKeyPress()){
21267             this.autoSize();
21268         }
21269     },
21270
21271     /**
21272      * Resets the current field value to the originally-loaded value and clears any validation messages.
21273      * Also adds emptyText and emptyClass if the original value was blank.
21274      */
21275     reset : function(){
21276         Roo.form.TextField.superclass.reset.call(this);
21277         this.applyEmptyText();
21278     },
21279
21280     applyEmptyText : function(){
21281         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
21282             this.setRawValue(this.emptyText);
21283             this.el.addClass(this.emptyClass);
21284         }
21285     },
21286
21287     // private
21288     preFocus : function(){
21289         if(this.emptyText){
21290             if(this.el.dom.value == this.emptyText){
21291                 this.setRawValue('');
21292             }
21293             this.el.removeClass(this.emptyClass);
21294         }
21295         if(this.selectOnFocus){
21296             this.el.dom.select();
21297         }
21298     },
21299
21300     // private
21301     postBlur : function(){
21302         this.applyEmptyText();
21303     },
21304
21305     // private
21306     filterKeys : function(e){
21307         var k = e.getKey();
21308         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
21309             return;
21310         }
21311         var c = e.getCharCode(), cc = String.fromCharCode(c);
21312         if(Roo.isIE && (e.isSpecialKey() || !cc)){
21313             return;
21314         }
21315         if(!this.maskRe.test(cc)){
21316             e.stopEvent();
21317         }
21318     },
21319
21320     setValue : function(v){
21321         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
21322             this.el.removeClass(this.emptyClass);
21323         }
21324         Roo.form.TextField.superclass.setValue.apply(this, arguments);
21325         this.applyEmptyText();
21326         this.autoSize();
21327     },
21328
21329     /**
21330      * Validates a value according to the field's validation rules and marks the field as invalid
21331      * if the validation fails
21332      * @param {Mixed} value The value to validate
21333      * @return {Boolean} True if the value is valid, else false
21334      */
21335     validateValue : function(value){
21336         if(value.length < 1 || value === this.emptyText){ // if it's blank
21337              if(this.allowBlank){
21338                 this.clearInvalid();
21339                 return true;
21340              }else{
21341                 this.markInvalid(this.blankText);
21342                 return false;
21343              }
21344         }
21345         if(value.length < this.minLength){
21346             this.markInvalid(String.format(this.minLengthText, this.minLength));
21347             return false;
21348         }
21349         if(value.length > this.maxLength){
21350             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
21351             return false;
21352         }
21353         if(this.vtype){
21354             var vt = Roo.form.VTypes;
21355             if(!vt[this.vtype](value, this)){
21356                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
21357                 return false;
21358             }
21359         }
21360         if(typeof this.validator == "function"){
21361             var msg = this.validator(value);
21362             if(msg !== true){
21363                 this.markInvalid(msg);
21364                 return false;
21365             }
21366         }
21367         if(this.regex && !this.regex.test(value)){
21368             this.markInvalid(this.regexText);
21369             return false;
21370         }
21371         return true;
21372     },
21373
21374     /**
21375      * Selects text in this field
21376      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
21377      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
21378      */
21379     selectText : function(start, end){
21380         var v = this.getRawValue();
21381         if(v.length > 0){
21382             start = start === undefined ? 0 : start;
21383             end = end === undefined ? v.length : end;
21384             var d = this.el.dom;
21385             if(d.setSelectionRange){
21386                 d.setSelectionRange(start, end);
21387             }else if(d.createTextRange){
21388                 var range = d.createTextRange();
21389                 range.moveStart("character", start);
21390                 range.moveEnd("character", v.length-end);
21391                 range.select();
21392             }
21393         }
21394     },
21395
21396     /**
21397      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
21398      * This only takes effect if grow = true, and fires the autosize event.
21399      */
21400     autoSize : function(){
21401         if(!this.grow || !this.rendered){
21402             return;
21403         }
21404         if(!this.metrics){
21405             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
21406         }
21407         var el = this.el;
21408         var v = el.dom.value;
21409         var d = document.createElement('div');
21410         d.appendChild(document.createTextNode(v));
21411         v = d.innerHTML;
21412         d = null;
21413         v += "&#160;";
21414         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
21415         this.el.setWidth(w);
21416         this.fireEvent("autosize", this, w);
21417     }
21418 });/*
21419  * Based on:
21420  * Ext JS Library 1.1.1
21421  * Copyright(c) 2006-2007, Ext JS, LLC.
21422  *
21423  * Originally Released Under LGPL - original licence link has changed is not relivant.
21424  *
21425  * Fork - LGPL
21426  * <script type="text/javascript">
21427  */
21428  
21429 /**
21430  * @class Roo.form.Hidden
21431  * @extends Roo.form.TextField
21432  * Simple Hidden element used on forms 
21433  * 
21434  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
21435  * 
21436  * @constructor
21437  * Creates a new Hidden form element.
21438  * @param {Object} config Configuration options
21439  */
21440
21441
21442
21443 // easy hidden field...
21444 Roo.form.Hidden = function(config){
21445     Roo.form.Hidden.superclass.constructor.call(this, config);
21446 };
21447   
21448 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
21449     fieldLabel:      '',
21450     inputType:      'hidden',
21451     width:          50,
21452     allowBlank:     true,
21453     labelSeparator: '',
21454     hidden:         true,
21455     itemCls :       'x-form-item-display-none'
21456
21457
21458 });
21459
21460
21461 /*
21462  * Based on:
21463  * Ext JS Library 1.1.1
21464  * Copyright(c) 2006-2007, Ext JS, LLC.
21465  *
21466  * Originally Released Under LGPL - original licence link has changed is not relivant.
21467  *
21468  * Fork - LGPL
21469  * <script type="text/javascript">
21470  */
21471  
21472 /**
21473  * @class Roo.form.TriggerField
21474  * @extends Roo.form.TextField
21475  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
21476  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
21477  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
21478  * for which you can provide a custom implementation.  For example:
21479  * <pre><code>
21480 var trigger = new Roo.form.TriggerField();
21481 trigger.onTriggerClick = myTriggerFn;
21482 trigger.applyTo('my-field');
21483 </code></pre>
21484  *
21485  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
21486  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
21487  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
21488  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
21489  * @constructor
21490  * Create a new TriggerField.
21491  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
21492  * to the base TextField)
21493  */
21494 Roo.form.TriggerField = function(config){
21495     this.mimicing = false;
21496     Roo.form.TriggerField.superclass.constructor.call(this, config);
21497 };
21498
21499 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
21500     /**
21501      * @cfg {String} triggerClass A CSS class to apply to the trigger
21502      */
21503     /**
21504      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21505      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
21506      */
21507     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
21508     /**
21509      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
21510      */
21511     hideTrigger:false,
21512
21513     /** @cfg {Boolean} grow @hide */
21514     /** @cfg {Number} growMin @hide */
21515     /** @cfg {Number} growMax @hide */
21516
21517     /**
21518      * @hide 
21519      * @method
21520      */
21521     autoSize: Roo.emptyFn,
21522     // private
21523     monitorTab : true,
21524     // private
21525     deferHeight : true,
21526
21527     
21528     actionMode : 'wrap',
21529     // private
21530     onResize : function(w, h){
21531         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
21532         if(typeof w == 'number'){
21533             this.el.setWidth(this.adjustWidth('input', w - this.trigger.getWidth()));
21534         }
21535     },
21536
21537     // private
21538     adjustSize : Roo.BoxComponent.prototype.adjustSize,
21539
21540     // private
21541     getResizeEl : function(){
21542         return this.wrap;
21543     },
21544
21545     // private
21546     getPositionEl : function(){
21547         return this.wrap;
21548     },
21549
21550     // private
21551     alignErrorIcon : function(){
21552         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
21553     },
21554
21555     // private
21556     onRender : function(ct, position){
21557         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
21558         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
21559         this.trigger = this.wrap.createChild(this.triggerConfig ||
21560                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
21561         if(this.hideTrigger){
21562             this.trigger.setDisplayed(false);
21563         }
21564         this.initTrigger();
21565         if(!this.width){
21566             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
21567         }
21568     },
21569
21570     // private
21571     initTrigger : function(){
21572         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
21573         this.trigger.addClassOnOver('x-form-trigger-over');
21574         this.trigger.addClassOnClick('x-form-trigger-click');
21575     },
21576
21577     // private
21578     onDestroy : function(){
21579         if(this.trigger){
21580             this.trigger.removeAllListeners();
21581             this.trigger.remove();
21582         }
21583         if(this.wrap){
21584             this.wrap.remove();
21585         }
21586         Roo.form.TriggerField.superclass.onDestroy.call(this);
21587     },
21588
21589     // private
21590     onFocus : function(){
21591         Roo.form.TriggerField.superclass.onFocus.call(this);
21592         if(!this.mimicing){
21593             this.wrap.addClass('x-trigger-wrap-focus');
21594             this.mimicing = true;
21595             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
21596             if(this.monitorTab){
21597                 this.el.on("keydown", this.checkTab, this);
21598             }
21599         }
21600     },
21601
21602     // private
21603     checkTab : function(e){
21604         if(e.getKey() == e.TAB){
21605             this.triggerBlur();
21606         }
21607     },
21608
21609     // private
21610     onBlur : function(){
21611         // do nothing
21612     },
21613
21614     // private
21615     mimicBlur : function(e, t){
21616         if(!this.wrap.contains(t) && this.validateBlur()){
21617             this.triggerBlur();
21618         }
21619     },
21620
21621     // private
21622     triggerBlur : function(){
21623         this.mimicing = false;
21624         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
21625         if(this.monitorTab){
21626             this.el.un("keydown", this.checkTab, this);
21627         }
21628         this.wrap.removeClass('x-trigger-wrap-focus');
21629         Roo.form.TriggerField.superclass.onBlur.call(this);
21630     },
21631
21632     // private
21633     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
21634     validateBlur : function(e, t){
21635         return true;
21636     },
21637
21638     // private
21639     onDisable : function(){
21640         Roo.form.TriggerField.superclass.onDisable.call(this);
21641         if(this.wrap){
21642             this.wrap.addClass('x-item-disabled');
21643         }
21644     },
21645
21646     // private
21647     onEnable : function(){
21648         Roo.form.TriggerField.superclass.onEnable.call(this);
21649         if(this.wrap){
21650             this.wrap.removeClass('x-item-disabled');
21651         }
21652     },
21653
21654     // private
21655     onShow : function(){
21656         var ae = this.getActionEl();
21657         
21658         if(ae){
21659             ae.dom.style.display = '';
21660             ae.dom.style.visibility = 'visible';
21661         }
21662     },
21663
21664     // private
21665     
21666     onHide : function(){
21667         var ae = this.getActionEl();
21668         ae.dom.style.display = 'none';
21669     },
21670
21671     /**
21672      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
21673      * by an implementing function.
21674      * @method
21675      * @param {EventObject} e
21676      */
21677     onTriggerClick : Roo.emptyFn
21678 });
21679
21680 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
21681 // to be extended by an implementing class.  For an example of implementing this class, see the custom
21682 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
21683 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
21684     initComponent : function(){
21685         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
21686
21687         this.triggerConfig = {
21688             tag:'span', cls:'x-form-twin-triggers', cn:[
21689             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
21690             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
21691         ]};
21692     },
21693
21694     getTrigger : function(index){
21695         return this.triggers[index];
21696     },
21697
21698     initTrigger : function(){
21699         var ts = this.trigger.select('.x-form-trigger', true);
21700         this.wrap.setStyle('overflow', 'hidden');
21701         var triggerField = this;
21702         ts.each(function(t, all, index){
21703             t.hide = function(){
21704                 var w = triggerField.wrap.getWidth();
21705                 this.dom.style.display = 'none';
21706                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21707             };
21708             t.show = function(){
21709                 var w = triggerField.wrap.getWidth();
21710                 this.dom.style.display = '';
21711                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21712             };
21713             var triggerIndex = 'Trigger'+(index+1);
21714
21715             if(this['hide'+triggerIndex]){
21716                 t.dom.style.display = 'none';
21717             }
21718             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
21719             t.addClassOnOver('x-form-trigger-over');
21720             t.addClassOnClick('x-form-trigger-click');
21721         }, this);
21722         this.triggers = ts.elements;
21723     },
21724
21725     onTrigger1Click : Roo.emptyFn,
21726     onTrigger2Click : Roo.emptyFn
21727 });/*
21728  * Based on:
21729  * Ext JS Library 1.1.1
21730  * Copyright(c) 2006-2007, Ext JS, LLC.
21731  *
21732  * Originally Released Under LGPL - original licence link has changed is not relivant.
21733  *
21734  * Fork - LGPL
21735  * <script type="text/javascript">
21736  */
21737  
21738 /**
21739  * @class Roo.form.TextArea
21740  * @extends Roo.form.TextField
21741  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
21742  * support for auto-sizing.
21743  * @constructor
21744  * Creates a new TextArea
21745  * @param {Object} config Configuration options
21746  */
21747 Roo.form.TextArea = function(config){
21748     Roo.form.TextArea.superclass.constructor.call(this, config);
21749     // these are provided exchanges for backwards compat
21750     // minHeight/maxHeight were replaced by growMin/growMax to be
21751     // compatible with TextField growing config values
21752     if(this.minHeight !== undefined){
21753         this.growMin = this.minHeight;
21754     }
21755     if(this.maxHeight !== undefined){
21756         this.growMax = this.maxHeight;
21757     }
21758 };
21759
21760 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
21761     /**
21762      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
21763      */
21764     growMin : 60,
21765     /**
21766      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
21767      */
21768     growMax: 1000,
21769     /**
21770      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
21771      * in the field (equivalent to setting overflow: hidden, defaults to false)
21772      */
21773     preventScrollbars: false,
21774     /**
21775      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21776      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
21777      */
21778
21779     // private
21780     onRender : function(ct, position){
21781         if(!this.el){
21782             this.defaultAutoCreate = {
21783                 tag: "textarea",
21784                 style:"width:300px;height:60px;",
21785                 autocomplete: "off"
21786             };
21787         }
21788         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
21789         if(this.grow){
21790             this.textSizeEl = Roo.DomHelper.append(document.body, {
21791                 tag: "pre", cls: "x-form-grow-sizer"
21792             });
21793             if(this.preventScrollbars){
21794                 this.el.setStyle("overflow", "hidden");
21795             }
21796             this.el.setHeight(this.growMin);
21797         }
21798     },
21799
21800     onDestroy : function(){
21801         if(this.textSizeEl){
21802             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
21803         }
21804         Roo.form.TextArea.superclass.onDestroy.call(this);
21805     },
21806
21807     // private
21808     onKeyUp : function(e){
21809         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
21810             this.autoSize();
21811         }
21812     },
21813
21814     /**
21815      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
21816      * This only takes effect if grow = true, and fires the autosize event if the height changes.
21817      */
21818     autoSize : function(){
21819         if(!this.grow || !this.textSizeEl){
21820             return;
21821         }
21822         var el = this.el;
21823         var v = el.dom.value;
21824         var ts = this.textSizeEl;
21825
21826         ts.innerHTML = '';
21827         ts.appendChild(document.createTextNode(v));
21828         v = ts.innerHTML;
21829
21830         Roo.fly(ts).setWidth(this.el.getWidth());
21831         if(v.length < 1){
21832             v = "&#160;&#160;";
21833         }else{
21834             if(Roo.isIE){
21835                 v = v.replace(/\n/g, '<p>&#160;</p>');
21836             }
21837             v += "&#160;\n&#160;";
21838         }
21839         ts.innerHTML = v;
21840         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
21841         if(h != this.lastHeight){
21842             this.lastHeight = h;
21843             this.el.setHeight(h);
21844             this.fireEvent("autosize", this, h);
21845         }
21846     }
21847 });/*
21848  * Based on:
21849  * Ext JS Library 1.1.1
21850  * Copyright(c) 2006-2007, Ext JS, LLC.
21851  *
21852  * Originally Released Under LGPL - original licence link has changed is not relivant.
21853  *
21854  * Fork - LGPL
21855  * <script type="text/javascript">
21856  */
21857  
21858
21859 /**
21860  * @class Roo.form.NumberField
21861  * @extends Roo.form.TextField
21862  * Numeric text field that provides automatic keystroke filtering and numeric validation.
21863  * @constructor
21864  * Creates a new NumberField
21865  * @param {Object} config Configuration options
21866  */
21867 Roo.form.NumberField = function(config){
21868     Roo.form.NumberField.superclass.constructor.call(this, config);
21869 };
21870
21871 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
21872     /**
21873      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
21874      */
21875     fieldClass: "x-form-field x-form-num-field",
21876     /**
21877      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
21878      */
21879     allowDecimals : true,
21880     /**
21881      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
21882      */
21883     decimalSeparator : ".",
21884     /**
21885      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
21886      */
21887     decimalPrecision : 2,
21888     /**
21889      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
21890      */
21891     allowNegative : true,
21892     /**
21893      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
21894      */
21895     minValue : Number.NEGATIVE_INFINITY,
21896     /**
21897      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
21898      */
21899     maxValue : Number.MAX_VALUE,
21900     /**
21901      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
21902      */
21903     minText : "The minimum value for this field is {0}",
21904     /**
21905      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
21906      */
21907     maxText : "The maximum value for this field is {0}",
21908     /**
21909      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
21910      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
21911      */
21912     nanText : "{0} is not a valid number",
21913
21914     // private
21915     initEvents : function(){
21916         Roo.form.NumberField.superclass.initEvents.call(this);
21917         var allowed = "0123456789";
21918         if(this.allowDecimals){
21919             allowed += this.decimalSeparator;
21920         }
21921         if(this.allowNegative){
21922             allowed += "-";
21923         }
21924         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
21925         var keyPress = function(e){
21926             var k = e.getKey();
21927             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
21928                 return;
21929             }
21930             var c = e.getCharCode();
21931             if(allowed.indexOf(String.fromCharCode(c)) === -1){
21932                 e.stopEvent();
21933             }
21934         };
21935         this.el.on("keypress", keyPress, this);
21936     },
21937
21938     // private
21939     validateValue : function(value){
21940         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
21941             return false;
21942         }
21943         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
21944              return true;
21945         }
21946         var num = this.parseValue(value);
21947         if(isNaN(num)){
21948             this.markInvalid(String.format(this.nanText, value));
21949             return false;
21950         }
21951         if(num < this.minValue){
21952             this.markInvalid(String.format(this.minText, this.minValue));
21953             return false;
21954         }
21955         if(num > this.maxValue){
21956             this.markInvalid(String.format(this.maxText, this.maxValue));
21957             return false;
21958         }
21959         return true;
21960     },
21961
21962     getValue : function(){
21963         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
21964     },
21965
21966     // private
21967     parseValue : function(value){
21968         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
21969         return isNaN(value) ? '' : value;
21970     },
21971
21972     // private
21973     fixPrecision : function(value){
21974         var nan = isNaN(value);
21975         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
21976             return nan ? '' : value;
21977         }
21978         return parseFloat(value).toFixed(this.decimalPrecision);
21979     },
21980
21981     setValue : function(v){
21982         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
21983     },
21984
21985     // private
21986     decimalPrecisionFcn : function(v){
21987         return Math.floor(v);
21988     },
21989
21990     beforeBlur : function(){
21991         var v = this.parseValue(this.getRawValue());
21992         if(v){
21993             this.setValue(this.fixPrecision(v));
21994         }
21995     }
21996 });/*
21997  * Based on:
21998  * Ext JS Library 1.1.1
21999  * Copyright(c) 2006-2007, Ext JS, LLC.
22000  *
22001  * Originally Released Under LGPL - original licence link has changed is not relivant.
22002  *
22003  * Fork - LGPL
22004  * <script type="text/javascript">
22005  */
22006  
22007 /**
22008  * @class Roo.form.DateField
22009  * @extends Roo.form.TriggerField
22010  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22011 * @constructor
22012 * Create a new DateField
22013 * @param {Object} config
22014  */
22015 Roo.form.DateField = function(config){
22016     Roo.form.DateField.superclass.constructor.call(this, config);
22017     
22018       this.addEvents({
22019          
22020         /**
22021          * @event select
22022          * Fires when a date is selected
22023              * @param {Roo.form.DateField} combo This combo box
22024              * @param {Date} date The date selected
22025              */
22026         'select' : true
22027          
22028     });
22029     
22030     
22031     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22032     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22033     this.ddMatch = null;
22034     if(this.disabledDates){
22035         var dd = this.disabledDates;
22036         var re = "(?:";
22037         for(var i = 0; i < dd.length; i++){
22038             re += dd[i];
22039             if(i != dd.length-1) re += "|";
22040         }
22041         this.ddMatch = new RegExp(re + ")");
22042     }
22043 };
22044
22045 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
22046     /**
22047      * @cfg {String} format
22048      * The default date format string which can be overriden for localization support.  The format must be
22049      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22050      */
22051     format : "m/d/y",
22052     /**
22053      * @cfg {String} altFormats
22054      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22055      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22056      */
22057     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22058     /**
22059      * @cfg {Array} disabledDays
22060      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22061      */
22062     disabledDays : null,
22063     /**
22064      * @cfg {String} disabledDaysText
22065      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22066      */
22067     disabledDaysText : "Disabled",
22068     /**
22069      * @cfg {Array} disabledDates
22070      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22071      * expression so they are very powerful. Some examples:
22072      * <ul>
22073      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22074      * <li>["03/08", "09/16"] would disable those days for every year</li>
22075      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22076      * <li>["03/../2006"] would disable every day in March 2006</li>
22077      * <li>["^03"] would disable every day in every March</li>
22078      * </ul>
22079      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22080      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22081      */
22082     disabledDates : null,
22083     /**
22084      * @cfg {String} disabledDatesText
22085      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22086      */
22087     disabledDatesText : "Disabled",
22088     /**
22089      * @cfg {Date/String} minValue
22090      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22091      * valid format (defaults to null).
22092      */
22093     minValue : null,
22094     /**
22095      * @cfg {Date/String} maxValue
22096      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22097      * valid format (defaults to null).
22098      */
22099     maxValue : null,
22100     /**
22101      * @cfg {String} minText
22102      * The error text to display when the date in the cell is before minValue (defaults to
22103      * 'The date in this field must be after {minValue}').
22104      */
22105     minText : "The date in this field must be equal to or after {0}",
22106     /**
22107      * @cfg {String} maxText
22108      * The error text to display when the date in the cell is after maxValue (defaults to
22109      * 'The date in this field must be before {maxValue}').
22110      */
22111     maxText : "The date in this field must be equal to or before {0}",
22112     /**
22113      * @cfg {String} invalidText
22114      * The error text to display when the date in the field is invalid (defaults to
22115      * '{value} is not a valid date - it must be in the format {format}').
22116      */
22117     invalidText : "{0} is not a valid date - it must be in the format {1}",
22118     /**
22119      * @cfg {String} triggerClass
22120      * An additional CSS class used to style the trigger button.  The trigger will always get the
22121      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22122      * which displays a calendar icon).
22123      */
22124     triggerClass : 'x-form-date-trigger',
22125     
22126
22127     /**
22128      * @cfg {bool} useIso
22129      * if enabled, then the date field will use a hidden field to store the 
22130      * real value as iso formated date. default (false)
22131      */ 
22132     useIso : false,
22133     /**
22134      * @cfg {String/Object} autoCreate
22135      * A DomHelper element spec, or true for a default element spec (defaults to
22136      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22137      */ 
22138     // private
22139     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
22140     
22141     // private
22142     hiddenField: false,
22143     
22144     onRender : function(ct, position)
22145     {
22146         Roo.form.DateField.superclass.onRender.call(this, ct, position);
22147         if (this.useIso) {
22148             this.el.dom.removeAttribute('name'); 
22149             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22150                     'before', true);
22151             this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
22152             // prevent input submission
22153             this.hiddenName = this.name;
22154         }
22155             
22156             
22157     },
22158     
22159     // private
22160     validateValue : function(value)
22161     {
22162         value = this.formatDate(value);
22163         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
22164             return false;
22165         }
22166         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22167              return true;
22168         }
22169         var svalue = value;
22170         value = this.parseDate(value);
22171         if(!value){
22172             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22173             return false;
22174         }
22175         var time = value.getTime();
22176         if(this.minValue && time < this.minValue.getTime()){
22177             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22178             return false;
22179         }
22180         if(this.maxValue && time > this.maxValue.getTime()){
22181             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22182             return false;
22183         }
22184         if(this.disabledDays){
22185             var day = value.getDay();
22186             for(var i = 0; i < this.disabledDays.length; i++) {
22187                 if(day === this.disabledDays[i]){
22188                     this.markInvalid(this.disabledDaysText);
22189                     return false;
22190                 }
22191             }
22192         }
22193         var fvalue = this.formatDate(value);
22194         if(this.ddMatch && this.ddMatch.test(fvalue)){
22195             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22196             return false;
22197         }
22198         return true;
22199     },
22200
22201     // private
22202     // Provides logic to override the default TriggerField.validateBlur which just returns true
22203     validateBlur : function(){
22204         return !this.menu || !this.menu.isVisible();
22205     },
22206
22207     /**
22208      * Returns the current date value of the date field.
22209      * @return {Date} The date value
22210      */
22211     getValue : function(){
22212         
22213         return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
22214     },
22215
22216     /**
22217      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22218      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
22219      * (the default format used is "m/d/y").
22220      * <br />Usage:
22221      * <pre><code>
22222 //All of these calls set the same date value (May 4, 2006)
22223
22224 //Pass a date object:
22225 var dt = new Date('5/4/06');
22226 dateField.setValue(dt);
22227
22228 //Pass a date string (default format):
22229 dateField.setValue('5/4/06');
22230
22231 //Pass a date string (custom format):
22232 dateField.format = 'Y-m-d';
22233 dateField.setValue('2006-5-4');
22234 </code></pre>
22235      * @param {String/Date} date The date or valid date string
22236      */
22237     setValue : function(date){
22238         if (this.hiddenField) {
22239             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22240         }
22241         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22242     },
22243
22244     // private
22245     parseDate : function(value){
22246         if(!value || value instanceof Date){
22247             return value;
22248         }
22249         var v = Date.parseDate(value, this.format);
22250         if(!v && this.altFormats){
22251             if(!this.altFormatsArray){
22252                 this.altFormatsArray = this.altFormats.split("|");
22253             }
22254             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22255                 v = Date.parseDate(value, this.altFormatsArray[i]);
22256             }
22257         }
22258         return v;
22259     },
22260
22261     // private
22262     formatDate : function(date, fmt){
22263         return (!date || !(date instanceof Date)) ?
22264                date : date.dateFormat(fmt || this.format);
22265     },
22266
22267     // private
22268     menuListeners : {
22269         select: function(m, d){
22270             this.setValue(d);
22271             this.fireEvent('select', this, d);
22272         },
22273         show : function(){ // retain focus styling
22274             this.onFocus();
22275         },
22276         hide : function(){
22277             this.focus.defer(10, this);
22278             var ml = this.menuListeners;
22279             this.menu.un("select", ml.select,  this);
22280             this.menu.un("show", ml.show,  this);
22281             this.menu.un("hide", ml.hide,  this);
22282         }
22283     },
22284
22285     // private
22286     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22287     onTriggerClick : function(){
22288         if(this.disabled){
22289             return;
22290         }
22291         if(this.menu == null){
22292             this.menu = new Roo.menu.DateMenu();
22293         }
22294         Roo.apply(this.menu.picker,  {
22295             showClear: this.allowBlank,
22296             minDate : this.minValue,
22297             maxDate : this.maxValue,
22298             disabledDatesRE : this.ddMatch,
22299             disabledDatesText : this.disabledDatesText,
22300             disabledDays : this.disabledDays,
22301             disabledDaysText : this.disabledDaysText,
22302             format : this.format,
22303             minText : String.format(this.minText, this.formatDate(this.minValue)),
22304             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22305         });
22306         this.menu.on(Roo.apply({}, this.menuListeners, {
22307             scope:this
22308         }));
22309         this.menu.picker.setValue(this.getValue() || new Date());
22310         this.menu.show(this.el, "tl-bl?");
22311     },
22312
22313     beforeBlur : function(){
22314         var v = this.parseDate(this.getRawValue());
22315         if(v){
22316             this.setValue(v);
22317         }
22318     }
22319
22320     /** @cfg {Boolean} grow @hide */
22321     /** @cfg {Number} growMin @hide */
22322     /** @cfg {Number} growMax @hide */
22323     /**
22324      * @hide
22325      * @method autoSize
22326      */
22327 });/*
22328  * Based on:
22329  * Ext JS Library 1.1.1
22330  * Copyright(c) 2006-2007, Ext JS, LLC.
22331  *
22332  * Originally Released Under LGPL - original licence link has changed is not relivant.
22333  *
22334  * Fork - LGPL
22335  * <script type="text/javascript">
22336  */
22337  
22338
22339 /**
22340  * @class Roo.form.ComboBox
22341  * @extends Roo.form.TriggerField
22342  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
22343  * @constructor
22344  * Create a new ComboBox.
22345  * @param {Object} config Configuration options
22346  */
22347 Roo.form.ComboBox = function(config){
22348     Roo.form.ComboBox.superclass.constructor.call(this, config);
22349     this.addEvents({
22350         /**
22351          * @event expand
22352          * Fires when the dropdown list is expanded
22353              * @param {Roo.form.ComboBox} combo This combo box
22354              */
22355         'expand' : true,
22356         /**
22357          * @event collapse
22358          * Fires when the dropdown list is collapsed
22359              * @param {Roo.form.ComboBox} combo This combo box
22360              */
22361         'collapse' : true,
22362         /**
22363          * @event beforeselect
22364          * Fires before a list item is selected. Return false to cancel the selection.
22365              * @param {Roo.form.ComboBox} combo This combo box
22366              * @param {Roo.data.Record} record The data record returned from the underlying store
22367              * @param {Number} index The index of the selected item in the dropdown list
22368              */
22369         'beforeselect' : true,
22370         /**
22371          * @event select
22372          * Fires when a list item is selected
22373              * @param {Roo.form.ComboBox} combo This combo box
22374              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
22375              * @param {Number} index The index of the selected item in the dropdown list
22376              */
22377         'select' : true,
22378         /**
22379          * @event beforequery
22380          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
22381          * The event object passed has these properties:
22382              * @param {Roo.form.ComboBox} combo This combo box
22383              * @param {String} query The query
22384              * @param {Boolean} forceAll true to force "all" query
22385              * @param {Boolean} cancel true to cancel the query
22386              * @param {Object} e The query event object
22387              */
22388         'beforequery': true
22389     });
22390     if(this.transform){
22391         this.allowDomMove = false;
22392         var s = Roo.getDom(this.transform);
22393         if(!this.hiddenName){
22394             this.hiddenName = s.name;
22395         }
22396         if(!this.store){
22397             this.mode = 'local';
22398             var d = [], opts = s.options;
22399             for(var i = 0, len = opts.length;i < len; i++){
22400                 var o = opts[i];
22401                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
22402                 if(o.selected) {
22403                     this.value = value;
22404                 }
22405                 d.push([value, o.text]);
22406             }
22407             this.store = new Roo.data.SimpleStore({
22408                 'id': 0,
22409                 fields: ['value', 'text'],
22410                 data : d
22411             });
22412             this.valueField = 'value';
22413             this.displayField = 'text';
22414         }
22415         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
22416         if(!this.lazyRender){
22417             this.target = true;
22418             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
22419             s.parentNode.removeChild(s); // remove it
22420             this.render(this.el.parentNode);
22421         }else{
22422             s.parentNode.removeChild(s); // remove it
22423         }
22424
22425     }
22426     if (this.store) {
22427         this.store = Roo.factory(this.store, Roo.data);
22428     }
22429     
22430     this.selectedIndex = -1;
22431     if(this.mode == 'local'){
22432         if(config.queryDelay === undefined){
22433             this.queryDelay = 10;
22434         }
22435         if(config.minChars === undefined){
22436             this.minChars = 0;
22437         }
22438     }
22439 };
22440
22441 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
22442     /**
22443      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
22444      */
22445     /**
22446      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
22447      * rendering into an Roo.Editor, defaults to false)
22448      */
22449     /**
22450      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
22451      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
22452      */
22453     /**
22454      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
22455      */
22456     /**
22457      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
22458      * the dropdown list (defaults to undefined, with no header element)
22459      */
22460
22461      /**
22462      * @cfg {String/Roo.Template} tpl The template to use to render the output
22463      */
22464      
22465     // private
22466     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
22467     /**
22468      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
22469      */
22470     listWidth: undefined,
22471     /**
22472      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
22473      * mode = 'remote' or 'text' if mode = 'local')
22474      */
22475     displayField: undefined,
22476     /**
22477      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
22478      * mode = 'remote' or 'value' if mode = 'local'). 
22479      * Note: use of a valueField requires the user make a selection
22480      * in order for a value to be mapped.
22481      */
22482     valueField: undefined,
22483     /**
22484      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
22485      * field's data value (defaults to the underlying DOM element's name)
22486      */
22487     hiddenName: undefined,
22488     /**
22489      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
22490      */
22491     listClass: '',
22492     /**
22493      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
22494      */
22495     selectedClass: 'x-combo-selected',
22496     /**
22497      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
22498      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
22499      * which displays a downward arrow icon).
22500      */
22501     triggerClass : 'x-form-arrow-trigger',
22502     /**
22503      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
22504      */
22505     shadow:'sides',
22506     /**
22507      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
22508      * anchor positions (defaults to 'tl-bl')
22509      */
22510     listAlign: 'tl-bl?',
22511     /**
22512      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
22513      */
22514     maxHeight: 300,
22515     /**
22516      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
22517      * query specified by the allQuery config option (defaults to 'query')
22518      */
22519     triggerAction: 'query',
22520     /**
22521      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
22522      * (defaults to 4, does not apply if editable = false)
22523      */
22524     minChars : 4,
22525     /**
22526      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
22527      * delay (typeAheadDelay) if it matches a known value (defaults to false)
22528      */
22529     typeAhead: false,
22530     /**
22531      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
22532      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
22533      */
22534     queryDelay: 500,
22535     /**
22536      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
22537      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
22538      */
22539     pageSize: 0,
22540     /**
22541      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
22542      * when editable = true (defaults to false)
22543      */
22544     selectOnFocus:false,
22545     /**
22546      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
22547      */
22548     queryParam: 'query',
22549     /**
22550      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
22551      * when mode = 'remote' (defaults to 'Loading...')
22552      */
22553     loadingText: 'Loading...',
22554     /**
22555      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
22556      */
22557     resizable: false,
22558     /**
22559      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
22560      */
22561     handleHeight : 8,
22562     /**
22563      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
22564      * traditional select (defaults to true)
22565      */
22566     editable: true,
22567     /**
22568      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
22569      */
22570     allQuery: '',
22571     /**
22572      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
22573      */
22574     mode: 'remote',
22575     /**
22576      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
22577      * listWidth has a higher value)
22578      */
22579     minListWidth : 70,
22580     /**
22581      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
22582      * allow the user to set arbitrary text into the field (defaults to false)
22583      */
22584     forceSelection:false,
22585     /**
22586      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
22587      * if typeAhead = true (defaults to 250)
22588      */
22589     typeAheadDelay : 250,
22590     /**
22591      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
22592      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
22593      */
22594     valueNotFoundText : undefined,
22595     /**
22596      * @cfg {bool} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
22597      */
22598     blockFocus : false,
22599     
22600     /**
22601      * @cfg {bool} disableClear Disable showing of clear button.
22602      */
22603     disableClear : false,
22604     
22605     // private
22606     onRender : function(ct, position){
22607         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
22608         if(this.hiddenName){
22609             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
22610                     'before', true);
22611             this.hiddenField.value =
22612                 this.hiddenValue !== undefined ? this.hiddenValue :
22613                 this.value !== undefined ? this.value : '';
22614
22615             // prevent input submission
22616             this.el.dom.removeAttribute('name');
22617         }
22618         if(Roo.isGecko){
22619             this.el.dom.setAttribute('autocomplete', 'off');
22620         }
22621
22622         var cls = 'x-combo-list';
22623
22624         this.list = new Roo.Layer({
22625             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
22626         });
22627
22628         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
22629         this.list.setWidth(lw);
22630         this.list.swallowEvent('mousewheel');
22631         this.assetHeight = 0;
22632
22633         if(this.title){
22634             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
22635             this.assetHeight += this.header.getHeight();
22636         }
22637
22638         this.innerList = this.list.createChild({cls:cls+'-inner'});
22639         this.innerList.on('mouseover', this.onViewOver, this);
22640         this.innerList.on('mousemove', this.onViewMove, this);
22641         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
22642         
22643         if(this.allowBlank && !this.pageSize && !this.disableClear){
22644             this.footer = this.list.createChild({cls:cls+'-ft'});
22645             this.pageTb = new Roo.Toolbar(this.footer);
22646            
22647         }
22648         if(this.pageSize){
22649             this.footer = this.list.createChild({cls:cls+'-ft'});
22650             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
22651                     {pageSize: this.pageSize});
22652             
22653         }
22654         
22655         if (this.pageTb && this.allowBlank && !this.disableClear) {
22656             var _this = this;
22657             this.pageTb.add(new Roo.Toolbar.Fill(), {
22658                 cls: 'x-btn-icon x-btn-clear',
22659                 text: '&#160;',
22660                 handler: function()
22661                 {
22662                     _this.collapse();
22663                     _this.clearValue();
22664                     _this.onSelect(false, -1);
22665                 }
22666             });
22667         }
22668         if (this.footer) {
22669             this.assetHeight += this.footer.getHeight();
22670         }
22671         
22672
22673         if(!this.tpl){
22674             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
22675         }
22676
22677         this.view = new Roo.View(this.innerList, this.tpl, {
22678             singleSelect:true, store: this.store, selectedClass: this.selectedClass
22679         });
22680
22681         this.view.on('click', this.onViewClick, this);
22682
22683         this.store.on('beforeload', this.onBeforeLoad, this);
22684         this.store.on('load', this.onLoad, this);
22685         this.store.on('loadexception', this.collapse, this);
22686
22687         if(this.resizable){
22688             this.resizer = new Roo.Resizable(this.list,  {
22689                pinned:true, handles:'se'
22690             });
22691             this.resizer.on('resize', function(r, w, h){
22692                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
22693                 this.listWidth = w;
22694                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
22695                 this.restrictHeight();
22696             }, this);
22697             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
22698         }
22699         if(!this.editable){
22700             this.editable = true;
22701             this.setEditable(false);
22702         }
22703     },
22704
22705     // private
22706     initEvents : function(){
22707         Roo.form.ComboBox.superclass.initEvents.call(this);
22708
22709         this.keyNav = new Roo.KeyNav(this.el, {
22710             "up" : function(e){
22711                 this.inKeyMode = true;
22712                 this.selectPrev();
22713             },
22714
22715             "down" : function(e){
22716                 if(!this.isExpanded()){
22717                     this.onTriggerClick();
22718                 }else{
22719                     this.inKeyMode = true;
22720                     this.selectNext();
22721                 }
22722             },
22723
22724             "enter" : function(e){
22725                 this.onViewClick();
22726                 //return true;
22727             },
22728
22729             "esc" : function(e){
22730                 this.collapse();
22731             },
22732
22733             "tab" : function(e){
22734                 this.onViewClick(false);
22735                 return true;
22736             },
22737
22738             scope : this,
22739
22740             doRelay : function(foo, bar, hname){
22741                 if(hname == 'down' || this.scope.isExpanded()){
22742                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
22743                 }
22744                 return true;
22745             },
22746
22747             forceKeyDown: true
22748         });
22749         this.queryDelay = Math.max(this.queryDelay || 10,
22750                 this.mode == 'local' ? 10 : 250);
22751         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
22752         if(this.typeAhead){
22753             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
22754         }
22755         if(this.editable !== false){
22756             this.el.on("keyup", this.onKeyUp, this);
22757         }
22758         if(this.forceSelection){
22759             this.on('blur', this.doForce, this);
22760         }
22761     },
22762
22763     onDestroy : function(){
22764         if(this.view){
22765             this.view.setStore(null);
22766             this.view.el.removeAllListeners();
22767             this.view.el.remove();
22768             this.view.purgeListeners();
22769         }
22770         if(this.list){
22771             this.list.destroy();
22772         }
22773         if(this.store){
22774             this.store.un('beforeload', this.onBeforeLoad, this);
22775             this.store.un('load', this.onLoad, this);
22776             this.store.un('loadexception', this.collapse, this);
22777         }
22778         Roo.form.ComboBox.superclass.onDestroy.call(this);
22779     },
22780
22781     // private
22782     fireKey : function(e){
22783         if(e.isNavKeyPress() && !this.list.isVisible()){
22784             this.fireEvent("specialkey", this, e);
22785         }
22786     },
22787
22788     // private
22789     onResize: function(w, h){
22790         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
22791         if(this.list && this.listWidth === undefined){
22792             var lw = Math.max(w, this.minListWidth);
22793             this.list.setWidth(lw);
22794             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
22795         }
22796     },
22797
22798     /**
22799      * Allow or prevent the user from directly editing the field text.  If false is passed,
22800      * the user will only be able to select from the items defined in the dropdown list.  This method
22801      * is the runtime equivalent of setting the 'editable' config option at config time.
22802      * @param {Boolean} value True to allow the user to directly edit the field text
22803      */
22804     setEditable : function(value){
22805         if(value == this.editable){
22806             return;
22807         }
22808         this.editable = value;
22809         if(!value){
22810             this.el.dom.setAttribute('readOnly', true);
22811             this.el.on('mousedown', this.onTriggerClick,  this);
22812             this.el.addClass('x-combo-noedit');
22813         }else{
22814             this.el.dom.setAttribute('readOnly', false);
22815             this.el.un('mousedown', this.onTriggerClick,  this);
22816             this.el.removeClass('x-combo-noedit');
22817         }
22818     },
22819
22820     // private
22821     onBeforeLoad : function(){
22822         if(!this.hasFocus){
22823             return;
22824         }
22825         this.innerList.update(this.loadingText ?
22826                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
22827         this.restrictHeight();
22828         this.selectedIndex = -1;
22829     },
22830
22831     // private
22832     onLoad : function(){
22833         if(!this.hasFocus){
22834             return;
22835         }
22836         if(this.store.getCount() > 0){
22837             this.expand();
22838             this.restrictHeight();
22839             if(this.lastQuery == this.allQuery){
22840                 if(this.editable){
22841                     this.el.dom.select();
22842                 }
22843                 if(!this.selectByValue(this.value, true)){
22844                     this.select(0, true);
22845                 }
22846             }else{
22847                 this.selectNext();
22848                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
22849                     this.taTask.delay(this.typeAheadDelay);
22850                 }
22851             }
22852         }else{
22853             this.onEmptyResults();
22854         }
22855         //this.el.focus();
22856     },
22857
22858     // private
22859     onTypeAhead : function(){
22860         if(this.store.getCount() > 0){
22861             var r = this.store.getAt(0);
22862             var newValue = r.data[this.displayField];
22863             var len = newValue.length;
22864             var selStart = this.getRawValue().length;
22865             if(selStart != len){
22866                 this.setRawValue(newValue);
22867                 this.selectText(selStart, newValue.length);
22868             }
22869         }
22870     },
22871
22872     // private
22873     onSelect : function(record, index){
22874         if(this.fireEvent('beforeselect', this, record, index) !== false){
22875             this.setFromData(index > -1 ? record.data : false);
22876             this.collapse();
22877             this.fireEvent('select', this, record, index);
22878         }
22879     },
22880
22881     /**
22882      * Returns the currently selected field value or empty string if no value is set.
22883      * @return {String} value The selected value
22884      */
22885     getValue : function(){
22886         if(this.valueField){
22887             return typeof this.value != 'undefined' ? this.value : '';
22888         }else{
22889             return Roo.form.ComboBox.superclass.getValue.call(this);
22890         }
22891     },
22892
22893     /**
22894      * Clears any text/value currently set in the field
22895      */
22896     clearValue : function(){
22897         if(this.hiddenField){
22898             this.hiddenField.value = '';
22899         }
22900         this.value = '';
22901         this.setRawValue('');
22902         this.lastSelectionText = '';
22903         this.applyEmptyText();
22904     },
22905
22906     /**
22907      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
22908      * will be displayed in the field.  If the value does not match the data value of an existing item,
22909      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
22910      * Otherwise the field will be blank (although the value will still be set).
22911      * @param {String} value The value to match
22912      */
22913     setValue : function(v){
22914         var text = v;
22915         if(this.valueField){
22916             var r = this.findRecord(this.valueField, v);
22917             if(r){
22918                 text = r.data[this.displayField];
22919             }else if(this.valueNotFoundText !== undefined){
22920                 text = this.valueNotFoundText;
22921             }
22922         }
22923         this.lastSelectionText = text;
22924         if(this.hiddenField){
22925             this.hiddenField.value = v;
22926         }
22927         Roo.form.ComboBox.superclass.setValue.call(this, text);
22928         this.value = v;
22929     },
22930     /**
22931      * @property {Object} the last set data for the element
22932      */
22933     
22934     lastData : false,
22935     /**
22936      * Sets the value of the field based on a object which is related to the record format for the store.
22937      * @param {Object} value the value to set as. or false on reset?
22938      */
22939     setFromData : function(o){
22940         var dv = ''; // display value
22941         var vv = ''; // value value..
22942         this.lastData = o;
22943         if (this.displayField) {
22944             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
22945         } else {
22946             // this is an error condition!!!
22947             console.log('no value field set for '+ this.name);
22948         }
22949         
22950         if(this.valueField){
22951             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
22952         }
22953         if(this.hiddenField){
22954             this.hiddenField.value = vv;
22955             
22956             this.lastSelectionText = dv;
22957             Roo.form.ComboBox.superclass.setValue.call(this, dv);
22958             this.value = vv;
22959             return;
22960         }
22961         // no hidden field.. - we store the value in 'value', but still display
22962         // display field!!!!
22963         this.lastSelectionText = dv;
22964         Roo.form.ComboBox.superclass.setValue.call(this, dv);
22965         this.value = vv;
22966         
22967         
22968     },
22969     // private
22970     reset : function(){
22971         // overridden so that last data is reset..
22972         this.setValue(this.originalValue);
22973         this.clearInvalid();
22974         this.lastData = false;
22975     },
22976     // private
22977     findRecord : function(prop, value){
22978         var record;
22979         if(this.store.getCount() > 0){
22980             this.store.each(function(r){
22981                 if(r.data[prop] == value){
22982                     record = r;
22983                     return false;
22984                 }
22985             });
22986         }
22987         return record;
22988     },
22989
22990     // private
22991     onViewMove : function(e, t){
22992         this.inKeyMode = false;
22993     },
22994
22995     // private
22996     onViewOver : function(e, t){
22997         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
22998             return;
22999         }
23000         var item = this.view.findItemFromChild(t);
23001         if(item){
23002             var index = this.view.indexOf(item);
23003             this.select(index, false);
23004         }
23005     },
23006
23007     // private
23008     onViewClick : function(doFocus){
23009         var index = this.view.getSelectedIndexes()[0];
23010         var r = this.store.getAt(index);
23011         if(r){
23012             this.onSelect(r, index);
23013         }
23014         if(doFocus !== false && !this.blockFocus){
23015             this.el.focus();
23016         }
23017     },
23018
23019     // private
23020     restrictHeight : function(){
23021         this.innerList.dom.style.height = '';
23022         var inner = this.innerList.dom;
23023         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
23024         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
23025         this.list.beginUpdate();
23026         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
23027         this.list.alignTo(this.el, this.listAlign);
23028         this.list.endUpdate();
23029     },
23030
23031     // private
23032     onEmptyResults : function(){
23033         this.collapse();
23034     },
23035
23036     /**
23037      * Returns true if the dropdown list is expanded, else false.
23038      */
23039     isExpanded : function(){
23040         return this.list.isVisible();
23041     },
23042
23043     /**
23044      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
23045      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23046      * @param {String} value The data value of the item to select
23047      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23048      * selected item if it is not currently in view (defaults to true)
23049      * @return {Boolean} True if the value matched an item in the list, else false
23050      */
23051     selectByValue : function(v, scrollIntoView){
23052         if(v !== undefined && v !== null){
23053             var r = this.findRecord(this.valueField || this.displayField, v);
23054             if(r){
23055                 this.select(this.store.indexOf(r), scrollIntoView);
23056                 return true;
23057             }
23058         }
23059         return false;
23060     },
23061
23062     /**
23063      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
23064      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23065      * @param {Number} index The zero-based index of the list item to select
23066      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23067      * selected item if it is not currently in view (defaults to true)
23068      */
23069     select : function(index, scrollIntoView){
23070         this.selectedIndex = index;
23071         this.view.select(index);
23072         if(scrollIntoView !== false){
23073             var el = this.view.getNode(index);
23074             if(el){
23075                 this.innerList.scrollChildIntoView(el, false);
23076             }
23077         }
23078     },
23079
23080     // private
23081     selectNext : function(){
23082         var ct = this.store.getCount();
23083         if(ct > 0){
23084             if(this.selectedIndex == -1){
23085                 this.select(0);
23086             }else if(this.selectedIndex < ct-1){
23087                 this.select(this.selectedIndex+1);
23088             }
23089         }
23090     },
23091
23092     // private
23093     selectPrev : function(){
23094         var ct = this.store.getCount();
23095         if(ct > 0){
23096             if(this.selectedIndex == -1){
23097                 this.select(0);
23098             }else if(this.selectedIndex != 0){
23099                 this.select(this.selectedIndex-1);
23100             }
23101         }
23102     },
23103
23104     // private
23105     onKeyUp : function(e){
23106         if(this.editable !== false && !e.isSpecialKey()){
23107             this.lastKey = e.getKey();
23108             this.dqTask.delay(this.queryDelay);
23109         }
23110     },
23111
23112     // private
23113     validateBlur : function(){
23114         return !this.list || !this.list.isVisible();   
23115     },
23116
23117     // private
23118     initQuery : function(){
23119         this.doQuery(this.getRawValue());
23120     },
23121
23122     // private
23123     doForce : function(){
23124         if(this.el.dom.value.length > 0){
23125             this.el.dom.value =
23126                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
23127             this.applyEmptyText();
23128         }
23129     },
23130
23131     /**
23132      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
23133      * query allowing the query action to be canceled if needed.
23134      * @param {String} query The SQL query to execute
23135      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
23136      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
23137      * saved in the current store (defaults to false)
23138      */
23139     doQuery : function(q, forceAll){
23140         if(q === undefined || q === null){
23141             q = '';
23142         }
23143         var qe = {
23144             query: q,
23145             forceAll: forceAll,
23146             combo: this,
23147             cancel:false
23148         };
23149         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
23150             return false;
23151         }
23152         q = qe.query;
23153         forceAll = qe.forceAll;
23154         if(forceAll === true || (q.length >= this.minChars)){
23155             if(this.lastQuery != q){
23156                 this.lastQuery = q;
23157                 if(this.mode == 'local'){
23158                     this.selectedIndex = -1;
23159                     if(forceAll){
23160                         this.store.clearFilter();
23161                     }else{
23162                         this.store.filter(this.displayField, q);
23163                     }
23164                     this.onLoad();
23165                 }else{
23166                     this.store.baseParams[this.queryParam] = q;
23167                     this.store.load({
23168                         params: this.getParams(q)
23169                     });
23170                     this.expand();
23171                 }
23172             }else{
23173                 this.selectedIndex = -1;
23174                 this.onLoad();   
23175             }
23176         }
23177     },
23178
23179     // private
23180     getParams : function(q){
23181         var p = {};
23182         //p[this.queryParam] = q;
23183         if(this.pageSize){
23184             p.start = 0;
23185             p.limit = this.pageSize;
23186         }
23187         return p;
23188     },
23189
23190     /**
23191      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
23192      */
23193     collapse : function(){
23194         if(!this.isExpanded()){
23195             return;
23196         }
23197         this.list.hide();
23198         Roo.get(document).un('mousedown', this.collapseIf, this);
23199         Roo.get(document).un('mousewheel', this.collapseIf, this);
23200         this.fireEvent('collapse', this);
23201     },
23202
23203     // private
23204     collapseIf : function(e){
23205         if(!e.within(this.wrap) && !e.within(this.list)){
23206             this.collapse();
23207         }
23208     },
23209
23210     /**
23211      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
23212      */
23213     expand : function(){
23214         if(this.isExpanded() || !this.hasFocus){
23215             return;
23216         }
23217         this.list.alignTo(this.el, this.listAlign);
23218         this.list.show();
23219         Roo.get(document).on('mousedown', this.collapseIf, this);
23220         Roo.get(document).on('mousewheel', this.collapseIf, this);
23221         this.fireEvent('expand', this);
23222     },
23223
23224     // private
23225     // Implements the default empty TriggerField.onTriggerClick function
23226     onTriggerClick : function(){
23227         if(this.disabled){
23228             return;
23229         }
23230         if(this.isExpanded()){
23231             this.collapse();
23232             if (!this.blockFocus) {
23233                 this.el.focus();
23234             }
23235             
23236         }else {
23237             this.hasFocus = true;
23238             if(this.triggerAction == 'all') {
23239                 this.doQuery(this.allQuery, true);
23240             } else {
23241                 this.doQuery(this.getRawValue());
23242             }
23243             if (!this.blockFocus) {
23244                 this.el.focus();
23245             }
23246         }
23247     }
23248
23249     /** 
23250     * @cfg {Boolean} grow 
23251     * @hide 
23252     */
23253     /** 
23254     * @cfg {Number} growMin 
23255     * @hide 
23256     */
23257     /** 
23258     * @cfg {Number} growMax 
23259     * @hide 
23260     */
23261     /**
23262      * @hide
23263      * @method autoSize
23264      */
23265 });/*
23266  * Based on:
23267  * Ext JS Library 1.1.1
23268  * Copyright(c) 2006-2007, Ext JS, LLC.
23269  *
23270  * Originally Released Under LGPL - original licence link has changed is not relivant.
23271  *
23272  * Fork - LGPL
23273  * <script type="text/javascript">
23274  */
23275 /**
23276  * @class Roo.form.Checkbox
23277  * @extends Roo.form.Field
23278  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
23279  * @constructor
23280  * Creates a new Checkbox
23281  * @param {Object} config Configuration options
23282  */
23283 Roo.form.Checkbox = function(config){
23284     Roo.form.Checkbox.superclass.constructor.call(this, config);
23285     this.addEvents({
23286         /**
23287          * @event check
23288          * Fires when the checkbox is checked or unchecked.
23289              * @param {Roo.form.Checkbox} this This checkbox
23290              * @param {Boolean} checked The new checked value
23291              */
23292         check : true
23293     });
23294 };
23295
23296 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
23297     /**
23298      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
23299      */
23300     focusClass : undefined,
23301     /**
23302      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
23303      */
23304     fieldClass: "x-form-field",
23305     /**
23306      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
23307      */
23308     checked: false,
23309     /**
23310      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
23311      * {tag: "input", type: "checkbox", autocomplete: "off"})
23312      */
23313     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
23314     /**
23315      * @cfg {String} boxLabel The text that appears beside the checkbox
23316      */
23317     boxLabel : "",
23318     /**
23319      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
23320      */  
23321     inputValue : '1',
23322     /**
23323      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23324      */
23325      valueOff: '0', // value when not checked..
23326
23327     actionMode : 'viewEl', 
23328     //
23329     // private
23330     itemCls : 'x-menu-check-item x-form-item',
23331     groupClass : 'x-menu-group-item',
23332     inputType : 'hidden',
23333     
23334     
23335     inSetChecked: false, // check that we are not calling self...
23336     
23337     inputElement: false, // real input element?
23338     basedOn: false, // ????
23339     
23340     isFormField: true, // not sure where this is needed!!!!
23341
23342     onResize : function(){
23343         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
23344         if(!this.boxLabel){
23345             this.el.alignTo(this.wrap, 'c-c');
23346         }
23347     },
23348
23349     initEvents : function(){
23350         Roo.form.Checkbox.superclass.initEvents.call(this);
23351         this.el.on("click", this.onClick,  this);
23352         this.el.on("change", this.onClick,  this);
23353     },
23354
23355
23356     getResizeEl : function(){
23357         return this.wrap;
23358     },
23359
23360     getPositionEl : function(){
23361         return this.wrap;
23362     },
23363
23364     // private
23365     onRender : function(ct, position){
23366         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
23367         /*
23368         if(this.inputValue !== undefined){
23369             this.el.dom.value = this.inputValue;
23370         }
23371         */
23372         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
23373         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
23374         var viewEl = this.wrap.createChild({ 
23375             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
23376         this.viewEl = viewEl;   
23377         this.wrap.on('click', this.onClick,  this); 
23378         
23379         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
23380         this.el.on('propertychange', this.setFromHidden,  this);  //ie
23381         
23382         
23383         
23384         if(this.boxLabel){
23385             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
23386         //    viewEl.on('click', this.onClick,  this); 
23387         }
23388         //if(this.checked){
23389             this.setChecked(this.checked);
23390         //}else{
23391             //this.checked = this.el.dom;
23392         //}
23393
23394     },
23395
23396     // private
23397     initValue : Roo.emptyFn,
23398
23399     /**
23400      * Returns the checked state of the checkbox.
23401      * @return {Boolean} True if checked, else false
23402      */
23403     getValue : function(){
23404         if(this.el){
23405             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
23406         }
23407         return this.valueOff;
23408         
23409     },
23410
23411         // private
23412     onClick : function(){ 
23413         this.setChecked(!this.checked);
23414
23415         //if(this.el.dom.checked != this.checked){
23416         //    this.setValue(this.el.dom.checked);
23417        // }
23418     },
23419
23420     /**
23421      * Sets the checked state of the checkbox.
23422      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
23423      */
23424     setValue : function(v,suppressEvent){
23425         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
23426         //if(this.el && this.el.dom){
23427         //    this.el.dom.checked = this.checked;
23428         //    this.el.dom.defaultChecked = this.checked;
23429         //}
23430         this.setChecked(v === this.inputValue);
23431         //this.fireEvent("check", this, this.checked);
23432     },
23433     // private..
23434     setChecked : function(state,suppressEvent)
23435     {
23436         if (this.inSetChecked) {
23437             this.checked = state;
23438             return;
23439         }
23440         
23441     
23442         if(this.wrap){
23443             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
23444         }
23445         this.checked = state;
23446         if(suppressEvent !== true){
23447             this.fireEvent('checkchange', this, state);
23448         }
23449         this.inSetChecked = true;
23450         this.el.dom.value = state ? this.inputValue : this.valueOff;
23451         this.inSetChecked = false;
23452         
23453     },
23454     // handle setting of hidden value by some other method!!?!?
23455     setFromHidden: function()
23456     {
23457         if(!this.el){
23458             return;
23459         }
23460         //console.log("SET FROM HIDDEN");
23461         //alert('setFrom hidden');
23462         this.setValue(this.el.dom.value);
23463     },
23464     
23465     onDestroy : function()
23466     {
23467         if(this.viewEl){
23468             Roo.get(this.viewEl).remove();
23469         }
23470          
23471         Roo.form.Checkbox.superclass.onDestroy.call(this);
23472     }
23473
23474 });/*
23475  * Based on:
23476  * Ext JS Library 1.1.1
23477  * Copyright(c) 2006-2007, Ext JS, LLC.
23478  *
23479  * Originally Released Under LGPL - original licence link has changed is not relivant.
23480  *
23481  * Fork - LGPL
23482  * <script type="text/javascript">
23483  */
23484  
23485 /**
23486  * @class Roo.form.Radio
23487  * @extends Roo.form.Checkbox
23488  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
23489  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
23490  * @constructor
23491  * Creates a new Radio
23492  * @param {Object} config Configuration options
23493  */
23494 Roo.form.Radio = function(){
23495     Roo.form.Radio.superclass.constructor.apply(this, arguments);
23496 };
23497 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
23498     inputType: 'radio',
23499
23500     /**
23501      * If this radio is part of a group, it will return the selected value
23502      * @return {String}
23503      */
23504     getGroupValue : function(){
23505         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
23506     }
23507 });//<script type="text/javascript">
23508
23509 /*
23510  * Ext JS Library 1.1.1
23511  * Copyright(c) 2006-2007, Ext JS, LLC.
23512  * licensing@extjs.com
23513  * 
23514  * http://www.extjs.com/license
23515  */
23516  
23517  /*
23518   * 
23519   * Known bugs:
23520   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
23521   * - IE ? - no idea how much works there.
23522   * 
23523   * 
23524   * 
23525   */
23526  
23527
23528 /**
23529  * @class Ext.form.HtmlEditor
23530  * @extends Ext.form.Field
23531  * Provides a lightweight HTML Editor component.
23532  * WARNING - THIS CURRENTlY ONLY WORKS ON FIREFOX - USE FCKeditor for a cross platform version
23533  * 
23534  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
23535  * supported by this editor.</b><br/><br/>
23536  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
23537  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23538  */
23539 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
23540       /**
23541      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23542      */
23543     toolbars : false,
23544     /**
23545      * @cfg {String} createLinkText The default text for the create link prompt
23546      */
23547     createLinkText : 'Please enter the URL for the link:',
23548     /**
23549      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
23550      */
23551     defaultLinkValue : 'http:/'+'/',
23552    
23553     
23554     // id of frame..
23555     frameId: false,
23556     
23557     // private properties
23558     validationEvent : false,
23559     deferHeight: true,
23560     initialized : false,
23561     activated : false,
23562     sourceEditMode : false,
23563     onFocus : Roo.emptyFn,
23564     iframePad:3,
23565     hideMode:'offsets',
23566     defaultAutoCreate : {
23567         tag: "textarea",
23568         style:"width:500px;height:300px;",
23569         autocomplete: "off"
23570     },
23571
23572     // private
23573     initComponent : function(){
23574         this.addEvents({
23575             /**
23576              * @event initialize
23577              * Fires when the editor is fully initialized (including the iframe)
23578              * @param {HtmlEditor} this
23579              */
23580             initialize: true,
23581             /**
23582              * @event activate
23583              * Fires when the editor is first receives the focus. Any insertion must wait
23584              * until after this event.
23585              * @param {HtmlEditor} this
23586              */
23587             activate: true,
23588              /**
23589              * @event beforesync
23590              * Fires before the textarea is updated with content from the editor iframe. Return false
23591              * to cancel the sync.
23592              * @param {HtmlEditor} this
23593              * @param {String} html
23594              */
23595             beforesync: true,
23596              /**
23597              * @event beforepush
23598              * Fires before the iframe editor is updated with content from the textarea. Return false
23599              * to cancel the push.
23600              * @param {HtmlEditor} this
23601              * @param {String} html
23602              */
23603             beforepush: true,
23604              /**
23605              * @event sync
23606              * Fires when the textarea is updated with content from the editor iframe.
23607              * @param {HtmlEditor} this
23608              * @param {String} html
23609              */
23610             sync: true,
23611              /**
23612              * @event push
23613              * Fires when the iframe editor is updated with content from the textarea.
23614              * @param {HtmlEditor} this
23615              * @param {String} html
23616              */
23617             push: true,
23618              /**
23619              * @event editmodechange
23620              * Fires when the editor switches edit modes
23621              * @param {HtmlEditor} this
23622              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23623              */
23624             editmodechange: true,
23625             /**
23626              * @event editorevent
23627              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23628              * @param {HtmlEditor} this
23629              */
23630             editorevent: true
23631         })
23632     },
23633
23634     /**
23635      * Protected method that will not generally be called directly. It
23636      * is called when the editor creates its toolbar. Override this method if you need to
23637      * add custom toolbar buttons.
23638      * @param {HtmlEditor} editor
23639      */
23640     createToolbar : function(editor){
23641         if (!editor.toolbars || !editor.toolbars.length) {
23642             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
23643         }
23644         
23645         for (var i =0 ; i < editor.toolbars.length;i++) {
23646             editor.toolbars[i].init(editor);
23647         }
23648          
23649         
23650     },
23651
23652     /**
23653      * Protected method that will not generally be called directly. It
23654      * is called when the editor initializes the iframe with HTML contents. Override this method if you
23655      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
23656      */
23657     getDocMarkup : function(){
23658         return '<html><head><style type="text/css">body{border:0;margin:0;padding:3px;height:98%;cursor:text;}</style></head><body></body></html>';
23659     },
23660
23661     // private
23662     onRender : function(ct, position){
23663         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
23664         this.el.dom.style.border = '0 none';
23665         this.el.dom.setAttribute('tabIndex', -1);
23666         this.el.addClass('x-hidden');
23667         if(Roo.isIE){ // fix IE 1px bogus margin
23668             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
23669         }
23670         this.wrap = this.el.wrap({
23671             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23672         });
23673
23674         this.frameId = Roo.id();
23675         this.createToolbar(this);
23676         
23677         
23678         
23679         
23680       
23681         
23682         var iframe = this.wrap.createChild({
23683             tag: 'iframe',
23684             id: this.frameId,
23685             name: this.frameId,
23686             frameBorder : 'no',
23687             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
23688         });
23689         
23690        // console.log(iframe);
23691         //this.wrap.dom.appendChild(iframe);
23692
23693         this.iframe = iframe.dom;
23694
23695          this.assignDocWin();
23696         
23697         this.doc.designMode = 'on';
23698        
23699         this.doc.open();
23700         this.doc.write(this.getDocMarkup());
23701         this.doc.close();
23702
23703         
23704         var task = { // must defer to wait for browser to be ready
23705             run : function(){
23706                 //console.log("run task?" + this.doc.readyState);
23707                 this.assignDocWin();
23708                 if(this.doc.body || this.doc.readyState == 'complete'){
23709                     try {
23710                         
23711                        
23712                         this.doc.designMode="on";
23713                     } catch (e) {
23714                         return;
23715                     }
23716                     Roo.TaskMgr.stop(task);
23717                     this.initEditor.defer(10, this);
23718                 }
23719             },
23720             interval : 10,
23721             duration:10000,
23722             scope: this
23723         };
23724         Roo.TaskMgr.start(task);
23725
23726         if(!this.width){
23727             this.setSize(this.el.getSize());
23728         }
23729     },
23730
23731     // private
23732     onResize : function(w, h){
23733         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
23734         if(this.el && this.iframe){
23735             if(typeof w == 'number'){
23736                 var aw = w - this.wrap.getFrameWidth('lr');
23737                 this.el.setWidth(this.adjustWidth('textarea', aw));
23738                 this.iframe.style.width = aw + 'px';
23739             }
23740             if(typeof h == 'number'){
23741                 var tbh = 0;
23742                 for (var i =0; i < this.toolbars.length;i++) {
23743                     // fixme - ask toolbars for heights?
23744                     tbh += this.toolbars[i].tb.el.getHeight();
23745                 }
23746                 
23747                 
23748                 
23749                 
23750                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23751                 this.el.setHeight(this.adjustWidth('textarea', ah));
23752                 this.iframe.style.height = ah + 'px';
23753                 if(this.doc){
23754                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
23755                 }
23756             }
23757         }
23758     },
23759
23760     /**
23761      * Toggles the editor between standard and source edit mode.
23762      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23763      */
23764     toggleSourceEdit : function(sourceEditMode){
23765         
23766         this.sourceEditMode = sourceEditMode === true;
23767         
23768         if(this.sourceEditMode){
23769           
23770             this.syncValue();
23771             this.iframe.className = 'x-hidden';
23772             this.el.removeClass('x-hidden');
23773             this.el.dom.removeAttribute('tabIndex');
23774             this.el.focus();
23775         }else{
23776              
23777             this.pushValue();
23778             this.iframe.className = '';
23779             this.el.addClass('x-hidden');
23780             this.el.dom.setAttribute('tabIndex', -1);
23781             this.deferFocus();
23782         }
23783         this.setSize(this.wrap.getSize());
23784         this.fireEvent('editmodechange', this, this.sourceEditMode);
23785     },
23786
23787     // private used internally
23788     createLink : function(){
23789         var url = prompt(this.createLinkText, this.defaultLinkValue);
23790         if(url && url != 'http:/'+'/'){
23791             this.relayCmd('createlink', url);
23792         }
23793     },
23794
23795     // private (for BoxComponent)
23796     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23797
23798     // private (for BoxComponent)
23799     getResizeEl : function(){
23800         return this.wrap;
23801     },
23802
23803     // private (for BoxComponent)
23804     getPositionEl : function(){
23805         return this.wrap;
23806     },
23807
23808     // private
23809     initEvents : function(){
23810         this.originalValue = this.getValue();
23811     },
23812
23813     /**
23814      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23815      * @method
23816      */
23817     markInvalid : Roo.emptyFn,
23818     /**
23819      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23820      * @method
23821      */
23822     clearInvalid : Roo.emptyFn,
23823
23824     setValue : function(v){
23825         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
23826         this.pushValue();
23827     },
23828
23829     /**
23830      * Protected method that will not generally be called directly. If you need/want
23831      * custom HTML cleanup, this is the method you should override.
23832      * @param {String} html The HTML to be cleaned
23833      * return {String} The cleaned HTML
23834      */
23835     cleanHtml : function(html){
23836         html = String(html);
23837         if(html.length > 5){
23838             if(Roo.isSafari){ // strip safari nonsense
23839                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
23840             }
23841         }
23842         if(html == '&nbsp;'){
23843             html = '';
23844         }
23845         return html;
23846     },
23847
23848     /**
23849      * Protected method that will not generally be called directly. Syncs the contents
23850      * of the editor iframe with the textarea.
23851      */
23852     syncValue : function(){
23853         if(this.initialized){
23854             var bd = (this.doc.body || this.doc.documentElement);
23855             var html = bd.innerHTML;
23856             if(Roo.isSafari){
23857                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
23858                 var m = bs.match(/text-align:(.*?);/i);
23859                 if(m && m[1]){
23860                     html = '<div style="'+m[0]+'">' + html + '</div>';
23861                 }
23862             }
23863             html = this.cleanHtml(html);
23864             if(this.fireEvent('beforesync', this, html) !== false){
23865                 this.el.dom.value = html;
23866                 this.fireEvent('sync', this, html);
23867             }
23868         }
23869     },
23870
23871     /**
23872      * Protected method that will not generally be called directly. Pushes the value of the textarea
23873      * into the iframe editor.
23874      */
23875     pushValue : function(){
23876         if(this.initialized){
23877             var v = this.el.dom.value;
23878             if(v.length < 1){
23879                 v = '&#160;';
23880             }
23881             if(this.fireEvent('beforepush', this, v) !== false){
23882                 (this.doc.body || this.doc.documentElement).innerHTML = v;
23883                 this.fireEvent('push', this, v);
23884             }
23885         }
23886     },
23887
23888     // private
23889     deferFocus : function(){
23890         this.focus.defer(10, this);
23891     },
23892
23893     // doc'ed in Field
23894     focus : function(){
23895         if(this.win && !this.sourceEditMode){
23896             this.win.focus();
23897         }else{
23898             this.el.focus();
23899         }
23900     },
23901     
23902     assignDocWin: function()
23903     {
23904         var iframe = this.iframe;
23905         
23906          if(Roo.isIE){
23907             this.doc = iframe.contentWindow.document;
23908             this.win = iframe.contentWindow;
23909         } else {
23910             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
23911             this.win = Roo.get(this.frameId).dom.contentWindow;
23912         }
23913     },
23914     
23915     // private
23916     initEditor : function(){
23917         //console.log("INIT EDITOR");
23918         this.assignDocWin();
23919         
23920         
23921         
23922         this.doc.designMode="on";
23923         this.doc.open();
23924         this.doc.write(this.getDocMarkup());
23925         this.doc.close();
23926         
23927         var dbody = (this.doc.body || this.doc.documentElement);
23928         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
23929         // this copies styles from the containing element into thsi one..
23930         // not sure why we need all of this..
23931         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
23932         ss['background-attachment'] = 'fixed'; // w3c
23933         dbody.bgProperties = 'fixed'; // ie
23934         Roo.DomHelper.applyStyles(dbody, ss);
23935         Roo.EventManager.on(this.doc, {
23936             'mousedown': this.onEditorEvent,
23937             'dblclick': this.onEditorEvent,
23938             'click': this.onEditorEvent,
23939             'keyup': this.onEditorEvent,
23940             buffer:100,
23941             scope: this
23942         });
23943         if(Roo.isGecko){
23944             Roo.EventManager.on(this.doc, 'keypress', this.applyCommand, this);
23945         }
23946         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
23947             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
23948         }
23949         this.initialized = true;
23950
23951         this.fireEvent('initialize', this);
23952         this.pushValue();
23953     },
23954
23955     // private
23956     onDestroy : function(){
23957         
23958         
23959         
23960         if(this.rendered){
23961             
23962             for (var i =0; i < this.toolbars.length;i++) {
23963                 // fixme - ask toolbars for heights?
23964                 this.toolbars[i].onDestroy();
23965             }
23966             
23967             this.wrap.dom.innerHTML = '';
23968             this.wrap.remove();
23969         }
23970     },
23971
23972     // private
23973     onFirstFocus : function(){
23974         
23975         this.assignDocWin();
23976         
23977         
23978         this.activated = true;
23979         for (var i =0; i < this.toolbars.length;i++) {
23980             this.toolbars[i].onFirstFocus();
23981         }
23982        
23983         if(Roo.isGecko){ // prevent silly gecko errors
23984             this.win.focus();
23985             var s = this.win.getSelection();
23986             if(!s.focusNode || s.focusNode.nodeType != 3){
23987                 var r = s.getRangeAt(0);
23988                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
23989                 r.collapse(true);
23990                 this.deferFocus();
23991             }
23992             try{
23993                 this.execCmd('useCSS', true);
23994                 this.execCmd('styleWithCSS', false);
23995             }catch(e){}
23996         }
23997         this.fireEvent('activate', this);
23998     },
23999
24000     // private
24001     adjustFont: function(btn){
24002         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24003         //if(Roo.isSafari){ // safari
24004         //    adjust *= 2;
24005        // }
24006         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24007         if(Roo.isSafari){ // safari
24008             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24009             v =  (v < 10) ? 10 : v;
24010             v =  (v > 48) ? 48 : v;
24011             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24012             
24013         }
24014         
24015         
24016         v = Math.max(1, v+adjust);
24017         
24018         this.execCmd('FontSize', v  );
24019     },
24020
24021     onEditorEvent : function(e){
24022         this.fireEvent('editorevent', this, e);
24023       //  this.updateToolbar();
24024         this.syncValue();
24025     },
24026
24027     insertTag : function(tg)
24028     {
24029         // could be a bit smarter... -> wrap the current selected tRoo..
24030         
24031         this.execCmd("formatblock",   tg);
24032         
24033     },
24034     
24035     insertText : function(txt)
24036     {
24037         
24038         
24039         range = this.createRange();
24040         range.deleteContents();
24041                //alert(Sender.getAttribute('label'));
24042                
24043         range.insertNode(this.doc.createTextNode(txt));
24044     } ,
24045     
24046     // private
24047     relayBtnCmd : function(btn){
24048         this.relayCmd(btn.cmd);
24049     },
24050
24051     /**
24052      * Executes a Midas editor command on the editor document and performs necessary focus and
24053      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24054      * @param {String} cmd The Midas command
24055      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24056      */
24057     relayCmd : function(cmd, value){
24058         this.win.focus();
24059         this.execCmd(cmd, value);
24060         this.fireEvent('editorevent', this);
24061         //this.updateToolbar();
24062         this.deferFocus();
24063     },
24064
24065     /**
24066      * Executes a Midas editor command directly on the editor document.
24067      * For visual commands, you should use {@link #relayCmd} instead.
24068      * <b>This should only be called after the editor is initialized.</b>
24069      * @param {String} cmd The Midas command
24070      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24071      */
24072     execCmd : function(cmd, value){
24073         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24074         this.syncValue();
24075     },
24076
24077     // private
24078     applyCommand : function(e){
24079         if(e.ctrlKey){
24080             var c = e.getCharCode(), cmd;
24081             if(c > 0){
24082                 c = String.fromCharCode(c);
24083                 switch(c){
24084                     case 'b':
24085                         cmd = 'bold';
24086                     break;
24087                     case 'i':
24088                         cmd = 'italic';
24089                     break;
24090                     case 'u':
24091                         cmd = 'underline';
24092                     break;
24093                 }
24094                 if(cmd){
24095                     this.win.focus();
24096                     this.execCmd(cmd);
24097                     this.deferFocus();
24098                     e.preventDefault();
24099                 }
24100             }
24101         }
24102     },
24103
24104     /**
24105      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24106      * to insert tRoo.
24107      * @param {String} text
24108      */
24109     insertAtCursor : function(text){
24110         if(!this.activated){
24111             return;
24112         }
24113         if(Roo.isIE){
24114             this.win.focus();
24115             var r = this.doc.selection.createRange();
24116             if(r){
24117                 r.collapse(true);
24118                 r.pasteHTML(text);
24119                 this.syncValue();
24120                 this.deferFocus();
24121             }
24122         }else if(Roo.isGecko || Roo.isOpera){
24123             this.win.focus();
24124             this.execCmd('InsertHTML', text);
24125             this.deferFocus();
24126         }else if(Roo.isSafari){
24127             this.execCmd('InsertText', text);
24128             this.deferFocus();
24129         }
24130     },
24131
24132     // private
24133     fixKeys : function(){ // load time branching for fastest keydown performance
24134         if(Roo.isIE){
24135             return function(e){
24136                 var k = e.getKey(), r;
24137                 if(k == e.TAB){
24138                     e.stopEvent();
24139                     r = this.doc.selection.createRange();
24140                     if(r){
24141                         r.collapse(true);
24142                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24143                         this.deferFocus();
24144                     }
24145                 }else if(k == e.ENTER){
24146                     r = this.doc.selection.createRange();
24147                     if(r){
24148                         var target = r.parentElement();
24149                         if(!target || target.tagName.toLowerCase() != 'li'){
24150                             e.stopEvent();
24151                             r.pasteHTML('<br />');
24152                             r.collapse(false);
24153                             r.select();
24154                         }
24155                     }
24156                 }
24157             };
24158         }else if(Roo.isOpera){
24159             return function(e){
24160                 var k = e.getKey();
24161                 if(k == e.TAB){
24162                     e.stopEvent();
24163                     this.win.focus();
24164                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24165                     this.deferFocus();
24166                 }
24167             };
24168         }else if(Roo.isSafari){
24169             return function(e){
24170                 var k = e.getKey();
24171                 if(k == e.TAB){
24172                     e.stopEvent();
24173                     this.execCmd('InsertText','\t');
24174                     this.deferFocus();
24175                 }
24176              };
24177         }
24178     }(),
24179     
24180     getAllAncestors: function()
24181     {
24182         var p = this.getSelectedNode();
24183         var a = [];
24184         if (!p) {
24185             a.push(p); // push blank onto stack..
24186             p = this.getParentElement();
24187         }
24188         
24189         
24190         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24191             a.push(p);
24192             p = p.parentNode;
24193         }
24194         a.push(this.doc.body);
24195         return a;
24196     },
24197     lastSel : false,
24198     lastSelNode : false,
24199     
24200     
24201     getSelection : function() 
24202     {
24203         this.assignDocWin();
24204         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24205     },
24206     
24207     getSelectedNode: function() 
24208     {
24209         // this may only work on Gecko!!!
24210         
24211         // should we cache this!!!!
24212         
24213         
24214         
24215          
24216         var range = this.createRange(this.getSelection());
24217         
24218         if (Roo.isIE) {
24219             var parent = range.parentElement();
24220             while (true) {
24221                 var testRange = range.duplicate();
24222                 testRange.moveToElementText(parent);
24223                 if (testRange.inRange(range)) {
24224                     break;
24225                 }
24226                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24227                     break;
24228                 }
24229                 parent = parent.parentElement;
24230             }
24231             return parent;
24232         }
24233         
24234         
24235         var ar = range.endContainer.childNodes;
24236         if (!ar.length) {
24237             ar = range.commonAncestorContainer.childNodes;
24238             //alert(ar.length);
24239         }
24240         var nodes = [];
24241         var other_nodes = [];
24242         var has_other_nodes = false;
24243         for (var i=0;i<ar.length;i++) {
24244             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24245                 continue;
24246             }
24247             // fullly contained node.
24248             
24249             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24250                 nodes.push(ar[i]);
24251                 continue;
24252             }
24253             
24254             // probably selected..
24255             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24256                 other_nodes.push(ar[i]);
24257                 continue;
24258             }
24259             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24260                 continue;
24261             }
24262             
24263             
24264             has_other_nodes = true;
24265         }
24266         if (!nodes.length && other_nodes.length) {
24267             nodes= other_nodes;
24268         }
24269         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24270             return false;
24271         }
24272         
24273         return nodes[0];
24274     },
24275     createRange: function(sel)
24276     {
24277         // this has strange effects when using with 
24278         // top toolbar - not sure if it's a great idea.
24279         //this.editor.contentWindow.focus();
24280         if (typeof sel != "undefined") {
24281             try {
24282                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24283             } catch(e) {
24284                 return this.doc.createRange();
24285             }
24286         } else {
24287             return this.doc.createRange();
24288         }
24289     },
24290     getParentElement: function()
24291     {
24292         
24293         this.assignDocWin();
24294         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24295         
24296         var range = this.createRange(sel);
24297          
24298         try {
24299             var p = range.commonAncestorContainer;
24300             while (p.nodeType == 3) { // text node
24301                 p = p.parentNode;
24302             }
24303             return p;
24304         } catch (e) {
24305             return null;
24306         }
24307     
24308     },
24309     
24310     
24311     
24312     // BC Hacks - cause I cant work out what i was trying to do..
24313     rangeIntersectsNode : function(range, node)
24314     {
24315         var nodeRange = node.ownerDocument.createRange();
24316         try {
24317             nodeRange.selectNode(node);
24318         }
24319         catch (e) {
24320             nodeRange.selectNodeContents(node);
24321         }
24322
24323         return range.compareBoundaryPoints(Range.END_TO_START, nodeRange) == -1 &&
24324                  range.compareBoundaryPoints(Range.START_TO_END, nodeRange) == 1;
24325     },
24326     rangeCompareNode : function(range, node) {
24327         var nodeRange = node.ownerDocument.createRange();
24328         try {
24329             nodeRange.selectNode(node);
24330         } catch (e) {
24331             nodeRange.selectNodeContents(node);
24332         }
24333         var nodeIsBefore = range.compareBoundaryPoints(Range.START_TO_START, nodeRange) == 1;
24334         var nodeIsAfter = range.compareBoundaryPoints(Range.END_TO_END, nodeRange) == -1;
24335
24336         if (nodeIsBefore && !nodeIsAfter)
24337             return 0;
24338         if (!nodeIsBefore && nodeIsAfter)
24339             return 1;
24340         if (nodeIsBefore && nodeIsAfter)
24341             return 2;
24342
24343         return 3;
24344     }
24345
24346     
24347     
24348     // hide stuff that is not compatible
24349     /**
24350      * @event blur
24351      * @hide
24352      */
24353     /**
24354      * @event change
24355      * @hide
24356      */
24357     /**
24358      * @event focus
24359      * @hide
24360      */
24361     /**
24362      * @event specialkey
24363      * @hide
24364      */
24365     /**
24366      * @cfg {String} fieldClass @hide
24367      */
24368     /**
24369      * @cfg {String} focusClass @hide
24370      */
24371     /**
24372      * @cfg {String} autoCreate @hide
24373      */
24374     /**
24375      * @cfg {String} inputType @hide
24376      */
24377     /**
24378      * @cfg {String} invalidClass @hide
24379      */
24380     /**
24381      * @cfg {String} invalidText @hide
24382      */
24383     /**
24384      * @cfg {String} msgFx @hide
24385      */
24386     /**
24387      * @cfg {String} validateOnBlur @hide
24388      */
24389 });// <script type="text/javascript">
24390 /*
24391  * Based on
24392  * Ext JS Library 1.1.1
24393  * Copyright(c) 2006-2007, Ext JS, LLC.
24394  *  
24395  
24396  */
24397
24398 /**
24399  * @class Roo.form.HtmlEditorToolbar1
24400  * Basic Toolbar
24401  * 
24402  * Usage:
24403  *
24404  new Roo.form.HtmlEditor({
24405     ....
24406     toolbars : [
24407         new Roo.form.HtmlEditorToolbar1({
24408             disable : { fonts: 1 , format: 1, ..., ... , ...],
24409             btns : [ .... ]
24410         })
24411     }
24412      
24413  * 
24414  * @cfg {Object} disable List of elements to disable..
24415  * @cfg {Array} btns List of additional buttons.
24416  * 
24417  * 
24418  * NEEDS Extra CSS? 
24419  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
24420  */
24421  
24422 Roo.form.HtmlEditor.ToolbarStandard = function(config)
24423 {
24424     
24425     Roo.apply(this, config);
24426     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
24427     // dont call parent... till later.
24428 }
24429
24430 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
24431     
24432     tb: false,
24433     
24434     rendered: false,
24435     
24436     editor : false,
24437     /**
24438      * @cfg {Object} disable  List of toolbar elements to disable
24439          
24440      */
24441     disable : false,
24442       /**
24443      * @cfg {Array} fontFamilies An array of available font families
24444      */
24445     fontFamilies : [
24446         'Arial',
24447         'Courier New',
24448         'Tahoma',
24449         'Times New Roman',
24450         'Verdana'
24451     ],
24452     
24453     specialChars : [
24454            "&#169;",
24455           "&#174;",     
24456           "&#8482;",    
24457           "&#163;" ,    
24458          // "&#8212;",    
24459           "&#8230;",    
24460           "&#247;" ,    
24461         //  "&#225;" ,     ?? a acute?
24462            "&#8364;"    , //Euro
24463        //   "&#8220;"    ,
24464         //  "&#8221;"    ,
24465         //  "&#8226;"    ,
24466           "&#176;"  //   , // degrees
24467
24468          // "&#233;"     , // e ecute
24469          // "&#250;"     , // u ecute?
24470     ],
24471     inputElements : [ 
24472             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
24473             "input:submit", "input:button", "select", "textarea", "label" ],
24474     formats : [
24475         ["p"] ,  
24476         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
24477         ["pre"],[ "code"], 
24478         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
24479     ],
24480      /**
24481      * @cfg {String} defaultFont default font to use.
24482      */
24483     defaultFont: 'tahoma',
24484    
24485     fontSelect : false,
24486     
24487     
24488     formatCombo : false,
24489     
24490     init : function(editor)
24491     {
24492         this.editor = editor;
24493         
24494         
24495         var fid = editor.frameId;
24496         var etb = this;
24497         function btn(id, toggle, handler){
24498             var xid = fid + '-'+ id ;
24499             return {
24500                 id : xid,
24501                 cmd : id,
24502                 cls : 'x-btn-icon x-edit-'+id,
24503                 enableToggle:toggle !== false,
24504                 scope: editor, // was editor...
24505                 handler:handler||editor.relayBtnCmd,
24506                 clickEvent:'mousedown',
24507                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
24508                 tabIndex:-1
24509             };
24510         }
24511         
24512         
24513         
24514         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
24515         this.tb = tb;
24516          // stop form submits
24517         tb.el.on('click', function(e){
24518             e.preventDefault(); // what does this do?
24519         });
24520
24521         if(!this.disable.font && !Roo.isSafari){
24522             /* why no safari for fonts
24523             editor.fontSelect = tb.el.createChild({
24524                 tag:'select',
24525                 tabIndex: -1,
24526                 cls:'x-font-select',
24527                 html: editor.createFontOptions()
24528             });
24529             editor.fontSelect.on('change', function(){
24530                 var font = editor.fontSelect.dom.value;
24531                 editor.relayCmd('fontname', font);
24532                 editor.deferFocus();
24533             }, editor);
24534             tb.add(
24535                 editor.fontSelect.dom,
24536                 '-'
24537             );
24538             */
24539         };
24540         if(!this.disable.formats){
24541             this.formatCombo = new Roo.form.ComboBox({
24542                 store: new Roo.data.SimpleStore({
24543                     id : 'tag',
24544                     fields: ['tag'],
24545                     data : this.formats // from states.js
24546                 }),
24547                 blockFocus : true,
24548                 //autoCreate : {tag: "div",  size: "20"},
24549                 displayField:'tag',
24550                 typeAhead: false,
24551                 mode: 'local',
24552                 editable : false,
24553                 triggerAction: 'all',
24554                 emptyText:'Add tag',
24555                 selectOnFocus:true,
24556                 width:135,
24557                 listeners : {
24558                     'select': function(c, r, i) {
24559                         editor.insertTag(r.get('tag'));
24560                         editor.focus();
24561                     }
24562                 }
24563
24564             });
24565             tb.addField(this.formatCombo);
24566             
24567         }
24568         
24569         if(!this.disable.format){
24570             tb.add(
24571                 btn('bold'),
24572                 btn('italic'),
24573                 btn('underline')
24574             );
24575         };
24576         if(!this.disable.fontSize){
24577             tb.add(
24578                 '-',
24579                 
24580                 
24581                 btn('increasefontsize', false, editor.adjustFont),
24582                 btn('decreasefontsize', false, editor.adjustFont)
24583             );
24584         };
24585         
24586         
24587         if(this.disable.colors){
24588             tb.add(
24589                 '-', {
24590                     id:editor.frameId +'-forecolor',
24591                     cls:'x-btn-icon x-edit-forecolor',
24592                     clickEvent:'mousedown',
24593                     tooltip: this.buttonTips['forecolor'] || undefined,
24594                     tabIndex:-1,
24595                     menu : new Roo.menu.ColorMenu({
24596                         allowReselect: true,
24597                         focus: Roo.emptyFn,
24598                         value:'000000',
24599                         plain:true,
24600                         selectHandler: function(cp, color){
24601                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
24602                             editor.deferFocus();
24603                         },
24604                         scope: editor,
24605                         clickEvent:'mousedown'
24606                     })
24607                 }, {
24608                     id:editor.frameId +'backcolor',
24609                     cls:'x-btn-icon x-edit-backcolor',
24610                     clickEvent:'mousedown',
24611                     tooltip: this.buttonTips['backcolor'] || undefined,
24612                     tabIndex:-1,
24613                     menu : new Roo.menu.ColorMenu({
24614                         focus: Roo.emptyFn,
24615                         value:'FFFFFF',
24616                         plain:true,
24617                         allowReselect: true,
24618                         selectHandler: function(cp, color){
24619                             if(Roo.isGecko){
24620                                 editor.execCmd('useCSS', false);
24621                                 editor.execCmd('hilitecolor', color);
24622                                 editor.execCmd('useCSS', true);
24623                                 editor.deferFocus();
24624                             }else{
24625                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
24626                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
24627                                 editor.deferFocus();
24628                             }
24629                         },
24630                         scope:editor,
24631                         clickEvent:'mousedown'
24632                     })
24633                 }
24634             );
24635         };
24636         // now add all the items...
24637         
24638
24639         if(!this.disable.alignments){
24640             tb.add(
24641                 '-',
24642                 btn('justifyleft'),
24643                 btn('justifycenter'),
24644                 btn('justifyright')
24645             );
24646         };
24647
24648         //if(!Roo.isSafari){
24649             if(!this.disable.links){
24650                 tb.add(
24651                     '-',
24652                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
24653                 );
24654             };
24655
24656             if(!this.disable.lists){
24657                 tb.add(
24658                     '-',
24659                     btn('insertorderedlist'),
24660                     btn('insertunorderedlist')
24661                 );
24662             }
24663             if(!this.disable.sourceEdit){
24664                 tb.add(
24665                     '-',
24666                     btn('sourceedit', true, function(btn){
24667                         this.toggleSourceEdit(btn.pressed);
24668                     })
24669                 );
24670             }
24671         //}
24672         
24673         var smenu = { };
24674         // special menu.. - needs to be tidied up..
24675         if (!this.disable.special) {
24676             smenu = {
24677                 text: "&#169;",
24678                 cls: 'x-edit-none',
24679                 menu : {
24680                     items : []
24681                    }
24682             };
24683             for (var i =0; i < this.specialChars.length; i++) {
24684                 smenu.menu.items.push({
24685                     
24686                     text: this.specialChars[i],
24687                     handler: function(a,b) {
24688                         editor.insertAtCursor(String.fromCharCode(a.text.replace('&#','').replace(';', '')));
24689                     },
24690                     tabIndex:-1
24691                 });
24692             }
24693             
24694             
24695             tb.add(smenu);
24696             
24697             
24698         }
24699         if (this.btns) {
24700             for(var i =0; i< this.btns.length;i++) {
24701                 var b = this.btns[i];
24702                 b.cls =  'x-edit-none';
24703                 b.scope = editor;
24704                 tb.add(b);
24705             }
24706         
24707         }
24708         
24709         
24710         
24711         // disable everything...
24712         
24713         this.tb.items.each(function(item){
24714            if(item.id != editor.frameId+ '-sourceedit'){
24715                 item.disable();
24716             }
24717         });
24718         this.rendered = true;
24719         
24720         // the all the btns;
24721         editor.on('editorevent', this.updateToolbar, this);
24722         // other toolbars need to implement this..
24723         //editor.on('editmodechange', this.updateToolbar, this);
24724     },
24725     
24726     
24727     
24728     /**
24729      * Protected method that will not generally be called directly. It triggers
24730      * a toolbar update by reading the markup state of the current selection in the editor.
24731      */
24732     updateToolbar: function(){
24733
24734         if(!this.editor.activated){
24735             this.editor.onFirstFocus();
24736             return;
24737         }
24738
24739         var btns = this.tb.items.map, 
24740             doc = this.editor.doc,
24741             frameId = this.editor.frameId;
24742
24743         if(!this.disable.font && !Roo.isSafari){
24744             /*
24745             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
24746             if(name != this.fontSelect.dom.value){
24747                 this.fontSelect.dom.value = name;
24748             }
24749             */
24750         }
24751         if(!this.disable.format){
24752             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
24753             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
24754             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
24755         }
24756         if(!this.disable.alignments){
24757             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
24758             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
24759             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
24760         }
24761         if(!Roo.isSafari && !this.disable.lists){
24762             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
24763             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
24764         }
24765         
24766         var ans = this.editor.getAllAncestors();
24767         if (this.formatCombo) {
24768             
24769             
24770             var store = this.formatCombo.store;
24771             this.formatCombo.setValue("");
24772             for (var i =0; i < ans.length;i++) {
24773                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), true).length) {
24774                     // select it..
24775                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24776                     break;
24777                 }
24778             }
24779         }
24780         
24781         
24782         
24783         // hides menus... - so this cant be on a menu...
24784         Roo.menu.MenuMgr.hideAll();
24785
24786         //this.editorsyncValue();
24787     },
24788    
24789     
24790     createFontOptions : function(){
24791         var buf = [], fs = this.fontFamilies, ff, lc;
24792         for(var i = 0, len = fs.length; i< len; i++){
24793             ff = fs[i];
24794             lc = ff.toLowerCase();
24795             buf.push(
24796                 '<option value="',lc,'" style="font-family:',ff,';"',
24797                     (this.defaultFont == lc ? ' selected="true">' : '>'),
24798                     ff,
24799                 '</option>'
24800             );
24801         }
24802         return buf.join('');
24803     },
24804     
24805     toggleSourceEdit : function(sourceEditMode){
24806         if(sourceEditMode === undefined){
24807             sourceEditMode = !this.sourceEditMode;
24808         }
24809         this.sourceEditMode = sourceEditMode === true;
24810         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
24811         // just toggle the button?
24812         if(btn.pressed !== this.editor.sourceEditMode){
24813             btn.toggle(this.editor.sourceEditMode);
24814             return;
24815         }
24816         
24817         if(this.sourceEditMode){
24818             this.tb.items.each(function(item){
24819                 if(item.cmd != 'sourceedit'){
24820                     item.disable();
24821                 }
24822             });
24823           
24824         }else{
24825             if(this.initialized){
24826                 this.tb.items.each(function(item){
24827                     item.enable();
24828                 });
24829             }
24830             
24831         }
24832         // tell the editor that it's been pressed..
24833         this.editor.toggleSourceEdit(sourceEditMode);
24834        
24835     },
24836      /**
24837      * Object collection of toolbar tooltips for the buttons in the editor. The key
24838      * is the command id associated with that button and the value is a valid QuickTips object.
24839      * For example:
24840 <pre><code>
24841 {
24842     bold : {
24843         title: 'Bold (Ctrl+B)',
24844         text: 'Make the selected text bold.',
24845         cls: 'x-html-editor-tip'
24846     },
24847     italic : {
24848         title: 'Italic (Ctrl+I)',
24849         text: 'Make the selected text italic.',
24850         cls: 'x-html-editor-tip'
24851     },
24852     ...
24853 </code></pre>
24854     * @type Object
24855      */
24856     buttonTips : {
24857         bold : {
24858             title: 'Bold (Ctrl+B)',
24859             text: 'Make the selected text bold.',
24860             cls: 'x-html-editor-tip'
24861         },
24862         italic : {
24863             title: 'Italic (Ctrl+I)',
24864             text: 'Make the selected text italic.',
24865             cls: 'x-html-editor-tip'
24866         },
24867         underline : {
24868             title: 'Underline (Ctrl+U)',
24869             text: 'Underline the selected text.',
24870             cls: 'x-html-editor-tip'
24871         },
24872         increasefontsize : {
24873             title: 'Grow Text',
24874             text: 'Increase the font size.',
24875             cls: 'x-html-editor-tip'
24876         },
24877         decreasefontsize : {
24878             title: 'Shrink Text',
24879             text: 'Decrease the font size.',
24880             cls: 'x-html-editor-tip'
24881         },
24882         backcolor : {
24883             title: 'Text Highlight Color',
24884             text: 'Change the background color of the selected text.',
24885             cls: 'x-html-editor-tip'
24886         },
24887         forecolor : {
24888             title: 'Font Color',
24889             text: 'Change the color of the selected text.',
24890             cls: 'x-html-editor-tip'
24891         },
24892         justifyleft : {
24893             title: 'Align Text Left',
24894             text: 'Align text to the left.',
24895             cls: 'x-html-editor-tip'
24896         },
24897         justifycenter : {
24898             title: 'Center Text',
24899             text: 'Center text in the editor.',
24900             cls: 'x-html-editor-tip'
24901         },
24902         justifyright : {
24903             title: 'Align Text Right',
24904             text: 'Align text to the right.',
24905             cls: 'x-html-editor-tip'
24906         },
24907         insertunorderedlist : {
24908             title: 'Bullet List',
24909             text: 'Start a bulleted list.',
24910             cls: 'x-html-editor-tip'
24911         },
24912         insertorderedlist : {
24913             title: 'Numbered List',
24914             text: 'Start a numbered list.',
24915             cls: 'x-html-editor-tip'
24916         },
24917         createlink : {
24918             title: 'Hyperlink',
24919             text: 'Make the selected text a hyperlink.',
24920             cls: 'x-html-editor-tip'
24921         },
24922         sourceedit : {
24923             title: 'Source Edit',
24924             text: 'Switch to source editing mode.',
24925             cls: 'x-html-editor-tip'
24926         }
24927     },
24928     // private
24929     onDestroy : function(){
24930         if(this.rendered){
24931             
24932             this.tb.items.each(function(item){
24933                 if(item.menu){
24934                     item.menu.removeAll();
24935                     if(item.menu.el){
24936                         item.menu.el.destroy();
24937                     }
24938                 }
24939                 item.destroy();
24940             });
24941              
24942         }
24943     },
24944     onFirstFocus: function() {
24945         this.tb.items.each(function(item){
24946            item.enable();
24947         });
24948     }
24949 });
24950
24951
24952
24953
24954 // <script type="text/javascript">
24955 /*
24956  * Based on
24957  * Ext JS Library 1.1.1
24958  * Copyright(c) 2006-2007, Ext JS, LLC.
24959  *  
24960  
24961  */
24962
24963  
24964 /**
24965  * @class Roo.form.HtmlEditor.ToolbarContext
24966  * Context Toolbar
24967  * 
24968  * Usage:
24969  *
24970  new Roo.form.HtmlEditor({
24971     ....
24972     toolbars : [
24973         new Roo.form.HtmlEditor.ToolbarStandard(),
24974         new Roo.form.HtmlEditor.ToolbarContext()
24975         })
24976     }
24977      
24978  * 
24979  * @config : {Object} disable List of elements to disable.. (not done yet.)
24980  * 
24981  * 
24982  */
24983
24984 Roo.form.HtmlEditor.ToolbarContext = function(config)
24985 {
24986     
24987     Roo.apply(this, config);
24988     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
24989     // dont call parent... till later.
24990 }
24991 Roo.form.HtmlEditor.ToolbarContext.types = {
24992     'IMG' : {
24993         width : {
24994             title: "Width",
24995             width: 40
24996         },
24997         height:  {
24998             title: "Height",
24999             width: 40
25000         },
25001         align: {
25002             title: "Align",
25003             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
25004             width : 80
25005             
25006         },
25007         border: {
25008             title: "Border",
25009             width: 40
25010         },
25011         alt: {
25012             title: "Alt",
25013             width: 120
25014         },
25015         src : {
25016             title: "Src",
25017             width: 220
25018         }
25019         
25020     },
25021     'A' : {
25022         name : {
25023             title: "Name",
25024             width: 50
25025         },
25026         href:  {
25027             title: "Href",
25028             width: 220
25029         } // border?
25030         
25031     },
25032     'TABLE' : {
25033         rows : {
25034             title: "Rows",
25035             width: 20
25036         },
25037         cols : {
25038             title: "Cols",
25039             width: 20
25040         },
25041         width : {
25042             title: "Width",
25043             width: 40
25044         },
25045         height : {
25046             title: "Height",
25047             width: 40
25048         },
25049         border : {
25050             title: "Border",
25051             width: 20
25052         }
25053     },
25054     'TD' : {
25055         width : {
25056             title: "Width",
25057             width: 40
25058         },
25059         height : {
25060             title: "Height",
25061             width: 40
25062         },   
25063         align: {
25064             title: "Align",
25065             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
25066             width: 40
25067         },
25068         valign: {
25069             title: "Valign",
25070             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
25071             width: 40
25072         },
25073         colspan: {
25074             title: "Colspan",
25075             width: 20
25076             
25077         }
25078     },
25079     'INPUT' : {
25080         name : {
25081             title: "name",
25082             width: 120
25083         },
25084         value : {
25085             title: "Value",
25086             width: 120
25087         },
25088         width : {
25089             title: "Width",
25090             width: 40
25091         }
25092     },
25093     'LABEL' : {
25094         'for' : {
25095             title: "For",
25096             width: 120
25097         }
25098     },
25099     'TEXTAREA' : {
25100           name : {
25101             title: "name",
25102             width: 120
25103         },
25104         rows : {
25105             title: "Rows",
25106             width: 20
25107         },
25108         cols : {
25109             title: "Cols",
25110             width: 20
25111         }
25112     },
25113     'SELECT' : {
25114         name : {
25115             title: "name",
25116             width: 120
25117         },
25118         selectoptions : {
25119             title: "Options",
25120             width: 200
25121         }
25122     },
25123     'BODY' : {
25124         title : {
25125             title: "title",
25126             width: 120,
25127             disabled : true
25128         }
25129     }
25130 };
25131
25132
25133
25134 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
25135     
25136     tb: false,
25137     
25138     rendered: false,
25139     
25140     editor : false,
25141     /**
25142      * @cfg {Object} disable  List of toolbar elements to disable
25143          
25144      */
25145     disable : false,
25146     
25147     
25148     
25149     toolbars : false,
25150     
25151     init : function(editor)
25152     {
25153         this.editor = editor;
25154         
25155         
25156         var fid = editor.frameId;
25157         var etb = this;
25158         function btn(id, toggle, handler){
25159             var xid = fid + '-'+ id ;
25160             return {
25161                 id : xid,
25162                 cmd : id,
25163                 cls : 'x-btn-icon x-edit-'+id,
25164                 enableToggle:toggle !== false,
25165                 scope: editor, // was editor...
25166                 handler:handler||editor.relayBtnCmd,
25167                 clickEvent:'mousedown',
25168                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
25169                 tabIndex:-1
25170             };
25171         }
25172         // create a new element.
25173         var wdiv = editor.wrap.createChild({
25174                 tag: 'div'
25175             }, editor.wrap.dom.firstChild.nextSibling, true);
25176         
25177         // can we do this more than once??
25178         
25179          // stop form submits
25180       
25181  
25182         // disable everything...
25183         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
25184         this.toolbars = {};
25185            
25186         for (var i in  ty) {
25187             this.toolbars[i] = this.buildToolbar(ty[i],i);
25188         }
25189         this.tb = this.toolbars.BODY;
25190         this.tb.el.show();
25191         
25192          
25193         this.rendered = true;
25194         
25195         // the all the btns;
25196         editor.on('editorevent', this.updateToolbar, this);
25197         // other toolbars need to implement this..
25198         //editor.on('editmodechange', this.updateToolbar, this);
25199     },
25200     
25201     
25202     
25203     /**
25204      * Protected method that will not generally be called directly. It triggers
25205      * a toolbar update by reading the markup state of the current selection in the editor.
25206      */
25207     updateToolbar: function(){
25208
25209         if(!this.editor.activated){
25210             this.editor.onFirstFocus();
25211             return;
25212         }
25213
25214         
25215         var ans = this.editor.getAllAncestors();
25216         
25217         // pick
25218         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
25219         var sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
25220         sel = sel ? sel : this.editor.doc.body;
25221         sel = sel.tagName.length ? sel : this.editor.doc.body;
25222         var tn = sel.tagName.toUpperCase();
25223         sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
25224         tn = sel.tagName.toUpperCase();
25225         if (this.tb.name  == tn) {
25226             return; // no change
25227         }
25228         this.tb.el.hide();
25229         ///console.log("show: " + tn);
25230         this.tb =  this.toolbars[tn];
25231         this.tb.el.show();
25232         this.tb.fields.each(function(e) {
25233             e.setValue(sel.getAttribute(e.name));
25234         });
25235         this.tb.selectedNode = sel;
25236         
25237         
25238         Roo.menu.MenuMgr.hideAll();
25239
25240         //this.editorsyncValue();
25241     },
25242    
25243        
25244     // private
25245     onDestroy : function(){
25246         if(this.rendered){
25247             
25248             this.tb.items.each(function(item){
25249                 if(item.menu){
25250                     item.menu.removeAll();
25251                     if(item.menu.el){
25252                         item.menu.el.destroy();
25253                     }
25254                 }
25255                 item.destroy();
25256             });
25257              
25258         }
25259     },
25260     onFirstFocus: function() {
25261         // need to do this for all the toolbars..
25262         this.tb.items.each(function(item){
25263            item.enable();
25264         });
25265     },
25266     buildToolbar: function(tlist, nm)
25267     {
25268         var editor = this.editor;
25269          // create a new element.
25270         var wdiv = editor.wrap.createChild({
25271                 tag: 'div'
25272             }, editor.wrap.dom.firstChild.nextSibling, true);
25273         
25274        
25275         var tb = new Roo.Toolbar(wdiv);
25276         tb.add(nm+ ":&nbsp;");
25277         for (var i in tlist) {
25278             var item = tlist[i];
25279             tb.add(item.title + ":&nbsp;");
25280             if (item.opts) {
25281                 // fixme
25282                 
25283               
25284                 tb.addField( new Roo.form.ComboBox({
25285                     store: new Roo.data.SimpleStore({
25286                         id : 'val',
25287                         fields: ['val'],
25288                         data : item.opts // from states.js
25289                     }),
25290                     name : i,
25291                     displayField:'val',
25292                     typeAhead: false,
25293                     mode: 'local',
25294                     editable : false,
25295                     triggerAction: 'all',
25296                     emptyText:'Select',
25297                     selectOnFocus:true,
25298                     width: item.width ? item.width  : 130,
25299                     listeners : {
25300                         'select': function(c, r, i) {
25301                             tb.selectedNode.setAttribute(c.name, r.get('val'));
25302                         }
25303                     }
25304
25305                 }));
25306                 continue;
25307                     
25308                 
25309                 
25310                 
25311                 
25312                 tb.addField( new Roo.form.TextField({
25313                     name: i,
25314                     width: 100,
25315                     //allowBlank:false,
25316                     value: ''
25317                 }));
25318                 continue;
25319             }
25320             tb.addField( new Roo.form.TextField({
25321                 name: i,
25322                 width: item.width,
25323                 //allowBlank:true,
25324                 value: '',
25325                 listeners: {
25326                     'change' : function(f, nv, ov) {
25327                         tb.selectedNode.setAttribute(f.name, nv);
25328                     }
25329                 }
25330             }));
25331              
25332         }
25333         tb.el.on('click', function(e){
25334             e.preventDefault(); // what does this do?
25335         });
25336         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
25337         tb.el.hide();
25338         tb.name = nm;
25339         // dont need to disable them... as they will get hidden
25340         return tb;
25341          
25342         
25343     }
25344     
25345     
25346     
25347     
25348 });
25349
25350
25351
25352
25353
25354 /*
25355  * Based on:
25356  * Ext JS Library 1.1.1
25357  * Copyright(c) 2006-2007, Ext JS, LLC.
25358  *
25359  * Originally Released Under LGPL - original licence link has changed is not relivant.
25360  *
25361  * Fork - LGPL
25362  * <script type="text/javascript">
25363  */
25364  
25365 /**
25366  * @class Roo.form.BasicForm
25367  * @extends Roo.util.Observable
25368  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
25369  * @constructor
25370  * @param {String/HTMLElement/Roo.Element} el The form element or its id
25371  * @param {Object} config Configuration options
25372  */
25373 Roo.form.BasicForm = function(el, config){
25374     Roo.apply(this, config);
25375     /*
25376      * The Roo.form.Field items in this form.
25377      * @type MixedCollection
25378      */
25379     this.items = new Roo.util.MixedCollection(false, function(o){
25380         return o.id || (o.id = Roo.id());
25381     });
25382     this.addEvents({
25383         /**
25384          * @event beforeaction
25385          * Fires before any action is performed. Return false to cancel the action.
25386          * @param {Form} this
25387          * @param {Action} action The action to be performed
25388          */
25389         beforeaction: true,
25390         /**
25391          * @event actionfailed
25392          * Fires when an action fails.
25393          * @param {Form} this
25394          * @param {Action} action The action that failed
25395          */
25396         actionfailed : true,
25397         /**
25398          * @event actioncomplete
25399          * Fires when an action is completed.
25400          * @param {Form} this
25401          * @param {Action} action The action that completed
25402          */
25403         actioncomplete : true
25404     });
25405     if(el){
25406         this.initEl(el);
25407     }
25408     Roo.form.BasicForm.superclass.constructor.call(this);
25409 };
25410
25411 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
25412     /**
25413      * @cfg {String} method
25414      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
25415      */
25416     /**
25417      * @cfg {DataReader} reader
25418      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
25419      * This is optional as there is built-in support for processing JSON.
25420      */
25421     /**
25422      * @cfg {DataReader} errorReader
25423      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
25424      * This is completely optional as there is built-in support for processing JSON.
25425      */
25426     /**
25427      * @cfg {String} url
25428      * The URL to use for form actions if one isn't supplied in the action options.
25429      */
25430     /**
25431      * @cfg {Boolean} fileUpload
25432      * Set to true if this form is a file upload.
25433      */
25434     /**
25435      * @cfg {Object} baseParams
25436      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
25437      */
25438     /**
25439      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
25440      */
25441     timeout: 30,
25442
25443     // private
25444     activeAction : null,
25445
25446     /**
25447      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
25448      * or setValues() data instead of when the form was first created.
25449      */
25450     trackResetOnLoad : false,
25451     
25452     
25453     /**
25454      * childForms - used for multi-tab forms
25455      * @type {Array}
25456      */
25457     childForms : false,
25458     
25459     /**
25460      * allFields - full list of fields.
25461      * @type {Array}
25462      */
25463     allFields : false,
25464     
25465     /**
25466      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
25467      * element by passing it or its id or mask the form itself by passing in true.
25468      * @type Mixed
25469      */
25470     waitMsgTarget : undefined,
25471
25472     // private
25473     initEl : function(el){
25474         this.el = Roo.get(el);
25475         this.id = this.el.id || Roo.id();
25476         this.el.on('submit', this.onSubmit, this);
25477         this.el.addClass('x-form');
25478     },
25479
25480     // private
25481     onSubmit : function(e){
25482         e.stopEvent();
25483     },
25484
25485     /**
25486      * Returns true if client-side validation on the form is successful.
25487      * @return Boolean
25488      */
25489     isValid : function(){
25490         var valid = true;
25491         this.items.each(function(f){
25492            if(!f.validate()){
25493                valid = false;
25494            }
25495         });
25496         return valid;
25497     },
25498
25499     /**
25500      * Returns true if any fields in this form have changed since their original load.
25501      * @return Boolean
25502      */
25503     isDirty : function(){
25504         var dirty = false;
25505         this.items.each(function(f){
25506            if(f.isDirty()){
25507                dirty = true;
25508                return false;
25509            }
25510         });
25511         return dirty;
25512     },
25513
25514     /**
25515      * Performs a predefined action (submit or load) or custom actions you define on this form.
25516      * @param {String} actionName The name of the action type
25517      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
25518      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
25519      * accept other config options):
25520      * <pre>
25521 Property          Type             Description
25522 ----------------  ---------------  ----------------------------------------------------------------------------------
25523 url               String           The url for the action (defaults to the form's url)
25524 method            String           The form method to use (defaults to the form's method, or POST if not defined)
25525 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
25526 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
25527                                    validate the form on the client (defaults to false)
25528      * </pre>
25529      * @return {BasicForm} this
25530      */
25531     doAction : function(action, options){
25532         if(typeof action == 'string'){
25533             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
25534         }
25535         if(this.fireEvent('beforeaction', this, action) !== false){
25536             this.beforeAction(action);
25537             action.run.defer(100, action);
25538         }
25539         return this;
25540     },
25541
25542     /**
25543      * Shortcut to do a submit action.
25544      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
25545      * @return {BasicForm} this
25546      */
25547     submit : function(options){
25548         this.doAction('submit', options);
25549         return this;
25550     },
25551
25552     /**
25553      * Shortcut to do a load action.
25554      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
25555      * @return {BasicForm} this
25556      */
25557     load : function(options){
25558         this.doAction('load', options);
25559         return this;
25560     },
25561
25562     /**
25563      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
25564      * @param {Record} record The record to edit
25565      * @return {BasicForm} this
25566      */
25567     updateRecord : function(record){
25568         record.beginEdit();
25569         var fs = record.fields;
25570         fs.each(function(f){
25571             var field = this.findField(f.name);
25572             if(field){
25573                 record.set(f.name, field.getValue());
25574             }
25575         }, this);
25576         record.endEdit();
25577         return this;
25578     },
25579
25580     /**
25581      * Loads an Roo.data.Record into this form.
25582      * @param {Record} record The record to load
25583      * @return {BasicForm} this
25584      */
25585     loadRecord : function(record){
25586         this.setValues(record.data);
25587         return this;
25588     },
25589
25590     // private
25591     beforeAction : function(action){
25592         var o = action.options;
25593         if(o.waitMsg){
25594             if(this.waitMsgTarget === true){
25595                 this.el.mask(o.waitMsg, 'x-mask-loading');
25596             }else if(this.waitMsgTarget){
25597                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
25598                 this.waitMsgTarget.mask(o.waitMsg, 'x-mask-loading');
25599             }else{
25600                 Roo.MessageBox.wait(o.waitMsg, o.waitTitle || this.waitTitle || 'Please Wait...');
25601             }
25602         }
25603     },
25604
25605     // private
25606     afterAction : function(action, success){
25607         this.activeAction = null;
25608         var o = action.options;
25609         if(o.waitMsg){
25610             if(this.waitMsgTarget === true){
25611                 this.el.unmask();
25612             }else if(this.waitMsgTarget){
25613                 this.waitMsgTarget.unmask();
25614             }else{
25615                 Roo.MessageBox.updateProgress(1);
25616                 Roo.MessageBox.hide();
25617             }
25618         }
25619         if(success){
25620             if(o.reset){
25621                 this.reset();
25622             }
25623             Roo.callback(o.success, o.scope, [this, action]);
25624             this.fireEvent('actioncomplete', this, action);
25625         }else{
25626             Roo.callback(o.failure, o.scope, [this, action]);
25627             this.fireEvent('actionfailed', this, action);
25628         }
25629     },
25630
25631     /**
25632      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
25633      * @param {String} id The value to search for
25634      * @return Field
25635      */
25636     findField : function(id){
25637         var field = this.items.get(id);
25638         if(!field){
25639             this.items.each(function(f){
25640                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
25641                     field = f;
25642                     return false;
25643                 }
25644             });
25645         }
25646         return field || null;
25647     },
25648
25649     /**
25650      * Add a secondary form to this one, 
25651      * Used to provide tabbed forms. One form is primary, with hidden values 
25652      * which mirror the elements from the other forms.
25653      * 
25654      * @param {Roo.form.Form} form to add.
25655      * 
25656      */
25657     addForm : function(form){
25658        
25659         this.childForms.push(form);
25660         form.allItems.each(function (fe) {
25661             
25662             if (this.findField(fe.name)) { // already added..
25663                 return;
25664             }
25665             this.add( new Roo.form.Hidden({
25666                 name : fe.name
25667             }));
25668         }, this);
25669         
25670     },
25671     /**
25672      * Mark fields in this form invalid in bulk.
25673      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
25674      * @return {BasicForm} this
25675      */
25676     markInvalid : function(errors){
25677         if(errors instanceof Array){
25678             for(var i = 0, len = errors.length; i < len; i++){
25679                 var fieldError = errors[i];
25680                 var f = this.findField(fieldError.id);
25681                 if(f){
25682                     f.markInvalid(fieldError.msg);
25683                 }
25684             }
25685         }else{
25686             var field, id;
25687             for(id in errors){
25688                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
25689                     field.markInvalid(errors[id]);
25690                 }
25691             }
25692         }
25693         Roo.each(this.childForms || [], function (f) {
25694             f.markInvalid(errors);
25695         });
25696         
25697         return this;
25698     },
25699
25700     /**
25701      * Set values for fields in this form in bulk.
25702      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
25703      * @return {BasicForm} this
25704      */
25705     setValues : function(values){
25706         if(values instanceof Array){ // array of objects
25707             for(var i = 0, len = values.length; i < len; i++){
25708                 var v = values[i];
25709                 var f = this.findField(v.id);
25710                 if(f){
25711                     f.setValue(v.value);
25712                     if(this.trackResetOnLoad){
25713                         f.originalValue = f.getValue();
25714                     }
25715                 }
25716             }
25717         }else{ // object hash
25718             var field, id;
25719             for(id in values){
25720                 if(typeof values[id] != 'function' && (field = this.findField(id))){
25721                     
25722                     if (field.setFromData && 
25723                         field.valueField && 
25724                         field.displayField &&
25725                         // combos' with local stores can 
25726                         // be queried via setValue()
25727                         // to set their value..
25728                         (field.store && !field.store.isLocal)
25729                         ) {
25730                         // it's a combo
25731                         var sd = { };
25732                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
25733                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
25734                         field.setFromData(sd);
25735                         
25736                     } else {
25737                         field.setValue(values[id]);
25738                     }
25739                     
25740                     
25741                     if(this.trackResetOnLoad){
25742                         field.originalValue = field.getValue();
25743                     }
25744                 }
25745             }
25746         }
25747          
25748         Roo.each(this.childForms || [], function (f) {
25749             f.setValues(values);
25750         });
25751                 
25752         return this;
25753     },
25754
25755     /**
25756      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
25757      * they are returned as an array.
25758      * @param {Boolean} asString
25759      * @return {Object}
25760      */
25761     getValues : function(asString){
25762         if (this.childForms) {
25763             // copy values from the child forms
25764             Roo.each(this.childForms, function (f) {
25765                 if (f.allFields) {
25766                     Roo.each(f.allFields, function (e) {
25767                         if (e.name && e.getValue && this.findField(e.name)) {
25768                             this.findField(e.name).setValue(e.getValue());
25769                         }
25770                     });
25771                 }
25772             }, this);
25773         }
25774         
25775         
25776         
25777         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
25778         if(asString === true){
25779             return fs;
25780         }
25781         return Roo.urlDecode(fs);
25782     },
25783
25784     /**
25785      * Clears all invalid messages in this form.
25786      * @return {BasicForm} this
25787      */
25788     clearInvalid : function(){
25789         this.items.each(function(f){
25790            f.clearInvalid();
25791         });
25792         
25793         Roo.each(this.childForms || [], function (f) {
25794             f.clearInvalid();
25795         });
25796         
25797         
25798         return this;
25799     },
25800
25801     /**
25802      * Resets this form.
25803      * @return {BasicForm} this
25804      */
25805     reset : function(){
25806         this.items.each(function(f){
25807             f.reset();
25808         });
25809         
25810         Roo.each(this.childForms || [], function (f) {
25811             f.reset();
25812         });
25813        
25814         
25815         return this;
25816     },
25817
25818     /**
25819      * Add Roo.form components to this form.
25820      * @param {Field} field1
25821      * @param {Field} field2 (optional)
25822      * @param {Field} etc (optional)
25823      * @return {BasicForm} this
25824      */
25825     add : function(){
25826         this.items.addAll(Array.prototype.slice.call(arguments, 0));
25827         return this;
25828     },
25829
25830
25831     /**
25832      * Removes a field from the items collection (does NOT remove its markup).
25833      * @param {Field} field
25834      * @return {BasicForm} this
25835      */
25836     remove : function(field){
25837         this.items.remove(field);
25838         return this;
25839     },
25840
25841     /**
25842      * Looks at the fields in this form, checks them for an id attribute,
25843      * and calls applyTo on the existing dom element with that id.
25844      * @return {BasicForm} this
25845      */
25846     render : function(){
25847         this.items.each(function(f){
25848             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
25849                 f.applyTo(f.id);
25850             }
25851         });
25852         return this;
25853     },
25854
25855     /**
25856      * Calls {@link Ext#apply} for all fields in this form with the passed object.
25857      * @param {Object} values
25858      * @return {BasicForm} this
25859      */
25860     applyToFields : function(o){
25861         this.items.each(function(f){
25862            Roo.apply(f, o);
25863         });
25864         return this;
25865     },
25866
25867     /**
25868      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
25869      * @param {Object} values
25870      * @return {BasicForm} this
25871      */
25872     applyIfToFields : function(o){
25873         this.items.each(function(f){
25874            Roo.applyIf(f, o);
25875         });
25876         return this;
25877     }
25878 });
25879
25880 // back compat
25881 Roo.BasicForm = Roo.form.BasicForm;/*
25882  * Based on:
25883  * Ext JS Library 1.1.1
25884  * Copyright(c) 2006-2007, Ext JS, LLC.
25885  *
25886  * Originally Released Under LGPL - original licence link has changed is not relivant.
25887  *
25888  * Fork - LGPL
25889  * <script type="text/javascript">
25890  */
25891
25892 /**
25893  * @class Roo.form.Form
25894  * @extends Roo.form.BasicForm
25895  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
25896  * @constructor
25897  * @param {Object} config Configuration options
25898  */
25899 Roo.form.Form = function(config){
25900     var xitems =  [];
25901     if (config.items) {
25902         xitems = config.items;
25903         delete config.items;
25904     }
25905     this.childForms = [];
25906     
25907     Roo.form.Form.superclass.constructor.call(this, null, config);
25908     this.url = this.url || this.action;
25909     if(!this.root){
25910         this.root = new Roo.form.Layout(Roo.applyIf({
25911             id: Roo.id()
25912         }, config));
25913     }
25914     this.active = this.root;
25915     /**
25916      * Array of all the buttons that have been added to this form via {@link addButton}
25917      * @type Array
25918      */
25919     this.buttons = [];
25920     this.allItems = [];
25921     this.addEvents({
25922         /**
25923          * @event clientvalidation
25924          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
25925          * @param {Form} this
25926          * @param {Boolean} valid true if the form has passed client-side validation
25927          */
25928         clientvalidation: true,
25929         /**
25930          * @event rendered
25931          * Fires when the form is rendered
25932          * @param {Roo.form.Form} form
25933          */
25934         rendered : true
25935     });
25936     
25937     Roo.each(xitems, this.addxtype, this);
25938     
25939     
25940     
25941 };
25942
25943 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
25944     /**
25945      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
25946      */
25947     /**
25948      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
25949      */
25950     /**
25951      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
25952      */
25953     buttonAlign:'center',
25954
25955     /**
25956      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
25957      */
25958     minButtonWidth:75,
25959
25960     /**
25961      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
25962      * This property cascades to child containers if not set.
25963      */
25964     labelAlign:'left',
25965
25966     /**
25967      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
25968      * fires a looping event with that state. This is required to bind buttons to the valid
25969      * state using the config value formBind:true on the button.
25970      */
25971     monitorValid : false,
25972
25973     /**
25974      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
25975      */
25976     monitorPoll : 200,
25977
25978   
25979     /**
25980      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
25981      * fields are added and the column is closed. If no fields are passed the column remains open
25982      * until end() is called.
25983      * @param {Object} config The config to pass to the column
25984      * @param {Field} field1 (optional)
25985      * @param {Field} field2 (optional)
25986      * @param {Field} etc (optional)
25987      * @return Column The column container object
25988      */
25989     column : function(c){
25990         var col = new Roo.form.Column(c);
25991         this.start(col);
25992         if(arguments.length > 1){ // duplicate code required because of Opera
25993             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25994             this.end();
25995         }
25996         return col;
25997     },
25998
25999     /**
26000      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
26001      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
26002      * until end() is called.
26003      * @param {Object} config The config to pass to the fieldset
26004      * @param {Field} field1 (optional)
26005      * @param {Field} field2 (optional)
26006      * @param {Field} etc (optional)
26007      * @return FieldSet The fieldset container object
26008      */
26009     fieldset : function(c){
26010         var fs = new Roo.form.FieldSet(c);
26011         this.start(fs);
26012         if(arguments.length > 1){ // duplicate code required because of Opera
26013             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
26014             this.end();
26015         }
26016         return fs;
26017     },
26018
26019     /**
26020      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
26021      * fields are added and the container is closed. If no fields are passed the container remains open
26022      * until end() is called.
26023      * @param {Object} config The config to pass to the Layout
26024      * @param {Field} field1 (optional)
26025      * @param {Field} field2 (optional)
26026      * @param {Field} etc (optional)
26027      * @return Layout The container object
26028      */
26029     container : function(c){
26030         var l = new Roo.form.Layout(c);
26031         this.start(l);
26032         if(arguments.length > 1){ // duplicate code required because of Opera
26033             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
26034             this.end();
26035         }
26036         return l;
26037     },
26038
26039     /**
26040      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
26041      * @param {Object} container A Roo.form.Layout or subclass of Layout
26042      * @return {Form} this
26043      */
26044     start : function(c){
26045         // cascade label info
26046         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
26047         this.active.stack.push(c);
26048         c.ownerCt = this.active;
26049         this.active = c;
26050         return this;
26051     },
26052
26053     /**
26054      * Closes the current open container
26055      * @return {Form} this
26056      */
26057     end : function(){
26058         if(this.active == this.root){
26059             return this;
26060         }
26061         this.active = this.active.ownerCt;
26062         return this;
26063     },
26064
26065     /**
26066      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
26067      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
26068      * as the label of the field.
26069      * @param {Field} field1
26070      * @param {Field} field2 (optional)
26071      * @param {Field} etc. (optional)
26072      * @return {Form} this
26073      */
26074     add : function(){
26075         this.active.stack.push.apply(this.active.stack, arguments);
26076         this.allItems.push.apply(this.allItems,arguments);
26077         var r = [];
26078         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
26079             if(a[i].isFormField){
26080                 r.push(a[i]);
26081             }
26082         }
26083         if(r.length > 0){
26084             Roo.form.Form.superclass.add.apply(this, r);
26085         }
26086         return this;
26087     },
26088     
26089
26090     
26091     
26092     
26093      /**
26094      * Find any element that has been added to a form, using it's ID or name
26095      * This can include framesets, columns etc. along with regular fields..
26096      * @param {String} id - id or name to find.
26097      
26098      * @return {Element} e - or false if nothing found.
26099      */
26100     findbyId : function(id)
26101     {
26102         var ret = false;
26103         if (!id) {
26104             return ret;
26105         }
26106         Ext.each(this.allItems, function(f){
26107             if (f.id == id || f.name == id ){
26108                 ret = f;
26109                 return false;
26110             }
26111         });
26112         return ret;
26113     },
26114
26115     
26116     
26117     /**
26118      * Render this form into the passed container. This should only be called once!
26119      * @param {String/HTMLElement/Element} container The element this component should be rendered into
26120      * @return {Form} this
26121      */
26122     render : function(ct){
26123         ct = Roo.get(ct);
26124         var o = this.autoCreate || {
26125             tag: 'form',
26126             method : this.method || 'POST',
26127             id : this.id || Roo.id()
26128         };
26129         this.initEl(ct.createChild(o));
26130
26131         this.root.render(this.el);
26132
26133         this.items.each(function(f){
26134             f.render('x-form-el-'+f.id);
26135         });
26136
26137         if(this.buttons.length > 0){
26138             // tables are required to maintain order and for correct IE layout
26139             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
26140                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
26141                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
26142             }}, null, true);
26143             var tr = tb.getElementsByTagName('tr')[0];
26144             for(var i = 0, len = this.buttons.length; i < len; i++) {
26145                 var b = this.buttons[i];
26146                 var td = document.createElement('td');
26147                 td.className = 'x-form-btn-td';
26148                 b.render(tr.appendChild(td));
26149             }
26150         }
26151         if(this.monitorValid){ // initialize after render
26152             this.startMonitoring();
26153         }
26154         this.fireEvent('rendered', this);
26155         return this;
26156     },
26157
26158     /**
26159      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
26160      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
26161      * object or a valid Roo.DomHelper element config
26162      * @param {Function} handler The function called when the button is clicked
26163      * @param {Object} scope (optional) The scope of the handler function
26164      * @return {Roo.Button}
26165      */
26166     addButton : function(config, handler, scope){
26167         var bc = {
26168             handler: handler,
26169             scope: scope,
26170             minWidth: this.minButtonWidth,
26171             hideParent:true
26172         };
26173         if(typeof config == "string"){
26174             bc.text = config;
26175         }else{
26176             Roo.apply(bc, config);
26177         }
26178         var btn = new Roo.Button(null, bc);
26179         this.buttons.push(btn);
26180         return btn;
26181     },
26182
26183      /**
26184      * Adds a series of form elements (using the xtype property as the factory method.
26185      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
26186      * @param {Object} config 
26187      */
26188     
26189     addxtype : function()
26190     {
26191         var ar = Array.prototype.slice.call(arguments, 0);
26192         var ret = false;
26193         for(var i = 0; i < ar.length; i++) {
26194             if (!ar[i]) {
26195                 continue; // skip -- if this happends something invalid got sent, we 
26196                 // should ignore it, as basically that interface element will not show up
26197                 // and that should be pretty obvious!!
26198             }
26199             
26200             if (Roo.form[ar[i].xtype]) {
26201                 ar[i].form = this;
26202                 var fe = Roo.factory(ar[i], Roo.form);
26203                 if (!ret) {
26204                     ret = fe;
26205                 }
26206                 fe.form = this;
26207                 if (fe.store) {
26208                     fe.store.form = this;
26209                 }
26210                 if (fe.isLayout) {  
26211                          
26212                     this.start(fe);
26213                     this.allItems.push(fe);
26214                     if (fe.items && fe.addxtype) {
26215                         fe.addxtype.apply(fe, fe.items);
26216                         delete fe.items;
26217                     }
26218                      this.end();
26219                     continue;
26220                 }
26221                 
26222                 
26223                  
26224                 this.add(fe);
26225               //  console.log('adding ' + ar[i].xtype);
26226             }
26227             if (ar[i].xtype == 'Button') {  
26228                 //console.log('adding button');
26229                 //console.log(ar[i]);
26230                 this.addButton(ar[i]);
26231                 this.allItems.push(fe);
26232                 continue;
26233             }
26234             
26235             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
26236                 alert('end is not supported on xtype any more, use items');
26237             //    this.end();
26238             //    //console.log('adding end');
26239             }
26240             
26241         }
26242         return ret;
26243     },
26244     
26245     /**
26246      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
26247      * option "monitorValid"
26248      */
26249     startMonitoring : function(){
26250         if(!this.bound){
26251             this.bound = true;
26252             Roo.TaskMgr.start({
26253                 run : this.bindHandler,
26254                 interval : this.monitorPoll || 200,
26255                 scope: this
26256             });
26257         }
26258     },
26259
26260     /**
26261      * Stops monitoring of the valid state of this form
26262      */
26263     stopMonitoring : function(){
26264         this.bound = false;
26265     },
26266
26267     // private
26268     bindHandler : function(){
26269         if(!this.bound){
26270             return false; // stops binding
26271         }
26272         var valid = true;
26273         this.items.each(function(f){
26274             if(!f.isValid(true)){
26275                 valid = false;
26276                 return false;
26277             }
26278         });
26279         for(var i = 0, len = this.buttons.length; i < len; i++){
26280             var btn = this.buttons[i];
26281             if(btn.formBind === true && btn.disabled === valid){
26282                 btn.setDisabled(!valid);
26283             }
26284         }
26285         this.fireEvent('clientvalidation', this, valid);
26286     }
26287     
26288     
26289     
26290     
26291     
26292     
26293     
26294     
26295 });
26296
26297
26298 // back compat
26299 Roo.Form = Roo.form.Form;
26300 /*
26301  * Based on:
26302  * Ext JS Library 1.1.1
26303  * Copyright(c) 2006-2007, Ext JS, LLC.
26304  *
26305  * Originally Released Under LGPL - original licence link has changed is not relivant.
26306  *
26307  * Fork - LGPL
26308  * <script type="text/javascript">
26309  */
26310  
26311  /**
26312  * @class Roo.form.Action
26313  * Internal Class used to handle form actions
26314  * @constructor
26315  * @param {Roo.form.BasicForm} el The form element or its id
26316  * @param {Object} config Configuration options
26317  */
26318  
26319  
26320 // define the action interface
26321 Roo.form.Action = function(form, options){
26322     this.form = form;
26323     this.options = options || {};
26324 };
26325 /**
26326  * Client Validation Failed
26327  * @const 
26328  */
26329 Roo.form.Action.CLIENT_INVALID = 'client';
26330 /**
26331  * Server Validation Failed
26332  * @const 
26333  */
26334  Roo.form.Action.SERVER_INVALID = 'server';
26335  /**
26336  * Connect to Server Failed
26337  * @const 
26338  */
26339 Roo.form.Action.CONNECT_FAILURE = 'connect';
26340 /**
26341  * Reading Data from Server Failed
26342  * @const 
26343  */
26344 Roo.form.Action.LOAD_FAILURE = 'load';
26345
26346 Roo.form.Action.prototype = {
26347     type : 'default',
26348     failureType : undefined,
26349     response : undefined,
26350     result : undefined,
26351
26352     // interface method
26353     run : function(options){
26354
26355     },
26356
26357     // interface method
26358     success : function(response){
26359
26360     },
26361
26362     // interface method
26363     handleResponse : function(response){
26364
26365     },
26366
26367     // default connection failure
26368     failure : function(response){
26369         this.response = response;
26370         this.failureType = Roo.form.Action.CONNECT_FAILURE;
26371         this.form.afterAction(this, false);
26372     },
26373
26374     processResponse : function(response){
26375         this.response = response;
26376         if(!response.responseText){
26377             return true;
26378         }
26379         this.result = this.handleResponse(response);
26380         return this.result;
26381     },
26382
26383     // utility functions used internally
26384     getUrl : function(appendParams){
26385         var url = this.options.url || this.form.url || this.form.el.dom.action;
26386         if(appendParams){
26387             var p = this.getParams();
26388             if(p){
26389                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
26390             }
26391         }
26392         return url;
26393     },
26394
26395     getMethod : function(){
26396         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
26397     },
26398
26399     getParams : function(){
26400         var bp = this.form.baseParams;
26401         var p = this.options.params;
26402         if(p){
26403             if(typeof p == "object"){
26404                 p = Roo.urlEncode(Roo.applyIf(p, bp));
26405             }else if(typeof p == 'string' && bp){
26406                 p += '&' + Roo.urlEncode(bp);
26407             }
26408         }else if(bp){
26409             p = Roo.urlEncode(bp);
26410         }
26411         return p;
26412     },
26413
26414     createCallback : function(){
26415         return {
26416             success: this.success,
26417             failure: this.failure,
26418             scope: this,
26419             timeout: (this.form.timeout*1000),
26420             upload: this.form.fileUpload ? this.success : undefined
26421         };
26422     }
26423 };
26424
26425 Roo.form.Action.Submit = function(form, options){
26426     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
26427 };
26428
26429 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
26430     type : 'submit',
26431
26432     run : function(){
26433         var o = this.options;
26434         var method = this.getMethod();
26435         var isPost = method == 'POST';
26436         if(o.clientValidation === false || this.form.isValid()){
26437             Roo.Ajax.request(Roo.apply(this.createCallback(), {
26438                 form:this.form.el.dom,
26439                 url:this.getUrl(!isPost),
26440                 method: method,
26441                 params:isPost ? this.getParams() : null,
26442                 isUpload: this.form.fileUpload
26443             }));
26444
26445         }else if (o.clientValidation !== false){ // client validation failed
26446             this.failureType = Roo.form.Action.CLIENT_INVALID;
26447             this.form.afterAction(this, false);
26448         }
26449     },
26450
26451     success : function(response){
26452         var result = this.processResponse(response);
26453         if(result === true || result.success){
26454             this.form.afterAction(this, true);
26455             return;
26456         }
26457         if(result.errors){
26458             this.form.markInvalid(result.errors);
26459             this.failureType = Roo.form.Action.SERVER_INVALID;
26460         }
26461         this.form.afterAction(this, false);
26462     },
26463
26464     handleResponse : function(response){
26465         if(this.form.errorReader){
26466             var rs = this.form.errorReader.read(response);
26467             var errors = [];
26468             if(rs.records){
26469                 for(var i = 0, len = rs.records.length; i < len; i++) {
26470                     var r = rs.records[i];
26471                     errors[i] = r.data;
26472                 }
26473             }
26474             if(errors.length < 1){
26475                 errors = null;
26476             }
26477             return {
26478                 success : rs.success,
26479                 errors : errors
26480             };
26481         }
26482         var ret = false;
26483         try {
26484             ret = Roo.decode(response.responseText);
26485         } catch (e) {
26486             ret = {
26487                 success: false,
26488                 errorMsg: "Failed to read server message: " + response.responseText,
26489                 errors : []
26490             };
26491         }
26492         return ret;
26493         
26494     }
26495 });
26496
26497
26498 Roo.form.Action.Load = function(form, options){
26499     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
26500     this.reader = this.form.reader;
26501 };
26502
26503 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
26504     type : 'load',
26505
26506     run : function(){
26507         Roo.Ajax.request(Roo.apply(
26508                 this.createCallback(), {
26509                     method:this.getMethod(),
26510                     url:this.getUrl(false),
26511                     params:this.getParams()
26512         }));
26513     },
26514
26515     success : function(response){
26516         var result = this.processResponse(response);
26517         if(result === true || !result.success || !result.data){
26518             this.failureType = Roo.form.Action.LOAD_FAILURE;
26519             this.form.afterAction(this, false);
26520             return;
26521         }
26522         this.form.clearInvalid();
26523         this.form.setValues(result.data);
26524         this.form.afterAction(this, true);
26525     },
26526
26527     handleResponse : function(response){
26528         if(this.form.reader){
26529             var rs = this.form.reader.read(response);
26530             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
26531             return {
26532                 success : rs.success,
26533                 data : data
26534             };
26535         }
26536         return Roo.decode(response.responseText);
26537     }
26538 });
26539
26540 Roo.form.Action.ACTION_TYPES = {
26541     'load' : Roo.form.Action.Load,
26542     'submit' : Roo.form.Action.Submit
26543 };/*
26544  * Based on:
26545  * Ext JS Library 1.1.1
26546  * Copyright(c) 2006-2007, Ext JS, LLC.
26547  *
26548  * Originally Released Under LGPL - original licence link has changed is not relivant.
26549  *
26550  * Fork - LGPL
26551  * <script type="text/javascript">
26552  */
26553  
26554 /**
26555  * @class Roo.form.Layout
26556  * @extends Roo.Component
26557  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
26558  * @constructor
26559  * @param {Object} config Configuration options
26560  */
26561 Roo.form.Layout = function(config){
26562     var xitems = [];
26563     if (config.items) {
26564         xitems = config.items;
26565         delete config.items;
26566     }
26567     Roo.form.Layout.superclass.constructor.call(this, config);
26568     this.stack = [];
26569     Roo.each(xitems, this.addxtype, this);
26570      
26571 };
26572
26573 Roo.extend(Roo.form.Layout, Roo.Component, {
26574     /**
26575      * @cfg {String/Object} autoCreate
26576      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
26577      */
26578     /**
26579      * @cfg {String/Object/Function} style
26580      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
26581      * a function which returns such a specification.
26582      */
26583     /**
26584      * @cfg {String} labelAlign
26585      * Valid values are "left," "top" and "right" (defaults to "left")
26586      */
26587     /**
26588      * @cfg {Number} labelWidth
26589      * Fixed width in pixels of all field labels (defaults to undefined)
26590      */
26591     /**
26592      * @cfg {Boolean} clear
26593      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
26594      */
26595     clear : true,
26596     /**
26597      * @cfg {String} labelSeparator
26598      * The separator to use after field labels (defaults to ':')
26599      */
26600     labelSeparator : ':',
26601     /**
26602      * @cfg {Boolean} hideLabels
26603      * True to suppress the display of field labels in this layout (defaults to false)
26604      */
26605     hideLabels : false,
26606
26607     // private
26608     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
26609     
26610     isLayout : true,
26611     
26612     // private
26613     onRender : function(ct, position){
26614         if(this.el){ // from markup
26615             this.el = Roo.get(this.el);
26616         }else {  // generate
26617             var cfg = this.getAutoCreate();
26618             this.el = ct.createChild(cfg, position);
26619         }
26620         if(this.style){
26621             this.el.applyStyles(this.style);
26622         }
26623         if(this.labelAlign){
26624             this.el.addClass('x-form-label-'+this.labelAlign);
26625         }
26626         if(this.hideLabels){
26627             this.labelStyle = "display:none";
26628             this.elementStyle = "padding-left:0;";
26629         }else{
26630             if(typeof this.labelWidth == 'number'){
26631                 this.labelStyle = "width:"+this.labelWidth+"px;";
26632                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
26633             }
26634             if(this.labelAlign == 'top'){
26635                 this.labelStyle = "width:auto;";
26636                 this.elementStyle = "padding-left:0;";
26637             }
26638         }
26639         var stack = this.stack;
26640         var slen = stack.length;
26641         if(slen > 0){
26642             if(!this.fieldTpl){
26643                 var t = new Roo.Template(
26644                     '<div class="x-form-item {5}">',
26645                         '<label for="{0}" style="{2}">{1}{4}</label>',
26646                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
26647                         '</div>',
26648                     '</div><div class="x-form-clear-left"></div>'
26649                 );
26650                 t.disableFormats = true;
26651                 t.compile();
26652                 Roo.form.Layout.prototype.fieldTpl = t;
26653             }
26654             for(var i = 0; i < slen; i++) {
26655                 if(stack[i].isFormField){
26656                     this.renderField(stack[i]);
26657                 }else{
26658                     this.renderComponent(stack[i]);
26659                 }
26660             }
26661         }
26662         if(this.clear){
26663             this.el.createChild({cls:'x-form-clear'});
26664         }
26665     },
26666
26667     // private
26668     renderField : function(f){
26669         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
26670                f.id, //0
26671                f.fieldLabel, //1
26672                f.labelStyle||this.labelStyle||'', //2
26673                this.elementStyle||'', //3
26674                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
26675                f.itemCls||this.itemCls||''  //5
26676        ], true).getPrevSibling());
26677     },
26678
26679     // private
26680     renderComponent : function(c){
26681         c.render(c.isLayout ? this.el : this.el.createChild());    
26682     },
26683     /**
26684      * Adds a object form elements (using the xtype property as the factory method.)
26685      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
26686      * @param {Object} config 
26687      */
26688     addxtype : function(o)
26689     {
26690         // create the lement.
26691         o.form = this.form;
26692         var fe = Roo.factory(o, Roo.form);
26693         this.form.allItems.push(fe);
26694         this.stack.push(fe);
26695         
26696         if (fe.isFormField) {
26697             this.form.items.add(fe);
26698         }
26699          
26700         return fe;
26701     }
26702 });
26703
26704 /**
26705  * @class Roo.form.Column
26706  * @extends Roo.form.Layout
26707  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
26708  * @constructor
26709  * @param {Object} config Configuration options
26710  */
26711 Roo.form.Column = function(config){
26712     Roo.form.Column.superclass.constructor.call(this, config);
26713 };
26714
26715 Roo.extend(Roo.form.Column, Roo.form.Layout, {
26716     /**
26717      * @cfg {Number/String} width
26718      * The fixed width of the column in pixels or CSS value (defaults to "auto")
26719      */
26720     /**
26721      * @cfg {String/Object} autoCreate
26722      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
26723      */
26724
26725     // private
26726     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
26727
26728     // private
26729     onRender : function(ct, position){
26730         Roo.form.Column.superclass.onRender.call(this, ct, position);
26731         if(this.width){
26732             this.el.setWidth(this.width);
26733         }
26734     }
26735 });
26736
26737
26738 /**
26739  * @class Roo.form.Row
26740  * @extends Roo.form.Layout
26741  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
26742  * @constructor
26743  * @param {Object} config Configuration options
26744  */
26745
26746  
26747 Roo.form.Row = function(config){
26748     Roo.form.Row.superclass.constructor.call(this, config);
26749 };
26750  
26751 Roo.extend(Roo.form.Row, Roo.form.Layout, {
26752       /**
26753      * @cfg {Number/String} width
26754      * The fixed width of the column in pixels or CSS value (defaults to "auto")
26755      */
26756     /**
26757      * @cfg {Number/String} height
26758      * The fixed height of the column in pixels or CSS value (defaults to "auto")
26759      */
26760     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
26761     
26762     padWidth : 20,
26763     // private
26764     onRender : function(ct, position){
26765         //console.log('row render');
26766         if(!this.rowTpl){
26767             var t = new Roo.Template(
26768                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
26769                     '<label for="{0}" style="{2}">{1}{4}</label>',
26770                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
26771                     '</div>',
26772                 '</div>'
26773             );
26774             t.disableFormats = true;
26775             t.compile();
26776             Roo.form.Layout.prototype.rowTpl = t;
26777         }
26778         this.fieldTpl = this.rowTpl;
26779         
26780         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
26781         var labelWidth = 100;
26782         
26783         if ((this.labelAlign != 'top')) {
26784             if (typeof this.labelWidth == 'number') {
26785                 labelWidth = this.labelWidth
26786             }
26787             this.padWidth =  20 + labelWidth;
26788             
26789         }
26790         
26791         Roo.form.Column.superclass.onRender.call(this, ct, position);
26792         if(this.width){
26793             this.el.setWidth(this.width);
26794         }
26795         if(this.height){
26796             this.el.setHeight(this.height);
26797         }
26798     },
26799     
26800     // private
26801     renderField : function(f){
26802         f.fieldEl = this.fieldTpl.append(this.el, [
26803                f.id, f.fieldLabel,
26804                f.labelStyle||this.labelStyle||'',
26805                this.elementStyle||'',
26806                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
26807                f.itemCls||this.itemCls||'',
26808                f.width ? f.width + this.padWidth : 160 + this.padWidth
26809        ],true);
26810     }
26811 });
26812  
26813
26814 /**
26815  * @class Roo.form.FieldSet
26816  * @extends Roo.form.Layout
26817  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
26818  * @constructor
26819  * @param {Object} config Configuration options
26820  */
26821 Roo.form.FieldSet = function(config){
26822     Roo.form.FieldSet.superclass.constructor.call(this, config);
26823 };
26824
26825 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
26826     /**
26827      * @cfg {String} legend
26828      * The text to display as the legend for the FieldSet (defaults to '')
26829      */
26830     /**
26831      * @cfg {String/Object} autoCreate
26832      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
26833      */
26834
26835     // private
26836     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
26837
26838     // private
26839     onRender : function(ct, position){
26840         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
26841         if(this.legend){
26842             this.setLegend(this.legend);
26843         }
26844     },
26845
26846     // private
26847     setLegend : function(text){
26848         if(this.rendered){
26849             this.el.child('legend').update(text);
26850         }
26851     }
26852 });/*
26853  * Based on:
26854  * Ext JS Library 1.1.1
26855  * Copyright(c) 2006-2007, Ext JS, LLC.
26856  *
26857  * Originally Released Under LGPL - original licence link has changed is not relivant.
26858  *
26859  * Fork - LGPL
26860  * <script type="text/javascript">
26861  */
26862 /**
26863  * @class Roo.form.VTypes
26864  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
26865  * @singleton
26866  */
26867 Roo.form.VTypes = function(){
26868     // closure these in so they are only created once.
26869     var alpha = /^[a-zA-Z_]+$/;
26870     var alphanum = /^[a-zA-Z0-9_]+$/;
26871     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
26872     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
26873
26874     // All these messages and functions are configurable
26875     return {
26876         /**
26877          * The function used to validate email addresses
26878          * @param {String} value The email address
26879          */
26880         'email' : function(v){
26881             return email.test(v);
26882         },
26883         /**
26884          * The error text to display when the email validation function returns false
26885          * @type String
26886          */
26887         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
26888         /**
26889          * The keystroke filter mask to be applied on email input
26890          * @type RegExp
26891          */
26892         'emailMask' : /[a-z0-9_\.\-@]/i,
26893
26894         /**
26895          * The function used to validate URLs
26896          * @param {String} value The URL
26897          */
26898         'url' : function(v){
26899             return url.test(v);
26900         },
26901         /**
26902          * The error text to display when the url validation function returns false
26903          * @type String
26904          */
26905         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
26906         
26907         /**
26908          * The function used to validate alpha values
26909          * @param {String} value The value
26910          */
26911         'alpha' : function(v){
26912             return alpha.test(v);
26913         },
26914         /**
26915          * The error text to display when the alpha validation function returns false
26916          * @type String
26917          */
26918         'alphaText' : 'This field should only contain letters and _',
26919         /**
26920          * The keystroke filter mask to be applied on alpha input
26921          * @type RegExp
26922          */
26923         'alphaMask' : /[a-z_]/i,
26924
26925         /**
26926          * The function used to validate alphanumeric values
26927          * @param {String} value The value
26928          */
26929         'alphanum' : function(v){
26930             return alphanum.test(v);
26931         },
26932         /**
26933          * The error text to display when the alphanumeric validation function returns false
26934          * @type String
26935          */
26936         'alphanumText' : 'This field should only contain letters, numbers and _',
26937         /**
26938          * The keystroke filter mask to be applied on alphanumeric input
26939          * @type RegExp
26940          */
26941         'alphanumMask' : /[a-z0-9_]/i
26942     };
26943 }();//<script type="text/javascript">
26944
26945 /**
26946  * @class Roo.form.FCKeditor
26947  * @extends Roo.form.TextArea
26948  * Wrapper around the FCKEditor http://www.fckeditor.net
26949  * @constructor
26950  * Creates a new FCKeditor
26951  * @param {Object} config Configuration options
26952  */
26953 Roo.form.FCKeditor = function(config){
26954     Roo.form.FCKeditor.superclass.constructor.call(this, config);
26955     this.addEvents({
26956          /**
26957          * @event editorinit
26958          * Fired when the editor is initialized - you can add extra handlers here..
26959          * @param {FCKeditor} this
26960          * @param {Object} the FCK object.
26961          */
26962         editorinit : true
26963     });
26964     
26965     
26966 };
26967 Roo.form.FCKeditor.editors = { };
26968 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
26969 {
26970     //defaultAutoCreate : {
26971     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
26972     //},
26973     // private
26974     /**
26975      * @cfg {Object} fck options - see fck manual for details.
26976      */
26977     fckconfig : false,
26978     
26979     /**
26980      * @cfg {Object} fck toolbar set (Basic or Default)
26981      */
26982     toolbarSet : 'Basic',
26983     /**
26984      * @cfg {Object} fck BasePath
26985      */ 
26986     basePath : '/fckeditor/',
26987     
26988     
26989     frame : false,
26990     
26991     value : '',
26992     
26993    
26994     onRender : function(ct, position)
26995     {
26996         if(!this.el){
26997             this.defaultAutoCreate = {
26998                 tag: "textarea",
26999                 style:"width:300px;height:60px;",
27000                 autocomplete: "off"
27001             };
27002         }
27003         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
27004         /*
27005         if(this.grow){
27006             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
27007             if(this.preventScrollbars){
27008                 this.el.setStyle("overflow", "hidden");
27009             }
27010             this.el.setHeight(this.growMin);
27011         }
27012         */
27013         //console.log('onrender' + this.getId() );
27014         Roo.form.FCKeditor.editors[this.getId()] = this;
27015          
27016
27017         this.replaceTextarea() ;
27018         
27019     },
27020     
27021     getEditor : function() {
27022         return this.fckEditor;
27023     },
27024     /**
27025      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
27026      * @param {Mixed} value The value to set
27027      */
27028     
27029     
27030     setValue : function(value)
27031     {
27032         //console.log('setValue: ' + value);
27033         
27034         if(typeof(value) == 'undefined') { // not sure why this is happending...
27035             return;
27036         }
27037         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
27038         
27039         //if(!this.el || !this.getEditor()) {
27040         //    this.value = value;
27041             //this.setValue.defer(100,this,[value]);    
27042         //    return;
27043         //} 
27044         
27045         if(!this.getEditor()) {
27046             return;
27047         }
27048         
27049         this.getEditor().SetData(value);
27050         
27051         //
27052
27053     },
27054
27055     /**
27056      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
27057      * @return {Mixed} value The field value
27058      */
27059     getValue : function()
27060     {
27061         
27062         if (this.frame && this.frame.dom.style.display == 'none') {
27063             return Roo.form.FCKeditor.superclass.getValue.call(this);
27064         }
27065         
27066         if(!this.el || !this.getEditor()) {
27067            
27068            // this.getValue.defer(100,this); 
27069             return this.value;
27070         }
27071        
27072         
27073         var value=this.getEditor().GetData();
27074         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
27075         return Roo.form.FCKeditor.superclass.getValue.call(this);
27076         
27077
27078     },
27079
27080     /**
27081      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
27082      * @return {Mixed} value The field value
27083      */
27084     getRawValue : function()
27085     {
27086         if (this.frame && this.frame.dom.style.display == 'none') {
27087             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
27088         }
27089         
27090         if(!this.el || !this.getEditor()) {
27091             //this.getRawValue.defer(100,this); 
27092             return this.value;
27093             return;
27094         }
27095         
27096         
27097         
27098         var value=this.getEditor().GetData();
27099         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
27100         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
27101          
27102     },
27103     
27104     setSize : function(w,h) {
27105         
27106         
27107         
27108         //if (this.frame && this.frame.dom.style.display == 'none') {
27109         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
27110         //    return;
27111         //}
27112         //if(!this.el || !this.getEditor()) {
27113         //    this.setSize.defer(100,this, [w,h]); 
27114         //    return;
27115         //}
27116         
27117         
27118         
27119         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
27120         
27121         this.frame.dom.setAttribute('width', w);
27122         this.frame.dom.setAttribute('height', h);
27123         this.frame.setSize(w,h);
27124         
27125     },
27126     
27127     toggleSourceEdit : function(value) {
27128         
27129       
27130          
27131         this.el.dom.style.display = value ? '' : 'none';
27132         this.frame.dom.style.display = value ?  'none' : '';
27133         
27134     },
27135     
27136     
27137     focus: function(tag)
27138     {
27139         if (this.frame.dom.style.display == 'none') {
27140             return Roo.form.FCKeditor.superclass.focus.call(this);
27141         }
27142         if(!this.el || !this.getEditor()) {
27143             this.focus.defer(100,this, [tag]); 
27144             return;
27145         }
27146         
27147         
27148         
27149         
27150         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
27151         this.getEditor().Focus();
27152         if (tgs.length) {
27153             if (!this.getEditor().Selection.GetSelection()) {
27154                 this.focus.defer(100,this, [tag]); 
27155                 return;
27156             }
27157             
27158             
27159             var r = this.getEditor().EditorDocument.createRange();
27160             r.setStart(tgs[0],0);
27161             r.setEnd(tgs[0],0);
27162             this.getEditor().Selection.GetSelection().removeAllRanges();
27163             this.getEditor().Selection.GetSelection().addRange(r);
27164             this.getEditor().Focus();
27165         }
27166         
27167     },
27168     
27169     
27170     
27171     replaceTextarea : function()
27172     {
27173         if ( document.getElementById( this.getId() + '___Frame' ) )
27174             return ;
27175         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
27176         //{
27177             // We must check the elements firstly using the Id and then the name.
27178         var oTextarea = document.getElementById( this.getId() );
27179         
27180         var colElementsByName = document.getElementsByName( this.getId() ) ;
27181          
27182         oTextarea.style.display = 'none' ;
27183
27184         if ( oTextarea.tabIndex ) {            
27185             this.TabIndex = oTextarea.tabIndex ;
27186         }
27187         
27188         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
27189         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
27190         this.frame = Roo.get(this.getId() + '___Frame')
27191     },
27192     
27193     _getConfigHtml : function()
27194     {
27195         var sConfig = '' ;
27196
27197         for ( var o in this.fckconfig ) {
27198             sConfig += sConfig.length > 0  ? '&amp;' : '';
27199             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
27200         }
27201
27202         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
27203     },
27204     
27205     
27206     _getIFrameHtml : function()
27207     {
27208         var sFile = 'fckeditor.html' ;
27209         /* no idea what this is about..
27210         try
27211         {
27212             if ( (/fcksource=true/i).test( window.top.location.search ) )
27213                 sFile = 'fckeditor.original.html' ;
27214         }
27215         catch (e) { 
27216         */
27217
27218         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
27219         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
27220         
27221         
27222         var html = '<iframe id="' + this.getId() +
27223             '___Frame" src="' + sLink +
27224             '" width="' + this.width +
27225             '" height="' + this.height + '"' +
27226             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
27227             ' frameborder="0" scrolling="no"></iframe>' ;
27228
27229         return html ;
27230     },
27231     
27232     _insertHtmlBefore : function( html, element )
27233     {
27234         if ( element.insertAdjacentHTML )       {
27235             // IE
27236             element.insertAdjacentHTML( 'beforeBegin', html ) ;
27237         } else { // Gecko
27238             var oRange = document.createRange() ;
27239             oRange.setStartBefore( element ) ;
27240             var oFragment = oRange.createContextualFragment( html );
27241             element.parentNode.insertBefore( oFragment, element ) ;
27242         }
27243     }
27244     
27245     
27246   
27247     
27248     
27249     
27250     
27251
27252 });
27253
27254 //Roo.reg('fckeditor', Roo.form.FCKeditor);
27255
27256 function FCKeditor_OnComplete(editorInstance){
27257     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
27258     f.fckEditor = editorInstance;
27259     //console.log("loaded");
27260     f.fireEvent('editorinit', f, editorInstance);
27261
27262   
27263
27264  
27265
27266
27267
27268
27269
27270
27271
27272
27273
27274
27275
27276
27277
27278
27279
27280 //<script type="text/javascript">
27281 /**
27282  * @class Roo.form.GridField
27283  * @extends Roo.form.Field
27284  * Embed a grid (or editable grid into a form)
27285  * STATUS ALPHA
27286  * @constructor
27287  * Creates a new GridField
27288  * @param {Object} config Configuration options
27289  */
27290 Roo.form.GridField = function(config){
27291     Roo.form.GridField.superclass.constructor.call(this, config);
27292      
27293 };
27294
27295 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
27296     /**
27297      * @cfg {Number} width  - used to restrict width of grid..
27298      */
27299     width : 100,
27300     /**
27301      * @cfg {Number} height - used to restrict height of grid..
27302      */
27303     height : 50,
27304      /**
27305      * @cfg {Object} xgrid (xtype'd description of grid) Grid or EditorGrid
27306      */
27307     xgrid : false, 
27308     /**
27309      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
27310      * {tag: "input", type: "checkbox", autocomplete: "off"})
27311      */
27312    // defaultAutoCreate : { tag: 'div' },
27313     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
27314     /**
27315      * @cfg {String} addTitle Text to include for adding a title.
27316      */
27317     addTitle : false,
27318     //
27319     onResize : function(){
27320         Roo.form.Field.superclass.onResize.apply(this, arguments);
27321     },
27322
27323     initEvents : function(){
27324         // Roo.form.Checkbox.superclass.initEvents.call(this);
27325         // has no events...
27326        
27327     },
27328
27329
27330     getResizeEl : function(){
27331         return this.wrap;
27332     },
27333
27334     getPositionEl : function(){
27335         return this.wrap;
27336     },
27337
27338     // private
27339     onRender : function(ct, position){
27340         
27341         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
27342         var style = this.style;
27343         delete this.style;
27344         
27345         Roo.form.DisplayImage.superclass.onRender.call(this, ct, position);
27346         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
27347         this.viewEl = this.wrap.createChild({ tag: 'div' });
27348         if (style) {
27349             this.viewEl.applyStyles(style);
27350         }
27351         if (this.width) {
27352             this.viewEl.setWidth(this.width);
27353         }
27354         if (this.height) {
27355             this.viewEl.setHeight(this.height);
27356         }
27357         //if(this.inputValue !== undefined){
27358         //this.setValue(this.value);
27359         
27360         
27361         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
27362         
27363         
27364         this.grid.render();
27365         this.grid.getDataSource().on('remove', this.refreshValue, this);
27366         this.grid.getDataSource().on('update', this.refreshValue, this);
27367         this.grid.on('afteredit', this.refreshValue, this);
27368  
27369     },
27370      
27371     
27372     /**
27373      * Sets the value of the item. 
27374      * @param {String} either an object  or a string..
27375      */
27376     setValue : function(v){
27377         //this.value = v;
27378         v = v || []; // empty set..
27379         // this does not seem smart - it really only affects memoryproxy grids..
27380         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
27381             var ds = this.grid.getDataSource();
27382             // assumes a json reader..
27383             var data = {}
27384             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
27385             ds.loadData( data);
27386         }
27387         Roo.form.GridField.superclass.setValue.call(this, v);
27388         this.refreshValue();
27389         // should load data in the grid really....
27390     },
27391     
27392     // private
27393     refreshValue: function() {
27394          var val = [];
27395         this.grid.getDataSource().each(function(r) {
27396             val.push(r.data);
27397         });
27398         this.el.dom.value = Roo.encode(val);
27399     }
27400     
27401      
27402     
27403     
27404 });//<script type="text/javasscript">
27405  
27406
27407 /**
27408  * @class Roo.DDView
27409  * A DnD enabled version of Roo.View.
27410  * @param {Element/String} container The Element in which to create the View.
27411  * @param {String} tpl The template string used to create the markup for each element of the View
27412  * @param {Object} config The configuration properties. These include all the config options of
27413  * {@link Roo.View} plus some specific to this class.<br>
27414  * <p>
27415  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
27416  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
27417  * <p>
27418  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
27419 .x-view-drag-insert-above {
27420         border-top:1px dotted #3366cc;
27421 }
27422 .x-view-drag-insert-below {
27423         border-bottom:1px dotted #3366cc;
27424 }
27425 </code></pre>
27426  * 
27427  */
27428  
27429 Roo.DDView = function(container, tpl, config) {
27430     Roo.DDView.superclass.constructor.apply(this, arguments);
27431     this.getEl().setStyle("outline", "0px none");
27432     this.getEl().unselectable();
27433     if (this.dragGroup) {
27434                 this.setDraggable(this.dragGroup.split(","));
27435     }
27436     if (this.dropGroup) {
27437                 this.setDroppable(this.dropGroup.split(","));
27438     }
27439     if (this.deletable) {
27440         this.setDeletable();
27441     }
27442     this.isDirtyFlag = false;
27443         this.addEvents({
27444                 "drop" : true
27445         });
27446 };
27447
27448 Roo.extend(Roo.DDView, Roo.View, {
27449 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
27450 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
27451 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
27452 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
27453
27454         isFormField: true,
27455
27456         reset: Roo.emptyFn,
27457         
27458         clearInvalid: Roo.form.Field.prototype.clearInvalid,
27459
27460         validate: function() {
27461                 return true;
27462         },
27463         
27464         destroy: function() {
27465                 this.purgeListeners();
27466                 this.getEl.removeAllListeners();
27467                 this.getEl().remove();
27468                 if (this.dragZone) {
27469                         if (this.dragZone.destroy) {
27470                                 this.dragZone.destroy();
27471                         }
27472                 }
27473                 if (this.dropZone) {
27474                         if (this.dropZone.destroy) {
27475                                 this.dropZone.destroy();
27476                         }
27477                 }
27478         },
27479
27480 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
27481         getName: function() {
27482                 return this.name;
27483         },
27484
27485 /**     Loads the View from a JSON string representing the Records to put into the Store. */
27486         setValue: function(v) {
27487                 if (!this.store) {
27488                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
27489                 }
27490                 var data = {};
27491                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
27492                 this.store.proxy = new Roo.data.MemoryProxy(data);
27493                 this.store.load();
27494         },
27495
27496 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
27497         getValue: function() {
27498                 var result = '(';
27499                 this.store.each(function(rec) {
27500                         result += rec.id + ',';
27501                 });
27502                 return result.substr(0, result.length - 1) + ')';
27503         },
27504         
27505         getIds: function() {
27506                 var i = 0, result = new Array(this.store.getCount());
27507                 this.store.each(function(rec) {
27508                         result[i++] = rec.id;
27509                 });
27510                 return result;
27511         },
27512         
27513         isDirty: function() {
27514                 return this.isDirtyFlag;
27515         },
27516
27517 /**
27518  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
27519  *      whole Element becomes the target, and this causes the drop gesture to append.
27520  */
27521     getTargetFromEvent : function(e) {
27522                 var target = e.getTarget();
27523                 while ((target !== null) && (target.parentNode != this.el.dom)) {
27524                 target = target.parentNode;
27525                 }
27526                 if (!target) {
27527                         target = this.el.dom.lastChild || this.el.dom;
27528                 }
27529                 return target;
27530     },
27531
27532 /**
27533  *      Create the drag data which consists of an object which has the property "ddel" as
27534  *      the drag proxy element. 
27535  */
27536     getDragData : function(e) {
27537         var target = this.findItemFromChild(e.getTarget());
27538                 if(target) {
27539                         this.handleSelection(e);
27540                         var selNodes = this.getSelectedNodes();
27541             var dragData = {
27542                 source: this,
27543                 copy: this.copy || (this.allowCopy && e.ctrlKey),
27544                 nodes: selNodes,
27545                 records: []
27546                         };
27547                         var selectedIndices = this.getSelectedIndexes();
27548                         for (var i = 0; i < selectedIndices.length; i++) {
27549                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
27550                         }
27551                         if (selNodes.length == 1) {
27552                                 dragData.ddel = target.cloneNode(true); // the div element
27553                         } else {
27554                                 var div = document.createElement('div'); // create the multi element drag "ghost"
27555                                 div.className = 'multi-proxy';
27556                                 for (var i = 0, len = selNodes.length; i < len; i++) {
27557                                         div.appendChild(selNodes[i].cloneNode(true));
27558                                 }
27559                                 dragData.ddel = div;
27560                         }
27561             //console.log(dragData)
27562             //console.log(dragData.ddel.innerHTML)
27563                         return dragData;
27564                 }
27565         //console.log('nodragData')
27566                 return false;
27567     },
27568     
27569 /**     Specify to which ddGroup items in this DDView may be dragged. */
27570     setDraggable: function(ddGroup) {
27571         if (ddGroup instanceof Array) {
27572                 Roo.each(ddGroup, this.setDraggable, this);
27573                 return;
27574         }
27575         if (this.dragZone) {
27576                 this.dragZone.addToGroup(ddGroup);
27577         } else {
27578                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
27579                                 containerScroll: true,
27580                                 ddGroup: ddGroup 
27581
27582                         });
27583 //                      Draggability implies selection. DragZone's mousedown selects the element.
27584                         if (!this.multiSelect) { this.singleSelect = true; }
27585
27586 //                      Wire the DragZone's handlers up to methods in *this*
27587                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
27588                 }
27589     },
27590
27591 /**     Specify from which ddGroup this DDView accepts drops. */
27592     setDroppable: function(ddGroup) {
27593         if (ddGroup instanceof Array) {
27594                 Roo.each(ddGroup, this.setDroppable, this);
27595                 return;
27596         }
27597         if (this.dropZone) {
27598                 this.dropZone.addToGroup(ddGroup);
27599         } else {
27600                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
27601                                 containerScroll: true,
27602                                 ddGroup: ddGroup
27603                         });
27604
27605 //                      Wire the DropZone's handlers up to methods in *this*
27606                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
27607                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
27608                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
27609                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
27610                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
27611                 }
27612     },
27613
27614 /**     Decide whether to drop above or below a View node. */
27615     getDropPoint : function(e, n, dd){
27616         if (n == this.el.dom) { return "above"; }
27617                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
27618                 var c = t + (b - t) / 2;
27619                 var y = Roo.lib.Event.getPageY(e);
27620                 if(y <= c) {
27621                         return "above";
27622                 }else{
27623                         return "below";
27624                 }
27625     },
27626
27627     onNodeEnter : function(n, dd, e, data){
27628                 return false;
27629     },
27630     
27631     onNodeOver : function(n, dd, e, data){
27632                 var pt = this.getDropPoint(e, n, dd);
27633                 // set the insert point style on the target node
27634                 var dragElClass = this.dropNotAllowed;
27635                 if (pt) {
27636                         var targetElClass;
27637                         if (pt == "above"){
27638                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
27639                                 targetElClass = "x-view-drag-insert-above";
27640                         } else {
27641                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
27642                                 targetElClass = "x-view-drag-insert-below";
27643                         }
27644                         if (this.lastInsertClass != targetElClass){
27645                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
27646                                 this.lastInsertClass = targetElClass;
27647                         }
27648                 }
27649                 return dragElClass;
27650         },
27651
27652     onNodeOut : function(n, dd, e, data){
27653                 this.removeDropIndicators(n);
27654     },
27655
27656     onNodeDrop : function(n, dd, e, data){
27657         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
27658                 return false;
27659         }
27660         var pt = this.getDropPoint(e, n, dd);
27661                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
27662                 if (pt == "below") { insertAt++; }
27663                 for (var i = 0; i < data.records.length; i++) {
27664                         var r = data.records[i];
27665                         var dup = this.store.getById(r.id);
27666                         if (dup && (dd != this.dragZone)) {
27667                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
27668                         } else {
27669                                 if (data.copy) {
27670                                         this.store.insert(insertAt++, r.copy());
27671                                 } else {
27672                                         data.source.isDirtyFlag = true;
27673                                         r.store.remove(r);
27674                                         this.store.insert(insertAt++, r);
27675                                 }
27676                                 this.isDirtyFlag = true;
27677                         }
27678                 }
27679                 this.dragZone.cachedTarget = null;
27680                 return true;
27681     },
27682
27683     removeDropIndicators : function(n){
27684                 if(n){
27685                         Roo.fly(n).removeClass([
27686                                 "x-view-drag-insert-above",
27687                                 "x-view-drag-insert-below"]);
27688                         this.lastInsertClass = "_noclass";
27689                 }
27690     },
27691
27692 /**
27693  *      Utility method. Add a delete option to the DDView's context menu.
27694  *      @param {String} imageUrl The URL of the "delete" icon image.
27695  */
27696         setDeletable: function(imageUrl) {
27697                 if (!this.singleSelect && !this.multiSelect) {
27698                         this.singleSelect = true;
27699                 }
27700                 var c = this.getContextMenu();
27701                 this.contextMenu.on("itemclick", function(item) {
27702                         switch (item.id) {
27703                                 case "delete":
27704                                         this.remove(this.getSelectedIndexes());
27705                                         break;
27706                         }
27707                 }, this);
27708                 this.contextMenu.add({
27709                         icon: imageUrl,
27710                         id: "delete",
27711                         text: 'Delete'
27712                 });
27713         },
27714         
27715 /**     Return the context menu for this DDView. */
27716         getContextMenu: function() {
27717                 if (!this.contextMenu) {
27718 //                      Create the View's context menu
27719                         this.contextMenu = new Roo.menu.Menu({
27720                                 id: this.id + "-contextmenu"
27721                         });
27722                         this.el.on("contextmenu", this.showContextMenu, this);
27723                 }
27724                 return this.contextMenu;
27725         },
27726         
27727         disableContextMenu: function() {
27728                 if (this.contextMenu) {
27729                         this.el.un("contextmenu", this.showContextMenu, this);
27730                 }
27731         },
27732
27733         showContextMenu: function(e, item) {
27734         item = this.findItemFromChild(e.getTarget());
27735                 if (item) {
27736                         e.stopEvent();
27737                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
27738                         this.contextMenu.showAt(e.getXY());
27739             }
27740     },
27741
27742 /**
27743  *      Remove {@link Roo.data.Record}s at the specified indices.
27744  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
27745  */
27746     remove: function(selectedIndices) {
27747                 selectedIndices = [].concat(selectedIndices);
27748                 for (var i = 0; i < selectedIndices.length; i++) {
27749                         var rec = this.store.getAt(selectedIndices[i]);
27750                         this.store.remove(rec);
27751                 }
27752     },
27753
27754 /**
27755  *      Double click fires the event, but also, if this is draggable, and there is only one other
27756  *      related DropZone, it transfers the selected node.
27757  */
27758     onDblClick : function(e){
27759         var item = this.findItemFromChild(e.getTarget());
27760         if(item){
27761             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
27762                 return false;
27763             }
27764             if (this.dragGroup) {
27765                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
27766                     while (targets.indexOf(this.dropZone) > -1) {
27767                             targets.remove(this.dropZone);
27768                                 }
27769                     if (targets.length == 1) {
27770                                         this.dragZone.cachedTarget = null;
27771                         var el = Roo.get(targets[0].getEl());
27772                         var box = el.getBox(true);
27773                         targets[0].onNodeDrop(el.dom, {
27774                                 target: el.dom,
27775                                 xy: [box.x, box.y + box.height - 1]
27776                         }, null, this.getDragData(e));
27777                     }
27778                 }
27779         }
27780     },
27781     
27782     handleSelection: function(e) {
27783                 this.dragZone.cachedTarget = null;
27784         var item = this.findItemFromChild(e.getTarget());
27785         if (!item) {
27786                 this.clearSelections(true);
27787                 return;
27788         }
27789                 if (item && (this.multiSelect || this.singleSelect)){
27790                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
27791                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
27792                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
27793                                 this.unselect(item);
27794                         } else {
27795                                 this.select(item, this.multiSelect && e.ctrlKey);
27796                                 this.lastSelection = item;
27797                         }
27798                 }
27799     },
27800
27801     onItemClick : function(item, index, e){
27802                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
27803                         return false;
27804                 }
27805                 return true;
27806     },
27807
27808     unselect : function(nodeInfo, suppressEvent){
27809                 var node = this.getNode(nodeInfo);
27810                 if(node && this.isSelected(node)){
27811                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
27812                                 Roo.fly(node).removeClass(this.selectedClass);
27813                                 this.selections.remove(node);
27814                                 if(!suppressEvent){
27815                                         this.fireEvent("selectionchange", this, this.selections);
27816                                 }
27817                         }
27818                 }
27819     }
27820 });
27821 /*
27822  * Based on:
27823  * Ext JS Library 1.1.1
27824  * Copyright(c) 2006-2007, Ext JS, LLC.
27825  *
27826  * Originally Released Under LGPL - original licence link has changed is not relivant.
27827  *
27828  * Fork - LGPL
27829  * <script type="text/javascript">
27830  */
27831  
27832 /**
27833  * @class Roo.LayoutManager
27834  * @extends Roo.util.Observable
27835  * Base class for layout managers.
27836  */
27837 Roo.LayoutManager = function(container, config){
27838     Roo.LayoutManager.superclass.constructor.call(this);
27839     this.el = Roo.get(container);
27840     // ie scrollbar fix
27841     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
27842         document.body.scroll = "no";
27843     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
27844         this.el.position('relative');
27845     }
27846     this.id = this.el.id;
27847     this.el.addClass("x-layout-container");
27848     /** false to disable window resize monitoring @type Boolean */
27849     this.monitorWindowResize = true;
27850     this.regions = {};
27851     this.addEvents({
27852         /**
27853          * @event layout
27854          * Fires when a layout is performed. 
27855          * @param {Roo.LayoutManager} this
27856          */
27857         "layout" : true,
27858         /**
27859          * @event regionresized
27860          * Fires when the user resizes a region. 
27861          * @param {Roo.LayoutRegion} region The resized region
27862          * @param {Number} newSize The new size (width for east/west, height for north/south)
27863          */
27864         "regionresized" : true,
27865         /**
27866          * @event regioncollapsed
27867          * Fires when a region is collapsed. 
27868          * @param {Roo.LayoutRegion} region The collapsed region
27869          */
27870         "regioncollapsed" : true,
27871         /**
27872          * @event regionexpanded
27873          * Fires when a region is expanded.  
27874          * @param {Roo.LayoutRegion} region The expanded region
27875          */
27876         "regionexpanded" : true
27877     });
27878     this.updating = false;
27879     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
27880 };
27881
27882 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
27883     /**
27884      * Returns true if this layout is currently being updated
27885      * @return {Boolean}
27886      */
27887     isUpdating : function(){
27888         return this.updating; 
27889     },
27890     
27891     /**
27892      * Suspend the LayoutManager from doing auto-layouts while
27893      * making multiple add or remove calls
27894      */
27895     beginUpdate : function(){
27896         this.updating = true;    
27897     },
27898     
27899     /**
27900      * Restore auto-layouts and optionally disable the manager from performing a layout
27901      * @param {Boolean} noLayout true to disable a layout update 
27902      */
27903     endUpdate : function(noLayout){
27904         this.updating = false;
27905         if(!noLayout){
27906             this.layout();
27907         }    
27908     },
27909     
27910     layout: function(){
27911         
27912     },
27913     
27914     onRegionResized : function(region, newSize){
27915         this.fireEvent("regionresized", region, newSize);
27916         this.layout();
27917     },
27918     
27919     onRegionCollapsed : function(region){
27920         this.fireEvent("regioncollapsed", region);
27921     },
27922     
27923     onRegionExpanded : function(region){
27924         this.fireEvent("regionexpanded", region);
27925     },
27926         
27927     /**
27928      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
27929      * performs box-model adjustments.
27930      * @return {Object} The size as an object {width: (the width), height: (the height)}
27931      */
27932     getViewSize : function(){
27933         var size;
27934         if(this.el.dom != document.body){
27935             size = this.el.getSize();
27936         }else{
27937             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
27938         }
27939         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
27940         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
27941         return size;
27942     },
27943     
27944     /**
27945      * Returns the Element this layout is bound to.
27946      * @return {Roo.Element}
27947      */
27948     getEl : function(){
27949         return this.el;
27950     },
27951     
27952     /**
27953      * Returns the specified region.
27954      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
27955      * @return {Roo.LayoutRegion}
27956      */
27957     getRegion : function(target){
27958         return this.regions[target.toLowerCase()];
27959     },
27960     
27961     onWindowResize : function(){
27962         if(this.monitorWindowResize){
27963             this.layout();
27964         }
27965     }
27966 });/*
27967  * Based on:
27968  * Ext JS Library 1.1.1
27969  * Copyright(c) 2006-2007, Ext JS, LLC.
27970  *
27971  * Originally Released Under LGPL - original licence link has changed is not relivant.
27972  *
27973  * Fork - LGPL
27974  * <script type="text/javascript">
27975  */
27976 /**
27977  * @class Roo.BorderLayout
27978  * @extends Roo.LayoutManager
27979  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
27980  * please see: <br><br>
27981  * <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>
27982  * <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>
27983  * Example:
27984  <pre><code>
27985  var layout = new Roo.BorderLayout(document.body, {
27986     north: {
27987         initialSize: 25,
27988         titlebar: false
27989     },
27990     west: {
27991         split:true,
27992         initialSize: 200,
27993         minSize: 175,
27994         maxSize: 400,
27995         titlebar: true,
27996         collapsible: true
27997     },
27998     east: {
27999         split:true,
28000         initialSize: 202,
28001         minSize: 175,
28002         maxSize: 400,
28003         titlebar: true,
28004         collapsible: true
28005     },
28006     south: {
28007         split:true,
28008         initialSize: 100,
28009         minSize: 100,
28010         maxSize: 200,
28011         titlebar: true,
28012         collapsible: true
28013     },
28014     center: {
28015         titlebar: true,
28016         autoScroll:true,
28017         resizeTabs: true,
28018         minTabWidth: 50,
28019         preferredTabWidth: 150
28020     }
28021 });
28022
28023 // shorthand
28024 var CP = Roo.ContentPanel;
28025
28026 layout.beginUpdate();
28027 layout.add("north", new CP("north", "North"));
28028 layout.add("south", new CP("south", {title: "South", closable: true}));
28029 layout.add("west", new CP("west", {title: "West"}));
28030 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
28031 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
28032 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
28033 layout.getRegion("center").showPanel("center1");
28034 layout.endUpdate();
28035 </code></pre>
28036
28037 <b>The container the layout is rendered into can be either the body element or any other element.
28038 If it is not the body element, the container needs to either be an absolute positioned element,
28039 or you will need to add "position:relative" to the css of the container.  You will also need to specify
28040 the container size if it is not the body element.</b>
28041
28042 * @constructor
28043 * Create a new BorderLayout
28044 * @param {String/HTMLElement/Element} container The container this layout is bound to
28045 * @param {Object} config Configuration options
28046  */
28047 Roo.BorderLayout = function(container, config){
28048     config = config || {};
28049     Roo.BorderLayout.superclass.constructor.call(this, container, config);
28050     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
28051     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
28052         var target = this.factory.validRegions[i];
28053         if(config[target]){
28054             this.addRegion(target, config[target]);
28055         }
28056     }
28057 };
28058
28059 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
28060     /**
28061      * Creates and adds a new region if it doesn't already exist.
28062      * @param {String} target The target region key (north, south, east, west or center).
28063      * @param {Object} config The regions config object
28064      * @return {BorderLayoutRegion} The new region
28065      */
28066     addRegion : function(target, config){
28067         if(!this.regions[target]){
28068             var r = this.factory.create(target, this, config);
28069             this.bindRegion(target, r);
28070         }
28071         return this.regions[target];
28072     },
28073
28074     // private (kinda)
28075     bindRegion : function(name, r){
28076         this.regions[name] = r;
28077         r.on("visibilitychange", this.layout, this);
28078         r.on("paneladded", this.layout, this);
28079         r.on("panelremoved", this.layout, this);
28080         r.on("invalidated", this.layout, this);
28081         r.on("resized", this.onRegionResized, this);
28082         r.on("collapsed", this.onRegionCollapsed, this);
28083         r.on("expanded", this.onRegionExpanded, this);
28084     },
28085
28086     /**
28087      * Performs a layout update.
28088      */
28089     layout : function(){
28090         if(this.updating) return;
28091         var size = this.getViewSize();
28092         var w = size.width;
28093         var h = size.height;
28094         var centerW = w;
28095         var centerH = h;
28096         var centerY = 0;
28097         var centerX = 0;
28098         //var x = 0, y = 0;
28099
28100         var rs = this.regions;
28101         var north = rs["north"];
28102         var south = rs["south"]; 
28103         var west = rs["west"];
28104         var east = rs["east"];
28105         var center = rs["center"];
28106         //if(this.hideOnLayout){ // not supported anymore
28107             //c.el.setStyle("display", "none");
28108         //}
28109         if(north && north.isVisible()){
28110             var b = north.getBox();
28111             var m = north.getMargins();
28112             b.width = w - (m.left+m.right);
28113             b.x = m.left;
28114             b.y = m.top;
28115             centerY = b.height + b.y + m.bottom;
28116             centerH -= centerY;
28117             north.updateBox(this.safeBox(b));
28118         }
28119         if(south && south.isVisible()){
28120             var b = south.getBox();
28121             var m = south.getMargins();
28122             b.width = w - (m.left+m.right);
28123             b.x = m.left;
28124             var totalHeight = (b.height + m.top + m.bottom);
28125             b.y = h - totalHeight + m.top;
28126             centerH -= totalHeight;
28127             south.updateBox(this.safeBox(b));
28128         }
28129         if(west && west.isVisible()){
28130             var b = west.getBox();
28131             var m = west.getMargins();
28132             b.height = centerH - (m.top+m.bottom);
28133             b.x = m.left;
28134             b.y = centerY + m.top;
28135             var totalWidth = (b.width + m.left + m.right);
28136             centerX += totalWidth;
28137             centerW -= totalWidth;
28138             west.updateBox(this.safeBox(b));
28139         }
28140         if(east && east.isVisible()){
28141             var b = east.getBox();
28142             var m = east.getMargins();
28143             b.height = centerH - (m.top+m.bottom);
28144             var totalWidth = (b.width + m.left + m.right);
28145             b.x = w - totalWidth + m.left;
28146             b.y = centerY + m.top;
28147             centerW -= totalWidth;
28148             east.updateBox(this.safeBox(b));
28149         }
28150         if(center){
28151             var m = center.getMargins();
28152             var centerBox = {
28153                 x: centerX + m.left,
28154                 y: centerY + m.top,
28155                 width: centerW - (m.left+m.right),
28156                 height: centerH - (m.top+m.bottom)
28157             };
28158             //if(this.hideOnLayout){
28159                 //center.el.setStyle("display", "block");
28160             //}
28161             center.updateBox(this.safeBox(centerBox));
28162         }
28163         this.el.repaint();
28164         this.fireEvent("layout", this);
28165     },
28166
28167     // private
28168     safeBox : function(box){
28169         box.width = Math.max(0, box.width);
28170         box.height = Math.max(0, box.height);
28171         return box;
28172     },
28173
28174     /**
28175      * Adds a ContentPanel (or subclass) to this layout.
28176      * @param {String} target The target region key (north, south, east, west or center).
28177      * @param {Roo.ContentPanel} panel The panel to add
28178      * @return {Roo.ContentPanel} The added panel
28179      */
28180     add : function(target, panel){
28181          
28182         target = target.toLowerCase();
28183         return this.regions[target].add(panel);
28184     },
28185
28186     /**
28187      * Remove a ContentPanel (or subclass) to this layout.
28188      * @param {String} target The target region key (north, south, east, west or center).
28189      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
28190      * @return {Roo.ContentPanel} The removed panel
28191      */
28192     remove : function(target, panel){
28193         target = target.toLowerCase();
28194         return this.regions[target].remove(panel);
28195     },
28196
28197     /**
28198      * Searches all regions for a panel with the specified id
28199      * @param {String} panelId
28200      * @return {Roo.ContentPanel} The panel or null if it wasn't found
28201      */
28202     findPanel : function(panelId){
28203         var rs = this.regions;
28204         for(var target in rs){
28205             if(typeof rs[target] != "function"){
28206                 var p = rs[target].getPanel(panelId);
28207                 if(p){
28208                     return p;
28209                 }
28210             }
28211         }
28212         return null;
28213     },
28214
28215     /**
28216      * Searches all regions for a panel with the specified id and activates (shows) it.
28217      * @param {String/ContentPanel} panelId The panels id or the panel itself
28218      * @return {Roo.ContentPanel} The shown panel or null
28219      */
28220     showPanel : function(panelId) {
28221       var rs = this.regions;
28222       for(var target in rs){
28223          var r = rs[target];
28224          if(typeof r != "function"){
28225             if(r.hasPanel(panelId)){
28226                return r.showPanel(panelId);
28227             }
28228          }
28229       }
28230       return null;
28231    },
28232
28233    /**
28234      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
28235      * @param {Roo.state.Provider} provider (optional) An alternate state provider
28236      */
28237     restoreState : function(provider){
28238         if(!provider){
28239             provider = Roo.state.Manager;
28240         }
28241         var sm = new Roo.LayoutStateManager();
28242         sm.init(this, provider);
28243     },
28244
28245     /**
28246      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
28247      * object should contain properties for each region to add ContentPanels to, and each property's value should be
28248      * a valid ContentPanel config object.  Example:
28249      * <pre><code>
28250 // Create the main layout
28251 var layout = new Roo.BorderLayout('main-ct', {
28252     west: {
28253         split:true,
28254         minSize: 175,
28255         titlebar: true
28256     },
28257     center: {
28258         title:'Components'
28259     }
28260 }, 'main-ct');
28261
28262 // Create and add multiple ContentPanels at once via configs
28263 layout.batchAdd({
28264    west: {
28265        id: 'source-files',
28266        autoCreate:true,
28267        title:'Ext Source Files',
28268        autoScroll:true,
28269        fitToFrame:true
28270    },
28271    center : {
28272        el: cview,
28273        autoScroll:true,
28274        fitToFrame:true,
28275        toolbar: tb,
28276        resizeEl:'cbody'
28277    }
28278 });
28279 </code></pre>
28280      * @param {Object} regions An object containing ContentPanel configs by region name
28281      */
28282     batchAdd : function(regions){
28283         this.beginUpdate();
28284         for(var rname in regions){
28285             var lr = this.regions[rname];
28286             if(lr){
28287                 this.addTypedPanels(lr, regions[rname]);
28288             }
28289         }
28290         this.endUpdate();
28291     },
28292
28293     // private
28294     addTypedPanels : function(lr, ps){
28295         if(typeof ps == 'string'){
28296             lr.add(new Roo.ContentPanel(ps));
28297         }
28298         else if(ps instanceof Array){
28299             for(var i =0, len = ps.length; i < len; i++){
28300                 this.addTypedPanels(lr, ps[i]);
28301             }
28302         }
28303         else if(!ps.events){ // raw config?
28304             var el = ps.el;
28305             delete ps.el; // prevent conflict
28306             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
28307         }
28308         else {  // panel object assumed!
28309             lr.add(ps);
28310         }
28311     },
28312     /**
28313      * Adds a xtype elements to the layout.
28314      * <pre><code>
28315
28316 layout.addxtype({
28317        xtype : 'ContentPanel',
28318        region: 'west',
28319        items: [ .... ]
28320    }
28321 );
28322
28323 layout.addxtype({
28324         xtype : 'NestedLayoutPanel',
28325         region: 'west',
28326         layout: {
28327            center: { },
28328            west: { }   
28329         },
28330         items : [ ... list of content panels or nested layout panels.. ]
28331    }
28332 );
28333 </code></pre>
28334      * @param {Object} cfg Xtype definition of item to add.
28335      */
28336     addxtype : function(cfg)
28337     {
28338         // basically accepts a pannel...
28339         // can accept a layout region..!?!?
28340        // console.log('BorderLayout add ' + cfg.xtype)
28341         
28342         if (!cfg.xtype.match(/Panel$/)) {
28343             return false;
28344         }
28345         var ret = false;
28346         var region = cfg.region;
28347         delete cfg.region;
28348         
28349           
28350         var xitems = [];
28351         if (cfg.items) {
28352             xitems = cfg.items;
28353             delete cfg.items;
28354         }
28355         
28356         
28357         switch(cfg.xtype) 
28358         {
28359             case 'ContentPanel':  // ContentPanel (el, cfg)
28360                 if(cfg.autoCreate) {
28361                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
28362                 } else {
28363                     var el = this.el.createChild();
28364                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
28365                 }
28366                 
28367                 this.add(region, ret);
28368                 break;
28369             
28370             
28371             case 'TreePanel': // our new panel!
28372                 cfg.el = this.el.createChild();
28373                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
28374                 this.add(region, ret);
28375                 break;
28376             
28377             case 'NestedLayoutPanel': 
28378                 // create a new Layout (which is  a Border Layout...
28379                 var el = this.el.createChild();
28380                 var clayout = cfg.layout;
28381                 delete cfg.layout;
28382                 clayout.items   = clayout.items  || [];
28383                 // replace this exitems with the clayout ones..
28384                 xitems = clayout.items;
28385                  
28386                 
28387                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
28388                     cfg.background = false;
28389                 }
28390                 var layout = new Roo.BorderLayout(el, clayout);
28391                 
28392                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
28393                 //console.log('adding nested layout panel '  + cfg.toSource());
28394                 this.add(region, ret);
28395                 
28396                 break;
28397                 
28398             case 'GridPanel': 
28399             
28400                 // needs grid and region
28401                 
28402                 //var el = this.getRegion(region).el.createChild();
28403                 var el = this.el.createChild();
28404                 // create the grid first...
28405                 
28406                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
28407                 delete cfg.grid;
28408                 if (region == 'center' && this.active ) {
28409                     cfg.background = false;
28410                 }
28411                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
28412                 
28413                 this.add(region, ret);
28414                 if (cfg.background) {
28415                     ret.on('activate', function(gp) {
28416                         if (!gp.grid.rendered) {
28417                             gp.grid.render();
28418                         }
28419                     });
28420                 } else {
28421                     grid.render();
28422                 }
28423                 break;
28424            
28425                
28426                 
28427                 
28428             default: 
28429                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
28430                 return;
28431              // GridPanel (grid, cfg)
28432             
28433         }
28434         this.beginUpdate();
28435         // add children..
28436         Roo.each(xitems, function(i)  {
28437             ret.addxtype(i);
28438         });
28439         this.endUpdate();
28440         return ret;
28441         
28442     }
28443 });
28444
28445 /**
28446  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
28447  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
28448  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
28449  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
28450  * <pre><code>
28451 // shorthand
28452 var CP = Roo.ContentPanel;
28453
28454 var layout = Roo.BorderLayout.create({
28455     north: {
28456         initialSize: 25,
28457         titlebar: false,
28458         panels: [new CP("north", "North")]
28459     },
28460     west: {
28461         split:true,
28462         initialSize: 200,
28463         minSize: 175,
28464         maxSize: 400,
28465         titlebar: true,
28466         collapsible: true,
28467         panels: [new CP("west", {title: "West"})]
28468     },
28469     east: {
28470         split:true,
28471         initialSize: 202,
28472         minSize: 175,
28473         maxSize: 400,
28474         titlebar: true,
28475         collapsible: true,
28476         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
28477     },
28478     south: {
28479         split:true,
28480         initialSize: 100,
28481         minSize: 100,
28482         maxSize: 200,
28483         titlebar: true,
28484         collapsible: true,
28485         panels: [new CP("south", {title: "South", closable: true})]
28486     },
28487     center: {
28488         titlebar: true,
28489         autoScroll:true,
28490         resizeTabs: true,
28491         minTabWidth: 50,
28492         preferredTabWidth: 150,
28493         panels: [
28494             new CP("center1", {title: "Close Me", closable: true}),
28495             new CP("center2", {title: "Center Panel", closable: false})
28496         ]
28497     }
28498 }, document.body);
28499
28500 layout.getRegion("center").showPanel("center1");
28501 </code></pre>
28502  * @param config
28503  * @param targetEl
28504  */
28505 Roo.BorderLayout.create = function(config, targetEl){
28506     var layout = new Roo.BorderLayout(targetEl || document.body, config);
28507     layout.beginUpdate();
28508     var regions = Roo.BorderLayout.RegionFactory.validRegions;
28509     for(var j = 0, jlen = regions.length; j < jlen; j++){
28510         var lr = regions[j];
28511         if(layout.regions[lr] && config[lr].panels){
28512             var r = layout.regions[lr];
28513             var ps = config[lr].panels;
28514             layout.addTypedPanels(r, ps);
28515         }
28516     }
28517     layout.endUpdate();
28518     return layout;
28519 };
28520
28521 // private
28522 Roo.BorderLayout.RegionFactory = {
28523     // private
28524     validRegions : ["north","south","east","west","center"],
28525
28526     // private
28527     create : function(target, mgr, config){
28528         target = target.toLowerCase();
28529         if(config.lightweight || config.basic){
28530             return new Roo.BasicLayoutRegion(mgr, config, target);
28531         }
28532         switch(target){
28533             case "north":
28534                 return new Roo.NorthLayoutRegion(mgr, config);
28535             case "south":
28536                 return new Roo.SouthLayoutRegion(mgr, config);
28537             case "east":
28538                 return new Roo.EastLayoutRegion(mgr, config);
28539             case "west":
28540                 return new Roo.WestLayoutRegion(mgr, config);
28541             case "center":
28542                 return new Roo.CenterLayoutRegion(mgr, config);
28543         }
28544         throw 'Layout region "'+target+'" not supported.';
28545     }
28546 };/*
28547  * Based on:
28548  * Ext JS Library 1.1.1
28549  * Copyright(c) 2006-2007, Ext JS, LLC.
28550  *
28551  * Originally Released Under LGPL - original licence link has changed is not relivant.
28552  *
28553  * Fork - LGPL
28554  * <script type="text/javascript">
28555  */
28556  
28557 /**
28558  * @class Roo.BasicLayoutRegion
28559  * @extends Roo.util.Observable
28560  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
28561  * and does not have a titlebar, tabs or any other features. All it does is size and position 
28562  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
28563  */
28564 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
28565     this.mgr = mgr;
28566     this.position  = pos;
28567     this.events = {
28568         /**
28569          * @scope Roo.BasicLayoutRegion
28570          */
28571         
28572         /**
28573          * @event beforeremove
28574          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
28575          * @param {Roo.LayoutRegion} this
28576          * @param {Roo.ContentPanel} panel The panel
28577          * @param {Object} e The cancel event object
28578          */
28579         "beforeremove" : true,
28580         /**
28581          * @event invalidated
28582          * Fires when the layout for this region is changed.
28583          * @param {Roo.LayoutRegion} this
28584          */
28585         "invalidated" : true,
28586         /**
28587          * @event visibilitychange
28588          * Fires when this region is shown or hidden 
28589          * @param {Roo.LayoutRegion} this
28590          * @param {Boolean} visibility true or false
28591          */
28592         "visibilitychange" : true,
28593         /**
28594          * @event paneladded
28595          * Fires when a panel is added. 
28596          * @param {Roo.LayoutRegion} this
28597          * @param {Roo.ContentPanel} panel The panel
28598          */
28599         "paneladded" : true,
28600         /**
28601          * @event panelremoved
28602          * Fires when a panel is removed. 
28603          * @param {Roo.LayoutRegion} this
28604          * @param {Roo.ContentPanel} panel The panel
28605          */
28606         "panelremoved" : true,
28607         /**
28608          * @event collapsed
28609          * Fires when this region is collapsed.
28610          * @param {Roo.LayoutRegion} this
28611          */
28612         "collapsed" : true,
28613         /**
28614          * @event expanded
28615          * Fires when this region is expanded.
28616          * @param {Roo.LayoutRegion} this
28617          */
28618         "expanded" : true,
28619         /**
28620          * @event slideshow
28621          * Fires when this region is slid into view.
28622          * @param {Roo.LayoutRegion} this
28623          */
28624         "slideshow" : true,
28625         /**
28626          * @event slidehide
28627          * Fires when this region slides out of view. 
28628          * @param {Roo.LayoutRegion} this
28629          */
28630         "slidehide" : true,
28631         /**
28632          * @event panelactivated
28633          * Fires when a panel is activated. 
28634          * @param {Roo.LayoutRegion} this
28635          * @param {Roo.ContentPanel} panel The activated panel
28636          */
28637         "panelactivated" : true,
28638         /**
28639          * @event resized
28640          * Fires when the user resizes this region. 
28641          * @param {Roo.LayoutRegion} this
28642          * @param {Number} newSize The new size (width for east/west, height for north/south)
28643          */
28644         "resized" : true
28645     };
28646     /** A collection of panels in this region. @type Roo.util.MixedCollection */
28647     this.panels = new Roo.util.MixedCollection();
28648     this.panels.getKey = this.getPanelId.createDelegate(this);
28649     this.box = null;
28650     this.activePanel = null;
28651     // ensure listeners are added...
28652     
28653     if (config.listeners || config.events) {
28654         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
28655             listeners : config.listeners || {},
28656             events : config.events || {}
28657         });
28658     }
28659     
28660     if(skipConfig !== true){
28661         this.applyConfig(config);
28662     }
28663 };
28664
28665 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
28666     getPanelId : function(p){
28667         return p.getId();
28668     },
28669     
28670     applyConfig : function(config){
28671         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
28672         this.config = config;
28673         
28674     },
28675     
28676     /**
28677      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
28678      * the width, for horizontal (north, south) the height.
28679      * @param {Number} newSize The new width or height
28680      */
28681     resizeTo : function(newSize){
28682         var el = this.el ? this.el :
28683                  (this.activePanel ? this.activePanel.getEl() : null);
28684         if(el){
28685             switch(this.position){
28686                 case "east":
28687                 case "west":
28688                     el.setWidth(newSize);
28689                     this.fireEvent("resized", this, newSize);
28690                 break;
28691                 case "north":
28692                 case "south":
28693                     el.setHeight(newSize);
28694                     this.fireEvent("resized", this, newSize);
28695                 break;                
28696             }
28697         }
28698     },
28699     
28700     getBox : function(){
28701         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
28702     },
28703     
28704     getMargins : function(){
28705         return this.margins;
28706     },
28707     
28708     updateBox : function(box){
28709         this.box = box;
28710         var el = this.activePanel.getEl();
28711         el.dom.style.left = box.x + "px";
28712         el.dom.style.top = box.y + "px";
28713         this.activePanel.setSize(box.width, box.height);
28714     },
28715     
28716     /**
28717      * Returns the container element for this region.
28718      * @return {Roo.Element}
28719      */
28720     getEl : function(){
28721         return this.activePanel;
28722     },
28723     
28724     /**
28725      * Returns true if this region is currently visible.
28726      * @return {Boolean}
28727      */
28728     isVisible : function(){
28729         return this.activePanel ? true : false;
28730     },
28731     
28732     setActivePanel : function(panel){
28733         panel = this.getPanel(panel);
28734         if(this.activePanel && this.activePanel != panel){
28735             this.activePanel.setActiveState(false);
28736             this.activePanel.getEl().setLeftTop(-10000,-10000);
28737         }
28738         this.activePanel = panel;
28739         panel.setActiveState(true);
28740         if(this.box){
28741             panel.setSize(this.box.width, this.box.height);
28742         }
28743         this.fireEvent("panelactivated", this, panel);
28744         this.fireEvent("invalidated");
28745     },
28746     
28747     /**
28748      * Show the specified panel.
28749      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
28750      * @return {Roo.ContentPanel} The shown panel or null
28751      */
28752     showPanel : function(panel){
28753         if(panel = this.getPanel(panel)){
28754             this.setActivePanel(panel);
28755         }
28756         return panel;
28757     },
28758     
28759     /**
28760      * Get the active panel for this region.
28761      * @return {Roo.ContentPanel} The active panel or null
28762      */
28763     getActivePanel : function(){
28764         return this.activePanel;
28765     },
28766     
28767     /**
28768      * Add the passed ContentPanel(s)
28769      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
28770      * @return {Roo.ContentPanel} The panel added (if only one was added)
28771      */
28772     add : function(panel){
28773         if(arguments.length > 1){
28774             for(var i = 0, len = arguments.length; i < len; i++) {
28775                 this.add(arguments[i]);
28776             }
28777             return null;
28778         }
28779         if(this.hasPanel(panel)){
28780             this.showPanel(panel);
28781             return panel;
28782         }
28783         var el = panel.getEl();
28784         if(el.dom.parentNode != this.mgr.el.dom){
28785             this.mgr.el.dom.appendChild(el.dom);
28786         }
28787         if(panel.setRegion){
28788             panel.setRegion(this);
28789         }
28790         this.panels.add(panel);
28791         el.setStyle("position", "absolute");
28792         if(!panel.background){
28793             this.setActivePanel(panel);
28794             if(this.config.initialSize && this.panels.getCount()==1){
28795                 this.resizeTo(this.config.initialSize);
28796             }
28797         }
28798         this.fireEvent("paneladded", this, panel);
28799         return panel;
28800     },
28801     
28802     /**
28803      * Returns true if the panel is in this region.
28804      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
28805      * @return {Boolean}
28806      */
28807     hasPanel : function(panel){
28808         if(typeof panel == "object"){ // must be panel obj
28809             panel = panel.getId();
28810         }
28811         return this.getPanel(panel) ? true : false;
28812     },
28813     
28814     /**
28815      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
28816      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
28817      * @param {Boolean} preservePanel Overrides the config preservePanel option
28818      * @return {Roo.ContentPanel} The panel that was removed
28819      */
28820     remove : function(panel, preservePanel){
28821         panel = this.getPanel(panel);
28822         if(!panel){
28823             return null;
28824         }
28825         var e = {};
28826         this.fireEvent("beforeremove", this, panel, e);
28827         if(e.cancel === true){
28828             return null;
28829         }
28830         var panelId = panel.getId();
28831         this.panels.removeKey(panelId);
28832         return panel;
28833     },
28834     
28835     /**
28836      * Returns the panel specified or null if it's not in this region.
28837      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
28838      * @return {Roo.ContentPanel}
28839      */
28840     getPanel : function(id){
28841         if(typeof id == "object"){ // must be panel obj
28842             return id;
28843         }
28844         return this.panels.get(id);
28845     },
28846     
28847     /**
28848      * Returns this regions position (north/south/east/west/center).
28849      * @return {String} 
28850      */
28851     getPosition: function(){
28852         return this.position;    
28853     }
28854 });/*
28855  * Based on:
28856  * Ext JS Library 1.1.1
28857  * Copyright(c) 2006-2007, Ext JS, LLC.
28858  *
28859  * Originally Released Under LGPL - original licence link has changed is not relivant.
28860  *
28861  * Fork - LGPL
28862  * <script type="text/javascript">
28863  */
28864  
28865 /**
28866  * @class Roo.LayoutRegion
28867  * @extends Roo.BasicLayoutRegion
28868  * This class represents a region in a layout manager.
28869  * @cfg {Boolean} collapsible False to disable collapsing (defaults to true)
28870  * @cfg {Boolean} collapsed True to set the initial display to collapsed (defaults to false)
28871  * @cfg {Boolean} floatable False to disable floating (defaults to true)
28872  * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
28873  * @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})
28874  * @cfg {String} tabPosition "top" or "bottom" (defaults to "bottom")
28875  * @cfg {String} collapsedTitle Optional string message to display in the collapsed block of a north or south region
28876  * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
28877  * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
28878  * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
28879  * @cfg {String} title The title for the region (overrides panel titles)
28880  * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
28881  * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
28882  * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
28883  * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
28884  * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
28885  * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
28886  * the space available, similar to FireFox 1.5 tabs (defaults to false)
28887  * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
28888  * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
28889  * @cfg {Boolean} showPin True to show a pin button
28890 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
28891 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
28892 * @cfg {Boolean} disableTabTips True to disable tab tooltips
28893 * @cfg {Number} width  For East/West panels
28894 * @cfg {Number} height For North/South panels
28895 * @cfg {Boolean} split To show the splitter
28896  */
28897 Roo.LayoutRegion = function(mgr, config, pos){
28898     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
28899     var dh = Roo.DomHelper;
28900     /** This region's container element 
28901     * @type Roo.Element */
28902     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
28903     /** This region's title element 
28904     * @type Roo.Element */
28905
28906     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
28907         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
28908         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
28909     ]}, true);
28910     this.titleEl.enableDisplayMode();
28911     /** This region's title text element 
28912     * @type HTMLElement */
28913     this.titleTextEl = this.titleEl.dom.firstChild;
28914     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
28915     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
28916     this.closeBtn.enableDisplayMode();
28917     this.closeBtn.on("click", this.closeClicked, this);
28918     this.closeBtn.hide();
28919
28920     this.createBody(config);
28921     this.visible = true;
28922     this.collapsed = false;
28923
28924     if(config.hideWhenEmpty){
28925         this.hide();
28926         this.on("paneladded", this.validateVisibility, this);
28927         this.on("panelremoved", this.validateVisibility, this);
28928     }
28929     this.applyConfig(config);
28930 };
28931
28932 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
28933
28934     createBody : function(){
28935         /** This region's body element 
28936         * @type Roo.Element */
28937         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
28938     },
28939
28940     applyConfig : function(c){
28941         if(c.collapsible && this.position != "center" && !this.collapsedEl){
28942             var dh = Roo.DomHelper;
28943             if(c.titlebar !== false){
28944                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
28945                 this.collapseBtn.on("click", this.collapse, this);
28946                 this.collapseBtn.enableDisplayMode();
28947
28948                 if(c.showPin === true || this.showPin){
28949                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
28950                     this.stickBtn.enableDisplayMode();
28951                     this.stickBtn.on("click", this.expand, this);
28952                     this.stickBtn.hide();
28953                 }
28954             }
28955             /** This region's collapsed element
28956             * @type Roo.Element */
28957             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
28958                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
28959             ]}, true);
28960             if(c.floatable !== false){
28961                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
28962                this.collapsedEl.on("click", this.collapseClick, this);
28963             }
28964
28965             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
28966                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
28967                    id: "message", unselectable: "on", style:{"float":"left"}});
28968                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
28969              }
28970             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
28971             this.expandBtn.on("click", this.expand, this);
28972         }
28973         if(this.collapseBtn){
28974             this.collapseBtn.setVisible(c.collapsible == true);
28975         }
28976         this.cmargins = c.cmargins || this.cmargins ||
28977                          (this.position == "west" || this.position == "east" ?
28978                              {top: 0, left: 2, right:2, bottom: 0} :
28979                              {top: 2, left: 0, right:0, bottom: 2});
28980         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
28981         this.bottomTabs = c.tabPosition != "top";
28982         this.autoScroll = c.autoScroll || false;
28983         if(this.autoScroll){
28984             this.bodyEl.setStyle("overflow", "auto");
28985         }else{
28986             this.bodyEl.setStyle("overflow", "hidden");
28987         }
28988         //if(c.titlebar !== false){
28989             if((!c.titlebar && !c.title) || c.titlebar === false){
28990                 this.titleEl.hide();
28991             }else{
28992                 this.titleEl.show();
28993                 if(c.title){
28994                     this.titleTextEl.innerHTML = c.title;
28995                 }
28996             }
28997         //}
28998         this.duration = c.duration || .30;
28999         this.slideDuration = c.slideDuration || .45;
29000         this.config = c;
29001         if(c.collapsed){
29002             this.collapse(true);
29003         }
29004         if(c.hidden){
29005             this.hide();
29006         }
29007     },
29008     /**
29009      * Returns true if this region is currently visible.
29010      * @return {Boolean}
29011      */
29012     isVisible : function(){
29013         return this.visible;
29014     },
29015
29016     /**
29017      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
29018      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
29019      */
29020     setCollapsedTitle : function(title){
29021         title = title || "&#160;";
29022         if(this.collapsedTitleTextEl){
29023             this.collapsedTitleTextEl.innerHTML = title;
29024         }
29025     },
29026
29027     getBox : function(){
29028         var b;
29029         if(!this.collapsed){
29030             b = this.el.getBox(false, true);
29031         }else{
29032             b = this.collapsedEl.getBox(false, true);
29033         }
29034         return b;
29035     },
29036
29037     getMargins : function(){
29038         return this.collapsed ? this.cmargins : this.margins;
29039     },
29040
29041     highlight : function(){
29042         this.el.addClass("x-layout-panel-dragover");
29043     },
29044
29045     unhighlight : function(){
29046         this.el.removeClass("x-layout-panel-dragover");
29047     },
29048
29049     updateBox : function(box){
29050         this.box = box;
29051         if(!this.collapsed){
29052             this.el.dom.style.left = box.x + "px";
29053             this.el.dom.style.top = box.y + "px";
29054             this.updateBody(box.width, box.height);
29055         }else{
29056             this.collapsedEl.dom.style.left = box.x + "px";
29057             this.collapsedEl.dom.style.top = box.y + "px";
29058             this.collapsedEl.setSize(box.width, box.height);
29059         }
29060         if(this.tabs){
29061             this.tabs.autoSizeTabs();
29062         }
29063     },
29064
29065     updateBody : function(w, h){
29066         if(w !== null){
29067             this.el.setWidth(w);
29068             w -= this.el.getBorderWidth("rl");
29069             if(this.config.adjustments){
29070                 w += this.config.adjustments[0];
29071             }
29072         }
29073         if(h !== null){
29074             this.el.setHeight(h);
29075             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
29076             h -= this.el.getBorderWidth("tb");
29077             if(this.config.adjustments){
29078                 h += this.config.adjustments[1];
29079             }
29080             this.bodyEl.setHeight(h);
29081             if(this.tabs){
29082                 h = this.tabs.syncHeight(h);
29083             }
29084         }
29085         if(this.panelSize){
29086             w = w !== null ? w : this.panelSize.width;
29087             h = h !== null ? h : this.panelSize.height;
29088         }
29089         if(this.activePanel){
29090             var el = this.activePanel.getEl();
29091             w = w !== null ? w : el.getWidth();
29092             h = h !== null ? h : el.getHeight();
29093             this.panelSize = {width: w, height: h};
29094             this.activePanel.setSize(w, h);
29095         }
29096         if(Roo.isIE && this.tabs){
29097             this.tabs.el.repaint();
29098         }
29099     },
29100
29101     /**
29102      * Returns the container element for this region.
29103      * @return {Roo.Element}
29104      */
29105     getEl : function(){
29106         return this.el;
29107     },
29108
29109     /**
29110      * Hides this region.
29111      */
29112     hide : function(){
29113         if(!this.collapsed){
29114             this.el.dom.style.left = "-2000px";
29115             this.el.hide();
29116         }else{
29117             this.collapsedEl.dom.style.left = "-2000px";
29118             this.collapsedEl.hide();
29119         }
29120         this.visible = false;
29121         this.fireEvent("visibilitychange", this, false);
29122     },
29123
29124     /**
29125      * Shows this region if it was previously hidden.
29126      */
29127     show : function(){
29128         if(!this.collapsed){
29129             this.el.show();
29130         }else{
29131             this.collapsedEl.show();
29132         }
29133         this.visible = true;
29134         this.fireEvent("visibilitychange", this, true);
29135     },
29136
29137     closeClicked : function(){
29138         if(this.activePanel){
29139             this.remove(this.activePanel);
29140         }
29141     },
29142
29143     collapseClick : function(e){
29144         if(this.isSlid){
29145            e.stopPropagation();
29146            this.slideIn();
29147         }else{
29148            e.stopPropagation();
29149            this.slideOut();
29150         }
29151     },
29152
29153     /**
29154      * Collapses this region.
29155      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
29156      */
29157     collapse : function(skipAnim){
29158         if(this.collapsed) return;
29159         this.collapsed = true;
29160         if(this.split){
29161             this.split.el.hide();
29162         }
29163         if(this.config.animate && skipAnim !== true){
29164             this.fireEvent("invalidated", this);
29165             this.animateCollapse();
29166         }else{
29167             this.el.setLocation(-20000,-20000);
29168             this.el.hide();
29169             this.collapsedEl.show();
29170             this.fireEvent("collapsed", this);
29171             this.fireEvent("invalidated", this);
29172         }
29173     },
29174
29175     animateCollapse : function(){
29176         // overridden
29177     },
29178
29179     /**
29180      * Expands this region if it was previously collapsed.
29181      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
29182      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
29183      */
29184     expand : function(e, skipAnim){
29185         if(e) e.stopPropagation();
29186         if(!this.collapsed || this.el.hasActiveFx()) return;
29187         if(this.isSlid){
29188             this.afterSlideIn();
29189             skipAnim = true;
29190         }
29191         this.collapsed = false;
29192         if(this.config.animate && skipAnim !== true){
29193             this.animateExpand();
29194         }else{
29195             this.el.show();
29196             if(this.split){
29197                 this.split.el.show();
29198             }
29199             this.collapsedEl.setLocation(-2000,-2000);
29200             this.collapsedEl.hide();
29201             this.fireEvent("invalidated", this);
29202             this.fireEvent("expanded", this);
29203         }
29204     },
29205
29206     animateExpand : function(){
29207         // overridden
29208     },
29209
29210     initTabs : function(){
29211         this.bodyEl.setStyle("overflow", "hidden");
29212         var ts = new Roo.TabPanel(this.bodyEl.dom, {
29213             tabPosition: this.bottomTabs ? 'bottom' : 'top',
29214             disableTooltips: this.config.disableTabTips
29215         });
29216         if(this.config.hideTabs){
29217             ts.stripWrap.setDisplayed(false);
29218         }
29219         this.tabs = ts;
29220         ts.resizeTabs = this.config.resizeTabs === true;
29221         ts.minTabWidth = this.config.minTabWidth || 40;
29222         ts.maxTabWidth = this.config.maxTabWidth || 250;
29223         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
29224         ts.monitorResize = false;
29225         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
29226         ts.bodyEl.addClass('x-layout-tabs-body');
29227         this.panels.each(this.initPanelAsTab, this);
29228     },
29229
29230     initPanelAsTab : function(panel){
29231         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
29232                     this.config.closeOnTab && panel.isClosable());
29233         if(panel.tabTip !== undefined){
29234             ti.setTooltip(panel.tabTip);
29235         }
29236         ti.on("activate", function(){
29237               this.setActivePanel(panel);
29238         }, this);
29239         if(this.config.closeOnTab){
29240             ti.on("beforeclose", function(t, e){
29241                 e.cancel = true;
29242                 this.remove(panel);
29243             }, this);
29244         }
29245         return ti;
29246     },
29247
29248     updatePanelTitle : function(panel, title){
29249         if(this.activePanel == panel){
29250             this.updateTitle(title);
29251         }
29252         if(this.tabs){
29253             var ti = this.tabs.getTab(panel.getEl().id);
29254             ti.setText(title);
29255             if(panel.tabTip !== undefined){
29256                 ti.setTooltip(panel.tabTip);
29257             }
29258         }
29259     },
29260
29261     updateTitle : function(title){
29262         if(this.titleTextEl && !this.config.title){
29263             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
29264         }
29265     },
29266
29267     setActivePanel : function(panel){
29268         panel = this.getPanel(panel);
29269         if(this.activePanel && this.activePanel != panel){
29270             this.activePanel.setActiveState(false);
29271         }
29272         this.activePanel = panel;
29273         panel.setActiveState(true);
29274         if(this.panelSize){
29275             panel.setSize(this.panelSize.width, this.panelSize.height);
29276         }
29277         if(this.closeBtn){
29278             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
29279         }
29280         this.updateTitle(panel.getTitle());
29281         if(this.tabs){
29282             this.fireEvent("invalidated", this);
29283         }
29284         this.fireEvent("panelactivated", this, panel);
29285     },
29286
29287     /**
29288      * Shows the specified panel.
29289      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
29290      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
29291      */
29292     showPanel : function(panel){
29293         if(panel = this.getPanel(panel)){
29294             if(this.tabs){
29295                 var tab = this.tabs.getTab(panel.getEl().id);
29296                 if(tab.isHidden()){
29297                     this.tabs.unhideTab(tab.id);
29298                 }
29299                 tab.activate();
29300             }else{
29301                 this.setActivePanel(panel);
29302             }
29303         }
29304         return panel;
29305     },
29306
29307     /**
29308      * Get the active panel for this region.
29309      * @return {Roo.ContentPanel} The active panel or null
29310      */
29311     getActivePanel : function(){
29312         return this.activePanel;
29313     },
29314
29315     validateVisibility : function(){
29316         if(this.panels.getCount() < 1){
29317             this.updateTitle("&#160;");
29318             this.closeBtn.hide();
29319             this.hide();
29320         }else{
29321             if(!this.isVisible()){
29322                 this.show();
29323             }
29324         }
29325     },
29326
29327     /**
29328      * Adds the passed ContentPanel(s) to this region.
29329      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
29330      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
29331      */
29332     add : function(panel){
29333         if(arguments.length > 1){
29334             for(var i = 0, len = arguments.length; i < len; i++) {
29335                 this.add(arguments[i]);
29336             }
29337             return null;
29338         }
29339         if(this.hasPanel(panel)){
29340             this.showPanel(panel);
29341             return panel;
29342         }
29343         panel.setRegion(this);
29344         this.panels.add(panel);
29345         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
29346             this.bodyEl.dom.appendChild(panel.getEl().dom);
29347             if(panel.background !== true){
29348                 this.setActivePanel(panel);
29349             }
29350             this.fireEvent("paneladded", this, panel);
29351             return panel;
29352         }
29353         if(!this.tabs){
29354             this.initTabs();
29355         }else{
29356             this.initPanelAsTab(panel);
29357         }
29358         if(panel.background !== true){
29359             this.tabs.activate(panel.getEl().id);
29360         }
29361         this.fireEvent("paneladded", this, panel);
29362         return panel;
29363     },
29364
29365     /**
29366      * Hides the tab for the specified panel.
29367      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
29368      */
29369     hidePanel : function(panel){
29370         if(this.tabs && (panel = this.getPanel(panel))){
29371             this.tabs.hideTab(panel.getEl().id);
29372         }
29373     },
29374
29375     /**
29376      * Unhides the tab for a previously hidden panel.
29377      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
29378      */
29379     unhidePanel : function(panel){
29380         if(this.tabs && (panel = this.getPanel(panel))){
29381             this.tabs.unhideTab(panel.getEl().id);
29382         }
29383     },
29384
29385     clearPanels : function(){
29386         while(this.panels.getCount() > 0){
29387              this.remove(this.panels.first());
29388         }
29389     },
29390
29391     /**
29392      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
29393      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
29394      * @param {Boolean} preservePanel Overrides the config preservePanel option
29395      * @return {Roo.ContentPanel} The panel that was removed
29396      */
29397     remove : function(panel, preservePanel){
29398         panel = this.getPanel(panel);
29399         if(!panel){
29400             return null;
29401         }
29402         var e = {};
29403         this.fireEvent("beforeremove", this, panel, e);
29404         if(e.cancel === true){
29405             return null;
29406         }
29407         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
29408         var panelId = panel.getId();
29409         this.panels.removeKey(panelId);
29410         if(preservePanel){
29411             document.body.appendChild(panel.getEl().dom);
29412         }
29413         if(this.tabs){
29414             this.tabs.removeTab(panel.getEl().id);
29415         }else if (!preservePanel){
29416             this.bodyEl.dom.removeChild(panel.getEl().dom);
29417         }
29418         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
29419             var p = this.panels.first();
29420             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
29421             tempEl.appendChild(p.getEl().dom);
29422             this.bodyEl.update("");
29423             this.bodyEl.dom.appendChild(p.getEl().dom);
29424             tempEl = null;
29425             this.updateTitle(p.getTitle());
29426             this.tabs = null;
29427             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
29428             this.setActivePanel(p);
29429         }
29430         panel.setRegion(null);
29431         if(this.activePanel == panel){
29432             this.activePanel = null;
29433         }
29434         if(this.config.autoDestroy !== false && preservePanel !== true){
29435             try{panel.destroy();}catch(e){}
29436         }
29437         this.fireEvent("panelremoved", this, panel);
29438         return panel;
29439     },
29440
29441     /**
29442      * Returns the TabPanel component used by this region
29443      * @return {Roo.TabPanel}
29444      */
29445     getTabs : function(){
29446         return this.tabs;
29447     },
29448
29449     createTool : function(parentEl, className){
29450         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
29451             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
29452         btn.addClassOnOver("x-layout-tools-button-over");
29453         return btn;
29454     }
29455 });/*
29456  * Based on:
29457  * Ext JS Library 1.1.1
29458  * Copyright(c) 2006-2007, Ext JS, LLC.
29459  *
29460  * Originally Released Under LGPL - original licence link has changed is not relivant.
29461  *
29462  * Fork - LGPL
29463  * <script type="text/javascript">
29464  */
29465  
29466
29467
29468 /**
29469  * @class Roo.SplitLayoutRegion
29470  * @extends Roo.LayoutRegion
29471  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
29472  */
29473 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
29474     this.cursor = cursor;
29475     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
29476 };
29477
29478 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
29479     splitTip : "Drag to resize.",
29480     collapsibleSplitTip : "Drag to resize. Double click to hide.",
29481     useSplitTips : false,
29482
29483     applyConfig : function(config){
29484         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
29485         if(config.split){
29486             if(!this.split){
29487                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
29488                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
29489                 /** The SplitBar for this region 
29490                 * @type Roo.SplitBar */
29491                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
29492                 this.split.on("moved", this.onSplitMove, this);
29493                 this.split.useShim = config.useShim === true;
29494                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
29495                 if(this.useSplitTips){
29496                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
29497                 }
29498                 if(config.collapsible){
29499                     this.split.el.on("dblclick", this.collapse,  this);
29500                 }
29501             }
29502             if(typeof config.minSize != "undefined"){
29503                 this.split.minSize = config.minSize;
29504             }
29505             if(typeof config.maxSize != "undefined"){
29506                 this.split.maxSize = config.maxSize;
29507             }
29508             if(config.hideWhenEmpty || config.hidden || config.collapsed){
29509                 this.hideSplitter();
29510             }
29511         }
29512     },
29513
29514     getHMaxSize : function(){
29515          var cmax = this.config.maxSize || 10000;
29516          var center = this.mgr.getRegion("center");
29517          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
29518     },
29519
29520     getVMaxSize : function(){
29521          var cmax = this.config.maxSize || 10000;
29522          var center = this.mgr.getRegion("center");
29523          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
29524     },
29525
29526     onSplitMove : function(split, newSize){
29527         this.fireEvent("resized", this, newSize);
29528     },
29529     
29530     /** 
29531      * Returns the {@link Roo.SplitBar} for this region.
29532      * @return {Roo.SplitBar}
29533      */
29534     getSplitBar : function(){
29535         return this.split;
29536     },
29537     
29538     hide : function(){
29539         this.hideSplitter();
29540         Roo.SplitLayoutRegion.superclass.hide.call(this);
29541     },
29542
29543     hideSplitter : function(){
29544         if(this.split){
29545             this.split.el.setLocation(-2000,-2000);
29546             this.split.el.hide();
29547         }
29548     },
29549
29550     show : function(){
29551         if(this.split){
29552             this.split.el.show();
29553         }
29554         Roo.SplitLayoutRegion.superclass.show.call(this);
29555     },
29556     
29557     beforeSlide: function(){
29558         if(Roo.isGecko){// firefox overflow auto bug workaround
29559             this.bodyEl.clip();
29560             if(this.tabs) this.tabs.bodyEl.clip();
29561             if(this.activePanel){
29562                 this.activePanel.getEl().clip();
29563                 
29564                 if(this.activePanel.beforeSlide){
29565                     this.activePanel.beforeSlide();
29566                 }
29567             }
29568         }
29569     },
29570     
29571     afterSlide : function(){
29572         if(Roo.isGecko){// firefox overflow auto bug workaround
29573             this.bodyEl.unclip();
29574             if(this.tabs) this.tabs.bodyEl.unclip();
29575             if(this.activePanel){
29576                 this.activePanel.getEl().unclip();
29577                 if(this.activePanel.afterSlide){
29578                     this.activePanel.afterSlide();
29579                 }
29580             }
29581         }
29582     },
29583
29584     initAutoHide : function(){
29585         if(this.autoHide !== false){
29586             if(!this.autoHideHd){
29587                 var st = new Roo.util.DelayedTask(this.slideIn, this);
29588                 this.autoHideHd = {
29589                     "mouseout": function(e){
29590                         if(!e.within(this.el, true)){
29591                             st.delay(500);
29592                         }
29593                     },
29594                     "mouseover" : function(e){
29595                         st.cancel();
29596                     },
29597                     scope : this
29598                 };
29599             }
29600             this.el.on(this.autoHideHd);
29601         }
29602     },
29603
29604     clearAutoHide : function(){
29605         if(this.autoHide !== false){
29606             this.el.un("mouseout", this.autoHideHd.mouseout);
29607             this.el.un("mouseover", this.autoHideHd.mouseover);
29608         }
29609     },
29610
29611     clearMonitor : function(){
29612         Roo.get(document).un("click", this.slideInIf, this);
29613     },
29614
29615     // these names are backwards but not changed for compat
29616     slideOut : function(){
29617         if(this.isSlid || this.el.hasActiveFx()){
29618             return;
29619         }
29620         this.isSlid = true;
29621         if(this.collapseBtn){
29622             this.collapseBtn.hide();
29623         }
29624         this.closeBtnState = this.closeBtn.getStyle('display');
29625         this.closeBtn.hide();
29626         if(this.stickBtn){
29627             this.stickBtn.show();
29628         }
29629         this.el.show();
29630         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
29631         this.beforeSlide();
29632         this.el.setStyle("z-index", 10001);
29633         this.el.slideIn(this.getSlideAnchor(), {
29634             callback: function(){
29635                 this.afterSlide();
29636                 this.initAutoHide();
29637                 Roo.get(document).on("click", this.slideInIf, this);
29638                 this.fireEvent("slideshow", this);
29639             },
29640             scope: this,
29641             block: true
29642         });
29643     },
29644
29645     afterSlideIn : function(){
29646         this.clearAutoHide();
29647         this.isSlid = false;
29648         this.clearMonitor();
29649         this.el.setStyle("z-index", "");
29650         if(this.collapseBtn){
29651             this.collapseBtn.show();
29652         }
29653         this.closeBtn.setStyle('display', this.closeBtnState);
29654         if(this.stickBtn){
29655             this.stickBtn.hide();
29656         }
29657         this.fireEvent("slidehide", this);
29658     },
29659
29660     slideIn : function(cb){
29661         if(!this.isSlid || this.el.hasActiveFx()){
29662             Roo.callback(cb);
29663             return;
29664         }
29665         this.isSlid = false;
29666         this.beforeSlide();
29667         this.el.slideOut(this.getSlideAnchor(), {
29668             callback: function(){
29669                 this.el.setLeftTop(-10000, -10000);
29670                 this.afterSlide();
29671                 this.afterSlideIn();
29672                 Roo.callback(cb);
29673             },
29674             scope: this,
29675             block: true
29676         });
29677     },
29678     
29679     slideInIf : function(e){
29680         if(!e.within(this.el)){
29681             this.slideIn();
29682         }
29683     },
29684
29685     animateCollapse : function(){
29686         this.beforeSlide();
29687         this.el.setStyle("z-index", 20000);
29688         var anchor = this.getSlideAnchor();
29689         this.el.slideOut(anchor, {
29690             callback : function(){
29691                 this.el.setStyle("z-index", "");
29692                 this.collapsedEl.slideIn(anchor, {duration:.3});
29693                 this.afterSlide();
29694                 this.el.setLocation(-10000,-10000);
29695                 this.el.hide();
29696                 this.fireEvent("collapsed", this);
29697             },
29698             scope: this,
29699             block: true
29700         });
29701     },
29702
29703     animateExpand : function(){
29704         this.beforeSlide();
29705         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
29706         this.el.setStyle("z-index", 20000);
29707         this.collapsedEl.hide({
29708             duration:.1
29709         });
29710         this.el.slideIn(this.getSlideAnchor(), {
29711             callback : function(){
29712                 this.el.setStyle("z-index", "");
29713                 this.afterSlide();
29714                 if(this.split){
29715                     this.split.el.show();
29716                 }
29717                 this.fireEvent("invalidated", this);
29718                 this.fireEvent("expanded", this);
29719             },
29720             scope: this,
29721             block: true
29722         });
29723     },
29724
29725     anchors : {
29726         "west" : "left",
29727         "east" : "right",
29728         "north" : "top",
29729         "south" : "bottom"
29730     },
29731
29732     sanchors : {
29733         "west" : "l",
29734         "east" : "r",
29735         "north" : "t",
29736         "south" : "b"
29737     },
29738
29739     canchors : {
29740         "west" : "tl-tr",
29741         "east" : "tr-tl",
29742         "north" : "tl-bl",
29743         "south" : "bl-tl"
29744     },
29745
29746     getAnchor : function(){
29747         return this.anchors[this.position];
29748     },
29749
29750     getCollapseAnchor : function(){
29751         return this.canchors[this.position];
29752     },
29753
29754     getSlideAnchor : function(){
29755         return this.sanchors[this.position];
29756     },
29757
29758     getAlignAdj : function(){
29759         var cm = this.cmargins;
29760         switch(this.position){
29761             case "west":
29762                 return [0, 0];
29763             break;
29764             case "east":
29765                 return [0, 0];
29766             break;
29767             case "north":
29768                 return [0, 0];
29769             break;
29770             case "south":
29771                 return [0, 0];
29772             break;
29773         }
29774     },
29775
29776     getExpandAdj : function(){
29777         var c = this.collapsedEl, cm = this.cmargins;
29778         switch(this.position){
29779             case "west":
29780                 return [-(cm.right+c.getWidth()+cm.left), 0];
29781             break;
29782             case "east":
29783                 return [cm.right+c.getWidth()+cm.left, 0];
29784             break;
29785             case "north":
29786                 return [0, -(cm.top+cm.bottom+c.getHeight())];
29787             break;
29788             case "south":
29789                 return [0, cm.top+cm.bottom+c.getHeight()];
29790             break;
29791         }
29792     }
29793 });/*
29794  * Based on:
29795  * Ext JS Library 1.1.1
29796  * Copyright(c) 2006-2007, Ext JS, LLC.
29797  *
29798  * Originally Released Under LGPL - original licence link has changed is not relivant.
29799  *
29800  * Fork - LGPL
29801  * <script type="text/javascript">
29802  */
29803 /*
29804  * These classes are private internal classes
29805  */
29806 Roo.CenterLayoutRegion = function(mgr, config){
29807     Roo.LayoutRegion.call(this, mgr, config, "center");
29808     this.visible = true;
29809     this.minWidth = config.minWidth || 20;
29810     this.minHeight = config.minHeight || 20;
29811 };
29812
29813 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
29814     hide : function(){
29815         // center panel can't be hidden
29816     },
29817     
29818     show : function(){
29819         // center panel can't be hidden
29820     },
29821     
29822     getMinWidth: function(){
29823         return this.minWidth;
29824     },
29825     
29826     getMinHeight: function(){
29827         return this.minHeight;
29828     }
29829 });
29830
29831
29832 Roo.NorthLayoutRegion = function(mgr, config){
29833     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
29834     if(this.split){
29835         this.split.placement = Roo.SplitBar.TOP;
29836         this.split.orientation = Roo.SplitBar.VERTICAL;
29837         this.split.el.addClass("x-layout-split-v");
29838     }
29839     var size = config.initialSize || config.height;
29840     if(typeof size != "undefined"){
29841         this.el.setHeight(size);
29842     }
29843 };
29844 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
29845     orientation: Roo.SplitBar.VERTICAL,
29846     getBox : function(){
29847         if(this.collapsed){
29848             return this.collapsedEl.getBox();
29849         }
29850         var box = this.el.getBox();
29851         if(this.split){
29852             box.height += this.split.el.getHeight();
29853         }
29854         return box;
29855     },
29856     
29857     updateBox : function(box){
29858         if(this.split && !this.collapsed){
29859             box.height -= this.split.el.getHeight();
29860             this.split.el.setLeft(box.x);
29861             this.split.el.setTop(box.y+box.height);
29862             this.split.el.setWidth(box.width);
29863         }
29864         if(this.collapsed){
29865             this.updateBody(box.width, null);
29866         }
29867         Roo.LayoutRegion.prototype.updateBox.call(this, box);
29868     }
29869 });
29870
29871 Roo.SouthLayoutRegion = function(mgr, config){
29872     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
29873     if(this.split){
29874         this.split.placement = Roo.SplitBar.BOTTOM;
29875         this.split.orientation = Roo.SplitBar.VERTICAL;
29876         this.split.el.addClass("x-layout-split-v");
29877     }
29878     var size = config.initialSize || config.height;
29879     if(typeof size != "undefined"){
29880         this.el.setHeight(size);
29881     }
29882 };
29883 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
29884     orientation: Roo.SplitBar.VERTICAL,
29885     getBox : function(){
29886         if(this.collapsed){
29887             return this.collapsedEl.getBox();
29888         }
29889         var box = this.el.getBox();
29890         if(this.split){
29891             var sh = this.split.el.getHeight();
29892             box.height += sh;
29893             box.y -= sh;
29894         }
29895         return box;
29896     },
29897     
29898     updateBox : function(box){
29899         if(this.split && !this.collapsed){
29900             var sh = this.split.el.getHeight();
29901             box.height -= sh;
29902             box.y += sh;
29903             this.split.el.setLeft(box.x);
29904             this.split.el.setTop(box.y-sh);
29905             this.split.el.setWidth(box.width);
29906         }
29907         if(this.collapsed){
29908             this.updateBody(box.width, null);
29909         }
29910         Roo.LayoutRegion.prototype.updateBox.call(this, box);
29911     }
29912 });
29913
29914 Roo.EastLayoutRegion = function(mgr, config){
29915     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
29916     if(this.split){
29917         this.split.placement = Roo.SplitBar.RIGHT;
29918         this.split.orientation = Roo.SplitBar.HORIZONTAL;
29919         this.split.el.addClass("x-layout-split-h");
29920     }
29921     var size = config.initialSize || config.width;
29922     if(typeof size != "undefined"){
29923         this.el.setWidth(size);
29924     }
29925 };
29926 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
29927     orientation: Roo.SplitBar.HORIZONTAL,
29928     getBox : function(){
29929         if(this.collapsed){
29930             return this.collapsedEl.getBox();
29931         }
29932         var box = this.el.getBox();
29933         if(this.split){
29934             var sw = this.split.el.getWidth();
29935             box.width += sw;
29936             box.x -= sw;
29937         }
29938         return box;
29939     },
29940
29941     updateBox : function(box){
29942         if(this.split && !this.collapsed){
29943             var sw = this.split.el.getWidth();
29944             box.width -= sw;
29945             this.split.el.setLeft(box.x);
29946             this.split.el.setTop(box.y);
29947             this.split.el.setHeight(box.height);
29948             box.x += sw;
29949         }
29950         if(this.collapsed){
29951             this.updateBody(null, box.height);
29952         }
29953         Roo.LayoutRegion.prototype.updateBox.call(this, box);
29954     }
29955 });
29956
29957 Roo.WestLayoutRegion = function(mgr, config){
29958     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
29959     if(this.split){
29960         this.split.placement = Roo.SplitBar.LEFT;
29961         this.split.orientation = Roo.SplitBar.HORIZONTAL;
29962         this.split.el.addClass("x-layout-split-h");
29963     }
29964     var size = config.initialSize || config.width;
29965     if(typeof size != "undefined"){
29966         this.el.setWidth(size);
29967     }
29968 };
29969 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
29970     orientation: Roo.SplitBar.HORIZONTAL,
29971     getBox : function(){
29972         if(this.collapsed){
29973             return this.collapsedEl.getBox();
29974         }
29975         var box = this.el.getBox();
29976         if(this.split){
29977             box.width += this.split.el.getWidth();
29978         }
29979         return box;
29980     },
29981     
29982     updateBox : function(box){
29983         if(this.split && !this.collapsed){
29984             var sw = this.split.el.getWidth();
29985             box.width -= sw;
29986             this.split.el.setLeft(box.x+box.width);
29987             this.split.el.setTop(box.y);
29988             this.split.el.setHeight(box.height);
29989         }
29990         if(this.collapsed){
29991             this.updateBody(null, box.height);
29992         }
29993         Roo.LayoutRegion.prototype.updateBox.call(this, box);
29994     }
29995 });
29996 /*
29997  * Based on:
29998  * Ext JS Library 1.1.1
29999  * Copyright(c) 2006-2007, Ext JS, LLC.
30000  *
30001  * Originally Released Under LGPL - original licence link has changed is not relivant.
30002  *
30003  * Fork - LGPL
30004  * <script type="text/javascript">
30005  */
30006  
30007  
30008 /*
30009  * Private internal class for reading and applying state
30010  */
30011 Roo.LayoutStateManager = function(layout){
30012      // default empty state
30013      this.state = {
30014         north: {},
30015         south: {},
30016         east: {},
30017         west: {}       
30018     };
30019 };
30020
30021 Roo.LayoutStateManager.prototype = {
30022     init : function(layout, provider){
30023         this.provider = provider;
30024         var state = provider.get(layout.id+"-layout-state");
30025         if(state){
30026             var wasUpdating = layout.isUpdating();
30027             if(!wasUpdating){
30028                 layout.beginUpdate();
30029             }
30030             for(var key in state){
30031                 if(typeof state[key] != "function"){
30032                     var rstate = state[key];
30033                     var r = layout.getRegion(key);
30034                     if(r && rstate){
30035                         if(rstate.size){
30036                             r.resizeTo(rstate.size);
30037                         }
30038                         if(rstate.collapsed == true){
30039                             r.collapse(true);
30040                         }else{
30041                             r.expand(null, true);
30042                         }
30043                     }
30044                 }
30045             }
30046             if(!wasUpdating){
30047                 layout.endUpdate();
30048             }
30049             this.state = state; 
30050         }
30051         this.layout = layout;
30052         layout.on("regionresized", this.onRegionResized, this);
30053         layout.on("regioncollapsed", this.onRegionCollapsed, this);
30054         layout.on("regionexpanded", this.onRegionExpanded, this);
30055     },
30056     
30057     storeState : function(){
30058         this.provider.set(this.layout.id+"-layout-state", this.state);
30059     },
30060     
30061     onRegionResized : function(region, newSize){
30062         this.state[region.getPosition()].size = newSize;
30063         this.storeState();
30064     },
30065     
30066     onRegionCollapsed : function(region){
30067         this.state[region.getPosition()].collapsed = true;
30068         this.storeState();
30069     },
30070     
30071     onRegionExpanded : function(region){
30072         this.state[region.getPosition()].collapsed = false;
30073         this.storeState();
30074     }
30075 };/*
30076  * Based on:
30077  * Ext JS Library 1.1.1
30078  * Copyright(c) 2006-2007, Ext JS, LLC.
30079  *
30080  * Originally Released Under LGPL - original licence link has changed is not relivant.
30081  *
30082  * Fork - LGPL
30083  * <script type="text/javascript">
30084  */
30085 /**
30086  * @class Roo.ContentPanel
30087  * @extends Roo.util.Observable
30088  * A basic ContentPanel element.
30089  * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes  (defaults to false)
30090  * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
30091  * @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
30092  * @cfg {Boolean} closable True if the panel can be closed/removed
30093  * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
30094  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
30095  * @cfg {Toolbar} toolbar A toolbar for this panel
30096  * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
30097  * @cfg {String} title The title for this panel
30098  * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
30099  * @cfg {String} url Calls {@link #setUrl} with this value
30100  * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
30101  * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
30102  * @constructor
30103  * Create a new ContentPanel.
30104  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
30105  * @param {String/Object} config A string to set only the title or a config object
30106  * @param {String} content (optional) Set the HTML content for this panel
30107  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
30108  */
30109 Roo.ContentPanel = function(el, config, content){
30110     
30111      
30112     /*
30113     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
30114         config = el;
30115         el = Roo.id();
30116     }
30117     if (config && config.parentLayout) { 
30118         el = config.parentLayout.el.createChild(); 
30119     }
30120     */
30121     if(el.autoCreate){ // xtype is available if this is called from factory
30122         config = el;
30123         el = Roo.id();
30124     }
30125     this.el = Roo.get(el);
30126     if(!this.el && config && config.autoCreate){
30127         if(typeof config.autoCreate == "object"){
30128             if(!config.autoCreate.id){
30129                 config.autoCreate.id = config.id||el;
30130             }
30131             this.el = Roo.DomHelper.append(document.body,
30132                         config.autoCreate, true);
30133         }else{
30134             this.el = Roo.DomHelper.append(document.body,
30135                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
30136         }
30137     }
30138     this.closable = false;
30139     this.loaded = false;
30140     this.active = false;
30141     if(typeof config == "string"){
30142         this.title = config;
30143     }else{
30144         Roo.apply(this, config);
30145     }
30146     
30147     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
30148         this.wrapEl = this.el.wrap();    
30149         this.toolbar = new Roo.Toolbar(this.el.insertSibling(false, 'before'), [] , this.toolbar);
30150         
30151     }
30152     
30153     
30154     
30155     if(this.resizeEl){
30156         this.resizeEl = Roo.get(this.resizeEl, true);
30157     }else{
30158         this.resizeEl = this.el;
30159     }
30160     this.addEvents({
30161         /**
30162          * @event activate
30163          * Fires when this panel is activated. 
30164          * @param {Roo.ContentPanel} this
30165          */
30166         "activate" : true,
30167         /**
30168          * @event deactivate
30169          * Fires when this panel is activated. 
30170          * @param {Roo.ContentPanel} this
30171          */
30172         "deactivate" : true,
30173
30174         /**
30175          * @event resize
30176          * Fires when this panel is resized if fitToFrame is true.
30177          * @param {Roo.ContentPanel} this
30178          * @param {Number} width The width after any component adjustments
30179          * @param {Number} height The height after any component adjustments
30180          */
30181         "resize" : true
30182     });
30183     if(this.autoScroll){
30184         this.resizeEl.setStyle("overflow", "auto");
30185     }
30186     content = content || this.content;
30187     if(content){
30188         this.setContent(content);
30189     }
30190     if(config && config.url){
30191         this.setUrl(this.url, this.params, this.loadOnce);
30192     }
30193     
30194     
30195     
30196     Roo.ContentPanel.superclass.constructor.call(this);
30197 };
30198
30199 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
30200     tabTip:'',
30201     setRegion : function(region){
30202         this.region = region;
30203         if(region){
30204            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
30205         }else{
30206            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
30207         } 
30208     },
30209     
30210     /**
30211      * Returns the toolbar for this Panel if one was configured. 
30212      * @return {Roo.Toolbar} 
30213      */
30214     getToolbar : function(){
30215         return this.toolbar;
30216     },
30217     
30218     setActiveState : function(active){
30219         this.active = active;
30220         if(!active){
30221             this.fireEvent("deactivate", this);
30222         }else{
30223             this.fireEvent("activate", this);
30224         }
30225     },
30226     /**
30227      * Updates this panel's element
30228      * @param {String} content The new content
30229      * @param {Boolean} loadScripts (optional) true to look for and process scripts
30230     */
30231     setContent : function(content, loadScripts){
30232         this.el.update(content, loadScripts);
30233     },
30234
30235     ignoreResize : function(w, h){
30236         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
30237             return true;
30238         }else{
30239             this.lastSize = {width: w, height: h};
30240             return false;
30241         }
30242     },
30243     /**
30244      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
30245      * @return {Roo.UpdateManager} The UpdateManager
30246      */
30247     getUpdateManager : function(){
30248         return this.el.getUpdateManager();
30249     },
30250      /**
30251      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
30252      * @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:
30253 <pre><code>
30254 panel.load({
30255     url: "your-url.php",
30256     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
30257     callback: yourFunction,
30258     scope: yourObject, //(optional scope)
30259     discardUrl: false,
30260     nocache: false,
30261     text: "Loading...",
30262     timeout: 30,
30263     scripts: false
30264 });
30265 </code></pre>
30266      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
30267      * 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.
30268      * @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}
30269      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
30270      * @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.
30271      * @return {Roo.ContentPanel} this
30272      */
30273     load : function(){
30274         var um = this.el.getUpdateManager();
30275         um.update.apply(um, arguments);
30276         return this;
30277     },
30278
30279
30280     /**
30281      * 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.
30282      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
30283      * @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)
30284      * @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)
30285      * @return {Roo.UpdateManager} The UpdateManager
30286      */
30287     setUrl : function(url, params, loadOnce){
30288         if(this.refreshDelegate){
30289             this.removeListener("activate", this.refreshDelegate);
30290         }
30291         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
30292         this.on("activate", this.refreshDelegate);
30293         return this.el.getUpdateManager();
30294     },
30295     
30296     _handleRefresh : function(url, params, loadOnce){
30297         if(!loadOnce || !this.loaded){
30298             var updater = this.el.getUpdateManager();
30299             updater.update(url, params, this._setLoaded.createDelegate(this));
30300         }
30301     },
30302     
30303     _setLoaded : function(){
30304         this.loaded = true;
30305     }, 
30306     
30307     /**
30308      * Returns this panel's id
30309      * @return {String} 
30310      */
30311     getId : function(){
30312         return this.el.id;
30313     },
30314     
30315     /** 
30316      * Returns this panel's element - used by regiosn to add.
30317      * @return {Roo.Element} 
30318      */
30319     getEl : function(){
30320         return this.wrapEl || this.el;
30321     },
30322     
30323     adjustForComponents : function(width, height){
30324         if(this.resizeEl != this.el){
30325             width -= this.el.getFrameWidth('lr');
30326             height -= this.el.getFrameWidth('tb');
30327         }
30328         if(this.toolbar){
30329             var te = this.toolbar.getEl();
30330             height -= te.getHeight();
30331             te.setWidth(width);
30332         }
30333         if(this.adjustments){
30334             width += this.adjustments[0];
30335             height += this.adjustments[1];
30336         }
30337         return {"width": width, "height": height};
30338     },
30339     
30340     setSize : function(width, height){
30341         if(this.fitToFrame && !this.ignoreResize(width, height)){
30342             if(this.fitContainer && this.resizeEl != this.el){
30343                 this.el.setSize(width, height);
30344             }
30345             var size = this.adjustForComponents(width, height);
30346             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
30347             this.fireEvent('resize', this, size.width, size.height);
30348         }
30349     },
30350     
30351     /**
30352      * Returns this panel's title
30353      * @return {String} 
30354      */
30355     getTitle : function(){
30356         return this.title;
30357     },
30358     
30359     /**
30360      * Set this panel's title
30361      * @param {String} title
30362      */
30363     setTitle : function(title){
30364         this.title = title;
30365         if(this.region){
30366             this.region.updatePanelTitle(this, title);
30367         }
30368     },
30369     
30370     /**
30371      * Returns true is this panel was configured to be closable
30372      * @return {Boolean} 
30373      */
30374     isClosable : function(){
30375         return this.closable;
30376     },
30377     
30378     beforeSlide : function(){
30379         this.el.clip();
30380         this.resizeEl.clip();
30381     },
30382     
30383     afterSlide : function(){
30384         this.el.unclip();
30385         this.resizeEl.unclip();
30386     },
30387     
30388     /**
30389      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
30390      *   Will fail silently if the {@link #setUrl} method has not been called.
30391      *   This does not activate the panel, just updates its content.
30392      */
30393     refresh : function(){
30394         if(this.refreshDelegate){
30395            this.loaded = false;
30396            this.refreshDelegate();
30397         }
30398     },
30399     
30400     /**
30401      * Destroys this panel
30402      */
30403     destroy : function(){
30404         this.el.removeAllListeners();
30405         var tempEl = document.createElement("span");
30406         tempEl.appendChild(this.el.dom);
30407         tempEl.innerHTML = "";
30408         this.el.remove();
30409         this.el = null;
30410     },
30411     
30412       /**
30413      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
30414      * <pre><code>
30415
30416 layout.addxtype({
30417        xtype : 'Form',
30418        items: [ .... ]
30419    }
30420 );
30421
30422 </code></pre>
30423      * @param {Object} cfg Xtype definition of item to add.
30424      */
30425     
30426     addxtype : function(cfg) {
30427         // add form..
30428         if (cfg.xtype.match(/^Form$/)) {
30429             var el = this.el.createChild();
30430
30431             this.form = new  Roo.form.Form(cfg);
30432             
30433             
30434             if ( this.form.allItems.length) this.form.render(el.dom);
30435             return this.form;
30436         }
30437         if (['View', 'JsonView'].indexOf(cfg.xtype) > -1) {
30438             // views..
30439             cfg.el = this.el;
30440             // factory?
30441             return new Roo[cfg.xtype](cfg);
30442             
30443         }
30444         return false;
30445         
30446     }
30447 });
30448
30449 /**
30450  * @class Roo.GridPanel
30451  * @extends Roo.ContentPanel
30452  * @constructor
30453  * Create a new GridPanel.
30454  * @param {Roo.grid.Grid} grid The grid for this panel
30455  * @param {String/Object} config A string to set only the panel's title, or a config object
30456  */
30457 Roo.GridPanel = function(grid, config){
30458     
30459   
30460     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
30461         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
30462         
30463     this.wrapper.dom.appendChild(grid.getGridEl().dom);
30464     
30465     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
30466     
30467     if(this.toolbar){
30468         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
30469     }
30470     // xtype created footer. - not sure if will work as we normally have to render first..
30471     if (this.footer && !this.footer.el && this.footer.xtype) {
30472         
30473         this.footer.container = this.grid.getView().getFooterPanel(true);
30474         this.footer.dataSource = this.grid.dataSource;
30475         this.footer = Roo.factory(this.footer, Roo);
30476         
30477     }
30478     
30479     grid.monitorWindowResize = false; // turn off autosizing
30480     grid.autoHeight = false;
30481     grid.autoWidth = false;
30482     this.grid = grid;
30483     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
30484 };
30485
30486 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
30487     getId : function(){
30488         return this.grid.id;
30489     },
30490     
30491     /**
30492      * Returns the grid for this panel
30493      * @return {Roo.grid.Grid} 
30494      */
30495     getGrid : function(){
30496         return this.grid;    
30497     },
30498     
30499     setSize : function(width, height){
30500         if(!this.ignoreResize(width, height)){
30501             var grid = this.grid;
30502             var size = this.adjustForComponents(width, height);
30503             grid.getGridEl().setSize(size.width, size.height);
30504             grid.autoSize();
30505         }
30506     },
30507     
30508     beforeSlide : function(){
30509         this.grid.getView().scroller.clip();
30510     },
30511     
30512     afterSlide : function(){
30513         this.grid.getView().scroller.unclip();
30514     },
30515     
30516     destroy : function(){
30517         this.grid.destroy();
30518         delete this.grid;
30519         Roo.GridPanel.superclass.destroy.call(this); 
30520     }
30521 });
30522
30523
30524 /**
30525  * @class Roo.NestedLayoutPanel
30526  * @extends Roo.ContentPanel
30527  * @constructor
30528  * Create a new NestedLayoutPanel.
30529  * 
30530  * 
30531  * @param {Roo.BorderLayout} layout The layout for this panel
30532  * @param {String/Object} config A string to set only the title or a config object
30533  */
30534 Roo.NestedLayoutPanel = function(layout, config)
30535 {
30536     // construct with only one argument..
30537     /* FIXME - implement nicer consturctors
30538     if (layout.layout) {
30539         config = layout;
30540         layout = config.layout;
30541         delete config.layout;
30542     }
30543     if (layout.xtype && !layout.getEl) {
30544         // then layout needs constructing..
30545         layout = Roo.factory(layout, Roo);
30546     }
30547     */
30548     
30549     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
30550     
30551     layout.monitorWindowResize = false; // turn off autosizing
30552     this.layout = layout;
30553     this.layout.getEl().addClass("x-layout-nested-layout");
30554     
30555     
30556     
30557 };
30558
30559 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
30560
30561     setSize : function(width, height){
30562         if(!this.ignoreResize(width, height)){
30563             var size = this.adjustForComponents(width, height);
30564             var el = this.layout.getEl();
30565             el.setSize(size.width, size.height);
30566             var touch = el.dom.offsetWidth;
30567             this.layout.layout();
30568             // ie requires a double layout on the first pass
30569             if(Roo.isIE && !this.initialized){
30570                 this.initialized = true;
30571                 this.layout.layout();
30572             }
30573         }
30574     },
30575     
30576     // activate all subpanels if not currently active..
30577     
30578     setActiveState : function(active){
30579         this.active = active;
30580         if(!active){
30581             this.fireEvent("deactivate", this);
30582             return;
30583         }
30584         
30585         this.fireEvent("activate", this);
30586         // not sure if this should happen before or after..
30587         if (!this.layout) {
30588             return; // should not happen..
30589         }
30590         var reg = false;
30591         for (var r in this.layout.regions) {
30592             reg = this.layout.getRegion(r);
30593             if (reg.getActivePanel()) {
30594                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
30595                 reg.setActivePanel(reg.getActivePanel());
30596                 continue;
30597             }
30598             if (!reg.panels.length) {
30599                 continue;
30600             }
30601             reg.showPanel(reg.getPanel(0));
30602         }
30603         
30604         
30605         
30606         
30607     },
30608     
30609     /**
30610      * Returns the nested BorderLayout for this panel
30611      * @return {Roo.BorderLayout} 
30612      */
30613     getLayout : function(){
30614         return this.layout;
30615     },
30616     
30617      /**
30618      * Adds a xtype elements to the layout of the nested panel
30619      * <pre><code>
30620
30621 panel.addxtype({
30622        xtype : 'ContentPanel',
30623        region: 'west',
30624        items: [ .... ]
30625    }
30626 );
30627
30628 panel.addxtype({
30629         xtype : 'NestedLayoutPanel',
30630         region: 'west',
30631         layout: {
30632            center: { },
30633            west: { }   
30634         },
30635         items : [ ... list of content panels or nested layout panels.. ]
30636    }
30637 );
30638 </code></pre>
30639      * @param {Object} cfg Xtype definition of item to add.
30640      */
30641     addxtype : function(cfg) {
30642         return this.layout.addxtype(cfg);
30643     
30644     }
30645 });
30646
30647 Roo.ScrollPanel = function(el, config, content){
30648     config = config || {};
30649     config.fitToFrame = true;
30650     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
30651     
30652     this.el.dom.style.overflow = "hidden";
30653     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
30654     this.el.removeClass("x-layout-inactive-content");
30655     this.el.on("mousewheel", this.onWheel, this);
30656
30657     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
30658     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
30659     up.unselectable(); down.unselectable();
30660     up.on("click", this.scrollUp, this);
30661     down.on("click", this.scrollDown, this);
30662     up.addClassOnOver("x-scroller-btn-over");
30663     down.addClassOnOver("x-scroller-btn-over");
30664     up.addClassOnClick("x-scroller-btn-click");
30665     down.addClassOnClick("x-scroller-btn-click");
30666     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
30667
30668     this.resizeEl = this.el;
30669     this.el = wrap; this.up = up; this.down = down;
30670 };
30671
30672 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
30673     increment : 100,
30674     wheelIncrement : 5,
30675     scrollUp : function(){
30676         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
30677     },
30678
30679     scrollDown : function(){
30680         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
30681     },
30682
30683     afterScroll : function(){
30684         var el = this.resizeEl;
30685         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
30686         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
30687         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
30688     },
30689
30690     setSize : function(){
30691         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
30692         this.afterScroll();
30693     },
30694
30695     onWheel : function(e){
30696         var d = e.getWheelDelta();
30697         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
30698         this.afterScroll();
30699         e.stopEvent();
30700     },
30701
30702     setContent : function(content, loadScripts){
30703         this.resizeEl.update(content, loadScripts);
30704     }
30705
30706 });
30707
30708
30709
30710
30711
30712
30713
30714
30715
30716 /**
30717  * @class Roo.TreePanel
30718  * @extends Roo.ContentPanel
30719  * @constructor
30720  * Create a new TreePanel.
30721  * @param {String/Object} config A string to set only the panel's title, or a config object
30722  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
30723  */
30724 Roo.TreePanel = function(config){
30725     var el = config.el;
30726     var tree = config.tree;
30727     delete config.tree; 
30728     delete config.el; // hopefull!
30729     Roo.TreePanel.superclass.constructor.call(this, el, config);
30730     var treeEl = el.createChild();
30731     this.tree = new Roo.tree.TreePanel(treeEl , tree);
30732     //console.log(tree);
30733     this.on('activate', function()
30734     {
30735         if (this.tree.rendered) {
30736             return;
30737         }
30738         //console.log('render tree');
30739         this.tree.render();
30740     });
30741     
30742     this.on('resize',  function (cp, w, h) {
30743             this.tree.innerCt.setWidth(w);
30744             this.tree.innerCt.setHeight(h);
30745             this.tree.innerCt.setStyle('overflow-y', 'auto');
30746     });
30747
30748         
30749     
30750 };
30751
30752 Roo.extend(Roo.TreePanel, Roo.ContentPanel);
30753
30754
30755
30756
30757
30758
30759
30760
30761
30762
30763
30764 /*
30765  * Based on:
30766  * Ext JS Library 1.1.1
30767  * Copyright(c) 2006-2007, Ext JS, LLC.
30768  *
30769  * Originally Released Under LGPL - original licence link has changed is not relivant.
30770  *
30771  * Fork - LGPL
30772  * <script type="text/javascript">
30773  */
30774  
30775
30776 /**
30777  * @class Roo.ReaderLayout
30778  * @extends Roo.BorderLayout
30779  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
30780  * center region containing two nested regions (a top one for a list view and one for item preview below),
30781  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
30782  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
30783  * expedites the setup of the overall layout and regions for this common application style.
30784  * Example:
30785  <pre><code>
30786 var reader = new Roo.ReaderLayout();
30787 var CP = Roo.ContentPanel;  // shortcut for adding
30788
30789 reader.beginUpdate();
30790 reader.add("north", new CP("north", "North"));
30791 reader.add("west", new CP("west", {title: "West"}));
30792 reader.add("east", new CP("east", {title: "East"}));
30793
30794 reader.regions.listView.add(new CP("listView", "List"));
30795 reader.regions.preview.add(new CP("preview", "Preview"));
30796 reader.endUpdate();
30797 </code></pre>
30798 * @constructor
30799 * Create a new ReaderLayout
30800 * @param {Object} config Configuration options
30801 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
30802 * document.body if omitted)
30803 */
30804 Roo.ReaderLayout = function(config, renderTo){
30805     var c = config || {size:{}};
30806     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
30807         north: c.north !== false ? Roo.apply({
30808             split:false,
30809             initialSize: 32,
30810             titlebar: false
30811         }, c.north) : false,
30812         west: c.west !== false ? Roo.apply({
30813             split:true,
30814             initialSize: 200,
30815             minSize: 175,
30816             maxSize: 400,
30817             titlebar: true,
30818             collapsible: true,
30819             animate: true,
30820             margins:{left:5,right:0,bottom:5,top:5},
30821             cmargins:{left:5,right:5,bottom:5,top:5}
30822         }, c.west) : false,
30823         east: c.east !== false ? Roo.apply({
30824             split:true,
30825             initialSize: 200,
30826             minSize: 175,
30827             maxSize: 400,
30828             titlebar: true,
30829             collapsible: true,
30830             animate: true,
30831             margins:{left:0,right:5,bottom:5,top:5},
30832             cmargins:{left:5,right:5,bottom:5,top:5}
30833         }, c.east) : false,
30834         center: Roo.apply({
30835             tabPosition: 'top',
30836             autoScroll:false,
30837             closeOnTab: true,
30838             titlebar:false,
30839             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
30840         }, c.center)
30841     });
30842
30843     this.el.addClass('x-reader');
30844
30845     this.beginUpdate();
30846
30847     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
30848         south: c.preview !== false ? Roo.apply({
30849             split:true,
30850             initialSize: 200,
30851             minSize: 100,
30852             autoScroll:true,
30853             collapsible:true,
30854             titlebar: true,
30855             cmargins:{top:5,left:0, right:0, bottom:0}
30856         }, c.preview) : false,
30857         center: Roo.apply({
30858             autoScroll:false,
30859             titlebar:false,
30860             minHeight:200
30861         }, c.listView)
30862     });
30863     this.add('center', new Roo.NestedLayoutPanel(inner,
30864             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
30865
30866     this.endUpdate();
30867
30868     this.regions.preview = inner.getRegion('south');
30869     this.regions.listView = inner.getRegion('center');
30870 };
30871
30872 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
30873  * Based on:
30874  * Ext JS Library 1.1.1
30875  * Copyright(c) 2006-2007, Ext JS, LLC.
30876  *
30877  * Originally Released Under LGPL - original licence link has changed is not relivant.
30878  *
30879  * Fork - LGPL
30880  * <script type="text/javascript">
30881  */
30882  
30883 /**
30884  * @class Roo.grid.Grid
30885  * @extends Roo.util.Observable
30886  * This class represents the primary interface of a component based grid control.
30887  * <br><br>Usage:<pre><code>
30888  var grid = new Roo.grid.Grid("my-container-id", {
30889      ds: myDataStore,
30890      cm: myColModel,
30891      selModel: mySelectionModel,
30892      autoSizeColumns: true,
30893      monitorWindowResize: false,
30894      trackMouseOver: true
30895  });
30896  // set any options
30897  grid.render();
30898  * </code></pre>
30899  * <b>Common Problems:</b><br/>
30900  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
30901  * element will correct this<br/>
30902  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
30903  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
30904  * are unpredictable.<br/>
30905  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
30906  * grid to calculate dimensions/offsets.<br/>
30907   * @constructor
30908  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
30909  * The container MUST have some type of size defined for the grid to fill. The container will be
30910  * automatically set to position relative if it isn't already.
30911  * @param {Object} config A config object that sets properties on this grid.
30912  */
30913 Roo.grid.Grid = function(container, config){
30914         // initialize the container
30915         this.container = Roo.get(container);
30916         this.container.update("");
30917         this.container.setStyle("overflow", "hidden");
30918     this.container.addClass('x-grid-container');
30919
30920     this.id = this.container.id;
30921
30922     Roo.apply(this, config);
30923     // check and correct shorthanded configs
30924     if(this.ds){
30925         this.dataSource = this.ds;
30926         delete this.ds;
30927     }
30928     if(this.cm){
30929         this.colModel = this.cm;
30930         delete this.cm;
30931     }
30932     if(this.sm){
30933         this.selModel = this.sm;
30934         delete this.sm;
30935     }
30936
30937     if (this.selModel) {
30938         this.selModel = Roo.factory(this.selModel, Roo.grid);
30939         this.sm = this.selModel;
30940         this.sm.xmodule = this.xmodule || false;
30941     }
30942     if (typeof(this.colModel.config) == 'undefined') {
30943         this.colModel = new Roo.grid.ColumnModel(this.colModel);
30944         this.cm = this.colModel;
30945         this.cm.xmodule = this.xmodule || false;
30946     }
30947     if (this.dataSource) {
30948         this.dataSource= Roo.factory(this.dataSource, Roo.data);
30949         this.ds = this.dataSource;
30950         this.ds.xmodule = this.xmodule || false;
30951         
30952     }
30953     
30954     
30955     
30956     if(this.width){
30957         this.container.setWidth(this.width);
30958     }
30959
30960     if(this.height){
30961         this.container.setHeight(this.height);
30962     }
30963     /** @private */
30964         this.addEvents({
30965             // raw events
30966             /**
30967              * @event click
30968              * The raw click event for the entire grid.
30969              * @param {Roo.EventObject} e
30970              */
30971             "click" : true,
30972             /**
30973              * @event dblclick
30974              * The raw dblclick event for the entire grid.
30975              * @param {Roo.EventObject} e
30976              */
30977             "dblclick" : true,
30978             /**
30979              * @event contextmenu
30980              * The raw contextmenu event for the entire grid.
30981              * @param {Roo.EventObject} e
30982              */
30983             "contextmenu" : true,
30984             /**
30985              * @event mousedown
30986              * The raw mousedown event for the entire grid.
30987              * @param {Roo.EventObject} e
30988              */
30989             "mousedown" : true,
30990             /**
30991              * @event mouseup
30992              * The raw mouseup event for the entire grid.
30993              * @param {Roo.EventObject} e
30994              */
30995             "mouseup" : true,
30996             /**
30997              * @event mouseover
30998              * The raw mouseover event for the entire grid.
30999              * @param {Roo.EventObject} e
31000              */
31001             "mouseover" : true,
31002             /**
31003              * @event mouseout
31004              * The raw mouseout event for the entire grid.
31005              * @param {Roo.EventObject} e
31006              */
31007             "mouseout" : true,
31008             /**
31009              * @event keypress
31010              * The raw keypress event for the entire grid.
31011              * @param {Roo.EventObject} e
31012              */
31013             "keypress" : true,
31014             /**
31015              * @event keydown
31016              * The raw keydown event for the entire grid.
31017              * @param {Roo.EventObject} e
31018              */
31019             "keydown" : true,
31020
31021             // custom events
31022
31023             /**
31024              * @event cellclick
31025              * Fires when a cell is clicked
31026              * @param {Grid} this
31027              * @param {Number} rowIndex
31028              * @param {Number} columnIndex
31029              * @param {Roo.EventObject} e
31030              */
31031             "cellclick" : true,
31032             /**
31033              * @event celldblclick
31034              * Fires when a cell is double clicked
31035              * @param {Grid} this
31036              * @param {Number} rowIndex
31037              * @param {Number} columnIndex
31038              * @param {Roo.EventObject} e
31039              */
31040             "celldblclick" : true,
31041             /**
31042              * @event rowclick
31043              * Fires when a row is clicked
31044              * @param {Grid} this
31045              * @param {Number} rowIndex
31046              * @param {Roo.EventObject} e
31047              */
31048             "rowclick" : true,
31049             /**
31050              * @event rowdblclick
31051              * Fires when a row is double clicked
31052              * @param {Grid} this
31053              * @param {Number} rowIndex
31054              * @param {Roo.EventObject} e
31055              */
31056             "rowdblclick" : true,
31057             /**
31058              * @event headerclick
31059              * Fires when a header is clicked
31060              * @param {Grid} this
31061              * @param {Number} columnIndex
31062              * @param {Roo.EventObject} e
31063              */
31064             "headerclick" : true,
31065             /**
31066              * @event headerdblclick
31067              * Fires when a header cell is double clicked
31068              * @param {Grid} this
31069              * @param {Number} columnIndex
31070              * @param {Roo.EventObject} e
31071              */
31072             "headerdblclick" : true,
31073             /**
31074              * @event rowcontextmenu
31075              * Fires when a row is right clicked
31076              * @param {Grid} this
31077              * @param {Number} rowIndex
31078              * @param {Roo.EventObject} e
31079              */
31080             "rowcontextmenu" : true,
31081             /**
31082          * @event cellcontextmenu
31083          * Fires when a cell is right clicked
31084          * @param {Grid} this
31085          * @param {Number} rowIndex
31086          * @param {Number} cellIndex
31087          * @param {Roo.EventObject} e
31088          */
31089          "cellcontextmenu" : true,
31090             /**
31091              * @event headercontextmenu
31092              * Fires when a header is right clicked
31093              * @param {Grid} this
31094              * @param {Number} columnIndex
31095              * @param {Roo.EventObject} e
31096              */
31097             "headercontextmenu" : true,
31098             /**
31099              * @event bodyscroll
31100              * Fires when the body element is scrolled
31101              * @param {Number} scrollLeft
31102              * @param {Number} scrollTop
31103              */
31104             "bodyscroll" : true,
31105             /**
31106              * @event columnresize
31107              * Fires when the user resizes a column
31108              * @param {Number} columnIndex
31109              * @param {Number} newSize
31110              */
31111             "columnresize" : true,
31112             /**
31113              * @event columnmove
31114              * Fires when the user moves a column
31115              * @param {Number} oldIndex
31116              * @param {Number} newIndex
31117              */
31118             "columnmove" : true,
31119             /**
31120              * @event startdrag
31121              * Fires when row(s) start being dragged
31122              * @param {Grid} this
31123              * @param {Roo.GridDD} dd The drag drop object
31124              * @param {event} e The raw browser event
31125              */
31126             "startdrag" : true,
31127             /**
31128              * @event enddrag
31129              * Fires when a drag operation is complete
31130              * @param {Grid} this
31131              * @param {Roo.GridDD} dd The drag drop object
31132              * @param {event} e The raw browser event
31133              */
31134             "enddrag" : true,
31135             /**
31136              * @event dragdrop
31137              * Fires when dragged row(s) are dropped on a valid DD target
31138              * @param {Grid} this
31139              * @param {Roo.GridDD} dd The drag drop object
31140              * @param {String} targetId The target drag drop object
31141              * @param {event} e The raw browser event
31142              */
31143             "dragdrop" : true,
31144             /**
31145              * @event dragover
31146              * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
31147              * @param {Grid} this
31148              * @param {Roo.GridDD} dd The drag drop object
31149              * @param {String} targetId The target drag drop object
31150              * @param {event} e The raw browser event
31151              */
31152             "dragover" : true,
31153             /**
31154              * @event dragenter
31155              *  Fires when the dragged row(s) first cross another DD target while being dragged
31156              * @param {Grid} this
31157              * @param {Roo.GridDD} dd The drag drop object
31158              * @param {String} targetId The target drag drop object
31159              * @param {event} e The raw browser event
31160              */
31161             "dragenter" : true,
31162             /**
31163              * @event dragout
31164              * Fires when the dragged row(s) leave another DD target while being dragged
31165              * @param {Grid} this
31166              * @param {Roo.GridDD} dd The drag drop object
31167              * @param {String} targetId The target drag drop object
31168              * @param {event} e The raw browser event
31169              */
31170             "dragout" : true,
31171         /**
31172          * @event render
31173          * Fires when the grid is rendered
31174          * @param {Grid} grid
31175          */
31176         render : true
31177     });
31178
31179     Roo.grid.Grid.superclass.constructor.call(this);
31180 };
31181 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
31182     /**
31183      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
31184          */
31185         minColumnWidth : 25,
31186
31187     /**
31188          * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
31189          * <b>on initial render.</b> It is more efficient to explicitly size the columns
31190          * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
31191          */
31192         autoSizeColumns : false,
31193
31194         /**
31195          * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
31196          */
31197         autoSizeHeaders : true,
31198
31199         /**
31200          * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
31201          */
31202         monitorWindowResize : true,
31203
31204         /**
31205          * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
31206          * rows measured to get a columns size. Default is 0 (all rows).
31207          */
31208         maxRowsToMeasure : 0,
31209
31210         /**
31211          * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
31212          */
31213         trackMouseOver : true,
31214
31215         /**
31216          * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
31217          */
31218         enableDragDrop : false,
31219
31220         /**
31221          * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
31222          */
31223         enableColumnMove : true,
31224
31225         /**
31226          * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
31227          */
31228         enableColumnHide : true,
31229
31230         /**
31231          * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
31232          */
31233         enableRowHeightSync : false,
31234
31235         /**
31236          * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
31237          */
31238         stripeRows : true,
31239
31240         /**
31241          * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
31242          */
31243         autoHeight : false,
31244
31245     /**
31246      * @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.
31247      */
31248     autoExpandColumn : false,
31249
31250     /**
31251     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
31252     * Default is 50.
31253     */
31254     autoExpandMin : 50,
31255
31256     /**
31257     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
31258     */
31259     autoExpandMax : 1000,
31260
31261     /**
31262          * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
31263          */
31264         view : null,
31265
31266         /**
31267      * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
31268          */
31269         loadMask : false,
31270
31271     // private
31272     rendered : false,
31273
31274     /**
31275     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
31276     * of a fixed width. Default is false.
31277     */
31278     /**
31279     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
31280     */
31281     /**
31282      * Called once after all setup has been completed and the grid is ready to be rendered.
31283      * @return {Roo.grid.Grid} this
31284      */
31285     render : function(){
31286         var c = this.container;
31287         // try to detect autoHeight/width mode
31288         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
31289             this.autoHeight = true;
31290         }
31291         var view = this.getView();
31292         view.init(this);
31293
31294         c.on("click", this.onClick, this);
31295         c.on("dblclick", this.onDblClick, this);
31296         c.on("contextmenu", this.onContextMenu, this);
31297         c.on("keydown", this.onKeyDown, this);
31298
31299         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
31300
31301         this.getSelectionModel().init(this);
31302
31303         view.render();
31304
31305         if(this.loadMask){
31306             this.loadMask = new Roo.LoadMask(this.container,
31307                     Roo.apply({store:this.dataSource}, this.loadMask));
31308         }
31309         
31310         
31311         if (this.toolbar && this.toolbar.xtype) {
31312             this.toolbar.container = this.getView().getHeaderPanel(true);
31313             this.toolbar = new Ext.Toolbar(this.toolbar);
31314         }
31315         if (this.footer && this.footer.xtype) {
31316             this.footer.dataSource = this.getDataSource();
31317             this.footer.container = this.getView().getFooterPanel(true);
31318             this.footer = Roo.factory(this.footer, Roo);
31319         }
31320         this.rendered = true;
31321         this.fireEvent('render', this);
31322         return this;
31323     },
31324
31325         /**
31326          * Reconfigures the grid to use a different Store and Column Model.
31327          * The View will be bound to the new objects and refreshed.
31328          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
31329          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
31330          */
31331     reconfigure : function(dataSource, colModel){
31332         if(this.loadMask){
31333             this.loadMask.destroy();
31334             this.loadMask = new Roo.LoadMask(this.container,
31335                     Roo.apply({store:dataSource}, this.loadMask));
31336         }
31337         this.view.bind(dataSource, colModel);
31338         this.dataSource = dataSource;
31339         this.colModel = colModel;
31340         this.view.refresh(true);
31341     },
31342
31343     // private
31344     onKeyDown : function(e){
31345         this.fireEvent("keydown", e);
31346     },
31347
31348     /**
31349      * Destroy this grid.
31350      * @param {Boolean} removeEl True to remove the element
31351      */
31352     destroy : function(removeEl, keepListeners){
31353         if(this.loadMask){
31354             this.loadMask.destroy();
31355         }
31356         var c = this.container;
31357         c.removeAllListeners();
31358         this.view.destroy();
31359         this.colModel.purgeListeners();
31360         if(!keepListeners){
31361             this.purgeListeners();
31362         }
31363         c.update("");
31364         if(removeEl === true){
31365             c.remove();
31366         }
31367     },
31368
31369     // private
31370     processEvent : function(name, e){
31371         this.fireEvent(name, e);
31372         var t = e.getTarget();
31373         var v = this.view;
31374         var header = v.findHeaderIndex(t);
31375         if(header !== false){
31376             this.fireEvent("header" + name, this, header, e);
31377         }else{
31378             var row = v.findRowIndex(t);
31379             var cell = v.findCellIndex(t);
31380             if(row !== false){
31381                 this.fireEvent("row" + name, this, row, e);
31382                 if(cell !== false){
31383                     this.fireEvent("cell" + name, this, row, cell, e);
31384                 }
31385             }
31386         }
31387     },
31388
31389     // private
31390     onClick : function(e){
31391         this.processEvent("click", e);
31392     },
31393
31394     // private
31395     onContextMenu : function(e, t){
31396         this.processEvent("contextmenu", e);
31397     },
31398
31399     // private
31400     onDblClick : function(e){
31401         this.processEvent("dblclick", e);
31402     },
31403
31404     // private
31405     walkCells : function(row, col, step, fn, scope){
31406         var cm = this.colModel, clen = cm.getColumnCount();
31407         var ds = this.dataSource, rlen = ds.getCount(), first = true;
31408         if(step < 0){
31409             if(col < 0){
31410                 row--;
31411                 first = false;
31412             }
31413             while(row >= 0){
31414                 if(!first){
31415                     col = clen-1;
31416                 }
31417                 first = false;
31418                 while(col >= 0){
31419                     if(fn.call(scope || this, row, col, cm) === true){
31420                         return [row, col];
31421                     }
31422                     col--;
31423                 }
31424                 row--;
31425             }
31426         } else {
31427             if(col >= clen){
31428                 row++;
31429                 first = false;
31430             }
31431             while(row < rlen){
31432                 if(!first){
31433                     col = 0;
31434                 }
31435                 first = false;
31436                 while(col < clen){
31437                     if(fn.call(scope || this, row, col, cm) === true){
31438                         return [row, col];
31439                     }
31440                     col++;
31441                 }
31442                 row++;
31443             }
31444         }
31445         return null;
31446     },
31447
31448     // private
31449     getSelections : function(){
31450         return this.selModel.getSelections();
31451     },
31452
31453     /**
31454      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
31455      * but if manual update is required this method will initiate it.
31456      */
31457     autoSize : function(){
31458         if(this.rendered){
31459             this.view.layout();
31460             if(this.view.adjustForScroll){
31461                 this.view.adjustForScroll();
31462             }
31463         }
31464     },
31465
31466     /**
31467      * Returns the grid's underlying element.
31468      * @return {Element} The element
31469      */
31470     getGridEl : function(){
31471         return this.container;
31472     },
31473
31474     // private for compatibility, overridden by editor grid
31475     stopEditing : function(){},
31476
31477     /**
31478      * Returns the grid's SelectionModel.
31479      * @return {SelectionModel}
31480      */
31481     getSelectionModel : function(){
31482         if(!this.selModel){
31483             this.selModel = new Roo.grid.RowSelectionModel();
31484         }
31485         return this.selModel;
31486     },
31487
31488     /**
31489      * Returns the grid's DataSource.
31490      * @return {DataSource}
31491      */
31492     getDataSource : function(){
31493         return this.dataSource;
31494     },
31495
31496     /**
31497      * Returns the grid's ColumnModel.
31498      * @return {ColumnModel}
31499      */
31500     getColumnModel : function(){
31501         return this.colModel;
31502     },
31503
31504     /**
31505      * Returns the grid's GridView object.
31506      * @return {GridView}
31507      */
31508     getView : function(){
31509         if(!this.view){
31510             this.view = new Roo.grid.GridView(this.viewConfig);
31511         }
31512         return this.view;
31513     },
31514     /**
31515      * Called to get grid's drag proxy text, by default returns this.ddText.
31516      * @return {String}
31517      */
31518     getDragDropText : function(){
31519         var count = this.selModel.getCount();
31520         return String.format(this.ddText, count, count == 1 ? '' : 's');
31521     }
31522 });
31523 /**
31524  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
31525  * %0 is replaced with the number of selected rows.
31526  * @type String
31527  */
31528 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
31529  * Based on:
31530  * Ext JS Library 1.1.1
31531  * Copyright(c) 2006-2007, Ext JS, LLC.
31532  *
31533  * Originally Released Under LGPL - original licence link has changed is not relivant.
31534  *
31535  * Fork - LGPL
31536  * <script type="text/javascript">
31537  */
31538  
31539 Roo.grid.AbstractGridView = function(){
31540         this.grid = null;
31541         
31542         this.events = {
31543             "beforerowremoved" : true,
31544             "beforerowsinserted" : true,
31545             "beforerefresh" : true,
31546             "rowremoved" : true,
31547             "rowsinserted" : true,
31548             "rowupdated" : true,
31549             "refresh" : true
31550         };
31551     Roo.grid.AbstractGridView.superclass.constructor.call(this);
31552 };
31553
31554 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
31555     rowClass : "x-grid-row",
31556     cellClass : "x-grid-cell",
31557     tdClass : "x-grid-td",
31558     hdClass : "x-grid-hd",
31559     splitClass : "x-grid-hd-split",
31560     
31561         init: function(grid){
31562         this.grid = grid;
31563                 var cid = this.grid.getGridEl().id;
31564         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
31565         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
31566         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
31567         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
31568         },
31569         
31570         getColumnRenderers : function(){
31571         var renderers = [];
31572         var cm = this.grid.colModel;
31573         var colCount = cm.getColumnCount();
31574         for(var i = 0; i < colCount; i++){
31575             renderers[i] = cm.getRenderer(i);
31576         }
31577         return renderers;
31578     },
31579     
31580     getColumnIds : function(){
31581         var ids = [];
31582         var cm = this.grid.colModel;
31583         var colCount = cm.getColumnCount();
31584         for(var i = 0; i < colCount; i++){
31585             ids[i] = cm.getColumnId(i);
31586         }
31587         return ids;
31588     },
31589     
31590     getDataIndexes : function(){
31591         if(!this.indexMap){
31592             this.indexMap = this.buildIndexMap();
31593         }
31594         return this.indexMap.colToData;
31595     },
31596     
31597     getColumnIndexByDataIndex : function(dataIndex){
31598         if(!this.indexMap){
31599             this.indexMap = this.buildIndexMap();
31600         }
31601         return this.indexMap.dataToCol[dataIndex];
31602     },
31603     
31604     /**
31605      * Set a css style for a column dynamically. 
31606      * @param {Number} colIndex The index of the column
31607      * @param {String} name The css property name
31608      * @param {String} value The css value
31609      */
31610     setCSSStyle : function(colIndex, name, value){
31611         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
31612         Roo.util.CSS.updateRule(selector, name, value);
31613     },
31614     
31615     generateRules : function(cm){
31616         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
31617         Roo.util.CSS.removeStyleSheet(rulesId);
31618         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
31619             var cid = cm.getColumnId(i);
31620             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
31621                          this.tdSelector, cid, " {\n}\n",
31622                          this.hdSelector, cid, " {\n}\n",
31623                          this.splitSelector, cid, " {\n}\n");
31624         }
31625         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
31626     }
31627 });/*
31628  * Based on:
31629  * Ext JS Library 1.1.1
31630  * Copyright(c) 2006-2007, Ext JS, LLC.
31631  *
31632  * Originally Released Under LGPL - original licence link has changed is not relivant.
31633  *
31634  * Fork - LGPL
31635  * <script type="text/javascript">
31636  */
31637
31638 // private
31639 // This is a support class used internally by the Grid components
31640 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
31641     this.grid = grid;
31642     this.view = grid.getView();
31643     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
31644     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
31645     if(hd2){
31646         this.setHandleElId(Roo.id(hd));
31647         this.setOuterHandleElId(Roo.id(hd2));
31648     }
31649     this.scroll = false;
31650 };
31651 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
31652     maxDragWidth: 120,
31653     getDragData : function(e){
31654         var t = Roo.lib.Event.getTarget(e);
31655         var h = this.view.findHeaderCell(t);
31656         if(h){
31657             return {ddel: h.firstChild, header:h};
31658         }
31659         return false;
31660     },
31661
31662     onInitDrag : function(e){
31663         this.view.headersDisabled = true;
31664         var clone = this.dragData.ddel.cloneNode(true);
31665         clone.id = Roo.id();
31666         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
31667         this.proxy.update(clone);
31668         return true;
31669     },
31670
31671     afterValidDrop : function(){
31672         var v = this.view;
31673         setTimeout(function(){
31674             v.headersDisabled = false;
31675         }, 50);
31676     },
31677
31678     afterInvalidDrop : function(){
31679         var v = this.view;
31680         setTimeout(function(){
31681             v.headersDisabled = false;
31682         }, 50);
31683     }
31684 });
31685 /*
31686  * Based on:
31687  * Ext JS Library 1.1.1
31688  * Copyright(c) 2006-2007, Ext JS, LLC.
31689  *
31690  * Originally Released Under LGPL - original licence link has changed is not relivant.
31691  *
31692  * Fork - LGPL
31693  * <script type="text/javascript">
31694  */
31695 // private
31696 // This is a support class used internally by the Grid components
31697 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
31698     this.grid = grid;
31699     this.view = grid.getView();
31700     // split the proxies so they don't interfere with mouse events
31701     this.proxyTop = Roo.DomHelper.append(document.body, {
31702         cls:"col-move-top", html:"&#160;"
31703     }, true);
31704     this.proxyBottom = Roo.DomHelper.append(document.body, {
31705         cls:"col-move-bottom", html:"&#160;"
31706     }, true);
31707     this.proxyTop.hide = this.proxyBottom.hide = function(){
31708         this.setLeftTop(-100,-100);
31709         this.setStyle("visibility", "hidden");
31710     };
31711     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
31712     // temporarily disabled
31713     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
31714     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
31715 };
31716 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
31717     proxyOffsets : [-4, -9],
31718     fly: Roo.Element.fly,
31719
31720     getTargetFromEvent : function(e){
31721         var t = Roo.lib.Event.getTarget(e);
31722         var cindex = this.view.findCellIndex(t);
31723         if(cindex !== false){
31724             return this.view.getHeaderCell(cindex);
31725         }
31726     },
31727
31728     nextVisible : function(h){
31729         var v = this.view, cm = this.grid.colModel;
31730         h = h.nextSibling;
31731         while(h){
31732             if(!cm.isHidden(v.getCellIndex(h))){
31733                 return h;
31734             }
31735             h = h.nextSibling;
31736         }
31737         return null;
31738     },
31739
31740     prevVisible : function(h){
31741         var v = this.view, cm = this.grid.colModel;
31742         h = h.prevSibling;
31743         while(h){
31744             if(!cm.isHidden(v.getCellIndex(h))){
31745                 return h;
31746             }
31747             h = h.prevSibling;
31748         }
31749         return null;
31750     },
31751
31752     positionIndicator : function(h, n, e){
31753         var x = Roo.lib.Event.getPageX(e);
31754         var r = Roo.lib.Dom.getRegion(n.firstChild);
31755         var px, pt, py = r.top + this.proxyOffsets[1];
31756         if((r.right - x) <= (r.right-r.left)/2){
31757             px = r.right+this.view.borderWidth;
31758             pt = "after";
31759         }else{
31760             px = r.left;
31761             pt = "before";
31762         }
31763         var oldIndex = this.view.getCellIndex(h);
31764         var newIndex = this.view.getCellIndex(n);
31765
31766         if(this.grid.colModel.isFixed(newIndex)){
31767             return false;
31768         }
31769
31770         var locked = this.grid.colModel.isLocked(newIndex);
31771
31772         if(pt == "after"){
31773             newIndex++;
31774         }
31775         if(oldIndex < newIndex){
31776             newIndex--;
31777         }
31778         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
31779             return false;
31780         }
31781         px +=  this.proxyOffsets[0];
31782         this.proxyTop.setLeftTop(px, py);
31783         this.proxyTop.show();
31784         if(!this.bottomOffset){
31785             this.bottomOffset = this.view.mainHd.getHeight();
31786         }
31787         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
31788         this.proxyBottom.show();
31789         return pt;
31790     },
31791
31792     onNodeEnter : function(n, dd, e, data){
31793         if(data.header != n){
31794             this.positionIndicator(data.header, n, e);
31795         }
31796     },
31797
31798     onNodeOver : function(n, dd, e, data){
31799         var result = false;
31800         if(data.header != n){
31801             result = this.positionIndicator(data.header, n, e);
31802         }
31803         if(!result){
31804             this.proxyTop.hide();
31805             this.proxyBottom.hide();
31806         }
31807         return result ? this.dropAllowed : this.dropNotAllowed;
31808     },
31809
31810     onNodeOut : function(n, dd, e, data){
31811         this.proxyTop.hide();
31812         this.proxyBottom.hide();
31813     },
31814
31815     onNodeDrop : function(n, dd, e, data){
31816         var h = data.header;
31817         if(h != n){
31818             var cm = this.grid.colModel;
31819             var x = Roo.lib.Event.getPageX(e);
31820             var r = Roo.lib.Dom.getRegion(n.firstChild);
31821             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
31822             var oldIndex = this.view.getCellIndex(h);
31823             var newIndex = this.view.getCellIndex(n);
31824             var locked = cm.isLocked(newIndex);
31825             if(pt == "after"){
31826                 newIndex++;
31827             }
31828             if(oldIndex < newIndex){
31829                 newIndex--;
31830             }
31831             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
31832                 return false;
31833             }
31834             cm.setLocked(oldIndex, locked, true);
31835             cm.moveColumn(oldIndex, newIndex);
31836             this.grid.fireEvent("columnmove", oldIndex, newIndex);
31837             return true;
31838         }
31839         return false;
31840     }
31841 });
31842 /*
31843  * Based on:
31844  * Ext JS Library 1.1.1
31845  * Copyright(c) 2006-2007, Ext JS, LLC.
31846  *
31847  * Originally Released Under LGPL - original licence link has changed is not relivant.
31848  *
31849  * Fork - LGPL
31850  * <script type="text/javascript">
31851  */
31852   
31853 /**
31854  * @class Roo.grid.GridView
31855  * @extends Roo.util.Observable
31856  *
31857  * @constructor
31858  * @param {Object} config
31859  */
31860 Roo.grid.GridView = function(config){
31861     Roo.grid.GridView.superclass.constructor.call(this);
31862     this.el = null;
31863
31864     Roo.apply(this, config);
31865 };
31866
31867 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
31868
31869     /**
31870      * Override this function to apply custom css classes to rows during rendering
31871      * @param {Record} record The record
31872      * @param {Number} index
31873      * @method getRowClass
31874      */
31875     rowClass : "x-grid-row",
31876
31877     cellClass : "x-grid-col",
31878
31879     tdClass : "x-grid-td",
31880
31881     hdClass : "x-grid-hd",
31882
31883     splitClass : "x-grid-split",
31884
31885     sortClasses : ["sort-asc", "sort-desc"],
31886
31887     enableMoveAnim : false,
31888
31889     hlColor: "C3DAF9",
31890
31891     dh : Roo.DomHelper,
31892
31893     fly : Roo.Element.fly,
31894
31895     css : Roo.util.CSS,
31896
31897     borderWidth: 1,
31898
31899     splitOffset: 3,
31900
31901     scrollIncrement : 22,
31902
31903     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
31904
31905     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
31906
31907     bind : function(ds, cm){
31908         if(this.ds){
31909             this.ds.un("load", this.onLoad, this);
31910             this.ds.un("datachanged", this.onDataChange, this);
31911             this.ds.un("add", this.onAdd, this);
31912             this.ds.un("remove", this.onRemove, this);
31913             this.ds.un("update", this.onUpdate, this);
31914             this.ds.un("clear", this.onClear, this);
31915         }
31916         if(ds){
31917             ds.on("load", this.onLoad, this);
31918             ds.on("datachanged", this.onDataChange, this);
31919             ds.on("add", this.onAdd, this);
31920             ds.on("remove", this.onRemove, this);
31921             ds.on("update", this.onUpdate, this);
31922             ds.on("clear", this.onClear, this);
31923         }
31924         this.ds = ds;
31925
31926         if(this.cm){
31927             this.cm.un("widthchange", this.onColWidthChange, this);
31928             this.cm.un("headerchange", this.onHeaderChange, this);
31929             this.cm.un("hiddenchange", this.onHiddenChange, this);
31930             this.cm.un("columnmoved", this.onColumnMove, this);
31931             this.cm.un("columnlockchange", this.onColumnLock, this);
31932         }
31933         if(cm){
31934             this.generateRules(cm);
31935             cm.on("widthchange", this.onColWidthChange, this);
31936             cm.on("headerchange", this.onHeaderChange, this);
31937             cm.on("hiddenchange", this.onHiddenChange, this);
31938             cm.on("columnmoved", this.onColumnMove, this);
31939             cm.on("columnlockchange", this.onColumnLock, this);
31940         }
31941         this.cm = cm;
31942     },
31943
31944     init: function(grid){
31945                 Roo.grid.GridView.superclass.init.call(this, grid);
31946
31947                 this.bind(grid.dataSource, grid.colModel);
31948
31949             grid.on("headerclick", this.handleHeaderClick, this);
31950
31951         if(grid.trackMouseOver){
31952             grid.on("mouseover", this.onRowOver, this);
31953                 grid.on("mouseout", this.onRowOut, this);
31954             }
31955             grid.cancelTextSelection = function(){};
31956                 this.gridId = grid.id;
31957
31958                 var tpls = this.templates || {};
31959
31960                 if(!tpls.master){
31961                     tpls.master = new Roo.Template(
31962                        '<div class="x-grid" hidefocus="true">',
31963                           '<div class="x-grid-topbar"></div>',
31964                           '<div class="x-grid-scroller"><div></div></div>',
31965                           '<div class="x-grid-locked">',
31966                               '<div class="x-grid-header">{lockedHeader}</div>',
31967                               '<div class="x-grid-body">{lockedBody}</div>',
31968                           "</div>",
31969                           '<div class="x-grid-viewport">',
31970                               '<div class="x-grid-header">{header}</div>',
31971                               '<div class="x-grid-body">{body}</div>',
31972                           "</div>",
31973                           '<div class="x-grid-bottombar"></div>',
31974                           '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
31975                           '<div class="x-grid-resize-proxy">&#160;</div>',
31976                        "</div>"
31977                     );
31978                     tpls.master.disableformats = true;
31979                 }
31980
31981                 if(!tpls.header){
31982                     tpls.header = new Roo.Template(
31983                        '<table border="0" cellspacing="0" cellpadding="0">',
31984                        '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
31985                        "</table>{splits}"
31986                     );
31987                     tpls.header.disableformats = true;
31988                 }
31989                 tpls.header.compile();
31990
31991                 if(!tpls.hcell){
31992                     tpls.hcell = new Roo.Template(
31993                         '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
31994                         '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
31995                         "</div></td>"
31996                      );
31997                      tpls.hcell.disableFormats = true;
31998                 }
31999                 tpls.hcell.compile();
32000
32001                 if(!tpls.hsplit){
32002                     tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
32003                     tpls.hsplit.disableFormats = true;
32004                 }
32005                 tpls.hsplit.compile();
32006
32007                 if(!tpls.body){
32008                     tpls.body = new Roo.Template(
32009                        '<table border="0" cellspacing="0" cellpadding="0">',
32010                        "<tbody>{rows}</tbody>",
32011                        "</table>"
32012                     );
32013                     tpls.body.disableFormats = true;
32014                 }
32015                 tpls.body.compile();
32016
32017                 if(!tpls.row){
32018                     tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
32019                     tpls.row.disableFormats = true;
32020                 }
32021                 tpls.row.compile();
32022
32023                 if(!tpls.cell){
32024                     tpls.cell = new Roo.Template(
32025                         '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
32026                         '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
32027                         "</td>"
32028                     );
32029             tpls.cell.disableFormats = true;
32030         }
32031                 tpls.cell.compile();
32032
32033                 this.templates = tpls;
32034         },
32035
32036         // remap these for backwards compat
32037     onColWidthChange : function(){
32038         this.updateColumns.apply(this, arguments);
32039     },
32040     onHeaderChange : function(){
32041         this.updateHeaders.apply(this, arguments);
32042     }, 
32043     onHiddenChange : function(){
32044         this.handleHiddenChange.apply(this, arguments);
32045     },
32046     onColumnMove : function(){
32047         this.handleColumnMove.apply(this, arguments);
32048     },
32049     onColumnLock : function(){
32050         this.handleLockChange.apply(this, arguments);
32051     },
32052
32053     onDataChange : function(){
32054         this.refresh();
32055         this.updateHeaderSortState();
32056     },
32057
32058         onClear : function(){
32059         this.refresh();
32060     },
32061
32062         onUpdate : function(ds, record){
32063         this.refreshRow(record);
32064     },
32065
32066     refreshRow : function(record){
32067         var ds = this.ds, index;
32068         if(typeof record == 'number'){
32069             index = record;
32070             record = ds.getAt(index);
32071         }else{
32072             index = ds.indexOf(record);
32073         }
32074         this.insertRows(ds, index, index, true);
32075         this.onRemove(ds, record, index+1, true);
32076         this.syncRowHeights(index, index);
32077         this.layout();
32078         this.fireEvent("rowupdated", this, index, record);
32079     },
32080
32081     onAdd : function(ds, records, index){
32082         this.insertRows(ds, index, index + (records.length-1));
32083     },
32084
32085     onRemove : function(ds, record, index, isUpdate){
32086         if(isUpdate !== true){
32087             this.fireEvent("beforerowremoved", this, index, record);
32088         }
32089         var bt = this.getBodyTable(), lt = this.getLockedTable();
32090         if(bt.rows[index]){
32091             bt.firstChild.removeChild(bt.rows[index]);
32092         }
32093         if(lt.rows[index]){
32094             lt.firstChild.removeChild(lt.rows[index]);
32095         }
32096         if(isUpdate !== true){
32097             this.stripeRows(index);
32098             this.syncRowHeights(index, index);
32099             this.layout();
32100             this.fireEvent("rowremoved", this, index, record);
32101         }
32102     },
32103
32104     onLoad : function(){
32105         this.scrollToTop();
32106     },
32107
32108     /**
32109      * Scrolls the grid to the top
32110      */
32111     scrollToTop : function(){
32112         if(this.scroller){
32113             this.scroller.dom.scrollTop = 0;
32114             this.syncScroll();
32115         }
32116     },
32117
32118     /**
32119      * Gets a panel in the header of the grid that can be used for toolbars etc.
32120      * After modifying the contents of this panel a call to grid.autoSize() may be
32121      * required to register any changes in size.
32122      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
32123      * @return Roo.Element
32124      */
32125     getHeaderPanel : function(doShow){
32126         if(doShow){
32127             this.headerPanel.show();
32128         }
32129         return this.headerPanel;
32130         },
32131
32132         /**
32133      * Gets a panel in the footer of the grid that can be used for toolbars etc.
32134      * After modifying the contents of this panel a call to grid.autoSize() may be
32135      * required to register any changes in size.
32136      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
32137      * @return Roo.Element
32138      */
32139     getFooterPanel : function(doShow){
32140         if(doShow){
32141             this.footerPanel.show();
32142         }
32143         return this.footerPanel;
32144         },
32145
32146         initElements : function(){
32147             var E = Roo.Element;
32148             var el = this.grid.getGridEl().dom.firstChild;
32149             var cs = el.childNodes;
32150
32151             this.el = new E(el);
32152             this.headerPanel = new E(el.firstChild);
32153             this.headerPanel.enableDisplayMode("block");
32154
32155         this.scroller = new E(cs[1]);
32156             this.scrollSizer = new E(this.scroller.dom.firstChild);
32157
32158             this.lockedWrap = new E(cs[2]);
32159             this.lockedHd = new E(this.lockedWrap.dom.firstChild);
32160             this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
32161
32162             this.mainWrap = new E(cs[3]);
32163             this.mainHd = new E(this.mainWrap.dom.firstChild);
32164             this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
32165
32166             this.footerPanel = new E(cs[4]);
32167             this.footerPanel.enableDisplayMode("block");
32168
32169         this.focusEl = new E(cs[5]);
32170         this.focusEl.swallowEvent("click", true);
32171         this.resizeProxy = new E(cs[6]);
32172
32173             this.headerSelector = String.format(
32174                '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
32175                this.lockedHd.id, this.mainHd.id
32176             );
32177
32178             this.splitterSelector = String.format(
32179                '#{0} div.x-grid-split, #{1} div.x-grid-split',
32180                this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
32181             );
32182     },
32183     idToCssName : function(s)
32184     {
32185         return s.replace(/[^a-z0-9]+/ig, '-');
32186     },
32187
32188         getHeaderCell : function(index){
32189             return Roo.DomQuery.select(this.headerSelector)[index];
32190         },
32191
32192         getHeaderCellMeasure : function(index){
32193             return this.getHeaderCell(index).firstChild;
32194         },
32195
32196         getHeaderCellText : function(index){
32197             return this.getHeaderCell(index).firstChild.firstChild;
32198         },
32199
32200         getLockedTable : function(){
32201             return this.lockedBody.dom.firstChild;
32202         },
32203
32204         getBodyTable : function(){
32205             return this.mainBody.dom.firstChild;
32206         },
32207
32208         getLockedRow : function(index){
32209             return this.getLockedTable().rows[index];
32210         },
32211
32212         getRow : function(index){
32213             return this.getBodyTable().rows[index];
32214         },
32215
32216         getRowComposite : function(index){
32217             if(!this.rowEl){
32218                 this.rowEl = new Roo.CompositeElementLite();
32219             }
32220         var els = [], lrow, mrow;
32221         if(lrow = this.getLockedRow(index)){
32222             els.push(lrow);
32223         }
32224         if(mrow = this.getRow(index)){
32225             els.push(mrow);
32226         }
32227         this.rowEl.elements = els;
32228             return this.rowEl;
32229         },
32230
32231         getCell : function(rowIndex, colIndex){
32232             var locked = this.cm.getLockedCount();
32233             var source;
32234             if(colIndex < locked){
32235                 source = this.lockedBody.dom.firstChild;
32236             }else{
32237                 source = this.mainBody.dom.firstChild;
32238                 colIndex -= locked;
32239             }
32240         return source.rows[rowIndex].childNodes[colIndex];
32241         },
32242
32243         getCellText : function(rowIndex, colIndex){
32244             return this.getCell(rowIndex, colIndex).firstChild.firstChild;
32245         },
32246
32247         getCellBox : function(cell){
32248             var b = this.fly(cell).getBox();
32249         if(Roo.isOpera){ // opera fails to report the Y
32250             b.y = cell.offsetTop + this.mainBody.getY();
32251         }
32252         return b;
32253     },
32254
32255     getCellIndex : function(cell){
32256         var id = String(cell.className).match(this.cellRE);
32257         if(id){
32258             return parseInt(id[1], 10);
32259         }
32260         return 0;
32261     },
32262
32263     findHeaderIndex : function(n){
32264         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
32265         return r ? this.getCellIndex(r) : false;
32266     },
32267
32268     findHeaderCell : function(n){
32269         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
32270         return r ? r : false;
32271     },
32272
32273     findRowIndex : function(n){
32274         if(!n){
32275             return false;
32276         }
32277         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
32278         return r ? r.rowIndex : false;
32279     },
32280
32281     findCellIndex : function(node){
32282         var stop = this.el.dom;
32283         while(node && node != stop){
32284             if(this.findRE.test(node.className)){
32285                 return this.getCellIndex(node);
32286             }
32287             node = node.parentNode;
32288         }
32289         return false;
32290     },
32291
32292     getColumnId : function(index){
32293             return this.cm.getColumnId(index);
32294         },
32295
32296         getSplitters : function(){
32297             if(this.splitterSelector){
32298                return Roo.DomQuery.select(this.splitterSelector);
32299             }else{
32300                 return null;
32301             }
32302         },
32303
32304         getSplitter : function(index){
32305             return this.getSplitters()[index];
32306         },
32307
32308     onRowOver : function(e, t){
32309         var row;
32310         if((row = this.findRowIndex(t)) !== false){
32311             this.getRowComposite(row).addClass("x-grid-row-over");
32312         }
32313     },
32314
32315     onRowOut : function(e, t){
32316         var row;
32317         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
32318             this.getRowComposite(row).removeClass("x-grid-row-over");
32319         }
32320     },
32321
32322     renderHeaders : function(){
32323             var cm = this.cm;
32324         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
32325         var cb = [], lb = [], sb = [], lsb = [], p = {};
32326         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
32327             p.cellId = "x-grid-hd-0-" + i;
32328             p.splitId = "x-grid-csplit-0-" + i;
32329             p.id = cm.getColumnId(i);
32330             p.title = cm.getColumnTooltip(i) || "";
32331             p.value = cm.getColumnHeader(i) || "";
32332             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
32333             if(!cm.isLocked(i)){
32334                 cb[cb.length] = ct.apply(p);
32335                 sb[sb.length] = st.apply(p);
32336             }else{
32337                 lb[lb.length] = ct.apply(p);
32338                 lsb[lsb.length] = st.apply(p);
32339             }
32340         }
32341         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
32342                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
32343         },
32344
32345         updateHeaders : function(){
32346         var html = this.renderHeaders();
32347         this.lockedHd.update(html[0]);
32348         this.mainHd.update(html[1]);
32349     },
32350
32351     /**
32352      * Focuses the specified row.
32353      * @param {Number} row The row index
32354      */
32355     focusRow : function(row){
32356         var x = this.scroller.dom.scrollLeft;
32357         this.focusCell(row, 0, false);
32358         this.scroller.dom.scrollLeft = x;
32359     },
32360
32361     /**
32362      * Focuses the specified cell.
32363      * @param {Number} row The row index
32364      * @param {Number} col The column index
32365      * @param {Boolean} hscroll false to disable horizontal scrolling
32366      */
32367     focusCell : function(row, col, hscroll){
32368         var el = this.ensureVisible(row, col, hscroll);
32369         this.focusEl.alignTo(el, "tl-tl");
32370         if(Roo.isGecko){
32371             this.focusEl.focus();
32372         }else{
32373             this.focusEl.focus.defer(1, this.focusEl);
32374         }
32375     },
32376
32377     /**
32378      * Scrolls the specified cell into view
32379      * @param {Number} row The row index
32380      * @param {Number} col The column index
32381      * @param {Boolean} hscroll false to disable horizontal scrolling
32382      */
32383     ensureVisible : function(row, col, hscroll){
32384         if(typeof row != "number"){
32385             row = row.rowIndex;
32386         }
32387         if(row < 0 && row >= this.ds.getCount()){
32388             return;
32389         }
32390         col = (col !== undefined ? col : 0);
32391         var cm = this.grid.colModel;
32392         while(cm.isHidden(col)){
32393             col++;
32394         }
32395
32396         var el = this.getCell(row, col);
32397         if(!el){
32398             return;
32399         }
32400         var c = this.scroller.dom;
32401
32402         var ctop = parseInt(el.offsetTop, 10);
32403         var cleft = parseInt(el.offsetLeft, 10);
32404         var cbot = ctop + el.offsetHeight;
32405         var cright = cleft + el.offsetWidth;
32406
32407         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
32408         var stop = parseInt(c.scrollTop, 10);
32409         var sleft = parseInt(c.scrollLeft, 10);
32410         var sbot = stop + ch;
32411         var sright = sleft + c.clientWidth;
32412
32413         if(ctop < stop){
32414                 c.scrollTop = ctop;
32415         }else if(cbot > sbot){
32416             c.scrollTop = cbot-ch;
32417         }
32418
32419         if(hscroll !== false){
32420             if(cleft < sleft){
32421                 c.scrollLeft = cleft;
32422             }else if(cright > sright){
32423                 c.scrollLeft = cright-c.clientWidth;
32424             }
32425         }
32426         return el;
32427     },
32428
32429     updateColumns : function(){
32430         this.grid.stopEditing();
32431         var cm = this.grid.colModel, colIds = this.getColumnIds();
32432         //var totalWidth = cm.getTotalWidth();
32433         var pos = 0;
32434         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
32435             //if(cm.isHidden(i)) continue;
32436             var w = cm.getColumnWidth(i);
32437             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
32438             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
32439         }
32440         this.updateSplitters();
32441     },
32442
32443     generateRules : function(cm){
32444         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
32445         Roo.util.CSS.removeStyleSheet(rulesId);
32446         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
32447             var cid = cm.getColumnId(i);
32448             var align = '';
32449             if(cm.config[i].align){
32450                 align = 'text-align:'+cm.config[i].align+';';
32451             }
32452             var hidden = '';
32453             if(cm.isHidden(i)){
32454                 hidden = 'display:none;';
32455             }
32456             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
32457             ruleBuf.push(
32458                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
32459                     this.hdSelector, cid, " {\n", align, width, "}\n",
32460                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
32461                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
32462         }
32463         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
32464     },
32465
32466     updateSplitters : function(){
32467         var cm = this.cm, s = this.getSplitters();
32468         if(s){ // splitters not created yet
32469             var pos = 0, locked = true;
32470             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
32471                 if(cm.isHidden(i)) continue;
32472                 var w = cm.getColumnWidth(i);
32473                 if(!cm.isLocked(i) && locked){
32474                     pos = 0;
32475                     locked = false;
32476                 }
32477                 pos += w;
32478                 s[i].style.left = (pos-this.splitOffset) + "px";
32479             }
32480         }
32481     },
32482
32483     handleHiddenChange : function(colModel, colIndex, hidden){
32484         if(hidden){
32485             this.hideColumn(colIndex);
32486         }else{
32487             this.unhideColumn(colIndex);
32488         }
32489     },
32490
32491     hideColumn : function(colIndex){
32492         var cid = this.getColumnId(colIndex);
32493         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
32494         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
32495         if(Roo.isSafari){
32496             this.updateHeaders();
32497         }
32498         this.updateSplitters();
32499         this.layout();
32500     },
32501
32502     unhideColumn : function(colIndex){
32503         var cid = this.getColumnId(colIndex);
32504         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
32505         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
32506
32507         if(Roo.isSafari){
32508             this.updateHeaders();
32509         }
32510         this.updateSplitters();
32511         this.layout();
32512     },
32513
32514     insertRows : function(dm, firstRow, lastRow, isUpdate){
32515         if(firstRow == 0 && lastRow == dm.getCount()-1){
32516             this.refresh();
32517         }else{
32518             if(!isUpdate){
32519                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
32520             }
32521             var s = this.getScrollState();
32522             var markup = this.renderRows(firstRow, lastRow);
32523             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
32524             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
32525             this.restoreScroll(s);
32526             if(!isUpdate){
32527                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
32528                 this.syncRowHeights(firstRow, lastRow);
32529                 this.stripeRows(firstRow);
32530                 this.layout();
32531             }
32532         }
32533     },
32534
32535     bufferRows : function(markup, target, index){
32536         var before = null, trows = target.rows, tbody = target.tBodies[0];
32537         if(index < trows.length){
32538             before = trows[index];
32539         }
32540         var b = document.createElement("div");
32541         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
32542         var rows = b.firstChild.rows;
32543         for(var i = 0, len = rows.length; i < len; i++){
32544             if(before){
32545                 tbody.insertBefore(rows[0], before);
32546             }else{
32547                 tbody.appendChild(rows[0]);
32548             }
32549         }
32550         b.innerHTML = "";
32551         b = null;
32552     },
32553
32554     deleteRows : function(dm, firstRow, lastRow){
32555         if(dm.getRowCount()<1){
32556             this.fireEvent("beforerefresh", this);
32557             this.mainBody.update("");
32558             this.lockedBody.update("");
32559             this.fireEvent("refresh", this);
32560         }else{
32561             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
32562             var bt = this.getBodyTable();
32563             var tbody = bt.firstChild;
32564             var rows = bt.rows;
32565             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
32566                 tbody.removeChild(rows[firstRow]);
32567             }
32568             this.stripeRows(firstRow);
32569             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
32570         }
32571     },
32572
32573     updateRows : function(dataSource, firstRow, lastRow){
32574         var s = this.getScrollState();
32575         this.refresh();
32576         this.restoreScroll(s);
32577     },
32578
32579     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
32580         if(!noRefresh){
32581            this.refresh();
32582         }
32583         this.updateHeaderSortState();
32584     },
32585
32586     getScrollState : function(){
32587         var sb = this.scroller.dom;
32588         return {left: sb.scrollLeft, top: sb.scrollTop};
32589     },
32590
32591     stripeRows : function(startRow){
32592         if(!this.grid.stripeRows || this.ds.getCount() < 1){
32593             return;
32594         }
32595         startRow = startRow || 0;
32596         var rows = this.getBodyTable().rows;
32597         var lrows = this.getLockedTable().rows;
32598         var cls = ' x-grid-row-alt ';
32599         for(var i = startRow, len = rows.length; i < len; i++){
32600             var row = rows[i], lrow = lrows[i];
32601             var isAlt = ((i+1) % 2 == 0);
32602             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
32603             if(isAlt == hasAlt){
32604                 continue;
32605             }
32606             if(isAlt){
32607                 row.className += " x-grid-row-alt";
32608             }else{
32609                 row.className = row.className.replace("x-grid-row-alt", "");
32610             }
32611             if(lrow){
32612                 lrow.className = row.className;
32613             }
32614         }
32615     },
32616
32617     restoreScroll : function(state){
32618         var sb = this.scroller.dom;
32619         sb.scrollLeft = state.left;
32620         sb.scrollTop = state.top;
32621         this.syncScroll();
32622     },
32623
32624     syncScroll : function(){
32625         var sb = this.scroller.dom;
32626         var sh = this.mainHd.dom;
32627         var bs = this.mainBody.dom;
32628         var lv = this.lockedBody.dom;
32629         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
32630         lv.scrollTop = bs.scrollTop = sb.scrollTop;
32631     },
32632
32633     handleScroll : function(e){
32634         this.syncScroll();
32635         var sb = this.scroller.dom;
32636         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
32637         e.stopEvent();
32638     },
32639
32640     handleWheel : function(e){
32641         var d = e.getWheelDelta();
32642         this.scroller.dom.scrollTop -= d*22;
32643         // set this here to prevent jumpy scrolling on large tables
32644         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
32645         e.stopEvent();
32646     },
32647
32648     renderRows : function(startRow, endRow){
32649         // pull in all the crap needed to render rows
32650         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
32651         var colCount = cm.getColumnCount();
32652
32653         if(ds.getCount() < 1){
32654             return ["", ""];
32655         }
32656
32657         // build a map for all the columns
32658         var cs = [];
32659         for(var i = 0; i < colCount; i++){
32660             var name = cm.getDataIndex(i);
32661             cs[i] = {
32662                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
32663                 renderer : cm.getRenderer(i),
32664                 id : cm.getColumnId(i),
32665                 locked : cm.isLocked(i)
32666             };
32667         }
32668
32669         startRow = startRow || 0;
32670         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
32671
32672         // records to render
32673         var rs = ds.getRange(startRow, endRow);
32674
32675         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
32676     },
32677
32678     // As much as I hate to duplicate code, this was branched because FireFox really hates
32679     // [].join("") on strings. The performance difference was substantial enough to
32680     // branch this function
32681     doRender : Roo.isGecko ?
32682             function(cs, rs, ds, startRow, colCount, stripe){
32683                 var ts = this.templates, ct = ts.cell, rt = ts.row;
32684                 // buffers
32685                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
32686                 for(var j = 0, len = rs.length; j < len; j++){
32687                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
32688                     for(var i = 0; i < colCount; i++){
32689                         c = cs[i];
32690                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
32691                         p.id = c.id;
32692                         p.css = p.attr = "";
32693                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
32694                         if(p.value == undefined || p.value === "") p.value = "&#160;";
32695                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
32696                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
32697                         }
32698                         var markup = ct.apply(p);
32699                         if(!c.locked){
32700                             cb+= markup;
32701                         }else{
32702                             lcb+= markup;
32703                         }
32704                     }
32705                     var alt = [];
32706                     if(stripe && ((rowIndex+1) % 2 == 0)){
32707                         alt[0] = "x-grid-row-alt";
32708                     }
32709                     if(r.dirty){
32710                         alt[1] = " x-grid-dirty-row";
32711                     }
32712                     rp.cells = lcb;
32713                     if(this.getRowClass){
32714                         alt[2] = this.getRowClass(r, rowIndex);
32715                     }
32716                     rp.alt = alt.join(" ");
32717                     lbuf+= rt.apply(rp);
32718                     rp.cells = cb;
32719                     buf+=  rt.apply(rp);
32720                 }
32721                 return [lbuf, buf];
32722             } :
32723             function(cs, rs, ds, startRow, colCount, stripe){
32724                 var ts = this.templates, ct = ts.cell, rt = ts.row;
32725                 // buffers
32726                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
32727                 for(var j = 0, len = rs.length; j < len; j++){
32728                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
32729                     for(var i = 0; i < colCount; i++){
32730                         c = cs[i];
32731                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
32732                         p.id = c.id;
32733                         p.css = p.attr = "";
32734                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
32735                         if(p.value == undefined || p.value === "") p.value = "&#160;";
32736                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
32737                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
32738                         }
32739                         var markup = ct.apply(p);
32740                         if(!c.locked){
32741                             cb[cb.length] = markup;
32742                         }else{
32743                             lcb[lcb.length] = markup;
32744                         }
32745                     }
32746                     var alt = [];
32747                     if(stripe && ((rowIndex+1) % 2 == 0)){
32748                         alt[0] = "x-grid-row-alt";
32749                     }
32750                     if(r.dirty){
32751                         alt[1] = " x-grid-dirty-row";
32752                     }
32753                     rp.cells = lcb;
32754                     if(this.getRowClass){
32755                         alt[2] = this.getRowClass(r, rowIndex);
32756                     }
32757                     rp.alt = alt.join(" ");
32758                     rp.cells = lcb.join("");
32759                     lbuf[lbuf.length] = rt.apply(rp);
32760                     rp.cells = cb.join("");
32761                     buf[buf.length] =  rt.apply(rp);
32762                 }
32763                 return [lbuf.join(""), buf.join("")];
32764             },
32765
32766     renderBody : function(){
32767         var markup = this.renderRows();
32768         var bt = this.templates.body;
32769         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
32770     },
32771
32772     /**
32773      * Refreshes the grid
32774      * @param {Boolean} headersToo
32775      */
32776     refresh : function(headersToo){
32777         this.fireEvent("beforerefresh", this);
32778         this.grid.stopEditing();
32779         var result = this.renderBody();
32780         this.lockedBody.update(result[0]);
32781         this.mainBody.update(result[1]);
32782         if(headersToo === true){
32783             this.updateHeaders();
32784             this.updateColumns();
32785             this.updateSplitters();
32786             this.updateHeaderSortState();
32787         }
32788         this.syncRowHeights();
32789         this.layout();
32790         this.fireEvent("refresh", this);
32791     },
32792
32793     handleColumnMove : function(cm, oldIndex, newIndex){
32794         this.indexMap = null;
32795         var s = this.getScrollState();
32796         this.refresh(true);
32797         this.restoreScroll(s);
32798         this.afterMove(newIndex);
32799     },
32800
32801     afterMove : function(colIndex){
32802         if(this.enableMoveAnim && Roo.enableFx){
32803             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
32804         }
32805     },
32806
32807     updateCell : function(dm, rowIndex, dataIndex){
32808         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
32809         if(typeof colIndex == "undefined"){ // not present in grid
32810             return;
32811         }
32812         var cm = this.grid.colModel;
32813         var cell = this.getCell(rowIndex, colIndex);
32814         var cellText = this.getCellText(rowIndex, colIndex);
32815
32816         var p = {
32817             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
32818             id : cm.getColumnId(colIndex),
32819             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
32820         };
32821         var renderer = cm.getRenderer(colIndex);
32822         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
32823         if(typeof val == "undefined" || val === "") val = "&#160;";
32824         cellText.innerHTML = val;
32825         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
32826         this.syncRowHeights(rowIndex, rowIndex);
32827     },
32828
32829     calcColumnWidth : function(colIndex, maxRowsToMeasure){
32830         var maxWidth = 0;
32831         if(this.grid.autoSizeHeaders){
32832             var h = this.getHeaderCellMeasure(colIndex);
32833             maxWidth = Math.max(maxWidth, h.scrollWidth);
32834         }
32835         var tb, index;
32836         if(this.cm.isLocked(colIndex)){
32837             tb = this.getLockedTable();
32838             index = colIndex;
32839         }else{
32840             tb = this.getBodyTable();
32841             index = colIndex - this.cm.getLockedCount();
32842         }
32843         if(tb && tb.rows){
32844             var rows = tb.rows;
32845             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
32846             for(var i = 0; i < stopIndex; i++){
32847                 var cell = rows[i].childNodes[index].firstChild;
32848                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
32849             }
32850         }
32851         return maxWidth + /*margin for error in IE*/ 5;
32852     },
32853     /**
32854      * Autofit a column to its content.
32855      * @param {Number} colIndex
32856      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
32857      */
32858      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
32859          if(this.cm.isHidden(colIndex)){
32860              return; // can't calc a hidden column
32861          }
32862         if(forceMinSize){
32863             var cid = this.cm.getColumnId(colIndex);
32864             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
32865            if(this.grid.autoSizeHeaders){
32866                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
32867            }
32868         }
32869         var newWidth = this.calcColumnWidth(colIndex);
32870         this.cm.setColumnWidth(colIndex,
32871             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
32872         if(!suppressEvent){
32873             this.grid.fireEvent("columnresize", colIndex, newWidth);
32874         }
32875     },
32876
32877     /**
32878      * Autofits all columns to their content and then expands to fit any extra space in the grid
32879      */
32880      autoSizeColumns : function(){
32881         var cm = this.grid.colModel;
32882         var colCount = cm.getColumnCount();
32883         for(var i = 0; i < colCount; i++){
32884             this.autoSizeColumn(i, true, true);
32885         }
32886         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
32887             this.fitColumns();
32888         }else{
32889             this.updateColumns();
32890             this.layout();
32891         }
32892     },
32893
32894     /**
32895      * Autofits all columns to the grid's width proportionate with their current size
32896      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
32897      */
32898     fitColumns : function(reserveScrollSpace){
32899         var cm = this.grid.colModel;
32900         var colCount = cm.getColumnCount();
32901         var cols = [];
32902         var width = 0;
32903         var i, w;
32904         for (i = 0; i < colCount; i++){
32905             if(!cm.isHidden(i) && !cm.isFixed(i)){
32906                 w = cm.getColumnWidth(i);
32907                 cols.push(i);
32908                 cols.push(w);
32909                 width += w;
32910             }
32911         }
32912         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
32913         if(reserveScrollSpace){
32914             avail -= 17;
32915         }
32916         var frac = (avail - cm.getTotalWidth())/width;
32917         while (cols.length){
32918             w = cols.pop();
32919             i = cols.pop();
32920             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
32921         }
32922         this.updateColumns();
32923         this.layout();
32924     },
32925
32926     onRowSelect : function(rowIndex){
32927         var row = this.getRowComposite(rowIndex);
32928         row.addClass("x-grid-row-selected");
32929     },
32930
32931     onRowDeselect : function(rowIndex){
32932         var row = this.getRowComposite(rowIndex);
32933         row.removeClass("x-grid-row-selected");
32934     },
32935
32936     onCellSelect : function(row, col){
32937         var cell = this.getCell(row, col);
32938         if(cell){
32939             Roo.fly(cell).addClass("x-grid-cell-selected");
32940         }
32941     },
32942
32943     onCellDeselect : function(row, col){
32944         var cell = this.getCell(row, col);
32945         if(cell){
32946             Roo.fly(cell).removeClass("x-grid-cell-selected");
32947         }
32948     },
32949
32950     updateHeaderSortState : function(){
32951         var state = this.ds.getSortState();
32952         if(!state){
32953             return;
32954         }
32955         this.sortState = state;
32956         var sortColumn = this.cm.findColumnIndex(state.field);
32957         if(sortColumn != -1){
32958             var sortDir = state.direction;
32959             var sc = this.sortClasses;
32960             var hds = this.el.select(this.headerSelector).removeClass(sc);
32961             hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
32962         }
32963     },
32964
32965     handleHeaderClick : function(g, index){
32966         if(this.headersDisabled){
32967             return;
32968         }
32969         var dm = g.dataSource, cm = g.colModel;
32970             if(!cm.isSortable(index)){
32971             return;
32972         }
32973             g.stopEditing();
32974         dm.sort(cm.getDataIndex(index));
32975     },
32976
32977
32978     destroy : function(){
32979         if(this.colMenu){
32980             this.colMenu.removeAll();
32981             Roo.menu.MenuMgr.unregister(this.colMenu);
32982             this.colMenu.getEl().remove();
32983             delete this.colMenu;
32984         }
32985         if(this.hmenu){
32986             this.hmenu.removeAll();
32987             Roo.menu.MenuMgr.unregister(this.hmenu);
32988             this.hmenu.getEl().remove();
32989             delete this.hmenu;
32990         }
32991         if(this.grid.enableColumnMove){
32992             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
32993             if(dds){
32994                 for(var dd in dds){
32995                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
32996                         var elid = dds[dd].dragElId;
32997                         dds[dd].unreg();
32998                         Roo.get(elid).remove();
32999                     } else if(dds[dd].config.isTarget){
33000                         dds[dd].proxyTop.remove();
33001                         dds[dd].proxyBottom.remove();
33002                         dds[dd].unreg();
33003                     }
33004                     if(Roo.dd.DDM.locationCache[dd]){
33005                         delete Roo.dd.DDM.locationCache[dd];
33006                     }
33007                 }
33008                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
33009             }
33010         }
33011         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
33012         this.bind(null, null);
33013         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
33014     },
33015
33016     handleLockChange : function(){
33017         this.refresh(true);
33018     },
33019
33020     onDenyColumnLock : function(){
33021
33022     },
33023
33024     onDenyColumnHide : function(){
33025
33026     },
33027
33028     handleHdMenuClick : function(item){
33029         var index = this.hdCtxIndex;
33030         var cm = this.cm, ds = this.ds;
33031         switch(item.id){
33032             case "asc":
33033                 ds.sort(cm.getDataIndex(index), "ASC");
33034                 break;
33035             case "desc":
33036                 ds.sort(cm.getDataIndex(index), "DESC");
33037                 break;
33038             case "lock":
33039                 var lc = cm.getLockedCount();
33040                 if(cm.getColumnCount(true) <= lc+1){
33041                     this.onDenyColumnLock();
33042                     return;
33043                 }
33044                 if(lc != index){
33045                     cm.setLocked(index, true, true);
33046                     cm.moveColumn(index, lc);
33047                     this.grid.fireEvent("columnmove", index, lc);
33048                 }else{
33049                     cm.setLocked(index, true);
33050                 }
33051             break;
33052             case "unlock":
33053                 var lc = cm.getLockedCount();
33054                 if((lc-1) != index){
33055                     cm.setLocked(index, false, true);
33056                     cm.moveColumn(index, lc-1);
33057                     this.grid.fireEvent("columnmove", index, lc-1);
33058                 }else{
33059                     cm.setLocked(index, false);
33060                 }
33061             break;
33062             default:
33063                 index = cm.getIndexById(item.id.substr(4));
33064                 if(index != -1){
33065                     if(item.checked && cm.getColumnCount(true) <= 1){
33066                         this.onDenyColumnHide();
33067                         return false;
33068                     }
33069                     cm.setHidden(index, item.checked);
33070                 }
33071         }
33072         return true;
33073     },
33074
33075     beforeColMenuShow : function(){
33076         var cm = this.cm,  colCount = cm.getColumnCount();
33077         this.colMenu.removeAll();
33078         for(var i = 0; i < colCount; i++){
33079             this.colMenu.add(new Roo.menu.CheckItem({
33080                 id: "col-"+cm.getColumnId(i),
33081                 text: cm.getColumnHeader(i),
33082                 checked: !cm.isHidden(i),
33083                 hideOnClick:false
33084             }));
33085         }
33086     },
33087
33088     handleHdCtx : function(g, index, e){
33089         e.stopEvent();
33090         var hd = this.getHeaderCell(index);
33091         this.hdCtxIndex = index;
33092         var ms = this.hmenu.items, cm = this.cm;
33093         ms.get("asc").setDisabled(!cm.isSortable(index));
33094         ms.get("desc").setDisabled(!cm.isSortable(index));
33095         if(this.grid.enableColLock !== false){
33096             ms.get("lock").setDisabled(cm.isLocked(index));
33097             ms.get("unlock").setDisabled(!cm.isLocked(index));
33098         }
33099         this.hmenu.show(hd, "tl-bl");
33100     },
33101
33102     handleHdOver : function(e){
33103         var hd = this.findHeaderCell(e.getTarget());
33104         if(hd && !this.headersDisabled){
33105             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
33106                this.fly(hd).addClass("x-grid-hd-over");
33107             }
33108         }
33109     },
33110
33111     handleHdOut : function(e){
33112         var hd = this.findHeaderCell(e.getTarget());
33113         if(hd){
33114             this.fly(hd).removeClass("x-grid-hd-over");
33115         }
33116     },
33117
33118     handleSplitDblClick : function(e, t){
33119         var i = this.getCellIndex(t);
33120         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
33121             this.autoSizeColumn(i, true);
33122             this.layout();
33123         }
33124     },
33125
33126     render : function(){
33127
33128         var cm = this.cm;
33129         var colCount = cm.getColumnCount();
33130
33131         if(this.grid.monitorWindowResize === true){
33132             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
33133         }
33134         var header = this.renderHeaders();
33135         var body = this.templates.body.apply({rows:""});
33136         var html = this.templates.master.apply({
33137             lockedBody: body,
33138             body: body,
33139             lockedHeader: header[0],
33140             header: header[1]
33141         });
33142
33143         //this.updateColumns();
33144
33145         this.grid.getGridEl().dom.innerHTML = html;
33146
33147         this.initElements();
33148
33149         this.scroller.on("scroll", this.handleScroll, this);
33150         this.lockedBody.on("mousewheel", this.handleWheel, this);
33151         this.mainBody.on("mousewheel", this.handleWheel, this);
33152
33153         this.mainHd.on("mouseover", this.handleHdOver, this);
33154         this.mainHd.on("mouseout", this.handleHdOut, this);
33155         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
33156                 {delegate: "."+this.splitClass});
33157
33158         this.lockedHd.on("mouseover", this.handleHdOver, this);
33159         this.lockedHd.on("mouseout", this.handleHdOut, this);
33160         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
33161                 {delegate: "."+this.splitClass});
33162
33163         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
33164             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
33165         }
33166
33167         this.updateSplitters();
33168
33169         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
33170             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
33171             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
33172         }
33173
33174         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
33175             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
33176             this.hmenu.add(
33177                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
33178                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
33179             );
33180             if(this.grid.enableColLock !== false){
33181                 this.hmenu.add('-',
33182                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
33183                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
33184                 );
33185             }
33186             if(this.grid.enableColumnHide !== false){
33187
33188                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
33189                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
33190                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
33191
33192                 this.hmenu.add('-',
33193                     {id:"columns", text: this.columnsText, menu: this.colMenu}
33194                 );
33195             }
33196             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
33197
33198             this.grid.on("headercontextmenu", this.handleHdCtx, this);
33199         }
33200
33201         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
33202             this.dd = new Roo.grid.GridDragZone(this.grid, {
33203                 ddGroup : this.grid.ddGroup || 'GridDD'
33204             });
33205         }
33206
33207         /*
33208         for(var i = 0; i < colCount; i++){
33209             if(cm.isHidden(i)){
33210                 this.hideColumn(i);
33211             }
33212             if(cm.config[i].align){
33213                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
33214                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
33215             }
33216         }*/
33217         
33218         this.updateHeaderSortState();
33219
33220         this.beforeInitialResize();
33221         this.layout(true);
33222
33223         // two part rendering gives faster view to the user
33224         this.renderPhase2.defer(1, this);
33225     },
33226
33227     renderPhase2 : function(){
33228         // render the rows now
33229         this.refresh();
33230         if(this.grid.autoSizeColumns){
33231             this.autoSizeColumns();
33232         }
33233     },
33234
33235     beforeInitialResize : function(){
33236
33237     },
33238
33239     onColumnSplitterMoved : function(i, w){
33240         this.userResized = true;
33241         var cm = this.grid.colModel;
33242         cm.setColumnWidth(i, w, true);
33243         var cid = cm.getColumnId(i);
33244         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
33245         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
33246         this.updateSplitters();
33247         this.layout();
33248         this.grid.fireEvent("columnresize", i, w);
33249     },
33250
33251     syncRowHeights : function(startIndex, endIndex){
33252         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
33253             startIndex = startIndex || 0;
33254             var mrows = this.getBodyTable().rows;
33255             var lrows = this.getLockedTable().rows;
33256             var len = mrows.length-1;
33257             endIndex = Math.min(endIndex || len, len);
33258             for(var i = startIndex; i <= endIndex; i++){
33259                 var m = mrows[i], l = lrows[i];
33260                 var h = Math.max(m.offsetHeight, l.offsetHeight);
33261                 m.style.height = l.style.height = h + "px";
33262             }
33263         }
33264     },
33265
33266     layout : function(initialRender, is2ndPass){
33267         var g = this.grid;
33268         var auto = g.autoHeight;
33269         var scrollOffset = 16;
33270         var c = g.getGridEl(), cm = this.cm,
33271                 expandCol = g.autoExpandColumn,
33272                 gv = this;
33273         //c.beginMeasure();
33274
33275         if(!c.dom.offsetWidth){ // display:none?
33276             if(initialRender){
33277                 this.lockedWrap.show();
33278                 this.mainWrap.show();
33279             }
33280             return;
33281         }
33282
33283         var hasLock = this.cm.isLocked(0);
33284
33285         var tbh = this.headerPanel.getHeight();
33286         var bbh = this.footerPanel.getHeight();
33287
33288         if(auto){
33289             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
33290             var newHeight = ch + c.getBorderWidth("tb");
33291             if(g.maxHeight){
33292                 newHeight = Math.min(g.maxHeight, newHeight);
33293             }
33294             c.setHeight(newHeight);
33295         }
33296
33297         if(g.autoWidth){
33298             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
33299         }
33300
33301         var s = this.scroller;
33302
33303         var csize = c.getSize(true);
33304
33305         this.el.setSize(csize.width, csize.height);
33306
33307         this.headerPanel.setWidth(csize.width);
33308         this.footerPanel.setWidth(csize.width);
33309
33310         var hdHeight = this.mainHd.getHeight();
33311         var vw = csize.width;
33312         var vh = csize.height - (tbh + bbh);
33313
33314         s.setSize(vw, vh);
33315
33316         var bt = this.getBodyTable();
33317         var ltWidth = hasLock ?
33318                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
33319
33320         var scrollHeight = bt.offsetHeight;
33321         var scrollWidth = ltWidth + bt.offsetWidth;
33322         var vscroll = false, hscroll = false;
33323
33324         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
33325
33326         var lw = this.lockedWrap, mw = this.mainWrap;
33327         var lb = this.lockedBody, mb = this.mainBody;
33328
33329         setTimeout(function(){
33330             var t = s.dom.offsetTop;
33331             var w = s.dom.clientWidth,
33332                 h = s.dom.clientHeight;
33333
33334             lw.setTop(t);
33335             lw.setSize(ltWidth, h);
33336
33337             mw.setLeftTop(ltWidth, t);
33338             mw.setSize(w-ltWidth, h);
33339
33340             lb.setHeight(h-hdHeight);
33341             mb.setHeight(h-hdHeight);
33342
33343             if(is2ndPass !== true && !gv.userResized && expandCol){
33344                 // high speed resize without full column calculation
33345                 
33346                 var ci = cm.getIndexById(expandCol);
33347                 if (ci < 0) {
33348                     ci = cm.findColumnIndex(expandCol);
33349                 }
33350                 ci = Math.max(0, ci); // make sure it's got at least the first col.
33351                 var expandId = cm.getColumnId(ci);
33352                 var  tw = cm.getTotalWidth(false);
33353                 var currentWidth = cm.getColumnWidth(ci);
33354                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
33355                 if(currentWidth != cw){
33356                     cm.setColumnWidth(ci, cw, true);
33357                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
33358                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
33359                     gv.updateSplitters();
33360                     gv.layout(false, true);
33361                 }
33362             }
33363
33364             if(initialRender){
33365                 lw.show();
33366                 mw.show();
33367             }
33368             //c.endMeasure();
33369         }, 10);
33370     },
33371
33372     onWindowResize : function(){
33373         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
33374             return;
33375         }
33376         this.layout();
33377     },
33378
33379     appendFooter : function(parentEl){
33380         return null;
33381     },
33382
33383     sortAscText : "Sort Ascending",
33384     sortDescText : "Sort Descending",
33385     lockText : "Lock Column",
33386     unlockText : "Unlock Column",
33387     columnsText : "Columns"
33388 });
33389
33390
33391 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
33392     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
33393     this.proxy.el.addClass('x-grid3-col-dd');
33394 };
33395
33396 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
33397     handleMouseDown : function(e){
33398
33399     },
33400
33401     callHandleMouseDown : function(e){
33402         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
33403     }
33404 });
33405 /*
33406  * Based on:
33407  * Ext JS Library 1.1.1
33408  * Copyright(c) 2006-2007, Ext JS, LLC.
33409  *
33410  * Originally Released Under LGPL - original licence link has changed is not relivant.
33411  *
33412  * Fork - LGPL
33413  * <script type="text/javascript">
33414  */
33415  
33416 // private
33417 // This is a support class used internally by the Grid components
33418 Roo.grid.SplitDragZone = function(grid, hd, hd2){
33419     this.grid = grid;
33420     this.view = grid.getView();
33421     this.proxy = this.view.resizeProxy;
33422     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
33423         "gridSplitters" + this.grid.getGridEl().id, {
33424         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
33425     });
33426     this.setHandleElId(Roo.id(hd));
33427     this.setOuterHandleElId(Roo.id(hd2));
33428     this.scroll = false;
33429 };
33430 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
33431     fly: Roo.Element.fly,
33432
33433     b4StartDrag : function(x, y){
33434         this.view.headersDisabled = true;
33435         this.proxy.setHeight(this.view.mainWrap.getHeight());
33436         var w = this.cm.getColumnWidth(this.cellIndex);
33437         var minw = Math.max(w-this.grid.minColumnWidth, 0);
33438         this.resetConstraints();
33439         this.setXConstraint(minw, 1000);
33440         this.setYConstraint(0, 0);
33441         this.minX = x - minw;
33442         this.maxX = x + 1000;
33443         this.startPos = x;
33444         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
33445     },
33446
33447
33448     handleMouseDown : function(e){
33449         ev = Roo.EventObject.setEvent(e);
33450         var t = this.fly(ev.getTarget());
33451         if(t.hasClass("x-grid-split")){
33452             this.cellIndex = this.view.getCellIndex(t.dom);
33453             this.split = t.dom;
33454             this.cm = this.grid.colModel;
33455             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
33456                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
33457             }
33458         }
33459     },
33460
33461     endDrag : function(e){
33462         this.view.headersDisabled = false;
33463         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
33464         var diff = endX - this.startPos;
33465         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
33466     },
33467
33468     autoOffset : function(){
33469         this.setDelta(0,0);
33470     }
33471 });/*
33472  * Based on:
33473  * Ext JS Library 1.1.1
33474  * Copyright(c) 2006-2007, Ext JS, LLC.
33475  *
33476  * Originally Released Under LGPL - original licence link has changed is not relivant.
33477  *
33478  * Fork - LGPL
33479  * <script type="text/javascript">
33480  */
33481  
33482 // private
33483 // This is a support class used internally by the Grid components
33484 Roo.grid.GridDragZone = function(grid, config){
33485     this.view = grid.getView();
33486     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
33487     if(this.view.lockedBody){
33488         this.setHandleElId(Roo.id(this.view.mainBody.dom));
33489         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
33490     }
33491     this.scroll = false;
33492     this.grid = grid;
33493     this.ddel = document.createElement('div');
33494     this.ddel.className = 'x-grid-dd-wrap';
33495 };
33496
33497 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
33498     ddGroup : "GridDD",
33499
33500     getDragData : function(e){
33501         var t = Roo.lib.Event.getTarget(e);
33502         var rowIndex = this.view.findRowIndex(t);
33503         if(rowIndex !== false){
33504             var sm = this.grid.selModel;
33505             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
33506               //  sm.mouseDown(e, t);
33507             //}
33508             if (e.hasModifier()){
33509                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
33510             }
33511             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
33512         }
33513         return false;
33514     },
33515
33516     onInitDrag : function(e){
33517         var data = this.dragData;
33518         this.ddel.innerHTML = this.grid.getDragDropText();
33519         this.proxy.update(this.ddel);
33520         // fire start drag?
33521     },
33522
33523     afterRepair : function(){
33524         this.dragging = false;
33525     },
33526
33527     getRepairXY : function(e, data){
33528         return false;
33529     },
33530
33531     onEndDrag : function(data, e){
33532         // fire end drag?
33533     },
33534
33535     onValidDrop : function(dd, e, id){
33536         // fire drag drop?
33537         this.hideProxy();
33538     },
33539
33540     beforeInvalidDrop : function(e, id){
33541
33542     }
33543 });/*
33544  * Based on:
33545  * Ext JS Library 1.1.1
33546  * Copyright(c) 2006-2007, Ext JS, LLC.
33547  *
33548  * Originally Released Under LGPL - original licence link has changed is not relivant.
33549  *
33550  * Fork - LGPL
33551  * <script type="text/javascript">
33552  */
33553  
33554
33555 /**
33556  * @class Roo.grid.ColumnModel
33557  * @extends Roo.util.Observable
33558  * This is the default implementation of a ColumnModel used by the Grid. It defines
33559  * the columns in the grid.
33560  * <br>Usage:<br>
33561  <pre><code>
33562  var colModel = new Roo.grid.ColumnModel([
33563         {header: "Ticker", width: 60, sortable: true, locked: true},
33564         {header: "Company Name", width: 150, sortable: true},
33565         {header: "Market Cap.", width: 100, sortable: true},
33566         {header: "$ Sales", width: 100, sortable: true, renderer: money},
33567         {header: "Employees", width: 100, sortable: true, resizable: false}
33568  ]);
33569  </code></pre>
33570  * <p>
33571  
33572  * The config options listed for this class are options which may appear in each
33573  * individual column definition.
33574  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
33575  * @constructor
33576  * @param {Object} config An Array of column config objects. See this class's
33577  * config objects for details.
33578 */
33579 Roo.grid.ColumnModel = function(config){
33580         /**
33581      * The config passed into the constructor
33582      */
33583     this.config = config;
33584     this.lookup = {};
33585
33586     // if no id, create one
33587     // if the column does not have a dataIndex mapping,
33588     // map it to the order it is in the config
33589     for(var i = 0, len = config.length; i < len; i++){
33590         var c = config[i];
33591         if(typeof c.dataIndex == "undefined"){
33592             c.dataIndex = i;
33593         }
33594         if(typeof c.renderer == "string"){
33595             c.renderer = Roo.util.Format[c.renderer];
33596         }
33597         if(typeof c.id == "undefined"){
33598             c.id = Roo.id();
33599         }
33600         if(c.editor && c.editor.xtype){
33601             c.editor  = Roo.factory(c.editor, Roo.grid);
33602         }
33603         if(c.editor && c.editor.isFormField){
33604             c.editor = new Roo.grid.GridEditor(c.editor);
33605         }
33606         this.lookup[c.id] = c;
33607     }
33608
33609     /**
33610      * The width of columns which have no width specified (defaults to 100)
33611      * @type Number
33612      */
33613     this.defaultWidth = 100;
33614
33615     /**
33616      * Default sortable of columns which have no sortable specified (defaults to false)
33617      * @type Boolean
33618      */
33619     this.defaultSortable = false;
33620
33621     this.addEvents({
33622         /**
33623              * @event widthchange
33624              * Fires when the width of a column changes.
33625              * @param {ColumnModel} this
33626              * @param {Number} columnIndex The column index
33627              * @param {Number} newWidth The new width
33628              */
33629             "widthchange": true,
33630         /**
33631              * @event headerchange
33632              * Fires when the text of a header changes.
33633              * @param {ColumnModel} this
33634              * @param {Number} columnIndex The column index
33635              * @param {Number} newText The new header text
33636              */
33637             "headerchange": true,
33638         /**
33639              * @event hiddenchange
33640              * Fires when a column is hidden or "unhidden".
33641              * @param {ColumnModel} this
33642              * @param {Number} columnIndex The column index
33643              * @param {Boolean} hidden true if hidden, false otherwise
33644              */
33645             "hiddenchange": true,
33646             /**
33647          * @event columnmoved
33648          * Fires when a column is moved.
33649          * @param {ColumnModel} this
33650          * @param {Number} oldIndex
33651          * @param {Number} newIndex
33652          */
33653         "columnmoved" : true,
33654         /**
33655          * @event columlockchange
33656          * Fires when a column's locked state is changed
33657          * @param {ColumnModel} this
33658          * @param {Number} colIndex
33659          * @param {Boolean} locked true if locked
33660          */
33661         "columnlockchange" : true
33662     });
33663     Roo.grid.ColumnModel.superclass.constructor.call(this);
33664 };
33665 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
33666     /**
33667      * @cfg {String} header The header text to display in the Grid view.
33668      */
33669     /**
33670      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
33671      * {@link Roo.data.Record} definition from which to draw the column's value. If not
33672      * specified, the column's index is used as an index into the Record's data Array.
33673      */
33674     /**
33675      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
33676      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
33677      */
33678     /**
33679      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
33680      * Defaults to the value of the {@link #defaultSortable} property.
33681      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
33682      */
33683     /**
33684      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
33685      */
33686     /**
33687      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
33688      */
33689     /**
33690      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
33691      */
33692     /**
33693      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
33694      */
33695     /**
33696      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
33697      * given the cell's data value. See {@link #setRenderer}. If not specified, the
33698      * default renderer uses the raw data value.
33699      */
33700        /**
33701      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
33702      */
33703     /**
33704      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
33705      */
33706
33707     /**
33708      * Returns the id of the column at the specified index.
33709      * @param {Number} index The column index
33710      * @return {String} the id
33711      */
33712     getColumnId : function(index){
33713         return this.config[index].id;
33714     },
33715
33716     /**
33717      * Returns the column for a specified id.
33718      * @param {String} id The column id
33719      * @return {Object} the column
33720      */
33721     getColumnById : function(id){
33722         return this.lookup[id];
33723     },
33724
33725     /**
33726      * Returns the index for a specified column id.
33727      * @param {String} id The column id
33728      * @return {Number} the index, or -1 if not found
33729      */
33730     getIndexById : function(id){
33731         for(var i = 0, len = this.config.length; i < len; i++){
33732             if(this.config[i].id == id){
33733                 return i;
33734             }
33735         }
33736         return -1;
33737     },
33738     /**
33739      * Returns the index for a specified column dataIndex.
33740      * @param {String} dataIndex The column dataIndex
33741      * @return {Number} the index, or -1 if not found
33742      */
33743     
33744     findColumnIndex : function(dataIndex){
33745         for(var i = 0, len = this.config.length; i < len; i++){
33746             if(this.config[i].dataIndex == dataIndex){
33747                 return i;
33748             }
33749         }
33750         return -1;
33751     },
33752     
33753     
33754     moveColumn : function(oldIndex, newIndex){
33755         var c = this.config[oldIndex];
33756         this.config.splice(oldIndex, 1);
33757         this.config.splice(newIndex, 0, c);
33758         this.dataMap = null;
33759         this.fireEvent("columnmoved", this, oldIndex, newIndex);
33760     },
33761
33762     isLocked : function(colIndex){
33763         return this.config[colIndex].locked === true;
33764     },
33765
33766     setLocked : function(colIndex, value, suppressEvent){
33767         if(this.isLocked(colIndex) == value){
33768             return;
33769         }
33770         this.config[colIndex].locked = value;
33771         if(!suppressEvent){
33772             this.fireEvent("columnlockchange", this, colIndex, value);
33773         }
33774     },
33775
33776     getTotalLockedWidth : function(){
33777         var totalWidth = 0;
33778         for(var i = 0; i < this.config.length; i++){
33779             if(this.isLocked(i) && !this.isHidden(i)){
33780                 this.totalWidth += this.getColumnWidth(i);
33781             }
33782         }
33783         return totalWidth;
33784     },
33785
33786     getLockedCount : function(){
33787         for(var i = 0, len = this.config.length; i < len; i++){
33788             if(!this.isLocked(i)){
33789                 return i;
33790             }
33791         }
33792     },
33793
33794     /**
33795      * Returns the number of columns.
33796      * @return {Number}
33797      */
33798     getColumnCount : function(visibleOnly){
33799         if(visibleOnly === true){
33800             var c = 0;
33801             for(var i = 0, len = this.config.length; i < len; i++){
33802                 if(!this.isHidden(i)){
33803                     c++;
33804                 }
33805             }
33806             return c;
33807         }
33808         return this.config.length;
33809     },
33810
33811     /**
33812      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
33813      * @param {Function} fn
33814      * @param {Object} scope (optional)
33815      * @return {Array} result
33816      */
33817     getColumnsBy : function(fn, scope){
33818         var r = [];
33819         for(var i = 0, len = this.config.length; i < len; i++){
33820             var c = this.config[i];
33821             if(fn.call(scope||this, c, i) === true){
33822                 r[r.length] = c;
33823             }
33824         }
33825         return r;
33826     },
33827
33828     /**
33829      * Returns true if the specified column is sortable.
33830      * @param {Number} col The column index
33831      * @return {Boolean}
33832      */
33833     isSortable : function(col){
33834         if(typeof this.config[col].sortable == "undefined"){
33835             return this.defaultSortable;
33836         }
33837         return this.config[col].sortable;
33838     },
33839
33840     /**
33841      * Returns the rendering (formatting) function defined for the column.
33842      * @param {Number} col The column index.
33843      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
33844      */
33845     getRenderer : function(col){
33846         if(!this.config[col].renderer){
33847             return Roo.grid.ColumnModel.defaultRenderer;
33848         }
33849         return this.config[col].renderer;
33850     },
33851
33852     /**
33853      * Sets the rendering (formatting) function for a column.
33854      * @param {Number} col The column index
33855      * @param {Function} fn The function to use to process the cell's raw data
33856      * to return HTML markup for the grid view. The render function is called with
33857      * the following parameters:<ul>
33858      * <li>Data value.</li>
33859      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
33860      * <li>css A CSS style string to apply to the table cell.</li>
33861      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
33862      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
33863      * <li>Row index</li>
33864      * <li>Column index</li>
33865      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
33866      */
33867     setRenderer : function(col, fn){
33868         this.config[col].renderer = fn;
33869     },
33870
33871     /**
33872      * Returns the width for the specified column.
33873      * @param {Number} col The column index
33874      * @return {Number}
33875      */
33876     getColumnWidth : function(col){
33877         return this.config[col].width || this.defaultWidth;
33878     },
33879
33880     /**
33881      * Sets the width for a column.
33882      * @param {Number} col The column index
33883      * @param {Number} width The new width
33884      */
33885     setColumnWidth : function(col, width, suppressEvent){
33886         this.config[col].width = width;
33887         this.totalWidth = null;
33888         if(!suppressEvent){
33889              this.fireEvent("widthchange", this, col, width);
33890         }
33891     },
33892
33893     /**
33894      * Returns the total width of all columns.
33895      * @param {Boolean} includeHidden True to include hidden column widths
33896      * @return {Number}
33897      */
33898     getTotalWidth : function(includeHidden){
33899         if(!this.totalWidth){
33900             this.totalWidth = 0;
33901             for(var i = 0, len = this.config.length; i < len; i++){
33902                 if(includeHidden || !this.isHidden(i)){
33903                     this.totalWidth += this.getColumnWidth(i);
33904                 }
33905             }
33906         }
33907         return this.totalWidth;
33908     },
33909
33910     /**
33911      * Returns the header for the specified column.
33912      * @param {Number} col The column index
33913      * @return {String}
33914      */
33915     getColumnHeader : function(col){
33916         return this.config[col].header;
33917     },
33918
33919     /**
33920      * Sets the header for a column.
33921      * @param {Number} col The column index
33922      * @param {String} header The new header
33923      */
33924     setColumnHeader : function(col, header){
33925         this.config[col].header = header;
33926         this.fireEvent("headerchange", this, col, header);
33927     },
33928
33929     /**
33930      * Returns the tooltip for the specified column.
33931      * @param {Number} col The column index
33932      * @return {String}
33933      */
33934     getColumnTooltip : function(col){
33935             return this.config[col].tooltip;
33936     },
33937     /**
33938      * Sets the tooltip for a column.
33939      * @param {Number} col The column index
33940      * @param {String} tooltip The new tooltip
33941      */
33942     setColumnTooltip : function(col, tooltip){
33943             this.config[col].tooltip = tooltip;
33944     },
33945
33946     /**
33947      * Returns the dataIndex for the specified column.
33948      * @param {Number} col The column index
33949      * @return {Number}
33950      */
33951     getDataIndex : function(col){
33952         return this.config[col].dataIndex;
33953     },
33954
33955     /**
33956      * Sets the dataIndex for a column.
33957      * @param {Number} col The column index
33958      * @param {Number} dataIndex The new dataIndex
33959      */
33960     setDataIndex : function(col, dataIndex){
33961         this.config[col].dataIndex = dataIndex;
33962     },
33963
33964     
33965     
33966     /**
33967      * Returns true if the cell is editable.
33968      * @param {Number} colIndex The column index
33969      * @param {Number} rowIndex The row index
33970      * @return {Boolean}
33971      */
33972     isCellEditable : function(colIndex, rowIndex){
33973         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
33974     },
33975
33976     /**
33977      * Returns the editor defined for the cell/column.
33978      * return false or null to disable editing.
33979      * @param {Number} colIndex The column index
33980      * @param {Number} rowIndex The row index
33981      * @return {Object}
33982      */
33983     getCellEditor : function(colIndex, rowIndex){
33984         return this.config[colIndex].editor;
33985     },
33986
33987     /**
33988      * Sets if a column is editable.
33989      * @param {Number} col The column index
33990      * @param {Boolean} editable True if the column is editable
33991      */
33992     setEditable : function(col, editable){
33993         this.config[col].editable = editable;
33994     },
33995
33996
33997     /**
33998      * Returns true if the column is hidden.
33999      * @param {Number} colIndex The column index
34000      * @return {Boolean}
34001      */
34002     isHidden : function(colIndex){
34003         return this.config[colIndex].hidden;
34004     },
34005
34006
34007     /**
34008      * Returns true if the column width cannot be changed
34009      */
34010     isFixed : function(colIndex){
34011         return this.config[colIndex].fixed;
34012     },
34013
34014     /**
34015      * Returns true if the column can be resized
34016      * @return {Boolean}
34017      */
34018     isResizable : function(colIndex){
34019         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
34020     },
34021     /**
34022      * Sets if a column is hidden.
34023      * @param {Number} colIndex The column index
34024      * @param {Boolean} hidden True if the column is hidden
34025      */
34026     setHidden : function(colIndex, hidden){
34027         this.config[colIndex].hidden = hidden;
34028         this.totalWidth = null;
34029         this.fireEvent("hiddenchange", this, colIndex, hidden);
34030     },
34031
34032     /**
34033      * Sets the editor for a column.
34034      * @param {Number} col The column index
34035      * @param {Object} editor The editor object
34036      */
34037     setEditor : function(col, editor){
34038         this.config[col].editor = editor;
34039     }
34040 });
34041
34042 Roo.grid.ColumnModel.defaultRenderer = function(value){
34043         if(typeof value == "string" && value.length < 1){
34044             return "&#160;";
34045         }
34046         return value;
34047 };
34048
34049 // Alias for backwards compatibility
34050 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
34051 /*
34052  * Based on:
34053  * Ext JS Library 1.1.1
34054  * Copyright(c) 2006-2007, Ext JS, LLC.
34055  *
34056  * Originally Released Under LGPL - original licence link has changed is not relivant.
34057  *
34058  * Fork - LGPL
34059  * <script type="text/javascript">
34060  */
34061
34062 /**
34063  * @class Roo.grid.AbstractSelectionModel
34064  * @extends Roo.util.Observable
34065  * Abstract base class for grid SelectionModels.  It provides the interface that should be
34066  * implemented by descendant classes.  This class should not be directly instantiated.
34067  * @constructor
34068  */
34069 Roo.grid.AbstractSelectionModel = function(){
34070     this.locked = false;
34071     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
34072 };
34073
34074 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
34075     /** @ignore Called by the grid automatically. Do not call directly. */
34076     init : function(grid){
34077         this.grid = grid;
34078         this.initEvents();
34079     },
34080
34081     /**
34082      * Locks the selections.
34083      */
34084     lock : function(){
34085         this.locked = true;
34086     },
34087
34088     /**
34089      * Unlocks the selections.
34090      */
34091     unlock : function(){
34092         this.locked = false;
34093     },
34094
34095     /**
34096      * Returns true if the selections are locked.
34097      * @return {Boolean}
34098      */
34099     isLocked : function(){
34100         return this.locked;
34101     }
34102 });/*
34103  * Based on:
34104  * Ext JS Library 1.1.1
34105  * Copyright(c) 2006-2007, Ext JS, LLC.
34106  *
34107  * Originally Released Under LGPL - original licence link has changed is not relivant.
34108  *
34109  * Fork - LGPL
34110  * <script type="text/javascript">
34111  */
34112 /**
34113  * @extends Roo.grid.AbstractSelectionModel
34114  * @class Roo.grid.RowSelectionModel
34115  * The default SelectionModel used by {@link Roo.grid.Grid}.
34116  * It supports multiple selections and keyboard selection/navigation. 
34117  * @constructor
34118  * @param {Object} config
34119  */
34120 Roo.grid.RowSelectionModel = function(config){
34121     Roo.apply(this, config);
34122     this.selections = new Roo.util.MixedCollection(false, function(o){
34123         return o.id;
34124     });
34125
34126     this.last = false;
34127     this.lastActive = false;
34128
34129     this.addEvents({
34130         /**
34131              * @event selectionchange
34132              * Fires when the selection changes
34133              * @param {SelectionModel} this
34134              */
34135             "selectionchange" : true,
34136         /**
34137              * @event afterselectionchange
34138              * Fires after the selection changes (eg. by key press or clicking)
34139              * @param {SelectionModel} this
34140              */
34141             "afterselectionchange" : true,
34142         /**
34143              * @event beforerowselect
34144              * Fires when a row is selected being selected, return false to cancel.
34145              * @param {SelectionModel} this
34146              * @param {Number} rowIndex The selected index
34147              * @param {Boolean} keepExisting False if other selections will be cleared
34148              */
34149             "beforerowselect" : true,
34150         /**
34151              * @event rowselect
34152              * Fires when a row is selected.
34153              * @param {SelectionModel} this
34154              * @param {Number} rowIndex The selected index
34155              * @param {Roo.data.Record} r The record
34156              */
34157             "rowselect" : true,
34158         /**
34159              * @event rowdeselect
34160              * Fires when a row is deselected.
34161              * @param {SelectionModel} this
34162              * @param {Number} rowIndex The selected index
34163              */
34164         "rowdeselect" : true
34165     });
34166     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
34167     this.locked = false;
34168 };
34169
34170 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
34171     /**
34172      * @cfg {Boolean} singleSelect
34173      * True to allow selection of only one row at a time (defaults to false)
34174      */
34175     singleSelect : false,
34176
34177     // private
34178     initEvents : function(){
34179
34180         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
34181             this.grid.on("mousedown", this.handleMouseDown, this);
34182         }else{ // allow click to work like normal
34183             this.grid.on("rowclick", this.handleDragableRowClick, this);
34184         }
34185
34186         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
34187             "up" : function(e){
34188                 if(!e.shiftKey){
34189                     this.selectPrevious(e.shiftKey);
34190                 }else if(this.last !== false && this.lastActive !== false){
34191                     var last = this.last;
34192                     this.selectRange(this.last,  this.lastActive-1);
34193                     this.grid.getView().focusRow(this.lastActive);
34194                     if(last !== false){
34195                         this.last = last;
34196                     }
34197                 }else{
34198                     this.selectFirstRow();
34199                 }
34200                 this.fireEvent("afterselectionchange", this);
34201             },
34202             "down" : function(e){
34203                 if(!e.shiftKey){
34204                     this.selectNext(e.shiftKey);
34205                 }else if(this.last !== false && this.lastActive !== false){
34206                     var last = this.last;
34207                     this.selectRange(this.last,  this.lastActive+1);
34208                     this.grid.getView().focusRow(this.lastActive);
34209                     if(last !== false){
34210                         this.last = last;
34211                     }
34212                 }else{
34213                     this.selectFirstRow();
34214                 }
34215                 this.fireEvent("afterselectionchange", this);
34216             },
34217             scope: this
34218         });
34219
34220         var view = this.grid.view;
34221         view.on("refresh", this.onRefresh, this);
34222         view.on("rowupdated", this.onRowUpdated, this);
34223         view.on("rowremoved", this.onRemove, this);
34224     },
34225
34226     // private
34227     onRefresh : function(){
34228         var ds = this.grid.dataSource, i, v = this.grid.view;
34229         var s = this.selections;
34230         s.each(function(r){
34231             if((i = ds.indexOfId(r.id)) != -1){
34232                 v.onRowSelect(i);
34233             }else{
34234                 s.remove(r);
34235             }
34236         });
34237     },
34238
34239     // private
34240     onRemove : function(v, index, r){
34241         this.selections.remove(r);
34242     },
34243
34244     // private
34245     onRowUpdated : function(v, index, r){
34246         if(this.isSelected(r)){
34247             v.onRowSelect(index);
34248         }
34249     },
34250
34251     /**
34252      * Select records.
34253      * @param {Array} records The records to select
34254      * @param {Boolean} keepExisting (optional) True to keep existing selections
34255      */
34256     selectRecords : function(records, keepExisting){
34257         if(!keepExisting){
34258             this.clearSelections();
34259         }
34260         var ds = this.grid.dataSource;
34261         for(var i = 0, len = records.length; i < len; i++){
34262             this.selectRow(ds.indexOf(records[i]), true);
34263         }
34264     },
34265
34266     /**
34267      * Gets the number of selected rows.
34268      * @return {Number}
34269      */
34270     getCount : function(){
34271         return this.selections.length;
34272     },
34273
34274     /**
34275      * Selects the first row in the grid.
34276      */
34277     selectFirstRow : function(){
34278         this.selectRow(0);
34279     },
34280
34281     /**
34282      * Select the last row.
34283      * @param {Boolean} keepExisting (optional) True to keep existing selections
34284      */
34285     selectLastRow : function(keepExisting){
34286         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
34287     },
34288
34289     /**
34290      * Selects the row immediately following the last selected row.
34291      * @param {Boolean} keepExisting (optional) True to keep existing selections
34292      */
34293     selectNext : function(keepExisting){
34294         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
34295             this.selectRow(this.last+1, keepExisting);
34296             this.grid.getView().focusRow(this.last);
34297         }
34298     },
34299
34300     /**
34301      * Selects the row that precedes the last selected row.
34302      * @param {Boolean} keepExisting (optional) True to keep existing selections
34303      */
34304     selectPrevious : function(keepExisting){
34305         if(this.last){
34306             this.selectRow(this.last-1, keepExisting);
34307             this.grid.getView().focusRow(this.last);
34308         }
34309     },
34310
34311     /**
34312      * Returns the selected records
34313      * @return {Array} Array of selected records
34314      */
34315     getSelections : function(){
34316         return [].concat(this.selections.items);
34317     },
34318
34319     /**
34320      * Returns the first selected record.
34321      * @return {Record}
34322      */
34323     getSelected : function(){
34324         return this.selections.itemAt(0);
34325     },
34326
34327
34328     /**
34329      * Clears all selections.
34330      */
34331     clearSelections : function(fast){
34332         if(this.locked) return;
34333         if(fast !== true){
34334             var ds = this.grid.dataSource;
34335             var s = this.selections;
34336             s.each(function(r){
34337                 this.deselectRow(ds.indexOfId(r.id));
34338             }, this);
34339             s.clear();
34340         }else{
34341             this.selections.clear();
34342         }
34343         this.last = false;
34344     },
34345
34346
34347     /**
34348      * Selects all rows.
34349      */
34350     selectAll : function(){
34351         if(this.locked) return;
34352         this.selections.clear();
34353         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
34354             this.selectRow(i, true);
34355         }
34356     },
34357
34358     /**
34359      * Returns True if there is a selection.
34360      * @return {Boolean}
34361      */
34362     hasSelection : function(){
34363         return this.selections.length > 0;
34364     },
34365
34366     /**
34367      * Returns True if the specified row is selected.
34368      * @param {Number/Record} record The record or index of the record to check
34369      * @return {Boolean}
34370      */
34371     isSelected : function(index){
34372         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
34373         return (r && this.selections.key(r.id) ? true : false);
34374     },
34375
34376     /**
34377      * Returns True if the specified record id is selected.
34378      * @param {String} id The id of record to check
34379      * @return {Boolean}
34380      */
34381     isIdSelected : function(id){
34382         return (this.selections.key(id) ? true : false);
34383     },
34384
34385     // private
34386     handleMouseDown : function(e, t){
34387         var view = this.grid.getView(), rowIndex;
34388         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
34389             return;
34390         };
34391         if(e.shiftKey && this.last !== false){
34392             var last = this.last;
34393             this.selectRange(last, rowIndex, e.ctrlKey);
34394             this.last = last; // reset the last
34395             view.focusRow(rowIndex);
34396         }else{
34397             var isSelected = this.isSelected(rowIndex);
34398             if(e.button !== 0 && isSelected){
34399                 view.focusRow(rowIndex);
34400             }else if(e.ctrlKey && isSelected){
34401                 this.deselectRow(rowIndex);
34402             }else if(!isSelected){
34403                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
34404                 view.focusRow(rowIndex);
34405             }
34406         }
34407         this.fireEvent("afterselectionchange", this);
34408     },
34409     // private
34410     handleDragableRowClick :  function(grid, rowIndex, e) 
34411     {
34412         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
34413             this.selectRow(rowIndex, false);
34414             grid.view.focusRow(rowIndex);
34415              this.fireEvent("afterselectionchange", this);
34416         }
34417     },
34418     
34419     /**
34420      * Selects multiple rows.
34421      * @param {Array} rows Array of the indexes of the row to select
34422      * @param {Boolean} keepExisting (optional) True to keep existing selections
34423      */
34424     selectRows : function(rows, keepExisting){
34425         if(!keepExisting){
34426             this.clearSelections();
34427         }
34428         for(var i = 0, len = rows.length; i < len; i++){
34429             this.selectRow(rows[i], true);
34430         }
34431     },
34432
34433     /**
34434      * Selects a range of rows. All rows in between startRow and endRow are also selected.
34435      * @param {Number} startRow The index of the first row in the range
34436      * @param {Number} endRow The index of the last row in the range
34437      * @param {Boolean} keepExisting (optional) True to retain existing selections
34438      */
34439     selectRange : function(startRow, endRow, keepExisting){
34440         if(this.locked) return;
34441         if(!keepExisting){
34442             this.clearSelections();
34443         }
34444         if(startRow <= endRow){
34445             for(var i = startRow; i <= endRow; i++){
34446                 this.selectRow(i, true);
34447             }
34448         }else{
34449             for(var i = startRow; i >= endRow; i--){
34450                 this.selectRow(i, true);
34451             }
34452         }
34453     },
34454
34455     /**
34456      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
34457      * @param {Number} startRow The index of the first row in the range
34458      * @param {Number} endRow The index of the last row in the range
34459      */
34460     deselectRange : function(startRow, endRow, preventViewNotify){
34461         if(this.locked) return;
34462         for(var i = startRow; i <= endRow; i++){
34463             this.deselectRow(i, preventViewNotify);
34464         }
34465     },
34466
34467     /**
34468      * Selects a row.
34469      * @param {Number} row The index of the row to select
34470      * @param {Boolean} keepExisting (optional) True to keep existing selections
34471      */
34472     selectRow : function(index, keepExisting, preventViewNotify){
34473         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
34474         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
34475             if(!keepExisting || this.singleSelect){
34476                 this.clearSelections();
34477             }
34478             var r = this.grid.dataSource.getAt(index);
34479             this.selections.add(r);
34480             this.last = this.lastActive = index;
34481             if(!preventViewNotify){
34482                 this.grid.getView().onRowSelect(index);
34483             }
34484             this.fireEvent("rowselect", this, index, r);
34485             this.fireEvent("selectionchange", this);
34486         }
34487     },
34488
34489     /**
34490      * Deselects a row.
34491      * @param {Number} row The index of the row to deselect
34492      */
34493     deselectRow : function(index, preventViewNotify){
34494         if(this.locked) return;
34495         if(this.last == index){
34496             this.last = false;
34497         }
34498         if(this.lastActive == index){
34499             this.lastActive = false;
34500         }
34501         var r = this.grid.dataSource.getAt(index);
34502         this.selections.remove(r);
34503         if(!preventViewNotify){
34504             this.grid.getView().onRowDeselect(index);
34505         }
34506         this.fireEvent("rowdeselect", this, index);
34507         this.fireEvent("selectionchange", this);
34508     },
34509
34510     // private
34511     restoreLast : function(){
34512         if(this._last){
34513             this.last = this._last;
34514         }
34515     },
34516
34517     // private
34518     acceptsNav : function(row, col, cm){
34519         return !cm.isHidden(col) && cm.isCellEditable(col, row);
34520     },
34521
34522     // private
34523     onEditorKey : function(field, e){
34524         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
34525         if(k == e.TAB){
34526             e.stopEvent();
34527             ed.completeEdit();
34528             if(e.shiftKey){
34529                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
34530             }else{
34531                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
34532             }
34533         }else if(k == e.ENTER && !e.ctrlKey){
34534             e.stopEvent();
34535             ed.completeEdit();
34536             if(e.shiftKey){
34537                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
34538             }else{
34539                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
34540             }
34541         }else if(k == e.ESC){
34542             ed.cancelEdit();
34543         }
34544         if(newCell){
34545             g.startEditing(newCell[0], newCell[1]);
34546         }
34547     }
34548 });/*
34549  * Based on:
34550  * Ext JS Library 1.1.1
34551  * Copyright(c) 2006-2007, Ext JS, LLC.
34552  *
34553  * Originally Released Under LGPL - original licence link has changed is not relivant.
34554  *
34555  * Fork - LGPL
34556  * <script type="text/javascript">
34557  */
34558 /**
34559  * @class Roo.grid.CellSelectionModel
34560  * @extends Roo.grid.AbstractSelectionModel
34561  * This class provides the basic implementation for cell selection in a grid.
34562  * @constructor
34563  * @param {Object} config The object containing the configuration of this model.
34564  */
34565 Roo.grid.CellSelectionModel = function(config){
34566     Roo.apply(this, config);
34567
34568     this.selection = null;
34569
34570     this.addEvents({
34571         /**
34572              * @event beforerowselect
34573              * Fires before a cell is selected.
34574              * @param {SelectionModel} this
34575              * @param {Number} rowIndex The selected row index
34576              * @param {Number} colIndex The selected cell index
34577              */
34578             "beforecellselect" : true,
34579         /**
34580              * @event cellselect
34581              * Fires when a cell is selected.
34582              * @param {SelectionModel} this
34583              * @param {Number} rowIndex The selected row index
34584              * @param {Number} colIndex The selected cell index
34585              */
34586             "cellselect" : true,
34587         /**
34588              * @event selectionchange
34589              * Fires when the active selection changes.
34590              * @param {SelectionModel} this
34591              * @param {Object} selection null for no selection or an object (o) with two properties
34592                 <ul>
34593                 <li>o.record: the record object for the row the selection is in</li>
34594                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
34595                 </ul>
34596              */
34597             "selectionchange" : true
34598     });
34599     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
34600 };
34601
34602 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
34603
34604     /** @ignore */
34605     initEvents : function(){
34606         this.grid.on("mousedown", this.handleMouseDown, this);
34607         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
34608         var view = this.grid.view;
34609         view.on("refresh", this.onViewChange, this);
34610         view.on("rowupdated", this.onRowUpdated, this);
34611         view.on("beforerowremoved", this.clearSelections, this);
34612         view.on("beforerowsinserted", this.clearSelections, this);
34613         if(this.grid.isEditor){
34614             this.grid.on("beforeedit", this.beforeEdit,  this);
34615         }
34616     },
34617
34618         //private
34619     beforeEdit : function(e){
34620         this.select(e.row, e.column, false, true, e.record);
34621     },
34622
34623         //private
34624     onRowUpdated : function(v, index, r){
34625         if(this.selection && this.selection.record == r){
34626             v.onCellSelect(index, this.selection.cell[1]);
34627         }
34628     },
34629
34630         //private
34631     onViewChange : function(){
34632         this.clearSelections(true);
34633     },
34634
34635         /**
34636          * Returns the currently selected cell,.
34637          * @return {Array} The selected cell (row, column) or null if none selected.
34638          */
34639     getSelectedCell : function(){
34640         return this.selection ? this.selection.cell : null;
34641     },
34642
34643     /**
34644      * Clears all selections.
34645      * @param {Boolean} true to prevent the gridview from being notified about the change.
34646      */
34647     clearSelections : function(preventNotify){
34648         var s = this.selection;
34649         if(s){
34650             if(preventNotify !== true){
34651                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
34652             }
34653             this.selection = null;
34654             this.fireEvent("selectionchange", this, null);
34655         }
34656     },
34657
34658     /**
34659      * Returns true if there is a selection.
34660      * @return {Boolean}
34661      */
34662     hasSelection : function(){
34663         return this.selection ? true : false;
34664     },
34665
34666     /** @ignore */
34667     handleMouseDown : function(e, t){
34668         var v = this.grid.getView();
34669         if(this.isLocked()){
34670             return;
34671         };
34672         var row = v.findRowIndex(t);
34673         var cell = v.findCellIndex(t);
34674         if(row !== false && cell !== false){
34675             this.select(row, cell);
34676         }
34677     },
34678
34679     /**
34680      * Selects a cell.
34681      * @param {Number} rowIndex
34682      * @param {Number} collIndex
34683      */
34684     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
34685         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
34686             this.clearSelections();
34687             r = r || this.grid.dataSource.getAt(rowIndex);
34688             this.selection = {
34689                 record : r,
34690                 cell : [rowIndex, colIndex]
34691             };
34692             if(!preventViewNotify){
34693                 var v = this.grid.getView();
34694                 v.onCellSelect(rowIndex, colIndex);
34695                 if(preventFocus !== true){
34696                     v.focusCell(rowIndex, colIndex);
34697                 }
34698             }
34699             this.fireEvent("cellselect", this, rowIndex, colIndex);
34700             this.fireEvent("selectionchange", this, this.selection);
34701         }
34702     },
34703
34704         //private
34705     isSelectable : function(rowIndex, colIndex, cm){
34706         return !cm.isHidden(colIndex);
34707     },
34708
34709     /** @ignore */
34710     handleKeyDown : function(e){
34711         if(!e.isNavKeyPress()){
34712             return;
34713         }
34714         var g = this.grid, s = this.selection;
34715         if(!s){
34716             e.stopEvent();
34717             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
34718             if(cell){
34719                 this.select(cell[0], cell[1]);
34720             }
34721             return;
34722         }
34723         var sm = this;
34724         var walk = function(row, col, step){
34725             return g.walkCells(row, col, step, sm.isSelectable,  sm);
34726         };
34727         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
34728         var newCell;
34729
34730         switch(k){
34731              case e.TAB:
34732                  if(e.shiftKey){
34733                      newCell = walk(r, c-1, -1);
34734                  }else{
34735                      newCell = walk(r, c+1, 1);
34736                  }
34737              break;
34738              case e.DOWN:
34739                  newCell = walk(r+1, c, 1);
34740              break;
34741              case e.UP:
34742                  newCell = walk(r-1, c, -1);
34743              break;
34744              case e.RIGHT:
34745                  newCell = walk(r, c+1, 1);
34746              break;
34747              case e.LEFT:
34748                  newCell = walk(r, c-1, -1);
34749              break;
34750              case e.ENTER:
34751                  if(g.isEditor && !g.editing){
34752                     g.startEditing(r, c);
34753                     e.stopEvent();
34754                     return;
34755                 }
34756              break;
34757         };
34758         if(newCell){
34759             this.select(newCell[0], newCell[1]);
34760             e.stopEvent();
34761         }
34762     },
34763
34764     acceptsNav : function(row, col, cm){
34765         return !cm.isHidden(col) && cm.isCellEditable(col, row);
34766     },
34767
34768     onEditorKey : function(field, e){
34769         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
34770         if(k == e.TAB){
34771             if(e.shiftKey){
34772                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
34773             }else{
34774                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
34775             }
34776             e.stopEvent();
34777         }else if(k == e.ENTER && !e.ctrlKey){
34778             ed.completeEdit();
34779             e.stopEvent();
34780         }else if(k == e.ESC){
34781             ed.cancelEdit();
34782         }
34783         if(newCell){
34784             g.startEditing(newCell[0], newCell[1]);
34785         }
34786     }
34787 });/*
34788  * Based on:
34789  * Ext JS Library 1.1.1
34790  * Copyright(c) 2006-2007, Ext JS, LLC.
34791  *
34792  * Originally Released Under LGPL - original licence link has changed is not relivant.
34793  *
34794  * Fork - LGPL
34795  * <script type="text/javascript">
34796  */
34797  
34798 /**
34799  * @class Roo.grid.EditorGrid
34800  * @extends Roo.grid.Grid
34801  * Class for creating and editable grid.
34802  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
34803  * The container MUST have some type of size defined for the grid to fill. The container will be 
34804  * automatically set to position relative if it isn't already.
34805  * @param {Object} dataSource The data model to bind to
34806  * @param {Object} colModel The column model with info about this grid's columns
34807  */
34808 Roo.grid.EditorGrid = function(container, config){
34809     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
34810     this.getGridEl().addClass("xedit-grid");
34811
34812     if(!this.selModel){
34813         this.selModel = new Roo.grid.CellSelectionModel();
34814     }
34815
34816     this.activeEditor = null;
34817
34818         this.addEvents({
34819             /**
34820              * @event beforeedit
34821              * Fires before cell editing is triggered. The edit event object has the following properties <br />
34822              * <ul style="padding:5px;padding-left:16px;">
34823              * <li>grid - This grid</li>
34824              * <li>record - The record being edited</li>
34825              * <li>field - The field name being edited</li>
34826              * <li>value - The value for the field being edited.</li>
34827              * <li>row - The grid row index</li>
34828              * <li>column - The grid column index</li>
34829              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
34830              * </ul>
34831              * @param {Object} e An edit event (see above for description)
34832              */
34833             "beforeedit" : true,
34834             /**
34835              * @event afteredit
34836              * Fires after a cell is edited. <br />
34837              * <ul style="padding:5px;padding-left:16px;">
34838              * <li>grid - This grid</li>
34839              * <li>record - The record being edited</li>
34840              * <li>field - The field name being edited</li>
34841              * <li>value - The value being set</li>
34842              * <li>originalValue - The original value for the field, before the edit.</li>
34843              * <li>row - The grid row index</li>
34844              * <li>column - The grid column index</li>
34845              * </ul>
34846              * @param {Object} e An edit event (see above for description)
34847              */
34848             "afteredit" : true,
34849             /**
34850              * @event validateedit
34851              * Fires after a cell is edited, but before the value is set in the record. 
34852          * You can use this to modify the value being set in the field, Return false
34853              * to cancel the change. The edit event object has the following properties <br />
34854              * <ul style="padding:5px;padding-left:16px;">
34855          * <li>editor - This editor</li>
34856              * <li>grid - This grid</li>
34857              * <li>record - The record being edited</li>
34858              * <li>field - The field name being edited</li>
34859              * <li>value - The value being set</li>
34860              * <li>originalValue - The original value for the field, before the edit.</li>
34861              * <li>row - The grid row index</li>
34862              * <li>column - The grid column index</li>
34863              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
34864              * </ul>
34865              * @param {Object} e An edit event (see above for description)
34866              */
34867             "validateedit" : true
34868         });
34869     this.on("bodyscroll", this.stopEditing,  this);
34870     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
34871 };
34872
34873 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
34874     /**
34875      * @cfg {Number} clicksToEdit
34876      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
34877      */
34878     clicksToEdit: 2,
34879
34880     // private
34881     isEditor : true,
34882     // private
34883     trackMouseOver: false, // causes very odd FF errors
34884
34885     onCellDblClick : function(g, row, col){
34886         this.startEditing(row, col);
34887     },
34888
34889     onEditComplete : function(ed, value, startValue){
34890         this.editing = false;
34891         this.activeEditor = null;
34892         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
34893         var r = ed.record;
34894         var field = this.colModel.getDataIndex(ed.col);
34895         var e = {
34896             grid: this,
34897             record: r,
34898             field: field,
34899             originalValue: startValue,
34900             value: value,
34901             row: ed.row,
34902             column: ed.col,
34903             cancel:false,
34904             editor: ed
34905         };
34906         if(String(value) !== String(startValue)){
34907             
34908             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
34909                 r.set(field, e.value);
34910                 delete e.cancel; //?? why!!!
34911                 this.fireEvent("afteredit", e);
34912             }
34913         } else {
34914             this.fireEvent("afteredit", e); // always fir it!
34915         }
34916         this.view.focusCell(ed.row, ed.col);
34917     },
34918
34919     /**
34920      * Starts editing the specified for the specified row/column
34921      * @param {Number} rowIndex
34922      * @param {Number} colIndex
34923      */
34924     startEditing : function(row, col){
34925         this.stopEditing();
34926         if(this.colModel.isCellEditable(col, row)){
34927             this.view.ensureVisible(row, col, true);
34928             var r = this.dataSource.getAt(row);
34929             var field = this.colModel.getDataIndex(col);
34930             var e = {
34931                 grid: this,
34932                 record: r,
34933                 field: field,
34934                 value: r.data[field],
34935                 row: row,
34936                 column: col,
34937                 cancel:false
34938             };
34939             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
34940                 this.editing = true;
34941                 var ed = this.colModel.getCellEditor(col, row);
34942                 
34943                 if (!ed) {
34944                     return;
34945                 }
34946                 if(!ed.rendered){
34947                     ed.render(ed.parentEl || document.body);
34948                 }
34949                 ed.field.reset();
34950                 (function(){ // complex but required for focus issues in safari, ie and opera
34951                     ed.row = row;
34952                     ed.col = col;
34953                     ed.record = r;
34954                     ed.on("complete", this.onEditComplete, this, {single: true});
34955                     ed.on("specialkey", this.selModel.onEditorKey, this.selModel);
34956                     this.activeEditor = ed;
34957                     var v = r.data[field];
34958                     ed.startEdit(this.view.getCell(row, col), v);
34959                 }).defer(50, this);
34960             }
34961         }
34962     },
34963         
34964     /**
34965      * Stops any active editing
34966      */
34967     stopEditing : function(){
34968         if(this.activeEditor){
34969             this.activeEditor.completeEdit();
34970         }
34971         this.activeEditor = null;
34972     }
34973 });/*
34974  * Based on:
34975  * Ext JS Library 1.1.1
34976  * Copyright(c) 2006-2007, Ext JS, LLC.
34977  *
34978  * Originally Released Under LGPL - original licence link has changed is not relivant.
34979  *
34980  * Fork - LGPL
34981  * <script type="text/javascript">
34982  */
34983
34984 // private - not really -- you end up using it !
34985 // This is a support class used internally by the Grid components
34986
34987 /**
34988  * @class Roo.grid.GridEditor
34989  * @extends Roo.Editor
34990  * Class for creating and editable grid elements.
34991  * @param {Object} config any settings (must include field)
34992  */
34993 Roo.grid.GridEditor = function(field, config){
34994     if (!config && field.field) {
34995         config = field;
34996         field = Roo.factory(config.field, Roo.form);
34997     }
34998     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
34999     field.monitorTab = false;
35000 };
35001
35002 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
35003     
35004     /**
35005      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
35006      */
35007     
35008     alignment: "tl-tl",
35009     autoSize: "width",
35010     hideEl : false,
35011     cls: "x-small-editor x-grid-editor",
35012     shim:false,
35013     shadow:"frame"
35014 });/*
35015  * Based on:
35016  * Ext JS Library 1.1.1
35017  * Copyright(c) 2006-2007, Ext JS, LLC.
35018  *
35019  * Originally Released Under LGPL - original licence link has changed is not relivant.
35020  *
35021  * Fork - LGPL
35022  * <script type="text/javascript">
35023  */
35024   
35025
35026   
35027 Roo.grid.PropertyRecord = Roo.data.Record.create([
35028     {name:'name',type:'string'},  'value'
35029 ]);
35030
35031
35032 Roo.grid.PropertyStore = function(grid, source){
35033     this.grid = grid;
35034     this.store = new Roo.data.Store({
35035         recordType : Roo.grid.PropertyRecord
35036     });
35037     this.store.on('update', this.onUpdate,  this);
35038     if(source){
35039         this.setSource(source);
35040     }
35041     Roo.grid.PropertyStore.superclass.constructor.call(this);
35042 };
35043
35044
35045
35046 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
35047     setSource : function(o){
35048         this.source = o;
35049         this.store.removeAll();
35050         var data = [];
35051         for(var k in o){
35052             if(this.isEditableValue(o[k])){
35053                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
35054             }
35055         }
35056         this.store.loadRecords({records: data}, {}, true);
35057     },
35058
35059     onUpdate : function(ds, record, type){
35060         if(type == Roo.data.Record.EDIT){
35061             var v = record.data['value'];
35062             var oldValue = record.modified['value'];
35063             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
35064                 this.source[record.id] = v;
35065                 record.commit();
35066                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
35067             }else{
35068                 record.reject();
35069             }
35070         }
35071     },
35072
35073     getProperty : function(row){
35074        return this.store.getAt(row);
35075     },
35076
35077     isEditableValue: function(val){
35078         if(val && val instanceof Date){
35079             return true;
35080         }else if(typeof val == 'object' || typeof val == 'function'){
35081             return false;
35082         }
35083         return true;
35084     },
35085
35086     setValue : function(prop, value){
35087         this.source[prop] = value;
35088         this.store.getById(prop).set('value', value);
35089     },
35090
35091     getSource : function(){
35092         return this.source;
35093     }
35094 });
35095
35096 Roo.grid.PropertyColumnModel = function(grid, store){
35097     this.grid = grid;
35098     var g = Roo.grid;
35099     g.PropertyColumnModel.superclass.constructor.call(this, [
35100         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
35101         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
35102     ]);
35103     this.store = store;
35104     this.bselect = Roo.DomHelper.append(document.body, {
35105         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
35106             {tag: 'option', value: 'true', html: 'true'},
35107             {tag: 'option', value: 'false', html: 'false'}
35108         ]
35109     });
35110     Roo.id(this.bselect);
35111     var f = Roo.form;
35112     this.editors = {
35113         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
35114         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
35115         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
35116         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
35117         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
35118     };
35119     this.renderCellDelegate = this.renderCell.createDelegate(this);
35120     this.renderPropDelegate = this.renderProp.createDelegate(this);
35121 };
35122
35123 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
35124     
35125     
35126     nameText : 'Name',
35127     valueText : 'Value',
35128     
35129     dateFormat : 'm/j/Y',
35130     
35131     
35132     renderDate : function(dateVal){
35133         return dateVal.dateFormat(this.dateFormat);
35134     },
35135
35136     renderBool : function(bVal){
35137         return bVal ? 'true' : 'false';
35138     },
35139
35140     isCellEditable : function(colIndex, rowIndex){
35141         return colIndex == 1;
35142     },
35143
35144     getRenderer : function(col){
35145         return col == 1 ?
35146             this.renderCellDelegate : this.renderPropDelegate;
35147     },
35148
35149     renderProp : function(v){
35150         return this.getPropertyName(v);
35151     },
35152
35153     renderCell : function(val){
35154         var rv = val;
35155         if(val instanceof Date){
35156             rv = this.renderDate(val);
35157         }else if(typeof val == 'boolean'){
35158             rv = this.renderBool(val);
35159         }
35160         return Roo.util.Format.htmlEncode(rv);
35161     },
35162
35163     getPropertyName : function(name){
35164         var pn = this.grid.propertyNames;
35165         return pn && pn[name] ? pn[name] : name;
35166     },
35167
35168     getCellEditor : function(colIndex, rowIndex){
35169         var p = this.store.getProperty(rowIndex);
35170         var n = p.data['name'], val = p.data['value'];
35171         
35172         if(typeof(this.grid.customEditors[n]) == 'string'){
35173             return this.editors[this.grid.customEditors[n]];
35174         }
35175         if(typeof(this.grid.customEditors[n]) != 'undefined'){
35176             return this.grid.customEditors[n];
35177         }
35178         if(val instanceof Date){
35179             return this.editors['date'];
35180         }else if(typeof val == 'number'){
35181             return this.editors['number'];
35182         }else if(typeof val == 'boolean'){
35183             return this.editors['boolean'];
35184         }else{
35185             return this.editors['string'];
35186         }
35187     }
35188 });
35189
35190 /**
35191  * @class Roo.grid.PropertyGrid
35192  * @extends Roo.grid.EditorGrid
35193  * This class represents the  interface of a component based property grid control.
35194  * <br><br>Usage:<pre><code>
35195  var grid = new Roo.grid.PropertyGrid("my-container-id", {
35196       
35197  });
35198  // set any options
35199  grid.render();
35200  * </code></pre>
35201   
35202  * @constructor
35203  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
35204  * The container MUST have some type of size defined for the grid to fill. The container will be
35205  * automatically set to position relative if it isn't already.
35206  * @param {Object} config A config object that sets properties on this grid.
35207  */
35208 Roo.grid.PropertyGrid = function(container, config){
35209     config = config || {};
35210     var store = new Roo.grid.PropertyStore(this);
35211     this.store = store;
35212     var cm = new Roo.grid.PropertyColumnModel(this, store);
35213     store.store.sort('name', 'ASC');
35214     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
35215         ds: store.store,
35216         cm: cm,
35217         enableColLock:false,
35218         enableColumnMove:false,
35219         stripeRows:false,
35220         trackMouseOver: false,
35221         clicksToEdit:1
35222     }, config));
35223     this.getGridEl().addClass('x-props-grid');
35224     this.lastEditRow = null;
35225     this.on('columnresize', this.onColumnResize, this);
35226     this.addEvents({
35227          /**
35228              * @event beforepropertychange
35229              * Fires before a property changes (return false to stop?)
35230              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
35231              * @param {String} id Record Id
35232              * @param {String} newval New Value
35233          * @param {String} oldval Old Value
35234              */
35235         "beforepropertychange": true,
35236         /**
35237              * @event propertychange
35238              * Fires after a property changes
35239              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
35240              * @param {String} id Record Id
35241              * @param {String} newval New Value
35242          * @param {String} oldval Old Value
35243              */
35244         "propertychange": true
35245     });
35246     this.customEditors = this.customEditors || {};
35247 };
35248 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
35249     
35250      /**
35251      * @cfg {Object} customEditors map of colnames=> custom editors.
35252      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
35253      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
35254      * false disables editing of the field.
35255          */
35256     
35257       /**
35258      * @cfg {Object} propertyNames map of property Names to their displayed value
35259          */
35260     
35261     render : function(){
35262         Roo.grid.PropertyGrid.superclass.render.call(this);
35263         this.autoSize.defer(100, this);
35264     },
35265
35266     autoSize : function(){
35267         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
35268         if(this.view){
35269             this.view.fitColumns();
35270         }
35271     },
35272
35273     onColumnResize : function(){
35274         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
35275         this.autoSize();
35276     },
35277     /**
35278      * Sets the data for the Grid
35279      * accepts a Key => Value object of all the elements avaiable.
35280      * @param {Object} data  to appear in grid.
35281      */
35282     setSource : function(source){
35283         this.store.setSource(source);
35284         //this.autoSize();
35285     },
35286     /**
35287      * Gets all the data from the grid.
35288      * @return {Object} data  data stored in grid
35289      */
35290     getSource : function(){
35291         return this.store.getSource();
35292     }
35293 });/*
35294  * Based on:
35295  * Ext JS Library 1.1.1
35296  * Copyright(c) 2006-2007, Ext JS, LLC.
35297  *
35298  * Originally Released Under LGPL - original licence link has changed is not relivant.
35299  *
35300  * Fork - LGPL
35301  * <script type="text/javascript">
35302  */
35303  
35304 /**
35305  * @class Roo.LoadMask
35306  * A simple utility class for generically masking elements while loading data.  If the element being masked has
35307  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
35308  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
35309  * element's UpdateManager load indicator and will be destroyed after the initial load.
35310  * @constructor
35311  * Create a new LoadMask
35312  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
35313  * @param {Object} config The config object
35314  */
35315 Roo.LoadMask = function(el, config){
35316     this.el = Roo.get(el);
35317     Roo.apply(this, config);
35318     if(this.store){
35319         this.store.on('beforeload', this.onBeforeLoad, this);
35320         this.store.on('load', this.onLoad, this);
35321         this.store.on('loadexception', this.onLoad, this);
35322         this.removeMask = false;
35323     }else{
35324         var um = this.el.getUpdateManager();
35325         um.showLoadIndicator = false; // disable the default indicator
35326         um.on('beforeupdate', this.onBeforeLoad, this);
35327         um.on('update', this.onLoad, this);
35328         um.on('failure', this.onLoad, this);
35329         this.removeMask = true;
35330     }
35331 };
35332
35333 Roo.LoadMask.prototype = {
35334     /**
35335      * @cfg {Boolean} removeMask
35336      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
35337      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
35338      */
35339     /**
35340      * @cfg {String} msg
35341      * The text to display in a centered loading message box (defaults to 'Loading...')
35342      */
35343     msg : 'Loading...',
35344     /**
35345      * @cfg {String} msgCls
35346      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
35347      */
35348     msgCls : 'x-mask-loading',
35349
35350     /**
35351      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
35352      * @type Boolean
35353      */
35354     disabled: false,
35355
35356     /**
35357      * Disables the mask to prevent it from being displayed
35358      */
35359     disable : function(){
35360        this.disabled = true;
35361     },
35362
35363     /**
35364      * Enables the mask so that it can be displayed
35365      */
35366     enable : function(){
35367         this.disabled = false;
35368     },
35369
35370     // private
35371     onLoad : function(){
35372         this.el.unmask(this.removeMask);
35373     },
35374
35375     // private
35376     onBeforeLoad : function(){
35377         if(!this.disabled){
35378             this.el.mask(this.msg, this.msgCls);
35379         }
35380     },
35381
35382     // private
35383     destroy : function(){
35384         if(this.store){
35385             this.store.un('beforeload', this.onBeforeLoad, this);
35386             this.store.un('load', this.onLoad, this);
35387             this.store.un('loadexception', this.onLoad, this);
35388         }else{
35389             var um = this.el.getUpdateManager();
35390             um.un('beforeupdate', this.onBeforeLoad, this);
35391             um.un('update', this.onLoad, this);
35392             um.un('failure', this.onLoad, this);
35393         }
35394     }
35395 };/*
35396  * Based on:
35397  * Ext JS Library 1.1.1
35398  * Copyright(c) 2006-2007, Ext JS, LLC.
35399  *
35400  * Originally Released Under LGPL - original licence link has changed is not relivant.
35401  *
35402  * Fork - LGPL
35403  * <script type="text/javascript">
35404  */
35405 Roo.XTemplate = function(){
35406     Roo.XTemplate.superclass.constructor.apply(this, arguments);
35407     var s = this.html;
35408
35409     s = ['<tpl>', s, '</tpl>'].join('');
35410
35411     var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/;
35412
35413     var nameRe = /^<tpl\b[^>]*?for="(.*?)"/;
35414     var ifRe = /^<tpl\b[^>]*?if="(.*?)"/;
35415     var execRe = /^<tpl\b[^>]*?exec="(.*?)"/;
35416     var m, id = 0;
35417     var tpls = [];
35418
35419     while(m = s.match(re)){
35420        var m2 = m[0].match(nameRe);
35421        var m3 = m[0].match(ifRe);
35422        var m4 = m[0].match(execRe);
35423        var exp = null, fn = null, exec = null;
35424        var name = m2 && m2[1] ? m2[1] : '';
35425        if(m3){
35426            exp = m3 && m3[1] ? m3[1] : null;
35427            if(exp){
35428                fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
35429            }
35430        }
35431        if(m4){
35432            exp = m4 && m4[1] ? m4[1] : null;
35433            if(exp){
35434                exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
35435            }
35436        }
35437        if(name){
35438            switch(name){
35439                case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
35440                case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
35441                default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
35442            }
35443        }
35444        tpls.push({
35445             id: id,
35446             target: name,
35447             exec: exec,
35448             test: fn,
35449             body: m[1]||''
35450         });
35451        s = s.replace(m[0], '{xtpl'+ id + '}');
35452        ++id;
35453     }
35454     for(var i = tpls.length-1; i >= 0; --i){
35455         this.compileTpl(tpls[i]);
35456     }
35457     this.master = tpls[tpls.length-1];
35458     this.tpls = tpls;
35459 };
35460 Roo.extend(Roo.XTemplate, Roo.Template, {
35461
35462     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
35463
35464     applySubTemplate : function(id, values, parent){
35465         var t = this.tpls[id];
35466         if(t.test && !t.test.call(this, values, parent)){
35467             return '';
35468         }
35469         if(t.exec && t.exec.call(this, values, parent)){
35470             return '';
35471         }
35472         var vs = t.target ? t.target.call(this, values, parent) : values;
35473         parent = t.target ? values : parent;
35474         if(t.target && vs instanceof Array){
35475             var buf = [];
35476             for(var i = 0, len = vs.length; i < len; i++){
35477                 buf[buf.length] = t.compiled.call(this, vs[i], parent);
35478             }
35479             return buf.join('');
35480         }
35481         return t.compiled.call(this, vs, parent);
35482     },
35483
35484     compileTpl : function(tpl){
35485         var fm = Roo.util.Format;
35486         var useF = this.disableFormats !== true;
35487         var sep = Roo.isGecko ? "+" : ",";
35488         var fn = function(m, name, format, args){
35489             if(name.substr(0, 4) == 'xtpl'){
35490                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
35491             }
35492             var v;
35493             if(name.indexOf('.') != -1){
35494                 v = name;
35495             }else{
35496                 v = "values['" + name + "']";
35497             }
35498             if(format && useF){
35499                 args = args ? ',' + args : "";
35500                 if(format.substr(0, 5) != "this."){
35501                     format = "fm." + format + '(';
35502                 }else{
35503                     format = 'this.call("'+ format.substr(5) + '", ';
35504                     args = ", values";
35505                 }
35506             }else{
35507                 args= ''; format = "("+v+" === undefined ? '' : ";
35508             }
35509             return "'"+ sep + format + v + args + ")"+sep+"'";
35510         };
35511         var body;
35512         // branched to use + in gecko and [].join() in others
35513         if(Roo.isGecko){
35514             body = "tpl.compiled = function(values, parent){ return '" +
35515                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
35516                     "';};";
35517         }else{
35518             body = ["tpl.compiled = function(values, parent){ return ['"];
35519             body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
35520             body.push("'].join('');};");
35521             body = body.join('');
35522         }
35523         /** eval:var:zzzzzzz */
35524         eval(body);
35525         return this;
35526     },
35527
35528     applyTemplate : function(values){
35529         return this.master.compiled.call(this, values, {});
35530         var s = this.subs;
35531     },
35532
35533     apply : function(){
35534         return this.applyTemplate.apply(this, arguments);
35535     },
35536
35537     compile : function(){return this;}
35538 });
35539
35540 Roo.XTemplate.from = function(el){
35541     el = Roo.getDom(el);
35542     return new Roo.XTemplate(el.value || el.innerHTML);
35543 };/*
35544  * Original code for Roojs - LGPL
35545  * <script type="text/javascript">
35546  */
35547  
35548 /**
35549  * @class Roo.XComponent
35550  * A delayed Element creator...
35551  * 
35552  * Mypart.xyx = new Roo.XComponent({
35553
35554     parent : 'Mypart.xyz', // empty == document.element.!!
35555     order : '001',
35556     name : 'xxxx'
35557     region : 'xxxx'
35558     disabled : function() {} 
35559      
35560     tree : function() { // return an tree of xtype declared components
35561         var MODULE = this;
35562         return 
35563         {
35564             xtype : 'NestedLayoutPanel',
35565             // technicall
35566         }
35567      ]
35568  *})
35569  * @extends Roo.util.Observable
35570  * @constructor
35571  * @param cfg {Object} configuration of component
35572  * 
35573  */
35574 Roo.XComponent = function(cfg) {
35575     Roo.apply(this, cfg);
35576     this.addEvents({ 
35577         /**
35578              * @event built
35579              * Fires when this the componnt is built
35580              * @param {Roo.XComponent} c the component
35581              */
35582         'built' : true,
35583         /**
35584              * @event buildcomplete
35585              * Fires on the top level element when all elements have been built
35586              * @param {Roo.XComponent} c the top level component.
35587          */
35588         'buildcomplete' : true,
35589         
35590     });
35591     
35592     Roo.XComponent.register(this);
35593     this.modules = false;
35594     this.el = false; // where the layout goes..
35595     
35596     
35597 }
35598 Roo.extend(Roo.XComponent, Roo.util.Observable, {
35599     /**
35600      * @property el
35601      * The created element (with Roo.factory())
35602      * @type {Roo.Layout}
35603      */
35604     el  : false,
35605     
35606     /**
35607      * @property el
35608      * for BC  - use el in new code
35609      * @type {Roo.Layout}
35610      */
35611     panel : false,
35612     
35613     /**
35614      * @property layout
35615      * for BC  - use el in new code
35616      * @type {Roo.Layout}
35617      */
35618     layout : false,
35619     
35620      /**
35621      * @cfg {Function|boolean} disabled
35622      * If this module is disabled by some rule, return true from the funtion
35623      */
35624     disabled : false,
35625     
35626     /**
35627      * @cfg {String} parent 
35628      * Name of parent element which it get xtype added to..
35629      */
35630     parent: false,
35631     
35632     /**
35633      * @cfg {String} order
35634      * Used to set the order in which elements are created (usefull for multiple tabs)
35635      */
35636     
35637     order : false,
35638     /**
35639      * @cfg {String} name
35640      * String to display while loading.
35641      */
35642     name : false,
35643     /**
35644      * @cfg {Array} items
35645      * A single item array - the first element is the root of the tree..
35646      * It's done this way to stay compatible with the Xtype system...
35647      */
35648     items : false,
35649      
35650      
35651     
35652 });
35653
35654 Roo.apply(Roo.XComponent, {
35655     
35656     /**
35657      * @property  buildCompleted
35658      * True when the builder has completed building the interface.
35659      * @type Boolean
35660      */
35661     buildCompleted : false,
35662      
35663     /**
35664      * @property  topModule
35665      * the upper most module - uses document.element as it's constructor.
35666      * @type Object
35667      */
35668      
35669     topModule  : false,
35670       
35671     /**
35672      * @property  modules
35673      * array of modules to be created by registration system.
35674      * @type Roo.XComponent
35675      */
35676     
35677     modules : [],
35678       
35679     
35680     /**
35681      * Register components to be built later.
35682      *
35683      * This solves the following issues
35684      * - Building is not done on page load, but after an authentication process has occured.
35685      * - Interface elements are registered on page load
35686      * - Parent Interface elements may not be loaded before child, so this handles that..
35687      * 
35688      *
35689      * example:
35690      * 
35691      * MyApp.register({
35692           order : '000001',
35693           module : 'Pman.Tab.projectMgr',
35694           region : 'center',
35695           parent : 'Pman.layout',
35696           disabled : false,  // or use a function..
35697         })
35698      
35699      * * @param {Object} details about module
35700      */
35701     register : function(obj) {
35702         this.modules.push(obj);
35703          
35704     },
35705     /**
35706      * convert a string to an object..
35707      * 
35708      */
35709     
35710     toObject : function(str)
35711     {
35712         if (!str || typeof(str) == 'object') {
35713             return str;
35714         }
35715         var ar = str.split('.');
35716         var rt, o;
35717         rt = ar.shift();
35718             /** eval:var:o */
35719         eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
35720         if (o === false) {
35721             throw "Module not found : " + str;
35722         }
35723         Roo.each(ar, function(e) {
35724             if (typeof(o[e]) == 'undefined') {
35725                 throw "Module not found : " + str;
35726             }
35727             o = o[e];
35728         });
35729         return o;
35730         
35731     },
35732     
35733     
35734     /**
35735      * move modules into their correct place in the tree..
35736      * 
35737      */
35738     preBuild : function ()
35739     {
35740         
35741         Roo.each(this.modules , function (obj)
35742         {
35743             obj.parent = this.toObject(obj.parent);
35744             
35745             if (!obj.parent) {
35746                 this.topModule = obj;
35747                 return;
35748             }
35749             
35750             if (!obj.parent.modules) {
35751                 obj.parent.modules = new Roo.util.MixedCollection(false, 
35752                     function(o) { return o.order + '' }
35753                 );
35754             }
35755             
35756             obj.parent.modules.add(obj);
35757         }, this);
35758     },
35759     
35760      /**
35761      * make a list of modules to build.
35762      * @return {Array} list of modules. 
35763      */ 
35764     
35765     buildOrder : function()
35766     {
35767         var _this = this;
35768         var cmp = function(a,b) {   
35769             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
35770         };
35771         
35772         if (!this.topModule || !this.topModule.modules) {
35773             throw "No top level modules to build";
35774         }
35775        
35776         // make a flat list in order of modules to build.
35777         var mods = [ this.topModule ];
35778         
35779         
35780         // add modules to their parents..
35781         var addMod = function(m) {
35782            // console.log(m.modKey);
35783             
35784             mods.push(m);
35785             if (m.modules) {
35786                 m.modules.keySort('ASC',  cmp );
35787                 m.modules.each(addMod);
35788             }
35789             // not sure if this is used any more..
35790             if (m.finalize) {
35791                 m.finalize.name = m.name + " (clean up) ";
35792                 mods.push(m.finalize);
35793             }
35794             
35795         }
35796         this.topModule.modules.keySort('ASC',  cmp );
35797         this.topModule.modules.each(addMod);
35798         return mods;
35799     },
35800     
35801      /**
35802      * Build the registered modules.
35803      * @param {Object} parent element.
35804      * @param {Function} optional method to call after module has been added.
35805      * 
35806      */ 
35807    
35808     build : function() 
35809     {
35810         
35811         this.preBuild();
35812         var mods = this.buildOrder();
35813       
35814         //this.allmods = mods;
35815         //console.log(mods);
35816         //return;
35817         if (!mods.length) { // should not happen
35818             throw "NO modules!!!";
35819         }
35820         
35821         
35822         
35823         // flash it up as modal - so we store the mask!?
35824         Roo.MessageBox.show({ title: 'loading' });
35825         Roo.MessageBox.show({
35826            title: "Please wait...",
35827            msg: "Building Interface...",
35828            width:450,
35829            progress:true,
35830            closable:false,
35831            modal: false
35832           
35833         });
35834         var total = mods.length;
35835         
35836         var _this = this;
35837         var progressRun = function() {
35838             if (!mods.length) {
35839                 console.log('hide?');
35840                 Roo.MessageBox.hide();
35841                 _this.topModule.fireEvent('buildcomplete', _this.topModule);
35842                 return;    
35843             }
35844             
35845             var m = mods.shift();
35846             console.log(m);
35847             if (typeof(m) == 'function') { // not sure if this is supported any more..
35848                 m.call(this);
35849                 return progressRun.defer(10, _this);
35850             } 
35851             
35852             Roo.MessageBox.updateProgress(
35853                 (total  - mods.length)/total,  "Building Interface " + (total  - mods.length) + 
35854                     " of " + total + 
35855                     (m.name ? (' - ' + m.name) : '')
35856                     );
35857             
35858          
35859             
35860             var disabled = (typeof(m.disabled) == 'function') ?
35861                 m.disabled.call(m.module.disabled) : m.disabled;    
35862             
35863             
35864             if (disabled) {
35865                 return progressRun(); // we do not update the display!
35866             }
35867             
35868             if (!m.parent) {
35869                 // it's a top level one..
35870                 var layoutbase = new Ext.BorderLayout(document.body, {
35871                
35872                     center: {
35873                          titlebar: false,
35874                          autoScroll:false,
35875                          closeOnTab: true,
35876                          tabPosition: 'top',
35877                          //resizeTabs: true,
35878                          alwaysShowTabs: true,
35879                          minTabWidth: 140
35880                     }
35881                 });
35882                 var tree = m.tree();
35883                 tree.region = 'center';
35884                 m.el = layoutbase.addxtype(tree);
35885                 m.panel = m.el;
35886                 m.layout = m.panel.layout;    
35887                 return progressRun.defer(10, _this);
35888             }
35889             
35890             var tree = m.tree();
35891             tree.region = tree.region || m.region;
35892             m.el = m.parent.el.addxtype(tree);
35893             m.fireEvent('built', m);
35894             m.panel = m.el;
35895             m.layout = m.panel.layout;    
35896             progressRun.defer(10, _this); 
35897             
35898         }
35899         progressRun.defer(1, _this);
35900      
35901         
35902         
35903     }
35904      
35905    
35906     
35907     
35908 });
35909  //<script type="text/javascript">
35910
35911
35912 /**
35913  * @class Roo.Login
35914  * @extends Roo.LayoutDialog
35915  * A generic Login Dialog..... - only one needed in theory!?!?
35916  *
35917  * Fires XComponent builder on success...
35918  * 
35919  * Sends 
35920  *    username,password, lang = for login actions.
35921  *    check = 1 for periodic checking that sesion is valid.
35922  *    passwordRequest = email request password
35923  *    logout = 1 = to logout
35924  * 
35925  * Affects: (this id="????" elements)
35926  *   loading  (removed) (used to indicate application is loading)
35927  *   loading-mask (hides) (used to hide application when it's building loading)
35928  *   
35929  * 
35930  * Usage: 
35931  *    
35932  * 
35933  * Myapp.login = Roo.Login({
35934      url: xxxx,
35935    
35936      realm : 'Myapp', 
35937      
35938      
35939      method : 'POST',
35940      
35941      
35942      * 
35943  })
35944  * 
35945  * 
35946  * 
35947  **/
35948  
35949 Roo.Login = function(cfg)
35950 {
35951     this.addEvents({
35952         'refreshed' : true,
35953     });
35954     
35955     Roo.apply(this,cfg);
35956     
35957     Roo.onReady(function() {
35958         this.onLoad();
35959     }, this);
35960     // call parent..
35961     
35962    
35963     Roo.Login.superclass.constructor.call(this, this);
35964     //this.addxtype(this.items[0]);
35965     
35966     
35967 }
35968
35969
35970 Roo.extend(Roo.Login, Roo.LayoutDialog, {
35971     
35972     /**
35973      * @cfg {String} method
35974      * Method used to query for login details.
35975      */
35976     
35977     method : 'POST',
35978     /**
35979      * @cfg {String} url
35980      * URL to query login data. - eg. baseURL + '/Login.php'
35981      */
35982     url : '',
35983     
35984     /**
35985      * @property user
35986      * The user data - if user.id < 0 then login will be bypassed. (used for inital setup situation.
35987      * @type {Object} 
35988      */
35989     user : false,
35990     /**
35991      * @property checkFails
35992      * Number of times we have attempted to get authentication check, and failed.
35993      * @type {Number} 
35994      */
35995     checkFails : 0,
35996       /**
35997      * @property intervalID
35998      * The window interval that does the constant login checking.
35999      * @type {Number} 
36000      */
36001     intervalID : 0,
36002     
36003     
36004     onLoad : function() // called on page load...
36005     {
36006         // load 
36007          
36008         if (Roo.get('loading')) { // clear any loading indicator..
36009             Roo.get('loading').remove();
36010         }
36011         
36012         //this.switchLang('en'); // set the language to english..
36013        
36014         this.check({
36015             success:  function(response, opts)  {  // check successfull...
36016             
36017                 var res = this.processResponse(response);
36018                 this.checkFails =0;
36019                 if (!res.success) { // error!
36020                     this.checkFails = 5;
36021                     //console.log('call failure');
36022                     return this.failure(response,opts);
36023                 }
36024                 
36025                 if (!res.data.id) { // id=0 == login failure.
36026                     return this.show();
36027                 }
36028                 
36029                               
36030                         //console.log(success);
36031                 this.fillAuth(res.data);   
36032                 this.checkFails =0;
36033                 Roo.XComponent.build();
36034             },
36035             failure : this.show
36036         });
36037         
36038     }, 
36039     
36040     
36041     check: function(cfg) // called every so often to refresh cookie etc..
36042     {
36043         if (cfg.again) { // could be undefined..
36044             this.checkFails++;
36045         } else {
36046             this.checkFails = 0;
36047         }
36048         var _this = this;
36049         if (this.sending) {
36050             if ( this.checkFails > 4) {
36051                 Roo.MessageBox.alert("Error",  
36052                     "Error getting authentication status. - try reloading, or wait a while", function() {
36053                         _this.sending = false;
36054                     }); 
36055                 return;
36056             }
36057             cfg.again = true;
36058             _this.check.defer(10000, _this, [ cfg ]); // check in 10 secs.
36059             return;
36060         }
36061         this.sending = true;
36062         
36063         Roo.Ajax.request({  
36064             url: this.url,
36065             params: {
36066                 getAuthUser: true
36067             },  
36068             method: this.method,
36069             success:  cfg.success || this.success,
36070             failure : cfg.failure || this.failure,
36071             scope : this,
36072             callCfg : cfg
36073               
36074         });  
36075     }, 
36076     
36077     
36078     logout: function()
36079     {
36080         window.onbeforeunload = function() { }; // false does not work for IE..
36081         this.user = false;
36082         var _this = this;
36083         
36084         Roo.Ajax.request({  
36085             url: this.url,
36086             params: {
36087                 logout: 1
36088             },  
36089             method: 'GET',
36090             failure : function() {
36091                 Roo.MessageBox.alert("Error", "Error logging out. - continuing anyway.", function() {
36092                     document.location = document.location.toString() + '?ts=' + Math.random();
36093                 });
36094                 
36095             },
36096             success : function() {
36097                 _this.user = false;
36098                 this.checkFails =0;
36099                 // fixme..
36100                 document.location = document.location.toString() + '?ts=' + Math.random();
36101             }
36102               
36103               
36104         }); 
36105     },
36106     
36107     processResponse : function (response)
36108     {
36109         var res = '';
36110         try {
36111             res = Roo.decode(response.responseText);
36112             // oops...
36113             if (typeof(res) != 'object') {
36114                 res = { success : false, errorMsg : res, errors : true };
36115             }
36116             if (typeof(res.success) == 'undefined') {
36117                 res.success = false;
36118             }
36119             
36120         } catch(e) {
36121             res = { success : false,  errorMsg : response.responseText, errors : true };
36122         }
36123         return res;
36124     },
36125     
36126     success : function(response, opts)  // check successfull...
36127     {  
36128         this.sending = false;
36129         var res = this.processResponse(response);
36130         if (!res.success) {
36131             return this.failure(response, opts);
36132         }
36133         if (!res.data || !res.data.id) {
36134             return this.failure(response,opts);
36135         }
36136         //console.log(res);
36137         this.fillAuth(res.data);
36138         
36139         this.checkFails =0;
36140         
36141     },
36142     
36143     
36144     failure : function (response, opts) // called if login 'check' fails.. (causes re-check)
36145     {
36146         this.authUser = -1;
36147         this.sending = false;
36148         var res = this.processResponse(response);
36149         //console.log(res);
36150         if ( this.checkFails > 2) {
36151         
36152             Roo.MessageBox.alert("Error", res.errorMsg ? res.errorMsg : 
36153                 "Error getting authentication status. - try reloading"); 
36154             return;
36155         }
36156         opts.callCfg.again = true;
36157         this.check.defer(1000, this, [ opts.callCfg ]);
36158         return;  
36159     },
36160     
36161     
36162     
36163     fillAuth: function(au) {
36164         this.startAuthCheck();
36165         this.authUserId = au.id;
36166         this.authUser = au;
36167         this.lastChecked = new Date();
36168         this.fireEvent('refreshed', au);
36169         //Pman.Tab.FaxQueue.newMaxId(au.faxMax);
36170         //Pman.Tab.FaxTab.setTitle(au.faxNumPending);
36171         au.lang = au.lang || 'en';
36172         //this.switchLang(Roo.state.Manager.get('Pman.Login.lang', 'en'));
36173         Roo.state.Manager.set( this.realm + 'lang' , au.lang);
36174         this.switchLang(au.lang );
36175         
36176      
36177         // open system... - -on setyp..
36178         if (this.authUserId  < 0) {
36179             Roo.MessageBox.alert("Warning", 
36180                 "This is an open system - please set up a admin user with a password.");  
36181         }
36182          
36183         //Pman.onload(); // which should do nothing if it's a re-auth result...
36184         
36185              
36186     },
36187     
36188     startAuthCheck : function() // starter for timeout checking..
36189     {
36190         if (this.intervalID) { // timer already in place...
36191             return false;
36192         }
36193         var _this = this;
36194         this.intervalID =  window.setInterval(function() {
36195               _this.check(false);
36196             }, 120000); // every 120 secs = 2mins..
36197         
36198         
36199     },
36200          
36201     
36202     switchLang : function (lang) 
36203     {
36204         _T = typeof(_T) == 'undefined' ? false : _T;
36205           if (!_T || !lang.length) {
36206             return;
36207         }
36208         
36209         if (!_T && lang != 'en') {
36210             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
36211             return;
36212         }
36213         
36214         if (typeof(_T.en) == 'undefined') {
36215             _T.en = {};
36216             Roo.apply(_T.en, _T);
36217         }
36218         
36219         if (typeof(_T[lang]) == 'undefined') {
36220             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
36221             return;
36222         }
36223         
36224         
36225         Roo.apply(_T, _T[lang]);
36226         // just need to set the text values for everything...
36227         var _this = this;
36228         /* this will not work ...
36229         if (this.form) { 
36230             
36231                
36232             function formLabel(name, val) {
36233                 _this.form.findField(name).fieldEl.child('label').dom.innerHTML  = val;
36234             }
36235             
36236             formLabel('password', "Password"+':');
36237             formLabel('username', "Email Address"+':');
36238             formLabel('lang', "Language"+':');
36239             this.dialog.setTitle("Login");
36240             this.dialog.buttons[0].setText("Forgot Password");
36241             this.dialog.buttons[1].setText("Login");
36242         }
36243         */
36244         
36245         
36246     },
36247     
36248     
36249     title: "Login",
36250     modal: true,
36251     width:  350,
36252     //height: 230,
36253     height: 180,
36254     shadow: true,
36255     minWidth:200,
36256     minHeight:180,
36257     //proxyDrag: true,
36258     closable: false,
36259     draggable: false,
36260     collapsible: false,
36261     resizable: false,
36262     center: {  // needed??
36263         autoScroll:false,
36264         titlebar: false,
36265        // tabPosition: 'top',
36266         hideTabs: true,
36267         closeOnTab: true,
36268         alwaysShowTabs: false
36269     } ,
36270     listeners : {
36271         
36272         show  : function(dlg)
36273         {
36274             //console.log(this);
36275             this.form = this.layout.getRegion('center').activePanel.form;
36276             this.form.dialog = dlg;
36277             this.buttons[0].form = this.form;
36278             this.buttons[0].dialog = dlg
36279             this.buttons[1].form = this.form;
36280             this.buttons[1].dialog = dlg;
36281            
36282            //this.resizeToLogo.defer(1000,this);
36283             // this is all related to resizing for logos..
36284             //var sz = Roo.get(Pman.Login.form.el.query('img')[0]).getSize();
36285            //// if (!sz) {
36286              //   this.resizeToLogo.defer(1000,this);
36287              //   return;
36288            // }
36289             //var w = Ext.lib.Dom.getViewWidth() - 100;
36290             //var h = Ext.lib.Dom.getViewHeight() - 100;
36291             //this.resizeTo(Math.max(350, Math.min(sz.width + 30, w)),Math.min(sz.height+200, h));
36292             //this.center();
36293             if (this.disabled) {
36294                 this.hide();
36295                 return;
36296             }
36297             
36298             if (this.user.id < 0) { // used for inital setup situations.
36299                 return;
36300             }
36301             
36302             if (this.intervalID) {
36303                 // remove the timer
36304                 window.clearInterval(this.intervalID);
36305                 this.intervalID = false;
36306             }
36307             
36308             
36309             if (Roo.get('loading')) {
36310                 Roo.get('loading').remove();
36311             }
36312             if (Roo.get('loading-mask')) {
36313                 Roo.get('loading-mask').hide();
36314             }
36315             
36316             //incomming._node = tnode;
36317             this.form.reset();
36318             //this.dialog.modal = !modal;
36319             //this.dialog.show();
36320             this.el.unmask(); 
36321             
36322             
36323             this.form.setValues({
36324                 'username' : Roo.state.Manager.get(this.realm + '.username', ''),
36325                 'lang' : Roo.state.Manager.get(this.realm + '.lang', 'en')
36326             });
36327             
36328             this.switchLang(Roo.state.Manager.get(this.realm + '.lang', 'en'));
36329             if (this.form.findField('username').getValue().length > 0 ){
36330                 this.form.findField('password').focus();
36331             } else {
36332                this.form.findField('username').focus();
36333             }
36334     
36335         }
36336     },
36337     items : [
36338          {
36339        
36340             xtype : 'ContentPanel',
36341             xns : Roo,
36342             region: 'center',
36343             fitToFrame : true,
36344             
36345             items : [
36346     
36347                 {
36348                
36349                     xtype : 'Form',
36350                     xns : Roo.form,
36351                     labelWidth: 100,
36352                     style : 'margin: 10px;',
36353                     
36354                     listeners : {
36355                         actionfailed : function(f, act) {
36356                             // form can return { errors: .... }
36357                                 
36358                             //act.result.errors // invalid form element list...
36359                             //act.result.errorMsg// invalid form element list...
36360                             
36361                             this.dialog.el.unmask();
36362                             Roo.MessageBox.alert("Error", act.result.errorMsg ? act.result.errorMsg : 
36363                                         "Login failed - communication error - try again.");
36364                                       
36365                         },
36366                         actioncomplete: function(re, act) {
36367                              
36368                             Roo.state.Manager.set(
36369                                 this.dialog.realm + '.username',  
36370                                     this.findField('username').getValue()
36371                             );
36372                             Roo.state.Manager.set(
36373                                 this.dialog.realm + '.lang',  
36374                                 this.findField('lang').getValue() 
36375                             );
36376                             
36377                             this.dialog.fillAuth(act.result.data);
36378                               
36379                             this.dialog.hide();
36380                             
36381                             if (Roo.get('loading-mask')) {
36382                                 Roo.get('loading-mask').show();
36383                             }
36384                             Roo.XComponent.build();
36385                             
36386                              
36387                             
36388                         }
36389                     },
36390                     items : [
36391                         {
36392                             xtype : 'TextField',
36393                             xns : Roo.form,
36394                             fieldLabel: "Email Address",
36395                             name: 'username',
36396                             width:200,
36397                             autoCreate : {tag: "input", type: "text", size: "20"}
36398                         },
36399                         {
36400                             xtype : 'TextField',
36401                             xns : Roo.form,
36402                             fieldLabel: "Password",
36403                             inputType: 'password',
36404                             name: 'password',
36405                             width:200,
36406                             autoCreate : {tag: "input", type: "text", size: "20"},
36407                             listeners : {
36408                                 specialkey : function(e,ev) {
36409                                     if (ev.keyCode == 13) {
36410                                         this.form.dialog.el.mask("Logging in");
36411                                         this.form.doAction('submit', {
36412                                             url: this.form.dialog.url,
36413                                             method: this.form.dialog.method,
36414                                         });
36415                                     }
36416                                 }
36417                             }  
36418                         },
36419                         {
36420                             xtype : 'ComboBox',
36421                             xns : Roo.form,
36422                             fieldLabel: "Language",
36423                             name : 'langdisp',
36424                             store: {
36425                                 xtype : 'SimpleStore',
36426                                 fields: ['lang', 'ldisp'],
36427                                 data : [
36428                                     [ 'en', 'English' ],
36429                                     [ 'zh_HK' , '\u7E41\u4E2D' ],
36430                                     [ 'zh_CN', '\u7C21\u4E2D' ]
36431                                 ]
36432                             },
36433                             
36434                             valueField : 'lang',
36435                             hiddenName:  'lang',
36436                             width: 200,
36437                             displayField:'ldisp',
36438                             typeAhead: false,
36439                             editable: false,
36440                             mode: 'local',
36441                             triggerAction: 'all',
36442                             emptyText:'Select a Language...',
36443                             selectOnFocus:true,
36444                             listeners : {
36445                                 select :  function(cb, rec, ix) {
36446                                     this.form.switchLang(rec.data.lang);
36447                                 }
36448                             }
36449                         
36450                         }
36451                     ]
36452                 }
36453                   
36454                 
36455             ]
36456         }
36457     ],
36458     buttons : [
36459         {
36460             xtype : 'Button',
36461             xns : 'Roo',
36462             text : "Forgot Password",
36463             listeners : {
36464                 click : function() {
36465                     //console.log(this);
36466                     var n = this.form.findField('username').getValue();
36467                     if (!n.length) {
36468                         Roo.MessageBox.alert("Error", "Fill in your email address");
36469                         return;
36470                     }
36471                     Roo.Ajax.request({
36472                         url: this.dialog.url,
36473                         params: {
36474                             passwordRequest: n
36475                         },
36476                         method: this.dialog.method,
36477                         success:  function(response, opts)  {  // check successfull...
36478                         
36479                             var res = this.dialog.processResponse(response);
36480                             if (!res.success) { // error!
36481                                Roo.MessageBox.alert("Error" ,
36482                                     res.errorMsg ? res.errorMsg  : "Problem Requesting Password Reset");
36483                                return;
36484                             }
36485                             Roo.MessageBox.alert("Notice" ,
36486                                 "Please check you email for the Password Reset message");
36487                         },
36488                         failure : function() {
36489                             Roo.MessageBox.alert("Error" , "Problem Requesting Password Reset");
36490                         }
36491                         
36492                     });
36493                 }
36494             }
36495         },
36496         {
36497             xtype : 'Button',
36498             xns : 'Roo',
36499             text : "Login",
36500             listeners : {
36501                 
36502                 click : function () {
36503                         
36504                     this.dialog.el.mask("Logging in");
36505                     this.form.doAction('submit', {
36506                             url: this.dialog.url,
36507                             method: this.dialog.method
36508                     });
36509                 }
36510             }
36511         }
36512     ]
36513   
36514   
36515 })
36516  
36517
36518
36519