images/slate/tabs/tab-sprite.gif
[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 meta was not loaded from remote source.. try requesting it.
4931             if (!this.reader.metaFromRemote) {
4932                 p._requestMeta = 1;
4933             }
4934             if(this.sortInfo && this.remoteSort){
4935                 var pn = this.paramNames;
4936                 p[pn["sort"]] = this.sortInfo.field;
4937                 p[pn["dir"]] = this.sortInfo.direction;
4938             }
4939             this.proxy.load(p, this.reader, this.loadRecords, this, options);
4940         }
4941     },
4942
4943     /**
4944      * Reloads the Record cache from the configured Proxy using the configured Reader and
4945      * the options from the last load operation performed.
4946      * @param {Object} options (optional) An object containing properties which may override the options
4947      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
4948      * the most recently used options are reused).
4949      */
4950     reload : function(options){
4951         this.load(Roo.applyIf(options||{}, this.lastOptions));
4952     },
4953
4954     // private
4955     // Called as a callback by the Reader during a load operation.
4956     loadRecords : function(o, options, success){
4957         if(!o || success === false){
4958             if(success !== false){
4959                 this.fireEvent("load", this, [], options);
4960             }
4961             if(options.callback){
4962                 options.callback.call(options.scope || this, [], options, false);
4963             }
4964             return;
4965         }
4966         // if data returned failure - throw an exception.
4967         if (o.success === false) {
4968             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
4969             return;
4970         }
4971         var r = o.records, t = o.totalRecords || r.length;
4972         if(!options || options.add !== true){
4973             if(this.pruneModifiedRecords){
4974                 this.modified = [];
4975             }
4976             for(var i = 0, len = r.length; i < len; i++){
4977                 r[i].join(this);
4978             }
4979             if(this.snapshot){
4980                 this.data = this.snapshot;
4981                 delete this.snapshot;
4982             }
4983             this.data.clear();
4984             this.data.addAll(r);
4985             this.totalLength = t;
4986             this.applySort();
4987             this.fireEvent("datachanged", this);
4988         }else{
4989             this.totalLength = Math.max(t, this.data.length+r.length);
4990             this.add(r);
4991         }
4992         this.fireEvent("load", this, r, options);
4993         if(options.callback){
4994             options.callback.call(options.scope || this, r, options, true);
4995         }
4996     },
4997
4998     /**
4999      * Loads data from a passed data block. A Reader which understands the format of the data
5000      * must have been configured in the constructor.
5001      * @param {Object} data The data block from which to read the Records.  The format of the data expected
5002      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
5003      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
5004      */
5005     loadData : function(o, append){
5006         var r = this.reader.readRecords(o);
5007         this.loadRecords(r, {add: append}, true);
5008     },
5009
5010     /**
5011      * Gets the number of cached records.
5012      * <p>
5013      * <em>If using paging, this may not be the total size of the dataset. If the data object
5014      * used by the Reader contains the dataset size, then the getTotalCount() function returns
5015      * the data set size</em>
5016      */
5017     getCount : function(){
5018         return this.data.length || 0;
5019     },
5020
5021     /**
5022      * Gets the total number of records in the dataset as returned by the server.
5023      * <p>
5024      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
5025      * the dataset size</em>
5026      */
5027     getTotalCount : function(){
5028         return this.totalLength || 0;
5029     },
5030
5031     /**
5032      * Returns the sort state of the Store as an object with two properties:
5033      * <pre><code>
5034  field {String} The name of the field by which the Records are sorted
5035  direction {String} The sort order, "ASC" or "DESC"
5036      * </code></pre>
5037      */
5038     getSortState : function(){
5039         return this.sortInfo;
5040     },
5041
5042     // private
5043     applySort : function(){
5044         if(this.sortInfo && !this.remoteSort){
5045             var s = this.sortInfo, f = s.field;
5046             var st = this.fields.get(f).sortType;
5047             var fn = function(r1, r2){
5048                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
5049                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
5050             };
5051             this.data.sort(s.direction, fn);
5052             if(this.snapshot && this.snapshot != this.data){
5053                 this.snapshot.sort(s.direction, fn);
5054             }
5055         }
5056     },
5057
5058     /**
5059      * Sets the default sort column and order to be used by the next load operation.
5060      * @param {String} fieldName The name of the field to sort by.
5061      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5062      */
5063     setDefaultSort : function(field, dir){
5064         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
5065     },
5066
5067     /**
5068      * Sort the Records.
5069      * If remote sorting is used, the sort is performed on the server, and the cache is
5070      * reloaded. If local sorting is used, the cache is sorted internally.
5071      * @param {String} fieldName The name of the field to sort by.
5072      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5073      */
5074     sort : function(fieldName, dir){
5075         var f = this.fields.get(fieldName);
5076         if(!dir){
5077             if(this.sortInfo && this.sortInfo.field == f.name){ // toggle sort dir
5078                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
5079             }else{
5080                 dir = f.sortDir;
5081             }
5082         }
5083         this.sortToggle[f.name] = dir;
5084         this.sortInfo = {field: f.name, direction: dir};
5085         if(!this.remoteSort){
5086             this.applySort();
5087             this.fireEvent("datachanged", this);
5088         }else{
5089             this.load(this.lastOptions);
5090         }
5091     },
5092
5093     /**
5094      * Calls the specified function for each of the Records in the cache.
5095      * @param {Function} fn The function to call. The Record is passed as the first parameter.
5096      * Returning <em>false</em> aborts and exits the iteration.
5097      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
5098      */
5099     each : function(fn, scope){
5100         this.data.each(fn, scope);
5101     },
5102
5103     /**
5104      * Gets all records modified since the last commit.  Modified records are persisted across load operations
5105      * (e.g., during paging).
5106      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
5107      */
5108     getModifiedRecords : function(){
5109         return this.modified;
5110     },
5111
5112     // private
5113     createFilterFn : function(property, value, anyMatch){
5114         if(!value.exec){ // not a regex
5115             value = String(value);
5116             if(value.length == 0){
5117                 return false;
5118             }
5119             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
5120         }
5121         return function(r){
5122             return value.test(r.data[property]);
5123         };
5124     },
5125
5126     /**
5127      * Sums the value of <i>property</i> for each record between start and end and returns the result.
5128      * @param {String} property A field on your records
5129      * @param {Number} start The record index to start at (defaults to 0)
5130      * @param {Number} end The last record index to include (defaults to length - 1)
5131      * @return {Number} The sum
5132      */
5133     sum : function(property, start, end){
5134         var rs = this.data.items, v = 0;
5135         start = start || 0;
5136         end = (end || end === 0) ? end : rs.length-1;
5137
5138         for(var i = start; i <= end; i++){
5139             v += (rs[i].data[property] || 0);
5140         }
5141         return v;
5142     },
5143
5144     /**
5145      * Filter the records by a specified property.
5146      * @param {String} field A field on your records
5147      * @param {String/RegExp} value Either a string that the field
5148      * should start with or a RegExp to test against the field
5149      * @param {Boolean} anyMatch True to match any part not just the beginning
5150      */
5151     filter : function(property, value, anyMatch){
5152         var fn = this.createFilterFn(property, value, anyMatch);
5153         return fn ? this.filterBy(fn) : this.clearFilter();
5154     },
5155
5156     /**
5157      * Filter by a function. The specified function will be called with each
5158      * record in this data source. If the function returns true the record is included,
5159      * otherwise it is filtered.
5160      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5161      * @param {Object} scope (optional) The scope of the function (defaults to this)
5162      */
5163     filterBy : function(fn, scope){
5164         this.snapshot = this.snapshot || this.data;
5165         this.data = this.queryBy(fn, scope||this);
5166         this.fireEvent("datachanged", this);
5167     },
5168
5169     /**
5170      * Query the records by a specified property.
5171      * @param {String} field A field on your records
5172      * @param {String/RegExp} value Either a string that the field
5173      * should start with or a RegExp to test against the field
5174      * @param {Boolean} anyMatch True to match any part not just the beginning
5175      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5176      */
5177     query : function(property, value, anyMatch){
5178         var fn = this.createFilterFn(property, value, anyMatch);
5179         return fn ? this.queryBy(fn) : this.data.clone();
5180     },
5181
5182     /**
5183      * Query by a function. The specified function will be called with each
5184      * record in this data source. If the function returns true the record is included
5185      * in the results.
5186      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5187      * @param {Object} scope (optional) The scope of the function (defaults to this)
5188       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5189      **/
5190     queryBy : function(fn, scope){
5191         var data = this.snapshot || this.data;
5192         return data.filterBy(fn, scope||this);
5193     },
5194
5195     /**
5196      * Collects unique values for a particular dataIndex from this store.
5197      * @param {String} dataIndex The property to collect
5198      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
5199      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
5200      * @return {Array} An array of the unique values
5201      **/
5202     collect : function(dataIndex, allowNull, bypassFilter){
5203         var d = (bypassFilter === true && this.snapshot) ?
5204                 this.snapshot.items : this.data.items;
5205         var v, sv, r = [], l = {};
5206         for(var i = 0, len = d.length; i < len; i++){
5207             v = d[i].data[dataIndex];
5208             sv = String(v);
5209             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
5210                 l[sv] = true;
5211                 r[r.length] = v;
5212             }
5213         }
5214         return r;
5215     },
5216
5217     /**
5218      * Revert to a view of the Record cache with no filtering applied.
5219      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
5220      */
5221     clearFilter : function(suppressEvent){
5222         if(this.snapshot && this.snapshot != this.data){
5223             this.data = this.snapshot;
5224             delete this.snapshot;
5225             if(suppressEvent !== true){
5226                 this.fireEvent("datachanged", this);
5227             }
5228         }
5229     },
5230
5231     // private
5232     afterEdit : function(record){
5233         if(this.modified.indexOf(record) == -1){
5234             this.modified.push(record);
5235         }
5236         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
5237     },
5238
5239     // private
5240     afterReject : function(record){
5241         this.modified.remove(record);
5242         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
5243     },
5244
5245     // private
5246     afterCommit : function(record){
5247         this.modified.remove(record);
5248         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
5249     },
5250
5251     /**
5252      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
5253      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
5254      */
5255     commitChanges : function(){
5256         var m = this.modified.slice(0);
5257         this.modified = [];
5258         for(var i = 0, len = m.length; i < len; i++){
5259             m[i].commit();
5260         }
5261     },
5262
5263     /**
5264      * Cancel outstanding changes on all changed records.
5265      */
5266     rejectChanges : function(){
5267         var m = this.modified.slice(0);
5268         this.modified = [];
5269         for(var i = 0, len = m.length; i < len; i++){
5270             m[i].reject();
5271         }
5272     },
5273
5274     onMetaChange : function(meta, rtype, o){
5275         this.recordType = rtype;
5276         this.fields = rtype.prototype.fields;
5277         delete this.snapshot;
5278         this.sortInfo = meta.sortInfo || this.sortInfo;
5279         this.modified = [];
5280         this.fireEvent('metachange', this, this.reader.meta);
5281     }
5282 });/*
5283  * Based on:
5284  * Ext JS Library 1.1.1
5285  * Copyright(c) 2006-2007, Ext JS, LLC.
5286  *
5287  * Originally Released Under LGPL - original licence link has changed is not relivant.
5288  *
5289  * Fork - LGPL
5290  * <script type="text/javascript">
5291  */
5292
5293 /**
5294  * @class Roo.data.SimpleStore
5295  * @extends Roo.data.Store
5296  * Small helper class to make creating Stores from Array data easier.
5297  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
5298  * @cfg {Array} fields An array of field definition objects, or field name strings.
5299  * @cfg {Array} data The multi-dimensional array of data
5300  * @constructor
5301  * @param {Object} config
5302  */
5303 Roo.data.SimpleStore = function(config){
5304     Roo.data.SimpleStore.superclass.constructor.call(this, {
5305         isLocal : true,
5306         reader: new Roo.data.ArrayReader({
5307                 id: config.id
5308             },
5309             Roo.data.Record.create(config.fields)
5310         ),
5311         proxy : new Roo.data.MemoryProxy(config.data)
5312     });
5313     this.load();
5314 };
5315 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
5316  * Based on:
5317  * Ext JS Library 1.1.1
5318  * Copyright(c) 2006-2007, Ext JS, LLC.
5319  *
5320  * Originally Released Under LGPL - original licence link has changed is not relivant.
5321  *
5322  * Fork - LGPL
5323  * <script type="text/javascript">
5324  */
5325
5326 /**
5327 /**
5328  * @extends Roo.data.Store
5329  * @class Roo.data.JsonStore
5330  * Small helper class to make creating Stores for JSON data easier. <br/>
5331 <pre><code>
5332 var store = new Roo.data.JsonStore({
5333     url: 'get-images.php',
5334     root: 'images',
5335     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
5336 });
5337 </code></pre>
5338  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
5339  * JsonReader and HttpProxy (unless inline data is provided).</b>
5340  * @cfg {Array} fields An array of field definition objects, or field name strings.
5341  * @constructor
5342  * @param {Object} config
5343  */
5344 Roo.data.JsonStore = function(c){
5345     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
5346         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
5347         reader: new Roo.data.JsonReader(c, c.fields)
5348     }));
5349 };
5350 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
5351  * Based on:
5352  * Ext JS Library 1.1.1
5353  * Copyright(c) 2006-2007, Ext JS, LLC.
5354  *
5355  * Originally Released Under LGPL - original licence link has changed is not relivant.
5356  *
5357  * Fork - LGPL
5358  * <script type="text/javascript">
5359  */
5360
5361  
5362 Roo.data.Field = function(config){
5363     if(typeof config == "string"){
5364         config = {name: config};
5365     }
5366     Roo.apply(this, config);
5367     
5368     if(!this.type){
5369         this.type = "auto";
5370     }
5371     
5372     var st = Roo.data.SortTypes;
5373     // named sortTypes are supported, here we look them up
5374     if(typeof this.sortType == "string"){
5375         this.sortType = st[this.sortType];
5376     }
5377     
5378     // set default sortType for strings and dates
5379     if(!this.sortType){
5380         switch(this.type){
5381             case "string":
5382                 this.sortType = st.asUCString;
5383                 break;
5384             case "date":
5385                 this.sortType = st.asDate;
5386                 break;
5387             default:
5388                 this.sortType = st.none;
5389         }
5390     }
5391
5392     // define once
5393     var stripRe = /[\$,%]/g;
5394
5395     // prebuilt conversion function for this field, instead of
5396     // switching every time we're reading a value
5397     if(!this.convert){
5398         var cv, dateFormat = this.dateFormat;
5399         switch(this.type){
5400             case "":
5401             case "auto":
5402             case undefined:
5403                 cv = function(v){ return v; };
5404                 break;
5405             case "string":
5406                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
5407                 break;
5408             case "int":
5409                 cv = function(v){
5410                     return v !== undefined && v !== null && v !== '' ?
5411                            parseInt(String(v).replace(stripRe, ""), 10) : '';
5412                     };
5413                 break;
5414             case "float":
5415                 cv = function(v){
5416                     return v !== undefined && v !== null && v !== '' ?
5417                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
5418                     };
5419                 break;
5420             case "bool":
5421             case "boolean":
5422                 cv = function(v){ return v === true || v === "true" || v == 1; };
5423                 break;
5424             case "date":
5425                 cv = function(v){
5426                     if(!v){
5427                         return '';
5428                     }
5429                     if(v instanceof Date){
5430                         return v;
5431                     }
5432                     if(dateFormat){
5433                         if(dateFormat == "timestamp"){
5434                             return new Date(v*1000);
5435                         }
5436                         return Date.parseDate(v, dateFormat);
5437                     }
5438                     var parsed = Date.parse(v);
5439                     return parsed ? new Date(parsed) : null;
5440                 };
5441              break;
5442             
5443         }
5444         this.convert = cv;
5445     }
5446 };
5447
5448 Roo.data.Field.prototype = {
5449     dateFormat: null,
5450     defaultValue: "",
5451     mapping: null,
5452     sortType : null,
5453     sortDir : "ASC"
5454 };/*
5455  * Based on:
5456  * Ext JS Library 1.1.1
5457  * Copyright(c) 2006-2007, Ext JS, LLC.
5458  *
5459  * Originally Released Under LGPL - original licence link has changed is not relivant.
5460  *
5461  * Fork - LGPL
5462  * <script type="text/javascript">
5463  */
5464  
5465 // Base class for reading structured data from a data source.  This class is intended to be
5466 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
5467
5468 /**
5469  * @class Roo.data.DataReader
5470  * Base class for reading structured data from a data source.  This class is intended to be
5471  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
5472  */
5473
5474 Roo.data.DataReader = function(meta, recordType){
5475     
5476     this.meta = meta;
5477     
5478     this.recordType = recordType instanceof Array ? 
5479         Roo.data.Record.create(recordType) : recordType;
5480 };
5481
5482 Roo.data.DataReader.prototype = {
5483      /**
5484      * Create an empty record
5485      * @param {Object} data (optional) - overlay some values
5486      * @return {Roo.data.Record} record created.
5487      */
5488     newRow :  function(d) {
5489         var da =  {};
5490         this.recordType.prototype.fields.each(function(c) {
5491             switch( c.type) {
5492                 case 'int' : da[c.name] = 0; break;
5493                 case 'date' : da[c.name] = new Date(); break;
5494                 case 'float' : da[c.name] = 0.0; break;
5495                 case 'boolean' : da[c.name] = false; break;
5496                 default : da[c.name] = ""; break;
5497             }
5498             
5499         });
5500         return new this.recordType(Roo.apply(da, d));
5501     }
5502     
5503 };/*
5504  * Based on:
5505  * Ext JS Library 1.1.1
5506  * Copyright(c) 2006-2007, Ext JS, LLC.
5507  *
5508  * Originally Released Under LGPL - original licence link has changed is not relivant.
5509  *
5510  * Fork - LGPL
5511  * <script type="text/javascript">
5512  */
5513
5514 /**
5515  * @class Roo.data.DataProxy
5516  * @extends Roo.data.Observable
5517  * This class is an abstract base class for implementations which provide retrieval of
5518  * unformatted data objects.<br>
5519  * <p>
5520  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
5521  * (of the appropriate type which knows how to parse the data object) to provide a block of
5522  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
5523  * <p>
5524  * Custom implementations must implement the load method as described in
5525  * {@link Roo.data.HttpProxy#load}.
5526  */
5527 Roo.data.DataProxy = function(){
5528     this.addEvents({
5529         /**
5530          * @event beforeload
5531          * Fires before a network request is made to retrieve a data object.
5532          * @param {Object} This DataProxy object.
5533          * @param {Object} params The params parameter to the load function.
5534          */
5535         beforeload : true,
5536         /**
5537          * @event load
5538          * Fires before the load method's callback is called.
5539          * @param {Object} This DataProxy object.
5540          * @param {Object} o The data object.
5541          * @param {Object} arg The callback argument object passed to the load function.
5542          */
5543         load : true,
5544         /**
5545          * @event loadexception
5546          * Fires if an Exception occurs during data retrieval.
5547          * @param {Object} This DataProxy object.
5548          * @param {Object} o The data object.
5549          * @param {Object} arg The callback argument object passed to the load function.
5550          * @param {Object} e The Exception.
5551          */
5552         loadexception : true
5553     });
5554     Roo.data.DataProxy.superclass.constructor.call(this);
5555 };
5556
5557 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
5558
5559     /**
5560      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
5561      */
5562 /*
5563  * Based on:
5564  * Ext JS Library 1.1.1
5565  * Copyright(c) 2006-2007, Ext JS, LLC.
5566  *
5567  * Originally Released Under LGPL - original licence link has changed is not relivant.
5568  *
5569  * Fork - LGPL
5570  * <script type="text/javascript">
5571  */
5572 /**
5573  * @class Roo.data.MemoryProxy
5574  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
5575  * to the Reader when its load method is called.
5576  * @constructor
5577  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
5578  */
5579 Roo.data.MemoryProxy = function(data){
5580     if (data.data) {
5581         data = data.data;
5582     }
5583     Roo.data.MemoryProxy.superclass.constructor.call(this);
5584     this.data = data;
5585 };
5586
5587 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
5588     /**
5589      * Load data from the requested source (in this case an in-memory
5590      * data object passed to the constructor), read the data object into
5591      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5592      * process that block using the passed callback.
5593      * @param {Object} params This parameter is not used by the MemoryProxy class.
5594      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5595      * object into a block of Roo.data.Records.
5596      * @param {Function} callback The function into which to pass the block of Roo.data.records.
5597      * The function must be passed <ul>
5598      * <li>The Record block object</li>
5599      * <li>The "arg" argument from the load function</li>
5600      * <li>A boolean success indicator</li>
5601      * </ul>
5602      * @param {Object} scope The scope in which to call the callback
5603      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5604      */
5605     load : function(params, reader, callback, scope, arg){
5606         params = params || {};
5607         var result;
5608         try {
5609             result = reader.readRecords(this.data);
5610         }catch(e){
5611             this.fireEvent("loadexception", this, arg, null, e);
5612             callback.call(scope, null, arg, false);
5613             return;
5614         }
5615         callback.call(scope, result, arg, true);
5616     },
5617     
5618     // private
5619     update : function(params, records){
5620         
5621     }
5622 });/*
5623  * Based on:
5624  * Ext JS Library 1.1.1
5625  * Copyright(c) 2006-2007, Ext JS, LLC.
5626  *
5627  * Originally Released Under LGPL - original licence link has changed is not relivant.
5628  *
5629  * Fork - LGPL
5630  * <script type="text/javascript">
5631  */
5632 /**
5633  * @class Roo.data.HttpProxy
5634  * @extends Roo.data.DataProxy
5635  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
5636  * configured to reference a certain URL.<br><br>
5637  * <p>
5638  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
5639  * from which the running page was served.<br><br>
5640  * <p>
5641  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
5642  * <p>
5643  * Be aware that to enable the browser to parse an XML document, the server must set
5644  * the Content-Type header in the HTTP response to "text/xml".
5645  * @constructor
5646  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
5647  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
5648  * will be used to make the request.
5649  */
5650 Roo.data.HttpProxy = function(conn){
5651     Roo.data.HttpProxy.superclass.constructor.call(this);
5652     // is conn a conn config or a real conn?
5653     this.conn = conn;
5654     this.useAjax = !conn || !conn.events;
5655   
5656 };
5657
5658 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
5659     // thse are take from connection...
5660     
5661     /**
5662      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
5663      */
5664     /**
5665      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
5666      * extra parameters to each request made by this object. (defaults to undefined)
5667      */
5668     /**
5669      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
5670      *  to each request made by this object. (defaults to undefined)
5671      */
5672     /**
5673      * @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)
5674      */
5675     /**
5676      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
5677      */
5678      /**
5679      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
5680      * @type Boolean
5681      */
5682   
5683
5684     /**
5685      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
5686      * @type Boolean
5687      */
5688     /**
5689      * Return the {@link Roo.data.Connection} object being used by this Proxy.
5690      * @return {Connection} The Connection object. This object may be used to subscribe to events on
5691      * a finer-grained basis than the DataProxy events.
5692      */
5693     getConnection : function(){
5694         return this.useAjax ? Roo.Ajax : this.conn;
5695     },
5696
5697     /**
5698      * Load data from the configured {@link Roo.data.Connection}, read the data object into
5699      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
5700      * process that block using the passed callback.
5701      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5702      * for the request to the remote server.
5703      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5704      * object into a block of Roo.data.Records.
5705      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5706      * The function must be passed <ul>
5707      * <li>The Record block object</li>
5708      * <li>The "arg" argument from the load function</li>
5709      * <li>A boolean success indicator</li>
5710      * </ul>
5711      * @param {Object} scope The scope in which to call the callback
5712      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5713      */
5714     load : function(params, reader, callback, scope, arg){
5715         if(this.fireEvent("beforeload", this, params) !== false){
5716             var  o = {
5717                 params : params || {},
5718                 request: {
5719                     callback : callback,
5720                     scope : scope,
5721                     arg : arg
5722                 },
5723                 reader: reader,
5724                 callback : this.loadResponse,
5725                 scope: this
5726             };
5727             if(this.useAjax){
5728                 Roo.applyIf(o, this.conn);
5729                 if(this.activeRequest){
5730                     Roo.Ajax.abort(this.activeRequest);
5731                 }
5732                 this.activeRequest = Roo.Ajax.request(o);
5733             }else{
5734                 this.conn.request(o);
5735             }
5736         }else{
5737             callback.call(scope||this, null, arg, false);
5738         }
5739     },
5740
5741     // private
5742     loadResponse : function(o, success, response){
5743         delete this.activeRequest;
5744         if(!success){
5745             this.fireEvent("loadexception", this, o, response);
5746             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5747             return;
5748         }
5749         var result;
5750         try {
5751             result = o.reader.read(response);
5752         }catch(e){
5753             this.fireEvent("loadexception", this, o, response, e);
5754             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5755             return;
5756         }
5757         
5758         this.fireEvent("load", this, o, o.request.arg);
5759         o.request.callback.call(o.request.scope, result, o.request.arg, true);
5760     },
5761
5762     // private
5763     update : function(dataSet){
5764
5765     },
5766
5767     // private
5768     updateResponse : function(dataSet){
5769
5770     }
5771 });/*
5772  * Based on:
5773  * Ext JS Library 1.1.1
5774  * Copyright(c) 2006-2007, Ext JS, LLC.
5775  *
5776  * Originally Released Under LGPL - original licence link has changed is not relivant.
5777  *
5778  * Fork - LGPL
5779  * <script type="text/javascript">
5780  */
5781
5782 /**
5783  * @class Roo.data.ScriptTagProxy
5784  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
5785  * other than the originating domain of the running page.<br><br>
5786  * <p>
5787  * <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
5788  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
5789  * <p>
5790  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
5791  * source code that is used as the source inside a &lt;script> tag.<br><br>
5792  * <p>
5793  * In order for the browser to process the returned data, the server must wrap the data object
5794  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
5795  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
5796  * depending on whether the callback name was passed:
5797  * <p>
5798  * <pre><code>
5799 boolean scriptTag = false;
5800 String cb = request.getParameter("callback");
5801 if (cb != null) {
5802     scriptTag = true;
5803     response.setContentType("text/javascript");
5804 } else {
5805     response.setContentType("application/x-json");
5806 }
5807 Writer out = response.getWriter();
5808 if (scriptTag) {
5809     out.write(cb + "(");
5810 }
5811 out.print(dataBlock.toJsonString());
5812 if (scriptTag) {
5813     out.write(");");
5814 }
5815 </pre></code>
5816  *
5817  * @constructor
5818  * @param {Object} config A configuration object.
5819  */
5820 Roo.data.ScriptTagProxy = function(config){
5821     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
5822     Roo.apply(this, config);
5823     this.head = document.getElementsByTagName("head")[0];
5824 };
5825
5826 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
5827
5828 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
5829     /**
5830      * @cfg {String} url The URL from which to request the data object.
5831      */
5832     /**
5833      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
5834      */
5835     timeout : 30000,
5836     /**
5837      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
5838      * the server the name of the callback function set up by the load call to process the returned data object.
5839      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
5840      * javascript output which calls this named function passing the data object as its only parameter.
5841      */
5842     callbackParam : "callback",
5843     /**
5844      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
5845      * name to the request.
5846      */
5847     nocache : true,
5848
5849     /**
5850      * Load data from the configured URL, read the data object into
5851      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5852      * process that block using the passed callback.
5853      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5854      * for the request to the remote server.
5855      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5856      * object into a block of Roo.data.Records.
5857      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5858      * The function must be passed <ul>
5859      * <li>The Record block object</li>
5860      * <li>The "arg" argument from the load function</li>
5861      * <li>A boolean success indicator</li>
5862      * </ul>
5863      * @param {Object} scope The scope in which to call the callback
5864      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5865      */
5866     load : function(params, reader, callback, scope, arg){
5867         if(this.fireEvent("beforeload", this, params) !== false){
5868
5869             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
5870
5871             var url = this.url;
5872             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
5873             if(this.nocache){
5874                 url += "&_dc=" + (new Date().getTime());
5875             }
5876             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
5877             var trans = {
5878                 id : transId,
5879                 cb : "stcCallback"+transId,
5880                 scriptId : "stcScript"+transId,
5881                 params : params,
5882                 arg : arg,
5883                 url : url,
5884                 callback : callback,
5885                 scope : scope,
5886                 reader : reader
5887             };
5888             var conn = this;
5889
5890             window[trans.cb] = function(o){
5891                 conn.handleResponse(o, trans);
5892             };
5893
5894             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
5895
5896             if(this.autoAbort !== false){
5897                 this.abort();
5898             }
5899
5900             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
5901
5902             var script = document.createElement("script");
5903             script.setAttribute("src", url);
5904             script.setAttribute("type", "text/javascript");
5905             script.setAttribute("id", trans.scriptId);
5906             this.head.appendChild(script);
5907
5908             this.trans = trans;
5909         }else{
5910             callback.call(scope||this, null, arg, false);
5911         }
5912     },
5913
5914     // private
5915     isLoading : function(){
5916         return this.trans ? true : false;
5917     },
5918
5919     /**
5920      * Abort the current server request.
5921      */
5922     abort : function(){
5923         if(this.isLoading()){
5924             this.destroyTrans(this.trans);
5925         }
5926     },
5927
5928     // private
5929     destroyTrans : function(trans, isLoaded){
5930         this.head.removeChild(document.getElementById(trans.scriptId));
5931         clearTimeout(trans.timeoutId);
5932         if(isLoaded){
5933             window[trans.cb] = undefined;
5934             try{
5935                 delete window[trans.cb];
5936             }catch(e){}
5937         }else{
5938             // if hasn't been loaded, wait for load to remove it to prevent script error
5939             window[trans.cb] = function(){
5940                 window[trans.cb] = undefined;
5941                 try{
5942                     delete window[trans.cb];
5943                 }catch(e){}
5944             };
5945         }
5946     },
5947
5948     // private
5949     handleResponse : function(o, trans){
5950         this.trans = false;
5951         this.destroyTrans(trans, true);
5952         var result;
5953         try {
5954             result = trans.reader.readRecords(o);
5955         }catch(e){
5956             this.fireEvent("loadexception", this, o, trans.arg, e);
5957             trans.callback.call(trans.scope||window, null, trans.arg, false);
5958             return;
5959         }
5960         this.fireEvent("load", this, o, trans.arg);
5961         trans.callback.call(trans.scope||window, result, trans.arg, true);
5962     },
5963
5964     // private
5965     handleFailure : function(trans){
5966         this.trans = false;
5967         this.destroyTrans(trans, false);
5968         this.fireEvent("loadexception", this, null, trans.arg);
5969         trans.callback.call(trans.scope||window, null, trans.arg, false);
5970     }
5971 });/*
5972  * Based on:
5973  * Ext JS Library 1.1.1
5974  * Copyright(c) 2006-2007, Ext JS, LLC.
5975  *
5976  * Originally Released Under LGPL - original licence link has changed is not relivant.
5977  *
5978  * Fork - LGPL
5979  * <script type="text/javascript">
5980  */
5981
5982 /**
5983  * @class Roo.data.JsonReader
5984  * @extends Roo.data.DataReader
5985  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
5986  * based on mappings in a provided Roo.data.Record constructor.
5987  * 
5988  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
5989  * in the reply previously. 
5990  * 
5991  * <p>
5992  * Example code:
5993  * <pre><code>
5994 var RecordDef = Roo.data.Record.create([
5995     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
5996     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
5997 ]);
5998 var myReader = new Roo.data.JsonReader({
5999     totalProperty: "results",    // The property which contains the total dataset size (optional)
6000     root: "rows",                // The property which contains an Array of row objects
6001     id: "id"                     // The property within each row object that provides an ID for the record (optional)
6002 }, RecordDef);
6003 </code></pre>
6004  * <p>
6005  * This would consume a JSON file like this:
6006  * <pre><code>
6007 { 'results': 2, 'rows': [
6008     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
6009     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
6010 }
6011 </code></pre>
6012  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
6013  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6014  * paged from the remote server.
6015  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
6016  * @cfg {String} root name of the property which contains the Array of row objects.
6017  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
6018  * @constructor
6019  * Create a new JsonReader
6020  * @param {Object} meta Metadata configuration options
6021  * @param {Object} recordType Either an Array of field definition objects,
6022  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
6023  */
6024 Roo.data.JsonReader = function(meta, recordType){
6025     
6026     meta = meta || {};
6027     // set some defaults:
6028     Roo.applyIf(meta, {
6029         totalProperty: 'total',
6030         successProperty : 'success',
6031         root : 'data',
6032         id : 'id'
6033     });
6034     
6035     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6036 };
6037 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
6038     
6039     /**
6040      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
6041      * Used by Store query builder to append _requestMeta to params.
6042      * 
6043      */
6044     metaFromRemote : false,
6045     /**
6046      * This method is only used by a DataProxy which has retrieved data from a remote server.
6047      * @param {Object} response The XHR object which contains the JSON data in its responseText.
6048      * @return {Object} data A data block which is used by an Roo.data.Store object as
6049      * a cache of Roo.data.Records.
6050      */
6051     read : function(response){
6052         var json = response.responseText;
6053        
6054         var o = /* eval:var:o */ eval("("+json+")");
6055         if(!o) {
6056             throw {message: "JsonReader.read: Json object not found"};
6057         }
6058         
6059         if(o.metaData){
6060             
6061             delete this.ef;
6062             this.metaFromRemote = true;
6063             this.meta = o.metaData;
6064             this.recordType = Roo.data.Record.create(o.metaData.fields);
6065             this.onMetaChange(this.meta, this.recordType, o);
6066         }
6067         return this.readRecords(o);
6068     },
6069
6070     // private function a store will implement
6071     onMetaChange : function(meta, recordType, o){
6072
6073     },
6074
6075     /**
6076          * @ignore
6077          */
6078     simpleAccess: function(obj, subsc) {
6079         return obj[subsc];
6080     },
6081
6082         /**
6083          * @ignore
6084          */
6085     getJsonAccessor: function(){
6086         var re = /[\[\.]/;
6087         return function(expr) {
6088             try {
6089                 return(re.test(expr))
6090                     ? new Function("obj", "return obj." + expr)
6091                     : function(obj){
6092                         return obj[expr];
6093                     };
6094             } catch(e){}
6095             return Roo.emptyFn;
6096         };
6097     }(),
6098
6099     /**
6100      * Create a data block containing Roo.data.Records from an XML document.
6101      * @param {Object} o An object which contains an Array of row objects in the property specified
6102      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
6103      * which contains the total size of the dataset.
6104      * @return {Object} data A data block which is used by an Roo.data.Store object as
6105      * a cache of Roo.data.Records.
6106      */
6107     readRecords : function(o){
6108         /**
6109          * After any data loads, the raw JSON data is available for further custom processing.
6110          * @type Object
6111          */
6112         this.jsonData = o;
6113         var s = this.meta, Record = this.recordType,
6114             f = Record.prototype.fields, fi = f.items, fl = f.length;
6115
6116 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
6117         if (!this.ef) {
6118             if(s.totalProperty) {
6119                     this.getTotal = this.getJsonAccessor(s.totalProperty);
6120                 }
6121                 if(s.successProperty) {
6122                     this.getSuccess = this.getJsonAccessor(s.successProperty);
6123                 }
6124                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
6125                 if (s.id) {
6126                         var g = this.getJsonAccessor(s.id);
6127                         this.getId = function(rec) {
6128                                 var r = g(rec);
6129                                 return (r === undefined || r === "") ? null : r;
6130                         };
6131                 } else {
6132                         this.getId = function(){return null;};
6133                 }
6134             this.ef = [];
6135             for(var jj = 0; jj < fl; jj++){
6136                 f = fi[jj];
6137                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
6138                 this.ef[jj] = this.getJsonAccessor(map);
6139             }
6140         }
6141
6142         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
6143         if(s.totalProperty){
6144             var vt = parseInt(this.getTotal(o), 10);
6145             if(!isNaN(vt)){
6146                 totalRecords = vt;
6147             }
6148         }
6149         if(s.successProperty){
6150             var vs = this.getSuccess(o);
6151             if(vs === false || vs === 'false'){
6152                 success = false;
6153             }
6154         }
6155         var records = [];
6156             for(var i = 0; i < c; i++){
6157                     var n = root[i];
6158                 var values = {};
6159                 var id = this.getId(n);
6160                 for(var j = 0; j < fl; j++){
6161                     f = fi[j];
6162                 var v = this.ef[j](n);
6163                 if (!f.convert) {
6164                     Roo.log('missing convert for ' + f.name);
6165                     Roo.log(f);
6166                     continue;
6167                 }
6168                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
6169                 }
6170                 var record = new Record(values, id);
6171                 record.json = n;
6172                 records[i] = record;
6173             }
6174             return {
6175                 success : success,
6176                 records : records,
6177                 totalRecords : totalRecords
6178             };
6179     }
6180 });/*
6181  * Based on:
6182  * Ext JS Library 1.1.1
6183  * Copyright(c) 2006-2007, Ext JS, LLC.
6184  *
6185  * Originally Released Under LGPL - original licence link has changed is not relivant.
6186  *
6187  * Fork - LGPL
6188  * <script type="text/javascript">
6189  */
6190
6191 /**
6192  * @class Roo.data.XmlReader
6193  * @extends Roo.data.DataReader
6194  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
6195  * based on mappings in a provided Roo.data.Record constructor.<br><br>
6196  * <p>
6197  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
6198  * header in the HTTP response must be set to "text/xml".</em>
6199  * <p>
6200  * Example code:
6201  * <pre><code>
6202 var RecordDef = Roo.data.Record.create([
6203    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6204    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6205 ]);
6206 var myReader = new Roo.data.XmlReader({
6207    totalRecords: "results", // The element which contains the total dataset size (optional)
6208    record: "row",           // The repeated element which contains row information
6209    id: "id"                 // The element within the row that provides an ID for the record (optional)
6210 }, RecordDef);
6211 </code></pre>
6212  * <p>
6213  * This would consume an XML file like this:
6214  * <pre><code>
6215 &lt;?xml?>
6216 &lt;dataset>
6217  &lt;results>2&lt;/results>
6218  &lt;row>
6219    &lt;id>1&lt;/id>
6220    &lt;name>Bill&lt;/name>
6221    &lt;occupation>Gardener&lt;/occupation>
6222  &lt;/row>
6223  &lt;row>
6224    &lt;id>2&lt;/id>
6225    &lt;name>Ben&lt;/name>
6226    &lt;occupation>Horticulturalist&lt;/occupation>
6227  &lt;/row>
6228 &lt;/dataset>
6229 </code></pre>
6230  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
6231  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6232  * paged from the remote server.
6233  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
6234  * @cfg {String} success The DomQuery path to the success attribute used by forms.
6235  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
6236  * a record identifier value.
6237  * @constructor
6238  * Create a new XmlReader
6239  * @param {Object} meta Metadata configuration options
6240  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
6241  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
6242  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
6243  */
6244 Roo.data.XmlReader = function(meta, recordType){
6245     meta = meta || {};
6246     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6247 };
6248 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
6249     /**
6250      * This method is only used by a DataProxy which has retrieved data from a remote server.
6251          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
6252          * to contain a method called 'responseXML' that returns an XML document object.
6253      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6254      * a cache of Roo.data.Records.
6255      */
6256     read : function(response){
6257         var doc = response.responseXML;
6258         if(!doc) {
6259             throw {message: "XmlReader.read: XML Document not available"};
6260         }
6261         return this.readRecords(doc);
6262     },
6263
6264     /**
6265      * Create a data block containing Roo.data.Records from an XML document.
6266          * @param {Object} doc A parsed XML document.
6267      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6268      * a cache of Roo.data.Records.
6269      */
6270     readRecords : function(doc){
6271         /**
6272          * After any data loads/reads, the raw XML Document is available for further custom processing.
6273          * @type XMLDocument
6274          */
6275         this.xmlData = doc;
6276         var root = doc.documentElement || doc;
6277         var q = Roo.DomQuery;
6278         var recordType = this.recordType, fields = recordType.prototype.fields;
6279         var sid = this.meta.id;
6280         var totalRecords = 0, success = true;
6281         if(this.meta.totalRecords){
6282             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
6283         }
6284         
6285         if(this.meta.success){
6286             var sv = q.selectValue(this.meta.success, root, true);
6287             success = sv !== false && sv !== 'false';
6288         }
6289         var records = [];
6290         var ns = q.select(this.meta.record, root);
6291         for(var i = 0, len = ns.length; i < len; i++) {
6292                 var n = ns[i];
6293                 var values = {};
6294                 var id = sid ? q.selectValue(sid, n) : undefined;
6295                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6296                     var f = fields.items[j];
6297                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
6298                     v = f.convert(v);
6299                     values[f.name] = v;
6300                 }
6301                 var record = new recordType(values, id);
6302                 record.node = n;
6303                 records[records.length] = record;
6304             }
6305
6306             return {
6307                 success : success,
6308                 records : records,
6309                 totalRecords : totalRecords || records.length
6310             };
6311     }
6312 });/*
6313  * Based on:
6314  * Ext JS Library 1.1.1
6315  * Copyright(c) 2006-2007, Ext JS, LLC.
6316  *
6317  * Originally Released Under LGPL - original licence link has changed is not relivant.
6318  *
6319  * Fork - LGPL
6320  * <script type="text/javascript">
6321  */
6322
6323 /**
6324  * @class Roo.data.ArrayReader
6325  * @extends Roo.data.DataReader
6326  * Data reader class to create an Array of Roo.data.Record objects from an Array.
6327  * Each element of that Array represents a row of data fields. The
6328  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
6329  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
6330  * <p>
6331  * Example code:.
6332  * <pre><code>
6333 var RecordDef = Roo.data.Record.create([
6334     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
6335     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
6336 ]);
6337 var myReader = new Roo.data.ArrayReader({
6338     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
6339 }, RecordDef);
6340 </code></pre>
6341  * <p>
6342  * This would consume an Array like this:
6343  * <pre><code>
6344 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
6345   </code></pre>
6346  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
6347  * @constructor
6348  * Create a new JsonReader
6349  * @param {Object} meta Metadata configuration options.
6350  * @param {Object} recordType Either an Array of field definition objects
6351  * as specified to {@link Roo.data.Record#create},
6352  * or an {@link Roo.data.Record} object
6353  * created using {@link Roo.data.Record#create}.
6354  */
6355 Roo.data.ArrayReader = function(meta, recordType){
6356     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
6357 };
6358
6359 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
6360     /**
6361      * Create a data block containing Roo.data.Records from an XML document.
6362      * @param {Object} o An Array of row objects which represents the dataset.
6363      * @return {Object} data A data block which is used by an Roo.data.Store object as
6364      * a cache of Roo.data.Records.
6365      */
6366     readRecords : function(o){
6367         var sid = this.meta ? this.meta.id : null;
6368         var recordType = this.recordType, fields = recordType.prototype.fields;
6369         var records = [];
6370         var root = o;
6371             for(var i = 0; i < root.length; i++){
6372                     var n = root[i];
6373                 var values = {};
6374                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
6375                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6376                 var f = fields.items[j];
6377                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
6378                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
6379                 v = f.convert(v);
6380                 values[f.name] = v;
6381             }
6382                 var record = new recordType(values, id);
6383                 record.json = n;
6384                 records[records.length] = record;
6385             }
6386             return {
6387                 records : records,
6388                 totalRecords : records.length
6389             };
6390     }
6391 });/*
6392  * Based on:
6393  * Ext JS Library 1.1.1
6394  * Copyright(c) 2006-2007, Ext JS, LLC.
6395  *
6396  * Originally Released Under LGPL - original licence link has changed is not relivant.
6397  *
6398  * Fork - LGPL
6399  * <script type="text/javascript">
6400  */
6401
6402
6403 /**
6404  * @class Roo.data.Tree
6405  * @extends Roo.util.Observable
6406  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
6407  * in the tree have most standard DOM functionality.
6408  * @constructor
6409  * @param {Node} root (optional) The root node
6410  */
6411 Roo.data.Tree = function(root){
6412    this.nodeHash = {};
6413    /**
6414     * The root node for this tree
6415     * @type Node
6416     */
6417    this.root = null;
6418    if(root){
6419        this.setRootNode(root);
6420    }
6421    this.addEvents({
6422        /**
6423         * @event append
6424         * Fires when a new child node is appended to a node in this tree.
6425         * @param {Tree} tree The owner tree
6426         * @param {Node} parent The parent node
6427         * @param {Node} node The newly appended node
6428         * @param {Number} index The index of the newly appended node
6429         */
6430        "append" : true,
6431        /**
6432         * @event remove
6433         * Fires when a child node is removed from a node in this tree.
6434         * @param {Tree} tree The owner tree
6435         * @param {Node} parent The parent node
6436         * @param {Node} node The child node removed
6437         */
6438        "remove" : true,
6439        /**
6440         * @event move
6441         * Fires when a node is moved to a new location in the tree
6442         * @param {Tree} tree The owner tree
6443         * @param {Node} node The node moved
6444         * @param {Node} oldParent The old parent of this node
6445         * @param {Node} newParent The new parent of this node
6446         * @param {Number} index The index it was moved to
6447         */
6448        "move" : true,
6449        /**
6450         * @event insert
6451         * Fires when a new child node is inserted in a node in this tree.
6452         * @param {Tree} tree The owner tree
6453         * @param {Node} parent The parent node
6454         * @param {Node} node The child node inserted
6455         * @param {Node} refNode The child node the node was inserted before
6456         */
6457        "insert" : true,
6458        /**
6459         * @event beforeappend
6460         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
6461         * @param {Tree} tree The owner tree
6462         * @param {Node} parent The parent node
6463         * @param {Node} node The child node to be appended
6464         */
6465        "beforeappend" : true,
6466        /**
6467         * @event beforeremove
6468         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
6469         * @param {Tree} tree The owner tree
6470         * @param {Node} parent The parent node
6471         * @param {Node} node The child node to be removed
6472         */
6473        "beforeremove" : true,
6474        /**
6475         * @event beforemove
6476         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
6477         * @param {Tree} tree The owner tree
6478         * @param {Node} node The node being moved
6479         * @param {Node} oldParent The parent of the node
6480         * @param {Node} newParent The new parent the node is moving to
6481         * @param {Number} index The index it is being moved to
6482         */
6483        "beforemove" : true,
6484        /**
6485         * @event beforeinsert
6486         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
6487         * @param {Tree} tree The owner tree
6488         * @param {Node} parent The parent node
6489         * @param {Node} node The child node to be inserted
6490         * @param {Node} refNode The child node the node is being inserted before
6491         */
6492        "beforeinsert" : true
6493    });
6494
6495     Roo.data.Tree.superclass.constructor.call(this);
6496 };
6497
6498 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
6499     pathSeparator: "/",
6500
6501     proxyNodeEvent : function(){
6502         return this.fireEvent.apply(this, arguments);
6503     },
6504
6505     /**
6506      * Returns the root node for this tree.
6507      * @return {Node}
6508      */
6509     getRootNode : function(){
6510         return this.root;
6511     },
6512
6513     /**
6514      * Sets the root node for this tree.
6515      * @param {Node} node
6516      * @return {Node}
6517      */
6518     setRootNode : function(node){
6519         this.root = node;
6520         node.ownerTree = this;
6521         node.isRoot = true;
6522         this.registerNode(node);
6523         return node;
6524     },
6525
6526     /**
6527      * Gets a node in this tree by its id.
6528      * @param {String} id
6529      * @return {Node}
6530      */
6531     getNodeById : function(id){
6532         return this.nodeHash[id];
6533     },
6534
6535     registerNode : function(node){
6536         this.nodeHash[node.id] = node;
6537     },
6538
6539     unregisterNode : function(node){
6540         delete this.nodeHash[node.id];
6541     },
6542
6543     toString : function(){
6544         return "[Tree"+(this.id?" "+this.id:"")+"]";
6545     }
6546 });
6547
6548 /**
6549  * @class Roo.data.Node
6550  * @extends Roo.util.Observable
6551  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
6552  * @cfg {String} id The id for this node. If one is not specified, one is generated.
6553  * @constructor
6554  * @param {Object} attributes The attributes/config for the node
6555  */
6556 Roo.data.Node = function(attributes){
6557     /**
6558      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
6559      * @type {Object}
6560      */
6561     this.attributes = attributes || {};
6562     this.leaf = this.attributes.leaf;
6563     /**
6564      * The node id. @type String
6565      */
6566     this.id = this.attributes.id;
6567     if(!this.id){
6568         this.id = Roo.id(null, "ynode-");
6569         this.attributes.id = this.id;
6570     }
6571     /**
6572      * All child nodes of this node. @type Array
6573      */
6574     this.childNodes = [];
6575     if(!this.childNodes.indexOf){ // indexOf is a must
6576         this.childNodes.indexOf = function(o){
6577             for(var i = 0, len = this.length; i < len; i++){
6578                 if(this[i] == o) {
6579                     return i;
6580                 }
6581             }
6582             return -1;
6583         };
6584     }
6585     /**
6586      * The parent node for this node. @type Node
6587      */
6588     this.parentNode = null;
6589     /**
6590      * The first direct child node of this node, or null if this node has no child nodes. @type Node
6591      */
6592     this.firstChild = null;
6593     /**
6594      * The last direct child node of this node, or null if this node has no child nodes. @type Node
6595      */
6596     this.lastChild = null;
6597     /**
6598      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
6599      */
6600     this.previousSibling = null;
6601     /**
6602      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
6603      */
6604     this.nextSibling = null;
6605
6606     this.addEvents({
6607        /**
6608         * @event append
6609         * Fires when a new child node is appended
6610         * @param {Tree} tree The owner tree
6611         * @param {Node} this This node
6612         * @param {Node} node The newly appended node
6613         * @param {Number} index The index of the newly appended node
6614         */
6615        "append" : true,
6616        /**
6617         * @event remove
6618         * Fires when a child node is removed
6619         * @param {Tree} tree The owner tree
6620         * @param {Node} this This node
6621         * @param {Node} node The removed node
6622         */
6623        "remove" : true,
6624        /**
6625         * @event move
6626         * Fires when this node is moved to a new location in the tree
6627         * @param {Tree} tree The owner tree
6628         * @param {Node} this This node
6629         * @param {Node} oldParent The old parent of this node
6630         * @param {Node} newParent The new parent of this node
6631         * @param {Number} index The index it was moved to
6632         */
6633        "move" : true,
6634        /**
6635         * @event insert
6636         * Fires when a new child node is inserted.
6637         * @param {Tree} tree The owner tree
6638         * @param {Node} this This node
6639         * @param {Node} node The child node inserted
6640         * @param {Node} refNode The child node the node was inserted before
6641         */
6642        "insert" : true,
6643        /**
6644         * @event beforeappend
6645         * Fires before a new child is appended, return false to cancel the append.
6646         * @param {Tree} tree The owner tree
6647         * @param {Node} this This node
6648         * @param {Node} node The child node to be appended
6649         */
6650        "beforeappend" : true,
6651        /**
6652         * @event beforeremove
6653         * Fires before a child is removed, return false to cancel the remove.
6654         * @param {Tree} tree The owner tree
6655         * @param {Node} this This node
6656         * @param {Node} node The child node to be removed
6657         */
6658        "beforeremove" : true,
6659        /**
6660         * @event beforemove
6661         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
6662         * @param {Tree} tree The owner tree
6663         * @param {Node} this This node
6664         * @param {Node} oldParent The parent of this node
6665         * @param {Node} newParent The new parent this node is moving to
6666         * @param {Number} index The index it is being moved to
6667         */
6668        "beforemove" : true,
6669        /**
6670         * @event beforeinsert
6671         * Fires before a new child is inserted, return false to cancel the insert.
6672         * @param {Tree} tree The owner tree
6673         * @param {Node} this This node
6674         * @param {Node} node The child node to be inserted
6675         * @param {Node} refNode The child node the node is being inserted before
6676         */
6677        "beforeinsert" : true
6678    });
6679     this.listeners = this.attributes.listeners;
6680     Roo.data.Node.superclass.constructor.call(this);
6681 };
6682
6683 Roo.extend(Roo.data.Node, Roo.util.Observable, {
6684     fireEvent : function(evtName){
6685         // first do standard event for this node
6686         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
6687             return false;
6688         }
6689         // then bubble it up to the tree if the event wasn't cancelled
6690         var ot = this.getOwnerTree();
6691         if(ot){
6692             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
6693                 return false;
6694             }
6695         }
6696         return true;
6697     },
6698
6699     /**
6700      * Returns true if this node is a leaf
6701      * @return {Boolean}
6702      */
6703     isLeaf : function(){
6704         return this.leaf === true;
6705     },
6706
6707     // private
6708     setFirstChild : function(node){
6709         this.firstChild = node;
6710     },
6711
6712     //private
6713     setLastChild : function(node){
6714         this.lastChild = node;
6715     },
6716
6717
6718     /**
6719      * Returns true if this node is the last child of its parent
6720      * @return {Boolean}
6721      */
6722     isLast : function(){
6723        return (!this.parentNode ? true : this.parentNode.lastChild == this);
6724     },
6725
6726     /**
6727      * Returns true if this node is the first child of its parent
6728      * @return {Boolean}
6729      */
6730     isFirst : function(){
6731        return (!this.parentNode ? true : this.parentNode.firstChild == this);
6732     },
6733
6734     hasChildNodes : function(){
6735         return !this.isLeaf() && this.childNodes.length > 0;
6736     },
6737
6738     /**
6739      * Insert node(s) as the last child node of this node.
6740      * @param {Node/Array} node The node or Array of nodes to append
6741      * @return {Node} The appended node if single append, or null if an array was passed
6742      */
6743     appendChild : function(node){
6744         var multi = false;
6745         if(node instanceof Array){
6746             multi = node;
6747         }else if(arguments.length > 1){
6748             multi = arguments;
6749         }
6750         // if passed an array or multiple args do them one by one
6751         if(multi){
6752             for(var i = 0, len = multi.length; i < len; i++) {
6753                 this.appendChild(multi[i]);
6754             }
6755         }else{
6756             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
6757                 return false;
6758             }
6759             var index = this.childNodes.length;
6760             var oldParent = node.parentNode;
6761             // it's a move, make sure we move it cleanly
6762             if(oldParent){
6763                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
6764                     return false;
6765                 }
6766                 oldParent.removeChild(node);
6767             }
6768             index = this.childNodes.length;
6769             if(index == 0){
6770                 this.setFirstChild(node);
6771             }
6772             this.childNodes.push(node);
6773             node.parentNode = this;
6774             var ps = this.childNodes[index-1];
6775             if(ps){
6776                 node.previousSibling = ps;
6777                 ps.nextSibling = node;
6778             }else{
6779                 node.previousSibling = null;
6780             }
6781             node.nextSibling = null;
6782             this.setLastChild(node);
6783             node.setOwnerTree(this.getOwnerTree());
6784             this.fireEvent("append", this.ownerTree, this, node, index);
6785             if(oldParent){
6786                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
6787             }
6788             return node;
6789         }
6790     },
6791
6792     /**
6793      * Removes a child node from this node.
6794      * @param {Node} node The node to remove
6795      * @return {Node} The removed node
6796      */
6797     removeChild : function(node){
6798         var index = this.childNodes.indexOf(node);
6799         if(index == -1){
6800             return false;
6801         }
6802         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
6803             return false;
6804         }
6805
6806         // remove it from childNodes collection
6807         this.childNodes.splice(index, 1);
6808
6809         // update siblings
6810         if(node.previousSibling){
6811             node.previousSibling.nextSibling = node.nextSibling;
6812         }
6813         if(node.nextSibling){
6814             node.nextSibling.previousSibling = node.previousSibling;
6815         }
6816
6817         // update child refs
6818         if(this.firstChild == node){
6819             this.setFirstChild(node.nextSibling);
6820         }
6821         if(this.lastChild == node){
6822             this.setLastChild(node.previousSibling);
6823         }
6824
6825         node.setOwnerTree(null);
6826         // clear any references from the node
6827         node.parentNode = null;
6828         node.previousSibling = null;
6829         node.nextSibling = null;
6830         this.fireEvent("remove", this.ownerTree, this, node);
6831         return node;
6832     },
6833
6834     /**
6835      * Inserts the first node before the second node in this nodes childNodes collection.
6836      * @param {Node} node The node to insert
6837      * @param {Node} refNode The node to insert before (if null the node is appended)
6838      * @return {Node} The inserted node
6839      */
6840     insertBefore : function(node, refNode){
6841         if(!refNode){ // like standard Dom, refNode can be null for append
6842             return this.appendChild(node);
6843         }
6844         // nothing to do
6845         if(node == refNode){
6846             return false;
6847         }
6848
6849         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
6850             return false;
6851         }
6852         var index = this.childNodes.indexOf(refNode);
6853         var oldParent = node.parentNode;
6854         var refIndex = index;
6855
6856         // when moving internally, indexes will change after remove
6857         if(oldParent == this && this.childNodes.indexOf(node) < index){
6858             refIndex--;
6859         }
6860
6861         // it's a move, make sure we move it cleanly
6862         if(oldParent){
6863             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
6864                 return false;
6865             }
6866             oldParent.removeChild(node);
6867         }
6868         if(refIndex == 0){
6869             this.setFirstChild(node);
6870         }
6871         this.childNodes.splice(refIndex, 0, node);
6872         node.parentNode = this;
6873         var ps = this.childNodes[refIndex-1];
6874         if(ps){
6875             node.previousSibling = ps;
6876             ps.nextSibling = node;
6877         }else{
6878             node.previousSibling = null;
6879         }
6880         node.nextSibling = refNode;
6881         refNode.previousSibling = node;
6882         node.setOwnerTree(this.getOwnerTree());
6883         this.fireEvent("insert", this.ownerTree, this, node, refNode);
6884         if(oldParent){
6885             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
6886         }
6887         return node;
6888     },
6889
6890     /**
6891      * Returns the child node at the specified index.
6892      * @param {Number} index
6893      * @return {Node}
6894      */
6895     item : function(index){
6896         return this.childNodes[index];
6897     },
6898
6899     /**
6900      * Replaces one child node in this node with another.
6901      * @param {Node} newChild The replacement node
6902      * @param {Node} oldChild The node to replace
6903      * @return {Node} The replaced node
6904      */
6905     replaceChild : function(newChild, oldChild){
6906         this.insertBefore(newChild, oldChild);
6907         this.removeChild(oldChild);
6908         return oldChild;
6909     },
6910
6911     /**
6912      * Returns the index of a child node
6913      * @param {Node} node
6914      * @return {Number} The index of the node or -1 if it was not found
6915      */
6916     indexOf : function(child){
6917         return this.childNodes.indexOf(child);
6918     },
6919
6920     /**
6921      * Returns the tree this node is in.
6922      * @return {Tree}
6923      */
6924     getOwnerTree : function(){
6925         // if it doesn't have one, look for one
6926         if(!this.ownerTree){
6927             var p = this;
6928             while(p){
6929                 if(p.ownerTree){
6930                     this.ownerTree = p.ownerTree;
6931                     break;
6932                 }
6933                 p = p.parentNode;
6934             }
6935         }
6936         return this.ownerTree;
6937     },
6938
6939     /**
6940      * Returns depth of this node (the root node has a depth of 0)
6941      * @return {Number}
6942      */
6943     getDepth : function(){
6944         var depth = 0;
6945         var p = this;
6946         while(p.parentNode){
6947             ++depth;
6948             p = p.parentNode;
6949         }
6950         return depth;
6951     },
6952
6953     // private
6954     setOwnerTree : function(tree){
6955         // if it's move, we need to update everyone
6956         if(tree != this.ownerTree){
6957             if(this.ownerTree){
6958                 this.ownerTree.unregisterNode(this);
6959             }
6960             this.ownerTree = tree;
6961             var cs = this.childNodes;
6962             for(var i = 0, len = cs.length; i < len; i++) {
6963                 cs[i].setOwnerTree(tree);
6964             }
6965             if(tree){
6966                 tree.registerNode(this);
6967             }
6968         }
6969     },
6970
6971     /**
6972      * Returns the path for this node. The path can be used to expand or select this node programmatically.
6973      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
6974      * @return {String} The path
6975      */
6976     getPath : function(attr){
6977         attr = attr || "id";
6978         var p = this.parentNode;
6979         var b = [this.attributes[attr]];
6980         while(p){
6981             b.unshift(p.attributes[attr]);
6982             p = p.parentNode;
6983         }
6984         var sep = this.getOwnerTree().pathSeparator;
6985         return sep + b.join(sep);
6986     },
6987
6988     /**
6989      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
6990      * function call will be the scope provided or the current node. The arguments to the function
6991      * will be the args provided or the current node. If the function returns false at any point,
6992      * the bubble is stopped.
6993      * @param {Function} fn The function to call
6994      * @param {Object} scope (optional) The scope of the function (defaults to current node)
6995      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
6996      */
6997     bubble : function(fn, scope, args){
6998         var p = this;
6999         while(p){
7000             if(fn.call(scope || p, args || p) === false){
7001                 break;
7002             }
7003             p = p.parentNode;
7004         }
7005     },
7006
7007     /**
7008      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7009      * function call will be the scope provided or the current node. The arguments to the function
7010      * will be the args provided or the current node. If the function returns false at any point,
7011      * the cascade is stopped on that branch.
7012      * @param {Function} fn The function to call
7013      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7014      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7015      */
7016     cascade : function(fn, scope, args){
7017         if(fn.call(scope || this, args || this) !== false){
7018             var cs = this.childNodes;
7019             for(var i = 0, len = cs.length; i < len; i++) {
7020                 cs[i].cascade(fn, scope, args);
7021             }
7022         }
7023     },
7024
7025     /**
7026      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
7027      * function call will be the scope provided or the current node. The arguments to the function
7028      * will be the args provided or the current node. If the function returns false at any point,
7029      * the iteration stops.
7030      * @param {Function} fn The function to call
7031      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7032      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7033      */
7034     eachChild : function(fn, scope, args){
7035         var cs = this.childNodes;
7036         for(var i = 0, len = cs.length; i < len; i++) {
7037                 if(fn.call(scope || this, args || cs[i]) === false){
7038                     break;
7039                 }
7040         }
7041     },
7042
7043     /**
7044      * Finds the first child that has the attribute with the specified value.
7045      * @param {String} attribute The attribute name
7046      * @param {Mixed} value The value to search for
7047      * @return {Node} The found child or null if none was found
7048      */
7049     findChild : function(attribute, value){
7050         var cs = this.childNodes;
7051         for(var i = 0, len = cs.length; i < len; i++) {
7052                 if(cs[i].attributes[attribute] == value){
7053                     return cs[i];
7054                 }
7055         }
7056         return null;
7057     },
7058
7059     /**
7060      * Finds the first child by a custom function. The child matches if the function passed
7061      * returns true.
7062      * @param {Function} fn
7063      * @param {Object} scope (optional)
7064      * @return {Node} The found child or null if none was found
7065      */
7066     findChildBy : function(fn, scope){
7067         var cs = this.childNodes;
7068         for(var i = 0, len = cs.length; i < len; i++) {
7069                 if(fn.call(scope||cs[i], cs[i]) === true){
7070                     return cs[i];
7071                 }
7072         }
7073         return null;
7074     },
7075
7076     /**
7077      * Sorts this nodes children using the supplied sort function
7078      * @param {Function} fn
7079      * @param {Object} scope (optional)
7080      */
7081     sort : function(fn, scope){
7082         var cs = this.childNodes;
7083         var len = cs.length;
7084         if(len > 0){
7085             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
7086             cs.sort(sortFn);
7087             for(var i = 0; i < len; i++){
7088                 var n = cs[i];
7089                 n.previousSibling = cs[i-1];
7090                 n.nextSibling = cs[i+1];
7091                 if(i == 0){
7092                     this.setFirstChild(n);
7093                 }
7094                 if(i == len-1){
7095                     this.setLastChild(n);
7096                 }
7097             }
7098         }
7099     },
7100
7101     /**
7102      * Returns true if this node is an ancestor (at any point) of the passed node.
7103      * @param {Node} node
7104      * @return {Boolean}
7105      */
7106     contains : function(node){
7107         return node.isAncestor(this);
7108     },
7109
7110     /**
7111      * Returns true if the passed node is an ancestor (at any point) of this node.
7112      * @param {Node} node
7113      * @return {Boolean}
7114      */
7115     isAncestor : function(node){
7116         var p = this.parentNode;
7117         while(p){
7118             if(p == node){
7119                 return true;
7120             }
7121             p = p.parentNode;
7122         }
7123         return false;
7124     },
7125
7126     toString : function(){
7127         return "[Node"+(this.id?" "+this.id:"")+"]";
7128     }
7129 });/*
7130  * Based on:
7131  * Ext JS Library 1.1.1
7132  * Copyright(c) 2006-2007, Ext JS, LLC.
7133  *
7134  * Originally Released Under LGPL - original licence link has changed is not relivant.
7135  *
7136  * Fork - LGPL
7137  * <script type="text/javascript">
7138  */
7139  
7140
7141 /**
7142  * @class Roo.ComponentMgr
7143  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
7144  * @singleton
7145  */
7146 Roo.ComponentMgr = function(){
7147     var all = new Roo.util.MixedCollection();
7148
7149     return {
7150         /**
7151          * Registers a component.
7152          * @param {Roo.Component} c The component
7153          */
7154         register : function(c){
7155             all.add(c);
7156         },
7157
7158         /**
7159          * Unregisters a component.
7160          * @param {Roo.Component} c The component
7161          */
7162         unregister : function(c){
7163             all.remove(c);
7164         },
7165
7166         /**
7167          * Returns a component by id
7168          * @param {String} id The component id
7169          */
7170         get : function(id){
7171             return all.get(id);
7172         },
7173
7174         /**
7175          * Registers a function that will be called when a specified component is added to ComponentMgr
7176          * @param {String} id The component id
7177          * @param {Funtction} fn The callback function
7178          * @param {Object} scope The scope of the callback
7179          */
7180         onAvailable : function(id, fn, scope){
7181             all.on("add", function(index, o){
7182                 if(o.id == id){
7183                     fn.call(scope || o, o);
7184                     all.un("add", fn, scope);
7185                 }
7186             });
7187         }
7188     };
7189 }();/*
7190  * Based on:
7191  * Ext JS Library 1.1.1
7192  * Copyright(c) 2006-2007, Ext JS, LLC.
7193  *
7194  * Originally Released Under LGPL - original licence link has changed is not relivant.
7195  *
7196  * Fork - LGPL
7197  * <script type="text/javascript">
7198  */
7199  
7200 /**
7201  * @class Roo.Component
7202  * @extends Roo.util.Observable
7203  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
7204  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
7205  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
7206  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
7207  * All visual components (widgets) that require rendering into a layout should subclass Component.
7208  * @constructor
7209  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
7210  * 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
7211  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
7212  */
7213 Roo.Component = function(config){
7214     config = config || {};
7215     if(config.tagName || config.dom || typeof config == "string"){ // element object
7216         config = {el: config, id: config.id || config};
7217     }
7218     this.initialConfig = config;
7219
7220     Roo.apply(this, config);
7221     this.addEvents({
7222         /**
7223          * @event disable
7224          * Fires after the component is disabled.
7225              * @param {Roo.Component} this
7226              */
7227         disable : true,
7228         /**
7229          * @event enable
7230          * Fires after the component is enabled.
7231              * @param {Roo.Component} this
7232              */
7233         enable : true,
7234         /**
7235          * @event beforeshow
7236          * Fires before the component is shown.  Return false to stop the show.
7237              * @param {Roo.Component} this
7238              */
7239         beforeshow : true,
7240         /**
7241          * @event show
7242          * Fires after the component is shown.
7243              * @param {Roo.Component} this
7244              */
7245         show : true,
7246         /**
7247          * @event beforehide
7248          * Fires before the component is hidden. Return false to stop the hide.
7249              * @param {Roo.Component} this
7250              */
7251         beforehide : true,
7252         /**
7253          * @event hide
7254          * Fires after the component is hidden.
7255              * @param {Roo.Component} this
7256              */
7257         hide : true,
7258         /**
7259          * @event beforerender
7260          * Fires before the component is rendered. Return false to stop the render.
7261              * @param {Roo.Component} this
7262              */
7263         beforerender : true,
7264         /**
7265          * @event render
7266          * Fires after the component is rendered.
7267              * @param {Roo.Component} this
7268              */
7269         render : true,
7270         /**
7271          * @event beforedestroy
7272          * Fires before the component is destroyed. Return false to stop the destroy.
7273              * @param {Roo.Component} this
7274              */
7275         beforedestroy : true,
7276         /**
7277          * @event destroy
7278          * Fires after the component is destroyed.
7279              * @param {Roo.Component} this
7280              */
7281         destroy : true
7282     });
7283     if(!this.id){
7284         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
7285     }
7286     Roo.ComponentMgr.register(this);
7287     Roo.Component.superclass.constructor.call(this);
7288     this.initComponent();
7289     if(this.renderTo){ // not supported by all components yet. use at your own risk!
7290         this.render(this.renderTo);
7291         delete this.renderTo;
7292     }
7293 };
7294
7295 // private
7296 Roo.Component.AUTO_ID = 1000;
7297
7298 Roo.extend(Roo.Component, Roo.util.Observable, {
7299     /**
7300      * @property {Boolean} hidden
7301      * true if this component is hidden. Read-only.
7302      */
7303     hidden : false,
7304     /**
7305      * true if this component is disabled. Read-only.
7306      */
7307     disabled : false,
7308     /**
7309      * true if this component has been rendered. Read-only.
7310      */
7311     rendered : false,
7312     
7313     /** @cfg {String} disableClass
7314      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
7315      */
7316     disabledClass : "x-item-disabled",
7317         /** @cfg {Boolean} allowDomMove
7318          * Whether the component can move the Dom node when rendering (defaults to true).
7319          */
7320     allowDomMove : true,
7321     /** @cfg {String} hideMode
7322      * How this component should hidden. Supported values are
7323      * "visibility" (css visibility), "offsets" (negative offset position) and
7324      * "display" (css display) - defaults to "display".
7325      */
7326     hideMode: 'display',
7327
7328     // private
7329     ctype : "Roo.Component",
7330
7331     /** @cfg {String} actionMode 
7332      * which property holds the element that used for  hide() / show() / disable() / enable()
7333      * default is 'el' 
7334      */
7335     actionMode : "el",
7336
7337     // private
7338     getActionEl : function(){
7339         return this[this.actionMode];
7340     },
7341
7342     initComponent : Roo.emptyFn,
7343     /**
7344      * If this is a lazy rendering component, render it to its container element.
7345      * @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.
7346      */
7347     render : function(container, position){
7348         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
7349             if(!container && this.el){
7350                 this.el = Roo.get(this.el);
7351                 container = this.el.dom.parentNode;
7352                 this.allowDomMove = false;
7353             }
7354             this.container = Roo.get(container);
7355             this.rendered = true;
7356             if(position !== undefined){
7357                 if(typeof position == 'number'){
7358                     position = this.container.dom.childNodes[position];
7359                 }else{
7360                     position = Roo.getDom(position);
7361                 }
7362             }
7363             this.onRender(this.container, position || null);
7364             if(this.cls){
7365                 this.el.addClass(this.cls);
7366                 delete this.cls;
7367             }
7368             if(this.style){
7369                 this.el.applyStyles(this.style);
7370                 delete this.style;
7371             }
7372             this.fireEvent("render", this);
7373             this.afterRender(this.container);
7374             if(this.hidden){
7375                 this.hide();
7376             }
7377             if(this.disabled){
7378                 this.disable();
7379             }
7380         }
7381         return this;
7382     },
7383
7384     // private
7385     // default function is not really useful
7386     onRender : function(ct, position){
7387         if(this.el){
7388             this.el = Roo.get(this.el);
7389             if(this.allowDomMove !== false){
7390                 ct.dom.insertBefore(this.el.dom, position);
7391             }
7392         }
7393     },
7394
7395     // private
7396     getAutoCreate : function(){
7397         var cfg = typeof this.autoCreate == "object" ?
7398                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
7399         if(this.id && !cfg.id){
7400             cfg.id = this.id;
7401         }
7402         return cfg;
7403     },
7404
7405     // private
7406     afterRender : Roo.emptyFn,
7407
7408     /**
7409      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
7410      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
7411      */
7412     destroy : function(){
7413         if(this.fireEvent("beforedestroy", this) !== false){
7414             this.purgeListeners();
7415             this.beforeDestroy();
7416             if(this.rendered){
7417                 this.el.removeAllListeners();
7418                 this.el.remove();
7419                 if(this.actionMode == "container"){
7420                     this.container.remove();
7421                 }
7422             }
7423             this.onDestroy();
7424             Roo.ComponentMgr.unregister(this);
7425             this.fireEvent("destroy", this);
7426         }
7427     },
7428
7429         // private
7430     beforeDestroy : function(){
7431
7432     },
7433
7434         // private
7435         onDestroy : function(){
7436
7437     },
7438
7439     /**
7440      * Returns the underlying {@link Roo.Element}.
7441      * @return {Roo.Element} The element
7442      */
7443     getEl : function(){
7444         return this.el;
7445     },
7446
7447     /**
7448      * Returns the id of this component.
7449      * @return {String}
7450      */
7451     getId : function(){
7452         return this.id;
7453     },
7454
7455     /**
7456      * Try to focus this component.
7457      * @param {Boolean} selectText True to also select the text in this component (if applicable)
7458      * @return {Roo.Component} this
7459      */
7460     focus : function(selectText){
7461         if(this.rendered){
7462             this.el.focus();
7463             if(selectText === true){
7464                 this.el.dom.select();
7465             }
7466         }
7467         return this;
7468     },
7469
7470     // private
7471     blur : function(){
7472         if(this.rendered){
7473             this.el.blur();
7474         }
7475         return this;
7476     },
7477
7478     /**
7479      * Disable this component.
7480      * @return {Roo.Component} this
7481      */
7482     disable : function(){
7483         if(this.rendered){
7484             this.onDisable();
7485         }
7486         this.disabled = true;
7487         this.fireEvent("disable", this);
7488         return this;
7489     },
7490
7491         // private
7492     onDisable : function(){
7493         this.getActionEl().addClass(this.disabledClass);
7494         this.el.dom.disabled = true;
7495     },
7496
7497     /**
7498      * Enable this component.
7499      * @return {Roo.Component} this
7500      */
7501     enable : function(){
7502         if(this.rendered){
7503             this.onEnable();
7504         }
7505         this.disabled = false;
7506         this.fireEvent("enable", this);
7507         return this;
7508     },
7509
7510         // private
7511     onEnable : function(){
7512         this.getActionEl().removeClass(this.disabledClass);
7513         this.el.dom.disabled = false;
7514     },
7515
7516     /**
7517      * Convenience function for setting disabled/enabled by boolean.
7518      * @param {Boolean} disabled
7519      */
7520     setDisabled : function(disabled){
7521         this[disabled ? "disable" : "enable"]();
7522     },
7523
7524     /**
7525      * Show this component.
7526      * @return {Roo.Component} this
7527      */
7528     show: function(){
7529         if(this.fireEvent("beforeshow", this) !== false){
7530             this.hidden = false;
7531             if(this.rendered){
7532                 this.onShow();
7533             }
7534             this.fireEvent("show", this);
7535         }
7536         return this;
7537     },
7538
7539     // private
7540     onShow : function(){
7541         var ae = this.getActionEl();
7542         if(this.hideMode == 'visibility'){
7543             ae.dom.style.visibility = "visible";
7544         }else if(this.hideMode == 'offsets'){
7545             ae.removeClass('x-hidden');
7546         }else{
7547             ae.dom.style.display = "";
7548         }
7549     },
7550
7551     /**
7552      * Hide this component.
7553      * @return {Roo.Component} this
7554      */
7555     hide: function(){
7556         if(this.fireEvent("beforehide", this) !== false){
7557             this.hidden = true;
7558             if(this.rendered){
7559                 this.onHide();
7560             }
7561             this.fireEvent("hide", this);
7562         }
7563         return this;
7564     },
7565
7566     // private
7567     onHide : function(){
7568         var ae = this.getActionEl();
7569         if(this.hideMode == 'visibility'){
7570             ae.dom.style.visibility = "hidden";
7571         }else if(this.hideMode == 'offsets'){
7572             ae.addClass('x-hidden');
7573         }else{
7574             ae.dom.style.display = "none";
7575         }
7576     },
7577
7578     /**
7579      * Convenience function to hide or show this component by boolean.
7580      * @param {Boolean} visible True to show, false to hide
7581      * @return {Roo.Component} this
7582      */
7583     setVisible: function(visible){
7584         if(visible) {
7585             this.show();
7586         }else{
7587             this.hide();
7588         }
7589         return this;
7590     },
7591
7592     /**
7593      * Returns true if this component is visible.
7594      */
7595     isVisible : function(){
7596         return this.getActionEl().isVisible();
7597     },
7598
7599     cloneConfig : function(overrides){
7600         overrides = overrides || {};
7601         var id = overrides.id || Roo.id();
7602         var cfg = Roo.applyIf(overrides, this.initialConfig);
7603         cfg.id = id; // prevent dup id
7604         return new this.constructor(cfg);
7605     }
7606 });/*
7607  * Based on:
7608  * Ext JS Library 1.1.1
7609  * Copyright(c) 2006-2007, Ext JS, LLC.
7610  *
7611  * Originally Released Under LGPL - original licence link has changed is not relivant.
7612  *
7613  * Fork - LGPL
7614  * <script type="text/javascript">
7615  */
7616  (function(){ 
7617 /**
7618  * @class Roo.Layer
7619  * @extends Roo.Element
7620  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
7621  * automatic maintaining of shadow/shim positions.
7622  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
7623  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
7624  * you can pass a string with a CSS class name. False turns off the shadow.
7625  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
7626  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
7627  * @cfg {String} cls CSS class to add to the element
7628  * @cfg {Number} zindex Starting z-index (defaults to 11000)
7629  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
7630  * @constructor
7631  * @param {Object} config An object with config options.
7632  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
7633  */
7634
7635 Roo.Layer = function(config, existingEl){
7636     config = config || {};
7637     var dh = Roo.DomHelper;
7638     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
7639     if(existingEl){
7640         this.dom = Roo.getDom(existingEl);
7641     }
7642     if(!this.dom){
7643         var o = config.dh || {tag: "div", cls: "x-layer"};
7644         this.dom = dh.append(pel, o);
7645     }
7646     if(config.cls){
7647         this.addClass(config.cls);
7648     }
7649     this.constrain = config.constrain !== false;
7650     this.visibilityMode = Roo.Element.VISIBILITY;
7651     if(config.id){
7652         this.id = this.dom.id = config.id;
7653     }else{
7654         this.id = Roo.id(this.dom);
7655     }
7656     this.zindex = config.zindex || this.getZIndex();
7657     this.position("absolute", this.zindex);
7658     if(config.shadow){
7659         this.shadowOffset = config.shadowOffset || 4;
7660         this.shadow = new Roo.Shadow({
7661             offset : this.shadowOffset,
7662             mode : config.shadow
7663         });
7664     }else{
7665         this.shadowOffset = 0;
7666     }
7667     this.useShim = config.shim !== false && Roo.useShims;
7668     this.useDisplay = config.useDisplay;
7669     this.hide();
7670 };
7671
7672 var supr = Roo.Element.prototype;
7673
7674 // shims are shared among layer to keep from having 100 iframes
7675 var shims = [];
7676
7677 Roo.extend(Roo.Layer, Roo.Element, {
7678
7679     getZIndex : function(){
7680         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
7681     },
7682
7683     getShim : function(){
7684         if(!this.useShim){
7685             return null;
7686         }
7687         if(this.shim){
7688             return this.shim;
7689         }
7690         var shim = shims.shift();
7691         if(!shim){
7692             shim = this.createShim();
7693             shim.enableDisplayMode('block');
7694             shim.dom.style.display = 'none';
7695             shim.dom.style.visibility = 'visible';
7696         }
7697         var pn = this.dom.parentNode;
7698         if(shim.dom.parentNode != pn){
7699             pn.insertBefore(shim.dom, this.dom);
7700         }
7701         shim.setStyle('z-index', this.getZIndex()-2);
7702         this.shim = shim;
7703         return shim;
7704     },
7705
7706     hideShim : function(){
7707         if(this.shim){
7708             this.shim.setDisplayed(false);
7709             shims.push(this.shim);
7710             delete this.shim;
7711         }
7712     },
7713
7714     disableShadow : function(){
7715         if(this.shadow){
7716             this.shadowDisabled = true;
7717             this.shadow.hide();
7718             this.lastShadowOffset = this.shadowOffset;
7719             this.shadowOffset = 0;
7720         }
7721     },
7722
7723     enableShadow : function(show){
7724         if(this.shadow){
7725             this.shadowDisabled = false;
7726             this.shadowOffset = this.lastShadowOffset;
7727             delete this.lastShadowOffset;
7728             if(show){
7729                 this.sync(true);
7730             }
7731         }
7732     },
7733
7734     // private
7735     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
7736     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
7737     sync : function(doShow){
7738         var sw = this.shadow;
7739         if(!this.updating && this.isVisible() && (sw || this.useShim)){
7740             var sh = this.getShim();
7741
7742             var w = this.getWidth(),
7743                 h = this.getHeight();
7744
7745             var l = this.getLeft(true),
7746                 t = this.getTop(true);
7747
7748             if(sw && !this.shadowDisabled){
7749                 if(doShow && !sw.isVisible()){
7750                     sw.show(this);
7751                 }else{
7752                     sw.realign(l, t, w, h);
7753                 }
7754                 if(sh){
7755                     if(doShow){
7756                        sh.show();
7757                     }
7758                     // fit the shim behind the shadow, so it is shimmed too
7759                     var a = sw.adjusts, s = sh.dom.style;
7760                     s.left = (Math.min(l, l+a.l))+"px";
7761                     s.top = (Math.min(t, t+a.t))+"px";
7762                     s.width = (w+a.w)+"px";
7763                     s.height = (h+a.h)+"px";
7764                 }
7765             }else if(sh){
7766                 if(doShow){
7767                    sh.show();
7768                 }
7769                 sh.setSize(w, h);
7770                 sh.setLeftTop(l, t);
7771             }
7772             
7773         }
7774     },
7775
7776     // private
7777     destroy : function(){
7778         this.hideShim();
7779         if(this.shadow){
7780             this.shadow.hide();
7781         }
7782         this.removeAllListeners();
7783         var pn = this.dom.parentNode;
7784         if(pn){
7785             pn.removeChild(this.dom);
7786         }
7787         Roo.Element.uncache(this.id);
7788     },
7789
7790     remove : function(){
7791         this.destroy();
7792     },
7793
7794     // private
7795     beginUpdate : function(){
7796         this.updating = true;
7797     },
7798
7799     // private
7800     endUpdate : function(){
7801         this.updating = false;
7802         this.sync(true);
7803     },
7804
7805     // private
7806     hideUnders : function(negOffset){
7807         if(this.shadow){
7808             this.shadow.hide();
7809         }
7810         this.hideShim();
7811     },
7812
7813     // private
7814     constrainXY : function(){
7815         if(this.constrain){
7816             var vw = Roo.lib.Dom.getViewWidth(),
7817                 vh = Roo.lib.Dom.getViewHeight();
7818             var s = Roo.get(document).getScroll();
7819
7820             var xy = this.getXY();
7821             var x = xy[0], y = xy[1];   
7822             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
7823             // only move it if it needs it
7824             var moved = false;
7825             // first validate right/bottom
7826             if((x + w) > vw+s.left){
7827                 x = vw - w - this.shadowOffset;
7828                 moved = true;
7829             }
7830             if((y + h) > vh+s.top){
7831                 y = vh - h - this.shadowOffset;
7832                 moved = true;
7833             }
7834             // then make sure top/left isn't negative
7835             if(x < s.left){
7836                 x = s.left;
7837                 moved = true;
7838             }
7839             if(y < s.top){
7840                 y = s.top;
7841                 moved = true;
7842             }
7843             if(moved){
7844                 if(this.avoidY){
7845                     var ay = this.avoidY;
7846                     if(y <= ay && (y+h) >= ay){
7847                         y = ay-h-5;   
7848                     }
7849                 }
7850                 xy = [x, y];
7851                 this.storeXY(xy);
7852                 supr.setXY.call(this, xy);
7853                 this.sync();
7854             }
7855         }
7856     },
7857
7858     isVisible : function(){
7859         return this.visible;    
7860     },
7861
7862     // private
7863     showAction : function(){
7864         this.visible = true; // track visibility to prevent getStyle calls
7865         if(this.useDisplay === true){
7866             this.setDisplayed("");
7867         }else if(this.lastXY){
7868             supr.setXY.call(this, this.lastXY);
7869         }else if(this.lastLT){
7870             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
7871         }
7872     },
7873
7874     // private
7875     hideAction : function(){
7876         this.visible = false;
7877         if(this.useDisplay === true){
7878             this.setDisplayed(false);
7879         }else{
7880             this.setLeftTop(-10000,-10000);
7881         }
7882     },
7883
7884     // overridden Element method
7885     setVisible : function(v, a, d, c, e){
7886         if(v){
7887             this.showAction();
7888         }
7889         if(a && v){
7890             var cb = function(){
7891                 this.sync(true);
7892                 if(c){
7893                     c();
7894                 }
7895             }.createDelegate(this);
7896             supr.setVisible.call(this, true, true, d, cb, e);
7897         }else{
7898             if(!v){
7899                 this.hideUnders(true);
7900             }
7901             var cb = c;
7902             if(a){
7903                 cb = function(){
7904                     this.hideAction();
7905                     if(c){
7906                         c();
7907                     }
7908                 }.createDelegate(this);
7909             }
7910             supr.setVisible.call(this, v, a, d, cb, e);
7911             if(v){
7912                 this.sync(true);
7913             }else if(!a){
7914                 this.hideAction();
7915             }
7916         }
7917     },
7918
7919     storeXY : function(xy){
7920         delete this.lastLT;
7921         this.lastXY = xy;
7922     },
7923
7924     storeLeftTop : function(left, top){
7925         delete this.lastXY;
7926         this.lastLT = [left, top];
7927     },
7928
7929     // private
7930     beforeFx : function(){
7931         this.beforeAction();
7932         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
7933     },
7934
7935     // private
7936     afterFx : function(){
7937         Roo.Layer.superclass.afterFx.apply(this, arguments);
7938         this.sync(this.isVisible());
7939     },
7940
7941     // private
7942     beforeAction : function(){
7943         if(!this.updating && this.shadow){
7944             this.shadow.hide();
7945         }
7946     },
7947
7948     // overridden Element method
7949     setLeft : function(left){
7950         this.storeLeftTop(left, this.getTop(true));
7951         supr.setLeft.apply(this, arguments);
7952         this.sync();
7953     },
7954
7955     setTop : function(top){
7956         this.storeLeftTop(this.getLeft(true), top);
7957         supr.setTop.apply(this, arguments);
7958         this.sync();
7959     },
7960
7961     setLeftTop : function(left, top){
7962         this.storeLeftTop(left, top);
7963         supr.setLeftTop.apply(this, arguments);
7964         this.sync();
7965     },
7966
7967     setXY : function(xy, a, d, c, e){
7968         this.fixDisplay();
7969         this.beforeAction();
7970         this.storeXY(xy);
7971         var cb = this.createCB(c);
7972         supr.setXY.call(this, xy, a, d, cb, e);
7973         if(!a){
7974             cb();
7975         }
7976     },
7977
7978     // private
7979     createCB : function(c){
7980         var el = this;
7981         return function(){
7982             el.constrainXY();
7983             el.sync(true);
7984             if(c){
7985                 c();
7986             }
7987         };
7988     },
7989
7990     // overridden Element method
7991     setX : function(x, a, d, c, e){
7992         this.setXY([x, this.getY()], a, d, c, e);
7993     },
7994
7995     // overridden Element method
7996     setY : function(y, a, d, c, e){
7997         this.setXY([this.getX(), y], a, d, c, e);
7998     },
7999
8000     // overridden Element method
8001     setSize : function(w, h, a, d, c, e){
8002         this.beforeAction();
8003         var cb = this.createCB(c);
8004         supr.setSize.call(this, w, h, a, d, cb, e);
8005         if(!a){
8006             cb();
8007         }
8008     },
8009
8010     // overridden Element method
8011     setWidth : function(w, a, d, c, e){
8012         this.beforeAction();
8013         var cb = this.createCB(c);
8014         supr.setWidth.call(this, w, a, d, cb, e);
8015         if(!a){
8016             cb();
8017         }
8018     },
8019
8020     // overridden Element method
8021     setHeight : function(h, a, d, c, e){
8022         this.beforeAction();
8023         var cb = this.createCB(c);
8024         supr.setHeight.call(this, h, a, d, cb, e);
8025         if(!a){
8026             cb();
8027         }
8028     },
8029
8030     // overridden Element method
8031     setBounds : function(x, y, w, h, a, d, c, e){
8032         this.beforeAction();
8033         var cb = this.createCB(c);
8034         if(!a){
8035             this.storeXY([x, y]);
8036             supr.setXY.call(this, [x, y]);
8037             supr.setSize.call(this, w, h, a, d, cb, e);
8038             cb();
8039         }else{
8040             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
8041         }
8042         return this;
8043     },
8044     
8045     /**
8046      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
8047      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
8048      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
8049      * @param {Number} zindex The new z-index to set
8050      * @return {this} The Layer
8051      */
8052     setZIndex : function(zindex){
8053         this.zindex = zindex;
8054         this.setStyle("z-index", zindex + 2);
8055         if(this.shadow){
8056             this.shadow.setZIndex(zindex + 1);
8057         }
8058         if(this.shim){
8059             this.shim.setStyle("z-index", zindex);
8060         }
8061     }
8062 });
8063 })();/*
8064  * Based on:
8065  * Ext JS Library 1.1.1
8066  * Copyright(c) 2006-2007, Ext JS, LLC.
8067  *
8068  * Originally Released Under LGPL - original licence link has changed is not relivant.
8069  *
8070  * Fork - LGPL
8071  * <script type="text/javascript">
8072  */
8073
8074
8075 /**
8076  * @class Roo.Shadow
8077  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
8078  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
8079  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
8080  * @constructor
8081  * Create a new Shadow
8082  * @param {Object} config The config object
8083  */
8084 Roo.Shadow = function(config){
8085     Roo.apply(this, config);
8086     if(typeof this.mode != "string"){
8087         this.mode = this.defaultMode;
8088     }
8089     var o = this.offset, a = {h: 0};
8090     var rad = Math.floor(this.offset/2);
8091     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
8092         case "drop":
8093             a.w = 0;
8094             a.l = a.t = o;
8095             a.t -= 1;
8096             if(Roo.isIE){
8097                 a.l -= this.offset + rad;
8098                 a.t -= this.offset + rad;
8099                 a.w -= rad;
8100                 a.h -= rad;
8101                 a.t += 1;
8102             }
8103         break;
8104         case "sides":
8105             a.w = (o*2);
8106             a.l = -o;
8107             a.t = o-1;
8108             if(Roo.isIE){
8109                 a.l -= (this.offset - rad);
8110                 a.t -= this.offset + rad;
8111                 a.l += 1;
8112                 a.w -= (this.offset - rad)*2;
8113                 a.w -= rad + 1;
8114                 a.h -= 1;
8115             }
8116         break;
8117         case "frame":
8118             a.w = a.h = (o*2);
8119             a.l = a.t = -o;
8120             a.t += 1;
8121             a.h -= 2;
8122             if(Roo.isIE){
8123                 a.l -= (this.offset - rad);
8124                 a.t -= (this.offset - rad);
8125                 a.l += 1;
8126                 a.w -= (this.offset + rad + 1);
8127                 a.h -= (this.offset + rad);
8128                 a.h += 1;
8129             }
8130         break;
8131     };
8132
8133     this.adjusts = a;
8134 };
8135
8136 Roo.Shadow.prototype = {
8137     /**
8138      * @cfg {String} mode
8139      * The shadow display mode.  Supports the following options:<br />
8140      * sides: Shadow displays on both sides and bottom only<br />
8141      * frame: Shadow displays equally on all four sides<br />
8142      * drop: Traditional bottom-right drop shadow (default)
8143      */
8144     /**
8145      * @cfg {String} offset
8146      * The number of pixels to offset the shadow from the element (defaults to 4)
8147      */
8148     offset: 4,
8149
8150     // private
8151     defaultMode: "drop",
8152
8153     /**
8154      * Displays the shadow under the target element
8155      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
8156      */
8157     show : function(target){
8158         target = Roo.get(target);
8159         if(!this.el){
8160             this.el = Roo.Shadow.Pool.pull();
8161             if(this.el.dom.nextSibling != target.dom){
8162                 this.el.insertBefore(target);
8163             }
8164         }
8165         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
8166         if(Roo.isIE){
8167             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
8168         }
8169         this.realign(
8170             target.getLeft(true),
8171             target.getTop(true),
8172             target.getWidth(),
8173             target.getHeight()
8174         );
8175         this.el.dom.style.display = "block";
8176     },
8177
8178     /**
8179      * Returns true if the shadow is visible, else false
8180      */
8181     isVisible : function(){
8182         return this.el ? true : false;  
8183     },
8184
8185     /**
8186      * Direct alignment when values are already available. Show must be called at least once before
8187      * calling this method to ensure it is initialized.
8188      * @param {Number} left The target element left position
8189      * @param {Number} top The target element top position
8190      * @param {Number} width The target element width
8191      * @param {Number} height The target element height
8192      */
8193     realign : function(l, t, w, h){
8194         if(!this.el){
8195             return;
8196         }
8197         var a = this.adjusts, d = this.el.dom, s = d.style;
8198         var iea = 0;
8199         s.left = (l+a.l)+"px";
8200         s.top = (t+a.t)+"px";
8201         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
8202         if(s.width != sws || s.height != shs){
8203             s.width = sws;
8204             s.height = shs;
8205             if(!Roo.isIE){
8206                 var cn = d.childNodes;
8207                 var sww = Math.max(0, (sw-12))+"px";
8208                 cn[0].childNodes[1].style.width = sww;
8209                 cn[1].childNodes[1].style.width = sww;
8210                 cn[2].childNodes[1].style.width = sww;
8211                 cn[1].style.height = Math.max(0, (sh-12))+"px";
8212             }
8213         }
8214     },
8215
8216     /**
8217      * Hides this shadow
8218      */
8219     hide : function(){
8220         if(this.el){
8221             this.el.dom.style.display = "none";
8222             Roo.Shadow.Pool.push(this.el);
8223             delete this.el;
8224         }
8225     },
8226
8227     /**
8228      * Adjust the z-index of this shadow
8229      * @param {Number} zindex The new z-index
8230      */
8231     setZIndex : function(z){
8232         this.zIndex = z;
8233         if(this.el){
8234             this.el.setStyle("z-index", z);
8235         }
8236     }
8237 };
8238
8239 // Private utility class that manages the internal Shadow cache
8240 Roo.Shadow.Pool = function(){
8241     var p = [];
8242     var markup = Roo.isIE ?
8243                  '<div class="x-ie-shadow"></div>' :
8244                  '<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>';
8245     return {
8246         pull : function(){
8247             var sh = p.shift();
8248             if(!sh){
8249                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
8250                 sh.autoBoxAdjust = false;
8251             }
8252             return sh;
8253         },
8254
8255         push : function(sh){
8256             p.push(sh);
8257         }
8258     };
8259 }();/*
8260  * Based on:
8261  * Ext JS Library 1.1.1
8262  * Copyright(c) 2006-2007, Ext JS, LLC.
8263  *
8264  * Originally Released Under LGPL - original licence link has changed is not relivant.
8265  *
8266  * Fork - LGPL
8267  * <script type="text/javascript">
8268  */
8269
8270 /**
8271  * @class Roo.BoxComponent
8272  * @extends Roo.Component
8273  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
8274  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
8275  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
8276  * layout containers.
8277  * @constructor
8278  * @param {Roo.Element/String/Object} config The configuration options.
8279  */
8280 Roo.BoxComponent = function(config){
8281     Roo.Component.call(this, config);
8282     this.addEvents({
8283         /**
8284          * @event resize
8285          * Fires after the component is resized.
8286              * @param {Roo.Component} this
8287              * @param {Number} adjWidth The box-adjusted width that was set
8288              * @param {Number} adjHeight The box-adjusted height that was set
8289              * @param {Number} rawWidth The width that was originally specified
8290              * @param {Number} rawHeight The height that was originally specified
8291              */
8292         resize : true,
8293         /**
8294          * @event move
8295          * Fires after the component is moved.
8296              * @param {Roo.Component} this
8297              * @param {Number} x The new x position
8298              * @param {Number} y The new y position
8299              */
8300         move : true
8301     });
8302 };
8303
8304 Roo.extend(Roo.BoxComponent, Roo.Component, {
8305     // private, set in afterRender to signify that the component has been rendered
8306     boxReady : false,
8307     // private, used to defer height settings to subclasses
8308     deferHeight: false,
8309     /** @cfg {Number} width
8310      * width (optional) size of component
8311      */
8312      /** @cfg {Number} height
8313      * height (optional) size of component
8314      */
8315      
8316     /**
8317      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
8318      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
8319      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
8320      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
8321      * @return {Roo.BoxComponent} this
8322      */
8323     setSize : function(w, h){
8324         // support for standard size objects
8325         if(typeof w == 'object'){
8326             h = w.height;
8327             w = w.width;
8328         }
8329         // not rendered
8330         if(!this.boxReady){
8331             this.width = w;
8332             this.height = h;
8333             return this;
8334         }
8335
8336         // prevent recalcs when not needed
8337         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
8338             return this;
8339         }
8340         this.lastSize = {width: w, height: h};
8341
8342         var adj = this.adjustSize(w, h);
8343         var aw = adj.width, ah = adj.height;
8344         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
8345             var rz = this.getResizeEl();
8346             if(!this.deferHeight && aw !== undefined && ah !== undefined){
8347                 rz.setSize(aw, ah);
8348             }else if(!this.deferHeight && ah !== undefined){
8349                 rz.setHeight(ah);
8350             }else if(aw !== undefined){
8351                 rz.setWidth(aw);
8352             }
8353             this.onResize(aw, ah, w, h);
8354             this.fireEvent('resize', this, aw, ah, w, h);
8355         }
8356         return this;
8357     },
8358
8359     /**
8360      * Gets the current size of the component's underlying element.
8361      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8362      */
8363     getSize : function(){
8364         return this.el.getSize();
8365     },
8366
8367     /**
8368      * Gets the current XY position of the component's underlying element.
8369      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8370      * @return {Array} The XY position of the element (e.g., [100, 200])
8371      */
8372     getPosition : function(local){
8373         if(local === true){
8374             return [this.el.getLeft(true), this.el.getTop(true)];
8375         }
8376         return this.xy || this.el.getXY();
8377     },
8378
8379     /**
8380      * Gets the current box measurements of the component's underlying element.
8381      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8382      * @returns {Object} box An object in the format {x, y, width, height}
8383      */
8384     getBox : function(local){
8385         var s = this.el.getSize();
8386         if(local){
8387             s.x = this.el.getLeft(true);
8388             s.y = this.el.getTop(true);
8389         }else{
8390             var xy = this.xy || this.el.getXY();
8391             s.x = xy[0];
8392             s.y = xy[1];
8393         }
8394         return s;
8395     },
8396
8397     /**
8398      * Sets the current box measurements of the component's underlying element.
8399      * @param {Object} box An object in the format {x, y, width, height}
8400      * @returns {Roo.BoxComponent} this
8401      */
8402     updateBox : function(box){
8403         this.setSize(box.width, box.height);
8404         this.setPagePosition(box.x, box.y);
8405         return this;
8406     },
8407
8408     // protected
8409     getResizeEl : function(){
8410         return this.resizeEl || this.el;
8411     },
8412
8413     // protected
8414     getPositionEl : function(){
8415         return this.positionEl || this.el;
8416     },
8417
8418     /**
8419      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
8420      * This method fires the move event.
8421      * @param {Number} left The new left
8422      * @param {Number} top The new top
8423      * @returns {Roo.BoxComponent} this
8424      */
8425     setPosition : function(x, y){
8426         this.x = x;
8427         this.y = y;
8428         if(!this.boxReady){
8429             return this;
8430         }
8431         var adj = this.adjustPosition(x, y);
8432         var ax = adj.x, ay = adj.y;
8433
8434         var el = this.getPositionEl();
8435         if(ax !== undefined || ay !== undefined){
8436             if(ax !== undefined && ay !== undefined){
8437                 el.setLeftTop(ax, ay);
8438             }else if(ax !== undefined){
8439                 el.setLeft(ax);
8440             }else if(ay !== undefined){
8441                 el.setTop(ay);
8442             }
8443             this.onPosition(ax, ay);
8444             this.fireEvent('move', this, ax, ay);
8445         }
8446         return this;
8447     },
8448
8449     /**
8450      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
8451      * This method fires the move event.
8452      * @param {Number} x The new x position
8453      * @param {Number} y The new y position
8454      * @returns {Roo.BoxComponent} this
8455      */
8456     setPagePosition : function(x, y){
8457         this.pageX = x;
8458         this.pageY = y;
8459         if(!this.boxReady){
8460             return;
8461         }
8462         if(x === undefined || y === undefined){ // cannot translate undefined points
8463             return;
8464         }
8465         var p = this.el.translatePoints(x, y);
8466         this.setPosition(p.left, p.top);
8467         return this;
8468     },
8469
8470     // private
8471     onRender : function(ct, position){
8472         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
8473         if(this.resizeEl){
8474             this.resizeEl = Roo.get(this.resizeEl);
8475         }
8476         if(this.positionEl){
8477             this.positionEl = Roo.get(this.positionEl);
8478         }
8479     },
8480
8481     // private
8482     afterRender : function(){
8483         Roo.BoxComponent.superclass.afterRender.call(this);
8484         this.boxReady = true;
8485         this.setSize(this.width, this.height);
8486         if(this.x || this.y){
8487             this.setPosition(this.x, this.y);
8488         }
8489         if(this.pageX || this.pageY){
8490             this.setPagePosition(this.pageX, this.pageY);
8491         }
8492     },
8493
8494     /**
8495      * Force the component's size to recalculate based on the underlying element's current height and width.
8496      * @returns {Roo.BoxComponent} this
8497      */
8498     syncSize : function(){
8499         delete this.lastSize;
8500         this.setSize(this.el.getWidth(), this.el.getHeight());
8501         return this;
8502     },
8503
8504     /**
8505      * Called after the component is resized, this method is empty by default but can be implemented by any
8506      * subclass that needs to perform custom logic after a resize occurs.
8507      * @param {Number} adjWidth The box-adjusted width that was set
8508      * @param {Number} adjHeight The box-adjusted height that was set
8509      * @param {Number} rawWidth The width that was originally specified
8510      * @param {Number} rawHeight The height that was originally specified
8511      */
8512     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
8513
8514     },
8515
8516     /**
8517      * Called after the component is moved, this method is empty by default but can be implemented by any
8518      * subclass that needs to perform custom logic after a move occurs.
8519      * @param {Number} x The new x position
8520      * @param {Number} y The new y position
8521      */
8522     onPosition : function(x, y){
8523
8524     },
8525
8526     // private
8527     adjustSize : function(w, h){
8528         if(this.autoWidth){
8529             w = 'auto';
8530         }
8531         if(this.autoHeight){
8532             h = 'auto';
8533         }
8534         return {width : w, height: h};
8535     },
8536
8537     // private
8538     adjustPosition : function(x, y){
8539         return {x : x, y: y};
8540     }
8541 });/*
8542  * Based on:
8543  * Ext JS Library 1.1.1
8544  * Copyright(c) 2006-2007, Ext JS, LLC.
8545  *
8546  * Originally Released Under LGPL - original licence link has changed is not relivant.
8547  *
8548  * Fork - LGPL
8549  * <script type="text/javascript">
8550  */
8551
8552
8553 /**
8554  * @class Roo.SplitBar
8555  * @extends Roo.util.Observable
8556  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
8557  * <br><br>
8558  * Usage:
8559  * <pre><code>
8560 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
8561                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
8562 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
8563 split.minSize = 100;
8564 split.maxSize = 600;
8565 split.animate = true;
8566 split.on('moved', splitterMoved);
8567 </code></pre>
8568  * @constructor
8569  * Create a new SplitBar
8570  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
8571  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
8572  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8573  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
8574                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
8575                         position of the SplitBar).
8576  */
8577 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
8578     
8579     /** @private */
8580     this.el = Roo.get(dragElement, true);
8581     this.el.dom.unselectable = "on";
8582     /** @private */
8583     this.resizingEl = Roo.get(resizingElement, true);
8584
8585     /**
8586      * @private
8587      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8588      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
8589      * @type Number
8590      */
8591     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
8592     
8593     /**
8594      * The minimum size of the resizing element. (Defaults to 0)
8595      * @type Number
8596      */
8597     this.minSize = 0;
8598     
8599     /**
8600      * The maximum size of the resizing element. (Defaults to 2000)
8601      * @type Number
8602      */
8603     this.maxSize = 2000;
8604     
8605     /**
8606      * Whether to animate the transition to the new size
8607      * @type Boolean
8608      */
8609     this.animate = false;
8610     
8611     /**
8612      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
8613      * @type Boolean
8614      */
8615     this.useShim = false;
8616     
8617     /** @private */
8618     this.shim = null;
8619     
8620     if(!existingProxy){
8621         /** @private */
8622         this.proxy = Roo.SplitBar.createProxy(this.orientation);
8623     }else{
8624         this.proxy = Roo.get(existingProxy).dom;
8625     }
8626     /** @private */
8627     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
8628     
8629     /** @private */
8630     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
8631     
8632     /** @private */
8633     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
8634     
8635     /** @private */
8636     this.dragSpecs = {};
8637     
8638     /**
8639      * @private The adapter to use to positon and resize elements
8640      */
8641     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
8642     this.adapter.init(this);
8643     
8644     if(this.orientation == Roo.SplitBar.HORIZONTAL){
8645         /** @private */
8646         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
8647         this.el.addClass("x-splitbar-h");
8648     }else{
8649         /** @private */
8650         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
8651         this.el.addClass("x-splitbar-v");
8652     }
8653     
8654     this.addEvents({
8655         /**
8656          * @event resize
8657          * Fires when the splitter is moved (alias for {@link #event-moved})
8658          * @param {Roo.SplitBar} this
8659          * @param {Number} newSize the new width or height
8660          */
8661         "resize" : true,
8662         /**
8663          * @event moved
8664          * Fires when the splitter is moved
8665          * @param {Roo.SplitBar} this
8666          * @param {Number} newSize the new width or height
8667          */
8668         "moved" : true,
8669         /**
8670          * @event beforeresize
8671          * Fires before the splitter is dragged
8672          * @param {Roo.SplitBar} this
8673          */
8674         "beforeresize" : true,
8675
8676         "beforeapply" : true
8677     });
8678
8679     Roo.util.Observable.call(this);
8680 };
8681
8682 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
8683     onStartProxyDrag : function(x, y){
8684         this.fireEvent("beforeresize", this);
8685         if(!this.overlay){
8686             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
8687             o.unselectable();
8688             o.enableDisplayMode("block");
8689             // all splitbars share the same overlay
8690             Roo.SplitBar.prototype.overlay = o;
8691         }
8692         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8693         this.overlay.show();
8694         Roo.get(this.proxy).setDisplayed("block");
8695         var size = this.adapter.getElementSize(this);
8696         this.activeMinSize = this.getMinimumSize();;
8697         this.activeMaxSize = this.getMaximumSize();;
8698         var c1 = size - this.activeMinSize;
8699         var c2 = Math.max(this.activeMaxSize - size, 0);
8700         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8701             this.dd.resetConstraints();
8702             this.dd.setXConstraint(
8703                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
8704                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
8705             );
8706             this.dd.setYConstraint(0, 0);
8707         }else{
8708             this.dd.resetConstraints();
8709             this.dd.setXConstraint(0, 0);
8710             this.dd.setYConstraint(
8711                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
8712                 this.placement == Roo.SplitBar.TOP ? c2 : c1
8713             );
8714          }
8715         this.dragSpecs.startSize = size;
8716         this.dragSpecs.startPoint = [x, y];
8717         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
8718     },
8719     
8720     /** 
8721      * @private Called after the drag operation by the DDProxy
8722      */
8723     onEndProxyDrag : function(e){
8724         Roo.get(this.proxy).setDisplayed(false);
8725         var endPoint = Roo.lib.Event.getXY(e);
8726         if(this.overlay){
8727             this.overlay.hide();
8728         }
8729         var newSize;
8730         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8731             newSize = this.dragSpecs.startSize + 
8732                 (this.placement == Roo.SplitBar.LEFT ?
8733                     endPoint[0] - this.dragSpecs.startPoint[0] :
8734                     this.dragSpecs.startPoint[0] - endPoint[0]
8735                 );
8736         }else{
8737             newSize = this.dragSpecs.startSize + 
8738                 (this.placement == Roo.SplitBar.TOP ?
8739                     endPoint[1] - this.dragSpecs.startPoint[1] :
8740                     this.dragSpecs.startPoint[1] - endPoint[1]
8741                 );
8742         }
8743         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
8744         if(newSize != this.dragSpecs.startSize){
8745             if(this.fireEvent('beforeapply', this, newSize) !== false){
8746                 this.adapter.setElementSize(this, newSize);
8747                 this.fireEvent("moved", this, newSize);
8748                 this.fireEvent("resize", this, newSize);
8749             }
8750         }
8751     },
8752     
8753     /**
8754      * Get the adapter this SplitBar uses
8755      * @return The adapter object
8756      */
8757     getAdapter : function(){
8758         return this.adapter;
8759     },
8760     
8761     /**
8762      * Set the adapter this SplitBar uses
8763      * @param {Object} adapter A SplitBar adapter object
8764      */
8765     setAdapter : function(adapter){
8766         this.adapter = adapter;
8767         this.adapter.init(this);
8768     },
8769     
8770     /**
8771      * Gets the minimum size for the resizing element
8772      * @return {Number} The minimum size
8773      */
8774     getMinimumSize : function(){
8775         return this.minSize;
8776     },
8777     
8778     /**
8779      * Sets the minimum size for the resizing element
8780      * @param {Number} minSize The minimum size
8781      */
8782     setMinimumSize : function(minSize){
8783         this.minSize = minSize;
8784     },
8785     
8786     /**
8787      * Gets the maximum size for the resizing element
8788      * @return {Number} The maximum size
8789      */
8790     getMaximumSize : function(){
8791         return this.maxSize;
8792     },
8793     
8794     /**
8795      * Sets the maximum size for the resizing element
8796      * @param {Number} maxSize The maximum size
8797      */
8798     setMaximumSize : function(maxSize){
8799         this.maxSize = maxSize;
8800     },
8801     
8802     /**
8803      * Sets the initialize size for the resizing element
8804      * @param {Number} size The initial size
8805      */
8806     setCurrentSize : function(size){
8807         var oldAnimate = this.animate;
8808         this.animate = false;
8809         this.adapter.setElementSize(this, size);
8810         this.animate = oldAnimate;
8811     },
8812     
8813     /**
8814      * Destroy this splitbar. 
8815      * @param {Boolean} removeEl True to remove the element
8816      */
8817     destroy : function(removeEl){
8818         if(this.shim){
8819             this.shim.remove();
8820         }
8821         this.dd.unreg();
8822         this.proxy.parentNode.removeChild(this.proxy);
8823         if(removeEl){
8824             this.el.remove();
8825         }
8826     }
8827 });
8828
8829 /**
8830  * @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.
8831  */
8832 Roo.SplitBar.createProxy = function(dir){
8833     var proxy = new Roo.Element(document.createElement("div"));
8834     proxy.unselectable();
8835     var cls = 'x-splitbar-proxy';
8836     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
8837     document.body.appendChild(proxy.dom);
8838     return proxy.dom;
8839 };
8840
8841 /** 
8842  * @class Roo.SplitBar.BasicLayoutAdapter
8843  * Default Adapter. It assumes the splitter and resizing element are not positioned
8844  * elements and only gets/sets the width of the element. Generally used for table based layouts.
8845  */
8846 Roo.SplitBar.BasicLayoutAdapter = function(){
8847 };
8848
8849 Roo.SplitBar.BasicLayoutAdapter.prototype = {
8850     // do nothing for now
8851     init : function(s){
8852     
8853     },
8854     /**
8855      * Called before drag operations to get the current size of the resizing element. 
8856      * @param {Roo.SplitBar} s The SplitBar using this adapter
8857      */
8858      getElementSize : function(s){
8859         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8860             return s.resizingEl.getWidth();
8861         }else{
8862             return s.resizingEl.getHeight();
8863         }
8864     },
8865     
8866     /**
8867      * Called after drag operations to set the size of the resizing element.
8868      * @param {Roo.SplitBar} s The SplitBar using this adapter
8869      * @param {Number} newSize The new size to set
8870      * @param {Function} onComplete A function to be invoked when resizing is complete
8871      */
8872     setElementSize : function(s, newSize, onComplete){
8873         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8874             if(!s.animate){
8875                 s.resizingEl.setWidth(newSize);
8876                 if(onComplete){
8877                     onComplete(s, newSize);
8878                 }
8879             }else{
8880                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
8881             }
8882         }else{
8883             
8884             if(!s.animate){
8885                 s.resizingEl.setHeight(newSize);
8886                 if(onComplete){
8887                     onComplete(s, newSize);
8888                 }
8889             }else{
8890                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
8891             }
8892         }
8893     }
8894 };
8895
8896 /** 
8897  *@class Roo.SplitBar.AbsoluteLayoutAdapter
8898  * @extends Roo.SplitBar.BasicLayoutAdapter
8899  * Adapter that  moves the splitter element to align with the resized sizing element. 
8900  * Used with an absolute positioned SplitBar.
8901  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
8902  * document.body, make sure you assign an id to the body element.
8903  */
8904 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
8905     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
8906     this.container = Roo.get(container);
8907 };
8908
8909 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
8910     init : function(s){
8911         this.basic.init(s);
8912     },
8913     
8914     getElementSize : function(s){
8915         return this.basic.getElementSize(s);
8916     },
8917     
8918     setElementSize : function(s, newSize, onComplete){
8919         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
8920     },
8921     
8922     moveSplitter : function(s){
8923         var yes = Roo.SplitBar;
8924         switch(s.placement){
8925             case yes.LEFT:
8926                 s.el.setX(s.resizingEl.getRight());
8927                 break;
8928             case yes.RIGHT:
8929                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
8930                 break;
8931             case yes.TOP:
8932                 s.el.setY(s.resizingEl.getBottom());
8933                 break;
8934             case yes.BOTTOM:
8935                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
8936                 break;
8937         }
8938     }
8939 };
8940
8941 /**
8942  * Orientation constant - Create a vertical SplitBar
8943  * @static
8944  * @type Number
8945  */
8946 Roo.SplitBar.VERTICAL = 1;
8947
8948 /**
8949  * Orientation constant - Create a horizontal SplitBar
8950  * @static
8951  * @type Number
8952  */
8953 Roo.SplitBar.HORIZONTAL = 2;
8954
8955 /**
8956  * Placement constant - The resizing element is to the left of the splitter element
8957  * @static
8958  * @type Number
8959  */
8960 Roo.SplitBar.LEFT = 1;
8961
8962 /**
8963  * Placement constant - The resizing element is to the right of the splitter element
8964  * @static
8965  * @type Number
8966  */
8967 Roo.SplitBar.RIGHT = 2;
8968
8969 /**
8970  * Placement constant - The resizing element is positioned above the splitter element
8971  * @static
8972  * @type Number
8973  */
8974 Roo.SplitBar.TOP = 3;
8975
8976 /**
8977  * Placement constant - The resizing element is positioned under splitter element
8978  * @static
8979  * @type Number
8980  */
8981 Roo.SplitBar.BOTTOM = 4;
8982 /*
8983  * Based on:
8984  * Ext JS Library 1.1.1
8985  * Copyright(c) 2006-2007, Ext JS, LLC.
8986  *
8987  * Originally Released Under LGPL - original licence link has changed is not relivant.
8988  *
8989  * Fork - LGPL
8990  * <script type="text/javascript">
8991  */
8992
8993 /**
8994  * @class Roo.View
8995  * @extends Roo.util.Observable
8996  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
8997  * This class also supports single and multi selection modes. <br>
8998  * Create a data model bound view:
8999  <pre><code>
9000  var store = new Roo.data.Store(...);
9001
9002  var view = new Roo.View({
9003     el : "my-element",
9004     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
9005  
9006     singleSelect: true,
9007     selectedClass: "ydataview-selected",
9008     store: store
9009  });
9010
9011  // listen for node click?
9012  view.on("click", function(vw, index, node, e){
9013  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9014  });
9015
9016  // load XML data
9017  dataModel.load("foobar.xml");
9018  </code></pre>
9019  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9020  * <br><br>
9021  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9022  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9023  * 
9024  * Note: old style constructor is still suported (container, template, config)
9025  * 
9026  * @constructor
9027  * Create a new View
9028  * @param {Object} config The config object
9029  * 
9030  */
9031 Roo.View = function(config, depreciated_tpl, depreciated_config){
9032     
9033     if (typeof(depreciated_tpl) == 'undefined') {
9034         // new way.. - universal constructor.
9035         Roo.apply(this, config);
9036         this.el  = Roo.get(this.el);
9037     } else {
9038         // old format..
9039         this.el  = Roo.get(config);
9040         this.tpl = depreciated_tpl;
9041         Roo.apply(this, depreciated_config);
9042     }
9043      
9044     
9045     if(typeof(this.tpl) == "string"){
9046         this.tpl = new Roo.Template(this.tpl);
9047     } else {
9048         // support xtype ctors..
9049         this.tpl = new Roo.factory(this.tpl, Roo);
9050     }
9051     
9052     
9053     this.tpl.compile();
9054    
9055
9056      
9057     /** @private */
9058     this.addEvents({
9059     /**
9060      * @event beforeclick
9061      * Fires before a click is processed. Returns false to cancel the default action.
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         "beforeclick" : true,
9068     /**
9069      * @event click
9070      * Fires when a template node is clicked.
9071      * @param {Roo.View} this
9072      * @param {Number} index The index of the target node
9073      * @param {HTMLElement} node The target node
9074      * @param {Roo.EventObject} e The raw event object
9075      */
9076         "click" : true,
9077     /**
9078      * @event dblclick
9079      * Fires when a template node is double clicked.
9080      * @param {Roo.View} this
9081      * @param {Number} index The index of the target node
9082      * @param {HTMLElement} node The target node
9083      * @param {Roo.EventObject} e The raw event object
9084      */
9085         "dblclick" : true,
9086     /**
9087      * @event contextmenu
9088      * Fires when a template node is right clicked.
9089      * @param {Roo.View} this
9090      * @param {Number} index The index of the target node
9091      * @param {HTMLElement} node The target node
9092      * @param {Roo.EventObject} e The raw event object
9093      */
9094         "contextmenu" : true,
9095     /**
9096      * @event selectionchange
9097      * Fires when the selected nodes change.
9098      * @param {Roo.View} this
9099      * @param {Array} selections Array of the selected nodes
9100      */
9101         "selectionchange" : true,
9102
9103     /**
9104      * @event beforeselect
9105      * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9106      * @param {Roo.View} this
9107      * @param {HTMLElement} node The node to be selected
9108      * @param {Array} selections Array of currently selected nodes
9109      */
9110         "beforeselect" : true
9111     });
9112
9113     this.el.on({
9114         "click": this.onClick,
9115         "dblclick": this.onDblClick,
9116         "contextmenu": this.onContextMenu,
9117         scope:this
9118     });
9119
9120     this.selections = [];
9121     this.nodes = [];
9122     this.cmp = new Roo.CompositeElementLite([]);
9123     if(this.store){
9124         this.store = Roo.factory(this.store, Roo.data);
9125         this.setStore(this.store, true);
9126     }
9127     Roo.View.superclass.constructor.call(this);
9128 };
9129
9130 Roo.extend(Roo.View, Roo.util.Observable, {
9131     
9132      /**
9133      * @cfg {Roo.data.Store} store Data store to load data from.
9134      */
9135     store : false,
9136     
9137     /**
9138      * @cfg {String|Roo.Element} el The container element.
9139      */
9140     el : '',
9141     
9142     /**
9143      * @cfg {String|Roo.Template} tpl The template used by this View 
9144      */
9145     tpl : false,
9146     
9147     /**
9148      * @cfg {String} selectedClass The css class to add to selected nodes
9149      */
9150     selectedClass : "x-view-selected",
9151      /**
9152      * @cfg {String} emptyText The empty text to show when nothing is loaded.
9153      */
9154     emptyText : "",
9155     /**
9156      * @cfg {Boolean} multiSelect Allow multiple selection
9157      */
9158     
9159     multiSelect : false,
9160     /**
9161      * @cfg {Boolean} singleSelect Allow single selection
9162      */
9163     singleSelect:  false,
9164     
9165     /**
9166      * Returns the element this view is bound to.
9167      * @return {Roo.Element}
9168      */
9169     getEl : function(){
9170         return this.el;
9171     },
9172
9173     /**
9174      * Refreshes the view.
9175      */
9176     refresh : function(){
9177         var t = this.tpl;
9178         this.clearSelections();
9179         this.el.update("");
9180         var html = [];
9181         var records = this.store.getRange();
9182         if(records.length < 1){
9183             this.el.update(this.emptyText);
9184             return;
9185         }
9186         for(var i = 0, len = records.length; i < len; i++){
9187             var data = this.prepareData(records[i].data, i, records[i]);
9188             html[html.length] = t.apply(data);
9189         }
9190         this.el.update(html.join(""));
9191         this.nodes = this.el.dom.childNodes;
9192         this.updateIndexes(0);
9193     },
9194
9195     /**
9196      * Function to override to reformat the data that is sent to
9197      * the template for each node.
9198      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9199      * a JSON object for an UpdateManager bound view).
9200      */
9201     prepareData : function(data){
9202         return data;
9203     },
9204
9205     onUpdate : function(ds, record){
9206         this.clearSelections();
9207         var index = this.store.indexOf(record);
9208         var n = this.nodes[index];
9209         this.tpl.insertBefore(n, this.prepareData(record.data));
9210         n.parentNode.removeChild(n);
9211         this.updateIndexes(index, index);
9212     },
9213
9214     onAdd : function(ds, records, index){
9215         this.clearSelections();
9216         if(this.nodes.length == 0){
9217             this.refresh();
9218             return;
9219         }
9220         var n = this.nodes[index];
9221         for(var i = 0, len = records.length; i < len; i++){
9222             var d = this.prepareData(records[i].data);
9223             if(n){
9224                 this.tpl.insertBefore(n, d);
9225             }else{
9226                 this.tpl.append(this.el, d);
9227             }
9228         }
9229         this.updateIndexes(index);
9230     },
9231
9232     onRemove : function(ds, record, index){
9233         this.clearSelections();
9234         this.el.dom.removeChild(this.nodes[index]);
9235         this.updateIndexes(index);
9236     },
9237
9238     /**
9239      * Refresh an individual node.
9240      * @param {Number} index
9241      */
9242     refreshNode : function(index){
9243         this.onUpdate(this.store, this.store.getAt(index));
9244     },
9245
9246     updateIndexes : function(startIndex, endIndex){
9247         var ns = this.nodes;
9248         startIndex = startIndex || 0;
9249         endIndex = endIndex || ns.length - 1;
9250         for(var i = startIndex; i <= endIndex; i++){
9251             ns[i].nodeIndex = i;
9252         }
9253     },
9254
9255     /**
9256      * Changes the data store this view uses and refresh the view.
9257      * @param {Store} store
9258      */
9259     setStore : function(store, initial){
9260         if(!initial && this.store){
9261             this.store.un("datachanged", this.refresh);
9262             this.store.un("add", this.onAdd);
9263             this.store.un("remove", this.onRemove);
9264             this.store.un("update", this.onUpdate);
9265             this.store.un("clear", this.refresh);
9266         }
9267         if(store){
9268           
9269             store.on("datachanged", this.refresh, this);
9270             store.on("add", this.onAdd, this);
9271             store.on("remove", this.onRemove, this);
9272             store.on("update", this.onUpdate, this);
9273             store.on("clear", this.refresh, this);
9274         }
9275         
9276         if(store){
9277             this.refresh();
9278         }
9279     },
9280
9281     /**
9282      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9283      * @param {HTMLElement} node
9284      * @return {HTMLElement} The template node
9285      */
9286     findItemFromChild : function(node){
9287         var el = this.el.dom;
9288         if(!node || node.parentNode == el){
9289                     return node;
9290             }
9291             var p = node.parentNode;
9292             while(p && p != el){
9293             if(p.parentNode == el){
9294                 return p;
9295             }
9296             p = p.parentNode;
9297         }
9298             return null;
9299     },
9300
9301     /** @ignore */
9302     onClick : function(e){
9303         var item = this.findItemFromChild(e.getTarget());
9304         if(item){
9305             var index = this.indexOf(item);
9306             if(this.onItemClick(item, index, e) !== false){
9307                 this.fireEvent("click", this, index, item, e);
9308             }
9309         }else{
9310             this.clearSelections();
9311         }
9312     },
9313
9314     /** @ignore */
9315     onContextMenu : function(e){
9316         var item = this.findItemFromChild(e.getTarget());
9317         if(item){
9318             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9319         }
9320     },
9321
9322     /** @ignore */
9323     onDblClick : function(e){
9324         var item = this.findItemFromChild(e.getTarget());
9325         if(item){
9326             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9327         }
9328     },
9329
9330     onItemClick : function(item, index, e){
9331         if(this.fireEvent("beforeclick", this, index, item, e) === false){
9332             return false;
9333         }
9334         if(this.multiSelect || this.singleSelect){
9335             if(this.multiSelect && e.shiftKey && this.lastSelection){
9336                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9337             }else{
9338                 this.select(item, this.multiSelect && e.ctrlKey);
9339                 this.lastSelection = item;
9340             }
9341             e.preventDefault();
9342         }
9343         return true;
9344     },
9345
9346     /**
9347      * Get the number of selected nodes.
9348      * @return {Number}
9349      */
9350     getSelectionCount : function(){
9351         return this.selections.length;
9352     },
9353
9354     /**
9355      * Get the currently selected nodes.
9356      * @return {Array} An array of HTMLElements
9357      */
9358     getSelectedNodes : function(){
9359         return this.selections;
9360     },
9361
9362     /**
9363      * Get the indexes of the selected nodes.
9364      * @return {Array}
9365      */
9366     getSelectedIndexes : function(){
9367         var indexes = [], s = this.selections;
9368         for(var i = 0, len = s.length; i < len; i++){
9369             indexes.push(s[i].nodeIndex);
9370         }
9371         return indexes;
9372     },
9373
9374     /**
9375      * Clear all selections
9376      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9377      */
9378     clearSelections : function(suppressEvent){
9379         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9380             this.cmp.elements = this.selections;
9381             this.cmp.removeClass(this.selectedClass);
9382             this.selections = [];
9383             if(!suppressEvent){
9384                 this.fireEvent("selectionchange", this, this.selections);
9385             }
9386         }
9387     },
9388
9389     /**
9390      * Returns true if the passed node is selected
9391      * @param {HTMLElement/Number} node The node or node index
9392      * @return {Boolean}
9393      */
9394     isSelected : function(node){
9395         var s = this.selections;
9396         if(s.length < 1){
9397             return false;
9398         }
9399         node = this.getNode(node);
9400         return s.indexOf(node) !== -1;
9401     },
9402
9403     /**
9404      * Selects nodes.
9405      * @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
9406      * @param {Boolean} keepExisting (optional) true to keep existing selections
9407      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9408      */
9409     select : function(nodeInfo, keepExisting, suppressEvent){
9410         if(nodeInfo instanceof Array){
9411             if(!keepExisting){
9412                 this.clearSelections(true);
9413             }
9414             for(var i = 0, len = nodeInfo.length; i < len; i++){
9415                 this.select(nodeInfo[i], true, true);
9416             }
9417         } else{
9418             var node = this.getNode(nodeInfo);
9419             if(node && !this.isSelected(node)){
9420                 if(!keepExisting){
9421                     this.clearSelections(true);
9422                 }
9423                 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9424                     Roo.fly(node).addClass(this.selectedClass);
9425                     this.selections.push(node);
9426                     if(!suppressEvent){
9427                         this.fireEvent("selectionchange", this, this.selections);
9428                     }
9429                 }
9430             }
9431         }
9432     },
9433
9434     /**
9435      * Gets a template 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 {HTMLElement} The node or null if it wasn't found
9438      */
9439     getNode : function(nodeInfo){
9440         if(typeof nodeInfo == "string"){
9441             return document.getElementById(nodeInfo);
9442         }else if(typeof nodeInfo == "number"){
9443             return this.nodes[nodeInfo];
9444         }
9445         return nodeInfo;
9446     },
9447
9448     /**
9449      * Gets a range template nodes.
9450      * @param {Number} startIndex
9451      * @param {Number} endIndex
9452      * @return {Array} An array of nodes
9453      */
9454     getNodes : function(start, end){
9455         var ns = this.nodes;
9456         start = start || 0;
9457         end = typeof end == "undefined" ? ns.length - 1 : end;
9458         var nodes = [];
9459         if(start <= end){
9460             for(var i = start; i <= end; i++){
9461                 nodes.push(ns[i]);
9462             }
9463         } else{
9464             for(var i = start; i >= end; i--){
9465                 nodes.push(ns[i]);
9466             }
9467         }
9468         return nodes;
9469     },
9470
9471     /**
9472      * Finds the index of the passed node
9473      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9474      * @return {Number} The index of the node or -1
9475      */
9476     indexOf : function(node){
9477         node = this.getNode(node);
9478         if(typeof node.nodeIndex == "number"){
9479             return node.nodeIndex;
9480         }
9481         var ns = this.nodes;
9482         for(var i = 0, len = ns.length; i < len; i++){
9483             if(ns[i] == node){
9484                 return i;
9485             }
9486         }
9487         return -1;
9488     }
9489 });
9490 /*
9491  * Based on:
9492  * Ext JS Library 1.1.1
9493  * Copyright(c) 2006-2007, Ext JS, LLC.
9494  *
9495  * Originally Released Under LGPL - original licence link has changed is not relivant.
9496  *
9497  * Fork - LGPL
9498  * <script type="text/javascript">
9499  */
9500
9501 /**
9502  * @class Roo.JsonView
9503  * @extends Roo.View
9504  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
9505 <pre><code>
9506 var view = new Roo.JsonView({
9507     container: "my-element",
9508     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
9509     multiSelect: true, 
9510     jsonRoot: "data" 
9511 });
9512
9513 // listen for node click?
9514 view.on("click", function(vw, index, node, e){
9515     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9516 });
9517
9518 // direct load of JSON data
9519 view.load("foobar.php");
9520
9521 // Example from my blog list
9522 var tpl = new Roo.Template(
9523     '&lt;div class="entry"&gt;' +
9524     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
9525     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
9526     "&lt;/div&gt;&lt;hr /&gt;"
9527 );
9528
9529 var moreView = new Roo.JsonView({
9530     container :  "entry-list", 
9531     template : tpl,
9532     jsonRoot: "posts"
9533 });
9534 moreView.on("beforerender", this.sortEntries, this);
9535 moreView.load({
9536     url: "/blog/get-posts.php",
9537     params: "allposts=true",
9538     text: "Loading Blog Entries..."
9539 });
9540 </code></pre>
9541
9542 * Note: old code is supported with arguments : (container, template, config)
9543
9544
9545  * @constructor
9546  * Create a new JsonView
9547  * 
9548  * @param {Object} config The config object
9549  * 
9550  */
9551 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
9552     
9553     
9554     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
9555
9556     var um = this.el.getUpdateManager();
9557     um.setRenderer(this);
9558     um.on("update", this.onLoad, this);
9559     um.on("failure", this.onLoadException, this);
9560
9561     /**
9562      * @event beforerender
9563      * Fires before rendering of the downloaded JSON data.
9564      * @param {Roo.JsonView} this
9565      * @param {Object} data The JSON data loaded
9566      */
9567     /**
9568      * @event load
9569      * Fires when data is loaded.
9570      * @param {Roo.JsonView} this
9571      * @param {Object} data The JSON data loaded
9572      * @param {Object} response The raw Connect response object
9573      */
9574     /**
9575      * @event loadexception
9576      * Fires when loading fails.
9577      * @param {Roo.JsonView} this
9578      * @param {Object} response The raw Connect response object
9579      */
9580     this.addEvents({
9581         'beforerender' : true,
9582         'load' : true,
9583         'loadexception' : true
9584     });
9585 };
9586 Roo.extend(Roo.JsonView, Roo.View, {
9587     /**
9588      * @type {String} The root property in the loaded JSON object that contains the data
9589      */
9590     jsonRoot : "",
9591
9592     /**
9593      * Refreshes the view.
9594      */
9595     refresh : function(){
9596         this.clearSelections();
9597         this.el.update("");
9598         var html = [];
9599         var o = this.jsonData;
9600         if(o && o.length > 0){
9601             for(var i = 0, len = o.length; i < len; i++){
9602                 var data = this.prepareData(o[i], i, o);
9603                 html[html.length] = this.tpl.apply(data);
9604             }
9605         }else{
9606             html.push(this.emptyText);
9607         }
9608         this.el.update(html.join(""));
9609         this.nodes = this.el.dom.childNodes;
9610         this.updateIndexes(0);
9611     },
9612
9613     /**
9614      * 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.
9615      * @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:
9616      <pre><code>
9617      view.load({
9618          url: "your-url.php",
9619          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
9620          callback: yourFunction,
9621          scope: yourObject, //(optional scope)
9622          discardUrl: false,
9623          nocache: false,
9624          text: "Loading...",
9625          timeout: 30,
9626          scripts: false
9627      });
9628      </code></pre>
9629      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
9630      * 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.
9631      * @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}
9632      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9633      * @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.
9634      */
9635     load : function(){
9636         var um = this.el.getUpdateManager();
9637         um.update.apply(um, arguments);
9638     },
9639
9640     render : function(el, response){
9641         this.clearSelections();
9642         this.el.update("");
9643         var o;
9644         try{
9645             o = Roo.util.JSON.decode(response.responseText);
9646             if(this.jsonRoot){
9647                 
9648                 o = o[this.jsonRoot];
9649             }
9650         } catch(e){
9651         }
9652         /**
9653          * The current JSON data or null
9654          */
9655         this.jsonData = o;
9656         this.beforeRender();
9657         this.refresh();
9658     },
9659
9660 /**
9661  * Get the number of records in the current JSON dataset
9662  * @return {Number}
9663  */
9664     getCount : function(){
9665         return this.jsonData ? this.jsonData.length : 0;
9666     },
9667
9668 /**
9669  * Returns the JSON object for the specified node(s)
9670  * @param {HTMLElement/Array} node The node or an array of nodes
9671  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
9672  * you get the JSON object for the node
9673  */
9674     getNodeData : function(node){
9675         if(node instanceof Array){
9676             var data = [];
9677             for(var i = 0, len = node.length; i < len; i++){
9678                 data.push(this.getNodeData(node[i]));
9679             }
9680             return data;
9681         }
9682         return this.jsonData[this.indexOf(node)] || null;
9683     },
9684
9685     beforeRender : function(){
9686         this.snapshot = this.jsonData;
9687         if(this.sortInfo){
9688             this.sort.apply(this, this.sortInfo);
9689         }
9690         this.fireEvent("beforerender", this, this.jsonData);
9691     },
9692
9693     onLoad : function(el, o){
9694         this.fireEvent("load", this, this.jsonData, o);
9695     },
9696
9697     onLoadException : function(el, o){
9698         this.fireEvent("loadexception", this, o);
9699     },
9700
9701 /**
9702  * Filter the data by a specific property.
9703  * @param {String} property A property on your JSON objects
9704  * @param {String/RegExp} value Either string that the property values
9705  * should start with, or a RegExp to test against the property
9706  */
9707     filter : function(property, value){
9708         if(this.jsonData){
9709             var data = [];
9710             var ss = this.snapshot;
9711             if(typeof value == "string"){
9712                 var vlen = value.length;
9713                 if(vlen == 0){
9714                     this.clearFilter();
9715                     return;
9716                 }
9717                 value = value.toLowerCase();
9718                 for(var i = 0, len = ss.length; i < len; i++){
9719                     var o = ss[i];
9720                     if(o[property].substr(0, vlen).toLowerCase() == value){
9721                         data.push(o);
9722                     }
9723                 }
9724             } else if(value.exec){ // regex?
9725                 for(var i = 0, len = ss.length; i < len; i++){
9726                     var o = ss[i];
9727                     if(value.test(o[property])){
9728                         data.push(o);
9729                     }
9730                 }
9731             } else{
9732                 return;
9733             }
9734             this.jsonData = data;
9735             this.refresh();
9736         }
9737     },
9738
9739 /**
9740  * Filter by a function. The passed function will be called with each
9741  * object in the current dataset. If the function returns true the value is kept,
9742  * otherwise it is filtered.
9743  * @param {Function} fn
9744  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
9745  */
9746     filterBy : function(fn, scope){
9747         if(this.jsonData){
9748             var data = [];
9749             var ss = this.snapshot;
9750             for(var i = 0, len = ss.length; i < len; i++){
9751                 var o = ss[i];
9752                 if(fn.call(scope || this, o)){
9753                     data.push(o);
9754                 }
9755             }
9756             this.jsonData = data;
9757             this.refresh();
9758         }
9759     },
9760
9761 /**
9762  * Clears the current filter.
9763  */
9764     clearFilter : function(){
9765         if(this.snapshot && this.jsonData != this.snapshot){
9766             this.jsonData = this.snapshot;
9767             this.refresh();
9768         }
9769     },
9770
9771
9772 /**
9773  * Sorts the data for this view and refreshes it.
9774  * @param {String} property A property on your JSON objects to sort on
9775  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
9776  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
9777  */
9778     sort : function(property, dir, sortType){
9779         this.sortInfo = Array.prototype.slice.call(arguments, 0);
9780         if(this.jsonData){
9781             var p = property;
9782             var dsc = dir && dir.toLowerCase() == "desc";
9783             var f = function(o1, o2){
9784                 var v1 = sortType ? sortType(o1[p]) : o1[p];
9785                 var v2 = sortType ? sortType(o2[p]) : o2[p];
9786                 ;
9787                 if(v1 < v2){
9788                     return dsc ? +1 : -1;
9789                 } else if(v1 > v2){
9790                     return dsc ? -1 : +1;
9791                 } else{
9792                     return 0;
9793                 }
9794             };
9795             this.jsonData.sort(f);
9796             this.refresh();
9797             if(this.jsonData != this.snapshot){
9798                 this.snapshot.sort(f);
9799             }
9800         }
9801     }
9802 });/*
9803  * Based on:
9804  * Ext JS Library 1.1.1
9805  * Copyright(c) 2006-2007, Ext JS, LLC.
9806  *
9807  * Originally Released Under LGPL - original licence link has changed is not relivant.
9808  *
9809  * Fork - LGPL
9810  * <script type="text/javascript">
9811  */
9812  
9813
9814 /**
9815  * @class Roo.ColorPalette
9816  * @extends Roo.Component
9817  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
9818  * Here's an example of typical usage:
9819  * <pre><code>
9820 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
9821 cp.render('my-div');
9822
9823 cp.on('select', function(palette, selColor){
9824     // do something with selColor
9825 });
9826 </code></pre>
9827  * @constructor
9828  * Create a new ColorPalette
9829  * @param {Object} config The config object
9830  */
9831 Roo.ColorPalette = function(config){
9832     Roo.ColorPalette.superclass.constructor.call(this, config);
9833     this.addEvents({
9834         /**
9835              * @event select
9836              * Fires when a color is selected
9837              * @param {ColorPalette} this
9838              * @param {String} color The 6-digit color hex code (without the # symbol)
9839              */
9840         select: true
9841     });
9842
9843     if(this.handler){
9844         this.on("select", this.handler, this.scope, true);
9845     }
9846 };
9847 Roo.extend(Roo.ColorPalette, Roo.Component, {
9848     /**
9849      * @cfg {String} itemCls
9850      * The CSS class to apply to the containing element (defaults to "x-color-palette")
9851      */
9852     itemCls : "x-color-palette",
9853     /**
9854      * @cfg {String} value
9855      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
9856      * the hex codes are case-sensitive.
9857      */
9858     value : null,
9859     clickEvent:'click',
9860     // private
9861     ctype: "Roo.ColorPalette",
9862
9863     /**
9864      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
9865      */
9866     allowReselect : false,
9867
9868     /**
9869      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
9870      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
9871      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
9872      * of colors with the width setting until the box is symmetrical.</p>
9873      * <p>You can override individual colors if needed:</p>
9874      * <pre><code>
9875 var cp = new Roo.ColorPalette();
9876 cp.colors[0] = "FF0000";  // change the first box to red
9877 </code></pre>
9878
9879 Or you can provide a custom array of your own for complete control:
9880 <pre><code>
9881 var cp = new Roo.ColorPalette();
9882 cp.colors = ["000000", "993300", "333300"];
9883 </code></pre>
9884      * @type Array
9885      */
9886     colors : [
9887         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
9888         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
9889         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
9890         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
9891         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
9892     ],
9893
9894     // private
9895     onRender : function(container, position){
9896         var t = new Roo.MasterTemplate(
9897             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
9898         );
9899         var c = this.colors;
9900         for(var i = 0, len = c.length; i < len; i++){
9901             t.add([c[i]]);
9902         }
9903         var el = document.createElement("div");
9904         el.className = this.itemCls;
9905         t.overwrite(el);
9906         container.dom.insertBefore(el, position);
9907         this.el = Roo.get(el);
9908         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
9909         if(this.clickEvent != 'click'){
9910             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
9911         }
9912     },
9913
9914     // private
9915     afterRender : function(){
9916         Roo.ColorPalette.superclass.afterRender.call(this);
9917         if(this.value){
9918             var s = this.value;
9919             this.value = null;
9920             this.select(s);
9921         }
9922     },
9923
9924     // private
9925     handleClick : function(e, t){
9926         e.preventDefault();
9927         if(!this.disabled){
9928             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
9929             this.select(c.toUpperCase());
9930         }
9931     },
9932
9933     /**
9934      * Selects the specified color in the palette (fires the select event)
9935      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
9936      */
9937     select : function(color){
9938         color = color.replace("#", "");
9939         if(color != this.value || this.allowReselect){
9940             var el = this.el;
9941             if(this.value){
9942                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
9943             }
9944             el.child("a.color-"+color).addClass("x-color-palette-sel");
9945             this.value = color;
9946             this.fireEvent("select", this, color);
9947         }
9948     }
9949 });/*
9950  * Based on:
9951  * Ext JS Library 1.1.1
9952  * Copyright(c) 2006-2007, Ext JS, LLC.
9953  *
9954  * Originally Released Under LGPL - original licence link has changed is not relivant.
9955  *
9956  * Fork - LGPL
9957  * <script type="text/javascript">
9958  */
9959  
9960 /**
9961  * @class Roo.DatePicker
9962  * @extends Roo.Component
9963  * Simple date picker class.
9964  * @constructor
9965  * Create a new DatePicker
9966  * @param {Object} config The config object
9967  */
9968 Roo.DatePicker = function(config){
9969     Roo.DatePicker.superclass.constructor.call(this, config);
9970
9971     this.value = config && config.value ?
9972                  config.value.clearTime() : new Date().clearTime();
9973
9974     this.addEvents({
9975         /**
9976              * @event select
9977              * Fires when a date is selected
9978              * @param {DatePicker} this
9979              * @param {Date} date The selected date
9980              */
9981         select: true
9982     });
9983
9984     if(this.handler){
9985         this.on("select", this.handler,  this.scope || this);
9986     }
9987     // build the disabledDatesRE
9988     if(!this.disabledDatesRE && this.disabledDates){
9989         var dd = this.disabledDates;
9990         var re = "(?:";
9991         for(var i = 0; i < dd.length; i++){
9992             re += dd[i];
9993             if(i != dd.length-1) re += "|";
9994         }
9995         this.disabledDatesRE = new RegExp(re + ")");
9996     }
9997 };
9998
9999 Roo.extend(Roo.DatePicker, Roo.Component, {
10000     /**
10001      * @cfg {String} todayText
10002      * The text to display on the button that selects the current date (defaults to "Today")
10003      */
10004     todayText : "Today",
10005     /**
10006      * @cfg {String} okText
10007      * The text to display on the ok button
10008      */
10009     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
10010     /**
10011      * @cfg {String} cancelText
10012      * The text to display on the cancel button
10013      */
10014     cancelText : "Cancel",
10015     /**
10016      * @cfg {String} todayTip
10017      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
10018      */
10019     todayTip : "{0} (Spacebar)",
10020     /**
10021      * @cfg {Date} minDate
10022      * Minimum allowable date (JavaScript date object, defaults to null)
10023      */
10024     minDate : null,
10025     /**
10026      * @cfg {Date} maxDate
10027      * Maximum allowable date (JavaScript date object, defaults to null)
10028      */
10029     maxDate : null,
10030     /**
10031      * @cfg {String} minText
10032      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
10033      */
10034     minText : "This date is before the minimum date",
10035     /**
10036      * @cfg {String} maxText
10037      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
10038      */
10039     maxText : "This date is after the maximum date",
10040     /**
10041      * @cfg {String} format
10042      * The default date format string which can be overriden for localization support.  The format must be
10043      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
10044      */
10045     format : "m/d/y",
10046     /**
10047      * @cfg {Array} disabledDays
10048      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
10049      */
10050     disabledDays : null,
10051     /**
10052      * @cfg {String} disabledDaysText
10053      * The tooltip to display when the date falls on a disabled day (defaults to "")
10054      */
10055     disabledDaysText : "",
10056     /**
10057      * @cfg {RegExp} disabledDatesRE
10058      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
10059      */
10060     disabledDatesRE : null,
10061     /**
10062      * @cfg {String} disabledDatesText
10063      * The tooltip text to display when the date falls on a disabled date (defaults to "")
10064      */
10065     disabledDatesText : "",
10066     /**
10067      * @cfg {Boolean} constrainToViewport
10068      * True to constrain the date picker to the viewport (defaults to true)
10069      */
10070     constrainToViewport : true,
10071     /**
10072      * @cfg {Array} monthNames
10073      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
10074      */
10075     monthNames : Date.monthNames,
10076     /**
10077      * @cfg {Array} dayNames
10078      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
10079      */
10080     dayNames : Date.dayNames,
10081     /**
10082      * @cfg {String} nextText
10083      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
10084      */
10085     nextText: 'Next Month (Control+Right)',
10086     /**
10087      * @cfg {String} prevText
10088      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
10089      */
10090     prevText: 'Previous Month (Control+Left)',
10091     /**
10092      * @cfg {String} monthYearText
10093      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
10094      */
10095     monthYearText: 'Choose a month (Control+Up/Down to move years)',
10096     /**
10097      * @cfg {Number} startDay
10098      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
10099      */
10100     startDay : 0,
10101     /**
10102      * @cfg {Bool} showClear
10103      * Show a clear button (usefull for date form elements that can be blank.)
10104      */
10105     
10106     showClear: false,
10107     
10108     /**
10109      * Sets the value of the date field
10110      * @param {Date} value The date to set
10111      */
10112     setValue : function(value){
10113         var old = this.value;
10114         this.value = value.clearTime(true);
10115         if(this.el){
10116             this.update(this.value);
10117         }
10118     },
10119
10120     /**
10121      * Gets the current selected value of the date field
10122      * @return {Date} The selected date
10123      */
10124     getValue : function(){
10125         return this.value;
10126     },
10127
10128     // private
10129     focus : function(){
10130         if(this.el){
10131             this.update(this.activeDate);
10132         }
10133     },
10134
10135     // private
10136     onRender : function(container, position){
10137         var m = [
10138              '<table cellspacing="0">',
10139                 '<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>',
10140                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
10141         var dn = this.dayNames;
10142         for(var i = 0; i < 7; i++){
10143             var d = this.startDay+i;
10144             if(d > 6){
10145                 d = d-7;
10146             }
10147             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
10148         }
10149         m[m.length] = "</tr></thead><tbody><tr>";
10150         for(var i = 0; i < 42; i++) {
10151             if(i % 7 == 0 && i != 0){
10152                 m[m.length] = "</tr><tr>";
10153             }
10154             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
10155         }
10156         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
10157             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
10158
10159         var el = document.createElement("div");
10160         el.className = "x-date-picker";
10161         el.innerHTML = m.join("");
10162
10163         container.dom.insertBefore(el, position);
10164
10165         this.el = Roo.get(el);
10166         this.eventEl = Roo.get(el.firstChild);
10167
10168         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
10169             handler: this.showPrevMonth,
10170             scope: this,
10171             preventDefault:true,
10172             stopDefault:true
10173         });
10174
10175         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
10176             handler: this.showNextMonth,
10177             scope: this,
10178             preventDefault:true,
10179             stopDefault:true
10180         });
10181
10182         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
10183
10184         this.monthPicker = this.el.down('div.x-date-mp');
10185         this.monthPicker.enableDisplayMode('block');
10186         
10187         var kn = new Roo.KeyNav(this.eventEl, {
10188             "left" : function(e){
10189                 e.ctrlKey ?
10190                     this.showPrevMonth() :
10191                     this.update(this.activeDate.add("d", -1));
10192             },
10193
10194             "right" : function(e){
10195                 e.ctrlKey ?
10196                     this.showNextMonth() :
10197                     this.update(this.activeDate.add("d", 1));
10198             },
10199
10200             "up" : function(e){
10201                 e.ctrlKey ?
10202                     this.showNextYear() :
10203                     this.update(this.activeDate.add("d", -7));
10204             },
10205
10206             "down" : function(e){
10207                 e.ctrlKey ?
10208                     this.showPrevYear() :
10209                     this.update(this.activeDate.add("d", 7));
10210             },
10211
10212             "pageUp" : function(e){
10213                 this.showNextMonth();
10214             },
10215
10216             "pageDown" : function(e){
10217                 this.showPrevMonth();
10218             },
10219
10220             "enter" : function(e){
10221                 e.stopPropagation();
10222                 return true;
10223             },
10224
10225             scope : this
10226         });
10227
10228         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
10229
10230         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
10231
10232         this.el.unselectable();
10233         
10234         this.cells = this.el.select("table.x-date-inner tbody td");
10235         this.textNodes = this.el.query("table.x-date-inner tbody span");
10236
10237         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
10238             text: "&#160;",
10239             tooltip: this.monthYearText
10240         });
10241
10242         this.mbtn.on('click', this.showMonthPicker, this);
10243         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
10244
10245
10246         var today = (new Date()).dateFormat(this.format);
10247         
10248         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
10249         baseTb.add({
10250             text: String.format(this.todayText, today),
10251             tooltip: String.format(this.todayTip, today),
10252             handler: this.selectToday,
10253             scope: this
10254         });
10255         
10256         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
10257             
10258         //});
10259         if (this.showClear) {
10260             
10261             baseTb.add( new Roo.Toolbar.Fill());
10262             baseTb.add({
10263                 text: '&#160;',
10264                 cls: 'x-btn-icon x-btn-clear',
10265                 handler: function() {
10266                     //this.value = '';
10267                     this.fireEvent("select", this, '');
10268                 },
10269                 scope: this
10270             });
10271         }
10272         
10273         
10274         if(Roo.isIE){
10275             this.el.repaint();
10276         }
10277         this.update(this.value);
10278     },
10279
10280     createMonthPicker : function(){
10281         if(!this.monthPicker.dom.firstChild){
10282             var buf = ['<table border="0" cellspacing="0">'];
10283             for(var i = 0; i < 6; i++){
10284                 buf.push(
10285                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
10286                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
10287                     i == 0 ?
10288                     '<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>' :
10289                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
10290                 );
10291             }
10292             buf.push(
10293                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
10294                     this.okText,
10295                     '</button><button type="button" class="x-date-mp-cancel">',
10296                     this.cancelText,
10297                     '</button></td></tr>',
10298                 '</table>'
10299             );
10300             this.monthPicker.update(buf.join(''));
10301             this.monthPicker.on('click', this.onMonthClick, this);
10302             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
10303
10304             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
10305             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
10306
10307             this.mpMonths.each(function(m, a, i){
10308                 i += 1;
10309                 if((i%2) == 0){
10310                     m.dom.xmonth = 5 + Math.round(i * .5);
10311                 }else{
10312                     m.dom.xmonth = Math.round((i-1) * .5);
10313                 }
10314             });
10315         }
10316     },
10317
10318     showMonthPicker : function(){
10319         this.createMonthPicker();
10320         var size = this.el.getSize();
10321         this.monthPicker.setSize(size);
10322         this.monthPicker.child('table').setSize(size);
10323
10324         this.mpSelMonth = (this.activeDate || this.value).getMonth();
10325         this.updateMPMonth(this.mpSelMonth);
10326         this.mpSelYear = (this.activeDate || this.value).getFullYear();
10327         this.updateMPYear(this.mpSelYear);
10328
10329         this.monthPicker.slideIn('t', {duration:.2});
10330     },
10331
10332     updateMPYear : function(y){
10333         this.mpyear = y;
10334         var ys = this.mpYears.elements;
10335         for(var i = 1; i <= 10; i++){
10336             var td = ys[i-1], y2;
10337             if((i%2) == 0){
10338                 y2 = y + Math.round(i * .5);
10339                 td.firstChild.innerHTML = y2;
10340                 td.xyear = y2;
10341             }else{
10342                 y2 = y - (5-Math.round(i * .5));
10343                 td.firstChild.innerHTML = y2;
10344                 td.xyear = y2;
10345             }
10346             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
10347         }
10348     },
10349
10350     updateMPMonth : function(sm){
10351         this.mpMonths.each(function(m, a, i){
10352             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
10353         });
10354     },
10355
10356     selectMPMonth: function(m){
10357         
10358     },
10359
10360     onMonthClick : function(e, t){
10361         e.stopEvent();
10362         var el = new Roo.Element(t), pn;
10363         if(el.is('button.x-date-mp-cancel')){
10364             this.hideMonthPicker();
10365         }
10366         else if(el.is('button.x-date-mp-ok')){
10367             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10368             this.hideMonthPicker();
10369         }
10370         else if(pn = el.up('td.x-date-mp-month', 2)){
10371             this.mpMonths.removeClass('x-date-mp-sel');
10372             pn.addClass('x-date-mp-sel');
10373             this.mpSelMonth = pn.dom.xmonth;
10374         }
10375         else if(pn = el.up('td.x-date-mp-year', 2)){
10376             this.mpYears.removeClass('x-date-mp-sel');
10377             pn.addClass('x-date-mp-sel');
10378             this.mpSelYear = pn.dom.xyear;
10379         }
10380         else if(el.is('a.x-date-mp-prev')){
10381             this.updateMPYear(this.mpyear-10);
10382         }
10383         else if(el.is('a.x-date-mp-next')){
10384             this.updateMPYear(this.mpyear+10);
10385         }
10386     },
10387
10388     onMonthDblClick : function(e, t){
10389         e.stopEvent();
10390         var el = new Roo.Element(t), pn;
10391         if(pn = el.up('td.x-date-mp-month', 2)){
10392             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
10393             this.hideMonthPicker();
10394         }
10395         else if(pn = el.up('td.x-date-mp-year', 2)){
10396             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10397             this.hideMonthPicker();
10398         }
10399     },
10400
10401     hideMonthPicker : function(disableAnim){
10402         if(this.monthPicker){
10403             if(disableAnim === true){
10404                 this.monthPicker.hide();
10405             }else{
10406                 this.monthPicker.slideOut('t', {duration:.2});
10407             }
10408         }
10409     },
10410
10411     // private
10412     showPrevMonth : function(e){
10413         this.update(this.activeDate.add("mo", -1));
10414     },
10415
10416     // private
10417     showNextMonth : function(e){
10418         this.update(this.activeDate.add("mo", 1));
10419     },
10420
10421     // private
10422     showPrevYear : function(){
10423         this.update(this.activeDate.add("y", -1));
10424     },
10425
10426     // private
10427     showNextYear : function(){
10428         this.update(this.activeDate.add("y", 1));
10429     },
10430
10431     // private
10432     handleMouseWheel : function(e){
10433         var delta = e.getWheelDelta();
10434         if(delta > 0){
10435             this.showPrevMonth();
10436             e.stopEvent();
10437         } else if(delta < 0){
10438             this.showNextMonth();
10439             e.stopEvent();
10440         }
10441     },
10442
10443     // private
10444     handleDateClick : function(e, t){
10445         e.stopEvent();
10446         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
10447             this.setValue(new Date(t.dateValue));
10448             this.fireEvent("select", this, this.value);
10449         }
10450     },
10451
10452     // private
10453     selectToday : function(){
10454         this.setValue(new Date().clearTime());
10455         this.fireEvent("select", this, this.value);
10456     },
10457
10458     // private
10459     update : function(date){
10460         var vd = this.activeDate;
10461         this.activeDate = date;
10462         if(vd && this.el){
10463             var t = date.getTime();
10464             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10465                 this.cells.removeClass("x-date-selected");
10466                 this.cells.each(function(c){
10467                    if(c.dom.firstChild.dateValue == t){
10468                        c.addClass("x-date-selected");
10469                        setTimeout(function(){
10470                             try{c.dom.firstChild.focus();}catch(e){}
10471                        }, 50);
10472                        return false;
10473                    }
10474                 });
10475                 return;
10476             }
10477         }
10478         var days = date.getDaysInMonth();
10479         var firstOfMonth = date.getFirstDateOfMonth();
10480         var startingPos = firstOfMonth.getDay()-this.startDay;
10481
10482         if(startingPos <= this.startDay){
10483             startingPos += 7;
10484         }
10485
10486         var pm = date.add("mo", -1);
10487         var prevStart = pm.getDaysInMonth()-startingPos;
10488
10489         var cells = this.cells.elements;
10490         var textEls = this.textNodes;
10491         days += startingPos;
10492
10493         // convert everything to numbers so it's fast
10494         var day = 86400000;
10495         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10496         var today = new Date().clearTime().getTime();
10497         var sel = date.clearTime().getTime();
10498         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10499         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10500         var ddMatch = this.disabledDatesRE;
10501         var ddText = this.disabledDatesText;
10502         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10503         var ddaysText = this.disabledDaysText;
10504         var format = this.format;
10505
10506         var setCellClass = function(cal, cell){
10507             cell.title = "";
10508             var t = d.getTime();
10509             cell.firstChild.dateValue = t;
10510             if(t == today){
10511                 cell.className += " x-date-today";
10512                 cell.title = cal.todayText;
10513             }
10514             if(t == sel){
10515                 cell.className += " x-date-selected";
10516                 setTimeout(function(){
10517                     try{cell.firstChild.focus();}catch(e){}
10518                 }, 50);
10519             }
10520             // disabling
10521             if(t < min) {
10522                 cell.className = " x-date-disabled";
10523                 cell.title = cal.minText;
10524                 return;
10525             }
10526             if(t > max) {
10527                 cell.className = " x-date-disabled";
10528                 cell.title = cal.maxText;
10529                 return;
10530             }
10531             if(ddays){
10532                 if(ddays.indexOf(d.getDay()) != -1){
10533                     cell.title = ddaysText;
10534                     cell.className = " x-date-disabled";
10535                 }
10536             }
10537             if(ddMatch && format){
10538                 var fvalue = d.dateFormat(format);
10539                 if(ddMatch.test(fvalue)){
10540                     cell.title = ddText.replace("%0", fvalue);
10541                     cell.className = " x-date-disabled";
10542                 }
10543             }
10544         };
10545
10546         var i = 0;
10547         for(; i < startingPos; i++) {
10548             textEls[i].innerHTML = (++prevStart);
10549             d.setDate(d.getDate()+1);
10550             cells[i].className = "x-date-prevday";
10551             setCellClass(this, cells[i]);
10552         }
10553         for(; i < days; i++){
10554             intDay = i - startingPos + 1;
10555             textEls[i].innerHTML = (intDay);
10556             d.setDate(d.getDate()+1);
10557             cells[i].className = "x-date-active";
10558             setCellClass(this, cells[i]);
10559         }
10560         var extraDays = 0;
10561         for(; i < 42; i++) {
10562              textEls[i].innerHTML = (++extraDays);
10563              d.setDate(d.getDate()+1);
10564              cells[i].className = "x-date-nextday";
10565              setCellClass(this, cells[i]);
10566         }
10567
10568         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
10569
10570         if(!this.internalRender){
10571             var main = this.el.dom.firstChild;
10572             var w = main.offsetWidth;
10573             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10574             Roo.fly(main).setWidth(w);
10575             this.internalRender = true;
10576             // opera does not respect the auto grow header center column
10577             // then, after it gets a width opera refuses to recalculate
10578             // without a second pass
10579             if(Roo.isOpera && !this.secondPass){
10580                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10581                 this.secondPass = true;
10582                 this.update.defer(10, this, [date]);
10583             }
10584         }
10585     }
10586 });/*
10587  * Based on:
10588  * Ext JS Library 1.1.1
10589  * Copyright(c) 2006-2007, Ext JS, LLC.
10590  *
10591  * Originally Released Under LGPL - original licence link has changed is not relivant.
10592  *
10593  * Fork - LGPL
10594  * <script type="text/javascript">
10595  */
10596 /**
10597  * @class Roo.TabPanel
10598  * @extends Roo.util.Observable
10599  * A lightweight tab container.
10600  * <br><br>
10601  * Usage:
10602  * <pre><code>
10603 // basic tabs 1, built from existing content
10604 var tabs = new Roo.TabPanel("tabs1");
10605 tabs.addTab("script", "View Script");
10606 tabs.addTab("markup", "View Markup");
10607 tabs.activate("script");
10608
10609 // more advanced tabs, built from javascript
10610 var jtabs = new Roo.TabPanel("jtabs");
10611 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
10612
10613 // set up the UpdateManager
10614 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
10615 var updater = tab2.getUpdateManager();
10616 updater.setDefaultUrl("ajax1.htm");
10617 tab2.on('activate', updater.refresh, updater, true);
10618
10619 // Use setUrl for Ajax loading
10620 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
10621 tab3.setUrl("ajax2.htm", null, true);
10622
10623 // Disabled tab
10624 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
10625 tab4.disable();
10626
10627 jtabs.activate("jtabs-1");
10628  * </code></pre>
10629  * @constructor
10630  * Create a new TabPanel.
10631  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
10632  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
10633  */
10634 Roo.TabPanel = function(container, config){
10635     /**
10636     * The container element for this TabPanel.
10637     * @type Roo.Element
10638     */
10639     this.el = Roo.get(container, true);
10640     if(config){
10641         if(typeof config == "boolean"){
10642             this.tabPosition = config ? "bottom" : "top";
10643         }else{
10644             Roo.apply(this, config);
10645         }
10646     }
10647     if(this.tabPosition == "bottom"){
10648         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10649         this.el.addClass("x-tabs-bottom");
10650     }
10651     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
10652     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
10653     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
10654     if(Roo.isIE){
10655         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
10656     }
10657     if(this.tabPosition != "bottom"){
10658     /** The body element that contains {@link Roo.TabPanelItem} bodies.
10659      * @type Roo.Element
10660      */
10661       this.bodyEl = Roo.get(this.createBody(this.el.dom));
10662       this.el.addClass("x-tabs-top");
10663     }
10664     this.items = [];
10665
10666     this.bodyEl.setStyle("position", "relative");
10667
10668     this.active = null;
10669     this.activateDelegate = this.activate.createDelegate(this);
10670
10671     this.addEvents({
10672         /**
10673          * @event tabchange
10674          * Fires when the active tab changes
10675          * @param {Roo.TabPanel} this
10676          * @param {Roo.TabPanelItem} activePanel The new active tab
10677          */
10678         "tabchange": true,
10679         /**
10680          * @event beforetabchange
10681          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
10682          * @param {Roo.TabPanel} this
10683          * @param {Object} e Set cancel to true on this object to cancel the tab change
10684          * @param {Roo.TabPanelItem} tab The tab being changed to
10685          */
10686         "beforetabchange" : true
10687     });
10688
10689     Roo.EventManager.onWindowResize(this.onResize, this);
10690     this.cpad = this.el.getPadding("lr");
10691     this.hiddenCount = 0;
10692
10693     Roo.TabPanel.superclass.constructor.call(this);
10694 };
10695
10696 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
10697         /*
10698          *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
10699          */
10700     tabPosition : "top",
10701         /*
10702          *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
10703          */
10704     currentTabWidth : 0,
10705         /*
10706          *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
10707          */
10708     minTabWidth : 40,
10709         /*
10710          *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
10711          */
10712     maxTabWidth : 250,
10713         /*
10714          *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
10715          */
10716     preferredTabWidth : 175,
10717         /*
10718          *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
10719          */
10720     resizeTabs : false,
10721         /*
10722          *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
10723          */
10724     monitorResize : true,
10725
10726     /**
10727      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
10728      * @param {String} id The id of the div to use <b>or create</b>
10729      * @param {String} text The text for the tab
10730      * @param {String} content (optional) Content to put in the TabPanelItem body
10731      * @param {Boolean} closable (optional) True to create a close icon on the tab
10732      * @return {Roo.TabPanelItem} The created TabPanelItem
10733      */
10734     addTab : function(id, text, content, closable){
10735         var item = new Roo.TabPanelItem(this, id, text, closable);
10736         this.addTabItem(item);
10737         if(content){
10738             item.setContent(content);
10739         }
10740         return item;
10741     },
10742
10743     /**
10744      * Returns the {@link Roo.TabPanelItem} with the specified id/index
10745      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
10746      * @return {Roo.TabPanelItem}
10747      */
10748     getTab : function(id){
10749         return this.items[id];
10750     },
10751
10752     /**
10753      * Hides the {@link Roo.TabPanelItem} with the specified id/index
10754      * @param {String/Number} id The id or index of the TabPanelItem to hide.
10755      */
10756     hideTab : function(id){
10757         var t = this.items[id];
10758         if(!t.isHidden()){
10759            t.setHidden(true);
10760            this.hiddenCount++;
10761            this.autoSizeTabs();
10762         }
10763     },
10764
10765     /**
10766      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
10767      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
10768      */
10769     unhideTab : function(id){
10770         var t = this.items[id];
10771         if(t.isHidden()){
10772            t.setHidden(false);
10773            this.hiddenCount--;
10774            this.autoSizeTabs();
10775         }
10776     },
10777
10778     /**
10779      * Adds an existing {@link Roo.TabPanelItem}.
10780      * @param {Roo.TabPanelItem} item The TabPanelItem to add
10781      */
10782     addTabItem : function(item){
10783         this.items[item.id] = item;
10784         this.items.push(item);
10785         if(this.resizeTabs){
10786            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
10787            this.autoSizeTabs();
10788         }else{
10789             item.autoSize();
10790         }
10791     },
10792
10793     /**
10794      * Removes a {@link Roo.TabPanelItem}.
10795      * @param {String/Number} id The id or index of the TabPanelItem to remove.
10796      */
10797     removeTab : function(id){
10798         var items = this.items;
10799         var tab = items[id];
10800         if(!tab) { return; }
10801         var index = items.indexOf(tab);
10802         if(this.active == tab && items.length > 1){
10803             var newTab = this.getNextAvailable(index);
10804             if(newTab) {
10805                 newTab.activate();
10806             }
10807         }
10808         this.stripEl.dom.removeChild(tab.pnode.dom);
10809         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
10810             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
10811         }
10812         items.splice(index, 1);
10813         delete this.items[tab.id];
10814         tab.fireEvent("close", tab);
10815         tab.purgeListeners();
10816         this.autoSizeTabs();
10817     },
10818
10819     getNextAvailable : function(start){
10820         var items = this.items;
10821         var index = start;
10822         // look for a next tab that will slide over to
10823         // replace the one being removed
10824         while(index < items.length){
10825             var item = items[++index];
10826             if(item && !item.isHidden()){
10827                 return item;
10828             }
10829         }
10830         // if one isn't found select the previous tab (on the left)
10831         index = start;
10832         while(index >= 0){
10833             var item = items[--index];
10834             if(item && !item.isHidden()){
10835                 return item;
10836             }
10837         }
10838         return null;
10839     },
10840
10841     /**
10842      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
10843      * @param {String/Number} id The id or index of the TabPanelItem to disable.
10844      */
10845     disableTab : function(id){
10846         var tab = this.items[id];
10847         if(tab && this.active != tab){
10848             tab.disable();
10849         }
10850     },
10851
10852     /**
10853      * Enables a {@link Roo.TabPanelItem} that is disabled.
10854      * @param {String/Number} id The id or index of the TabPanelItem to enable.
10855      */
10856     enableTab : function(id){
10857         var tab = this.items[id];
10858         tab.enable();
10859     },
10860
10861     /**
10862      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
10863      * @param {String/Number} id The id or index of the TabPanelItem to activate.
10864      * @return {Roo.TabPanelItem} The TabPanelItem.
10865      */
10866     activate : function(id){
10867         var tab = this.items[id];
10868         if(!tab){
10869             return null;
10870         }
10871         if(tab == this.active || tab.disabled){
10872             return tab;
10873         }
10874         var e = {};
10875         this.fireEvent("beforetabchange", this, e, tab);
10876         if(e.cancel !== true && !tab.disabled){
10877             if(this.active){
10878                 this.active.hide();
10879             }
10880             this.active = this.items[id];
10881             this.active.show();
10882             this.fireEvent("tabchange", this, this.active);
10883         }
10884         return tab;
10885     },
10886
10887     /**
10888      * Gets the active {@link Roo.TabPanelItem}.
10889      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
10890      */
10891     getActiveTab : function(){
10892         return this.active;
10893     },
10894
10895     /**
10896      * Updates the tab body element to fit the height of the container element
10897      * for overflow scrolling
10898      * @param {Number} targetHeight (optional) Override the starting height from the elements height
10899      */
10900     syncHeight : function(targetHeight){
10901         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
10902         var bm = this.bodyEl.getMargins();
10903         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
10904         this.bodyEl.setHeight(newHeight);
10905         return newHeight;
10906     },
10907
10908     onResize : function(){
10909         if(this.monitorResize){
10910             this.autoSizeTabs();
10911         }
10912     },
10913
10914     /**
10915      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
10916      */
10917     beginUpdate : function(){
10918         this.updating = true;
10919     },
10920
10921     /**
10922      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
10923      */
10924     endUpdate : function(){
10925         this.updating = false;
10926         this.autoSizeTabs();
10927     },
10928
10929     /**
10930      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
10931      */
10932     autoSizeTabs : function(){
10933         var count = this.items.length;
10934         var vcount = count - this.hiddenCount;
10935         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
10936         var w = Math.max(this.el.getWidth() - this.cpad, 10);
10937         var availWidth = Math.floor(w / vcount);
10938         var b = this.stripBody;
10939         if(b.getWidth() > w){
10940             var tabs = this.items;
10941             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
10942             if(availWidth < this.minTabWidth){
10943                 /*if(!this.sleft){    // incomplete scrolling code
10944                     this.createScrollButtons();
10945                 }
10946                 this.showScroll();
10947                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
10948             }
10949         }else{
10950             if(this.currentTabWidth < this.preferredTabWidth){
10951                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
10952             }
10953         }
10954     },
10955
10956     /**
10957      * Returns the number of tabs in this TabPanel.
10958      * @return {Number}
10959      */
10960      getCount : function(){
10961          return this.items.length;
10962      },
10963
10964     /**
10965      * Resizes all the tabs to the passed width
10966      * @param {Number} The new width
10967      */
10968     setTabWidth : function(width){
10969         this.currentTabWidth = width;
10970         for(var i = 0, len = this.items.length; i < len; i++) {
10971                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
10972         }
10973     },
10974
10975     /**
10976      * Destroys this TabPanel
10977      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
10978      */
10979     destroy : function(removeEl){
10980         Roo.EventManager.removeResizeListener(this.onResize, this);
10981         for(var i = 0, len = this.items.length; i < len; i++){
10982             this.items[i].purgeListeners();
10983         }
10984         if(removeEl === true){
10985             this.el.update("");
10986             this.el.remove();
10987         }
10988     }
10989 });
10990
10991 /**
10992  * @class Roo.TabPanelItem
10993  * @extends Roo.util.Observable
10994  * Represents an individual item (tab plus body) in a TabPanel.
10995  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
10996  * @param {String} id The id of this TabPanelItem
10997  * @param {String} text The text for the tab of this TabPanelItem
10998  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
10999  */
11000 Roo.TabPanelItem = function(tabPanel, id, text, closable){
11001     /**
11002      * The {@link Roo.TabPanel} this TabPanelItem belongs to
11003      * @type Roo.TabPanel
11004      */
11005     this.tabPanel = tabPanel;
11006     /**
11007      * The id for this TabPanelItem
11008      * @type String
11009      */
11010     this.id = id;
11011     /** @private */
11012     this.disabled = false;
11013     /** @private */
11014     this.text = text;
11015     /** @private */
11016     this.loaded = false;
11017     this.closable = closable;
11018
11019     /**
11020      * The body element for this TabPanelItem.
11021      * @type Roo.Element
11022      */
11023     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
11024     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
11025     this.bodyEl.setStyle("display", "block");
11026     this.bodyEl.setStyle("zoom", "1");
11027     this.hideAction();
11028
11029     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
11030     /** @private */
11031     this.el = Roo.get(els.el, true);
11032     this.inner = Roo.get(els.inner, true);
11033     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
11034     this.pnode = Roo.get(els.el.parentNode, true);
11035     this.el.on("mousedown", this.onTabMouseDown, this);
11036     this.el.on("click", this.onTabClick, this);
11037     /** @private */
11038     if(closable){
11039         var c = Roo.get(els.close, true);
11040         c.dom.title = this.closeText;
11041         c.addClassOnOver("close-over");
11042         c.on("click", this.closeClick, this);
11043      }
11044
11045     this.addEvents({
11046          /**
11047          * @event activate
11048          * Fires when this tab becomes the active tab.
11049          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11050          * @param {Roo.TabPanelItem} this
11051          */
11052         "activate": true,
11053         /**
11054          * @event beforeclose
11055          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
11056          * @param {Roo.TabPanelItem} this
11057          * @param {Object} e Set cancel to true on this object to cancel the close.
11058          */
11059         "beforeclose": true,
11060         /**
11061          * @event close
11062          * Fires when this tab is closed.
11063          * @param {Roo.TabPanelItem} this
11064          */
11065          "close": true,
11066         /**
11067          * @event deactivate
11068          * Fires when this tab is no longer the active tab.
11069          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11070          * @param {Roo.TabPanelItem} this
11071          */
11072          "deactivate" : true
11073     });
11074     this.hidden = false;
11075
11076     Roo.TabPanelItem.superclass.constructor.call(this);
11077 };
11078
11079 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
11080     purgeListeners : function(){
11081        Roo.util.Observable.prototype.purgeListeners.call(this);
11082        this.el.removeAllListeners();
11083     },
11084     /**
11085      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
11086      */
11087     show : function(){
11088         this.pnode.addClass("on");
11089         this.showAction();
11090         if(Roo.isOpera){
11091             this.tabPanel.stripWrap.repaint();
11092         }
11093         this.fireEvent("activate", this.tabPanel, this);
11094     },
11095
11096     /**
11097      * Returns true if this tab is the active tab.
11098      * @return {Boolean}
11099      */
11100     isActive : function(){
11101         return this.tabPanel.getActiveTab() == this;
11102     },
11103
11104     /**
11105      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
11106      */
11107     hide : function(){
11108         this.pnode.removeClass("on");
11109         this.hideAction();
11110         this.fireEvent("deactivate", this.tabPanel, this);
11111     },
11112
11113     hideAction : function(){
11114         this.bodyEl.hide();
11115         this.bodyEl.setStyle("position", "absolute");
11116         this.bodyEl.setLeft("-20000px");
11117         this.bodyEl.setTop("-20000px");
11118     },
11119
11120     showAction : function(){
11121         this.bodyEl.setStyle("position", "relative");
11122         this.bodyEl.setTop("");
11123         this.bodyEl.setLeft("");
11124         this.bodyEl.show();
11125     },
11126
11127     /**
11128      * Set the tooltip for the tab.
11129      * @param {String} tooltip The tab's tooltip
11130      */
11131     setTooltip : function(text){
11132         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
11133             this.textEl.dom.qtip = text;
11134             this.textEl.dom.removeAttribute('title');
11135         }else{
11136             this.textEl.dom.title = text;
11137         }
11138     },
11139
11140     onTabClick : function(e){
11141         e.preventDefault();
11142         this.tabPanel.activate(this.id);
11143     },
11144
11145     onTabMouseDown : function(e){
11146         e.preventDefault();
11147         this.tabPanel.activate(this.id);
11148     },
11149
11150     getWidth : function(){
11151         return this.inner.getWidth();
11152     },
11153
11154     setWidth : function(width){
11155         var iwidth = width - this.pnode.getPadding("lr");
11156         this.inner.setWidth(iwidth);
11157         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
11158         this.pnode.setWidth(width);
11159     },
11160
11161     /**
11162      * Show or hide the tab
11163      * @param {Boolean} hidden True to hide or false to show.
11164      */
11165     setHidden : function(hidden){
11166         this.hidden = hidden;
11167         this.pnode.setStyle("display", hidden ? "none" : "");
11168     },
11169
11170     /**
11171      * Returns true if this tab is "hidden"
11172      * @return {Boolean}
11173      */
11174     isHidden : function(){
11175         return this.hidden;
11176     },
11177
11178     /**
11179      * Returns the text for this tab
11180      * @return {String}
11181      */
11182     getText : function(){
11183         return this.text;
11184     },
11185
11186     autoSize : function(){
11187         //this.el.beginMeasure();
11188         this.textEl.setWidth(1);
11189         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
11190         //this.el.endMeasure();
11191     },
11192
11193     /**
11194      * Sets the text for the tab (Note: this also sets the tooltip text)
11195      * @param {String} text The tab's text and tooltip
11196      */
11197     setText : function(text){
11198         this.text = text;
11199         this.textEl.update(text);
11200         this.setTooltip(text);
11201         if(!this.tabPanel.resizeTabs){
11202             this.autoSize();
11203         }
11204     },
11205     /**
11206      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
11207      */
11208     activate : function(){
11209         this.tabPanel.activate(this.id);
11210     },
11211
11212     /**
11213      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
11214      */
11215     disable : function(){
11216         if(this.tabPanel.active != this){
11217             this.disabled = true;
11218             this.pnode.addClass("disabled");
11219         }
11220     },
11221
11222     /**
11223      * Enables this TabPanelItem if it was previously disabled.
11224      */
11225     enable : function(){
11226         this.disabled = false;
11227         this.pnode.removeClass("disabled");
11228     },
11229
11230     /**
11231      * Sets the content for this TabPanelItem.
11232      * @param {String} content The content
11233      * @param {Boolean} loadScripts true to look for and load scripts
11234      */
11235     setContent : function(content, loadScripts){
11236         this.bodyEl.update(content, loadScripts);
11237     },
11238
11239     /**
11240      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
11241      * @return {Roo.UpdateManager} The UpdateManager
11242      */
11243     getUpdateManager : function(){
11244         return this.bodyEl.getUpdateManager();
11245     },
11246
11247     /**
11248      * Set a URL to be used to load the content for this TabPanelItem.
11249      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
11250      * @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)
11251      * @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)
11252      * @return {Roo.UpdateManager} The UpdateManager
11253      */
11254     setUrl : function(url, params, loadOnce){
11255         if(this.refreshDelegate){
11256             this.un('activate', this.refreshDelegate);
11257         }
11258         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
11259         this.on("activate", this.refreshDelegate);
11260         return this.bodyEl.getUpdateManager();
11261     },
11262
11263     /** @private */
11264     _handleRefresh : function(url, params, loadOnce){
11265         if(!loadOnce || !this.loaded){
11266             var updater = this.bodyEl.getUpdateManager();
11267             updater.update(url, params, this._setLoaded.createDelegate(this));
11268         }
11269     },
11270
11271     /**
11272      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
11273      *   Will fail silently if the setUrl method has not been called.
11274      *   This does not activate the panel, just updates its content.
11275      */
11276     refresh : function(){
11277         if(this.refreshDelegate){
11278            this.loaded = false;
11279            this.refreshDelegate();
11280         }
11281     },
11282
11283     /** @private */
11284     _setLoaded : function(){
11285         this.loaded = true;
11286     },
11287
11288     /** @private */
11289     closeClick : function(e){
11290         var o = {};
11291         e.stopEvent();
11292         this.fireEvent("beforeclose", this, o);
11293         if(o.cancel !== true){
11294             this.tabPanel.removeTab(this.id);
11295         }
11296     },
11297     /**
11298      * The text displayed in the tooltip for the close icon.
11299      * @type String
11300      */
11301     closeText : "Close this tab"
11302 });
11303
11304 /** @private */
11305 Roo.TabPanel.prototype.createStrip = function(container){
11306     var strip = document.createElement("div");
11307     strip.className = "x-tabs-wrap";
11308     container.appendChild(strip);
11309     return strip;
11310 };
11311 /** @private */
11312 Roo.TabPanel.prototype.createStripList = function(strip){
11313     // div wrapper for retard IE
11314     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>';
11315     return strip.firstChild.firstChild.firstChild.firstChild;
11316 };
11317 /** @private */
11318 Roo.TabPanel.prototype.createBody = function(container){
11319     var body = document.createElement("div");
11320     Roo.id(body, "tab-body");
11321     Roo.fly(body).addClass("x-tabs-body");
11322     container.appendChild(body);
11323     return body;
11324 };
11325 /** @private */
11326 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
11327     var body = Roo.getDom(id);
11328     if(!body){
11329         body = document.createElement("div");
11330         body.id = id;
11331     }
11332     Roo.fly(body).addClass("x-tabs-item-body");
11333     bodyEl.insertBefore(body, bodyEl.firstChild);
11334     return body;
11335 };
11336 /** @private */
11337 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
11338     var td = document.createElement("td");
11339     stripEl.appendChild(td);
11340     if(closable){
11341         td.className = "x-tabs-closable";
11342         if(!this.closeTpl){
11343             this.closeTpl = new Roo.Template(
11344                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11345                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
11346                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
11347             );
11348         }
11349         var el = this.closeTpl.overwrite(td, {"text": text});
11350         var close = el.getElementsByTagName("div")[0];
11351         var inner = el.getElementsByTagName("em")[0];
11352         return {"el": el, "close": close, "inner": inner};
11353     } else {
11354         if(!this.tabTpl){
11355             this.tabTpl = new Roo.Template(
11356                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11357                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
11358             );
11359         }
11360         var el = this.tabTpl.overwrite(td, {"text": text});
11361         var inner = el.getElementsByTagName("em")[0];
11362         return {"el": el, "inner": inner};
11363     }
11364 };/*
11365  * Based on:
11366  * Ext JS Library 1.1.1
11367  * Copyright(c) 2006-2007, Ext JS, LLC.
11368  *
11369  * Originally Released Under LGPL - original licence link has changed is not relivant.
11370  *
11371  * Fork - LGPL
11372  * <script type="text/javascript">
11373  */
11374
11375 /**
11376  * @class Roo.Button
11377  * @extends Roo.util.Observable
11378  * Simple Button class
11379  * @cfg {String} text The button text
11380  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
11381  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
11382  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
11383  * @cfg {Object} scope The scope of the handler
11384  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
11385  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
11386  * @cfg {Boolean} hidden True to start hidden (defaults to false)
11387  * @cfg {Boolean} disabled True to start disabled (defaults to false)
11388  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
11389  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
11390    applies if enableToggle = true)
11391  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
11392  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
11393   an {@link Roo.util.ClickRepeater} config object (defaults to false).
11394  * @constructor
11395  * Create a new button
11396  * @param {Object} config The config object
11397  */
11398 Roo.Button = function(renderTo, config)
11399 {
11400     if (!config) {
11401         config = renderTo;
11402         renderTo = config.renderTo || false;
11403     }
11404     
11405     Roo.apply(this, config);
11406     this.addEvents({
11407         /**
11408              * @event click
11409              * Fires when this button is clicked
11410              * @param {Button} this
11411              * @param {EventObject} e The click event
11412              */
11413             "click" : true,
11414         /**
11415              * @event toggle
11416              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
11417              * @param {Button} this
11418              * @param {Boolean} pressed
11419              */
11420             "toggle" : true,
11421         /**
11422              * @event mouseover
11423              * Fires when the mouse hovers over the button
11424              * @param {Button} this
11425              * @param {Event} e The event object
11426              */
11427         'mouseover' : true,
11428         /**
11429              * @event mouseout
11430              * Fires when the mouse exits the button
11431              * @param {Button} this
11432              * @param {Event} e The event object
11433              */
11434         'mouseout': true,
11435          /**
11436              * @event render
11437              * Fires when the button is rendered
11438              * @param {Button} this
11439              */
11440         'render': true
11441     });
11442     if(this.menu){
11443         this.menu = Roo.menu.MenuMgr.get(this.menu);
11444     }
11445     if(renderTo){
11446         this.render(renderTo);
11447     }
11448     
11449     Roo.util.Observable.call(this);
11450 };
11451
11452 Roo.extend(Roo.Button, Roo.util.Observable, {
11453     /**
11454      * 
11455      */
11456     
11457     /**
11458      * Read-only. True if this button is hidden
11459      * @type Boolean
11460      */
11461     hidden : false,
11462     /**
11463      * Read-only. True if this button is disabled
11464      * @type Boolean
11465      */
11466     disabled : false,
11467     /**
11468      * Read-only. True if this button is pressed (only if enableToggle = true)
11469      * @type Boolean
11470      */
11471     pressed : false,
11472
11473     /**
11474      * @cfg {Number} tabIndex 
11475      * The DOM tabIndex for this button (defaults to undefined)
11476      */
11477     tabIndex : undefined,
11478
11479     /**
11480      * @cfg {Boolean} enableToggle
11481      * True to enable pressed/not pressed toggling (defaults to false)
11482      */
11483     enableToggle: false,
11484     /**
11485      * @cfg {Mixed} menu
11486      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
11487      */
11488     menu : undefined,
11489     /**
11490      * @cfg {String} menuAlign
11491      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
11492      */
11493     menuAlign : "tl-bl?",
11494
11495     /**
11496      * @cfg {String} iconCls
11497      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
11498      */
11499     iconCls : undefined,
11500     /**
11501      * @cfg {String} type
11502      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
11503      */
11504     type : 'button',
11505
11506     // private
11507     menuClassTarget: 'tr',
11508
11509     /**
11510      * @cfg {String} clickEvent
11511      * The type of event to map to the button's event handler (defaults to 'click')
11512      */
11513     clickEvent : 'click',
11514
11515     /**
11516      * @cfg {Boolean} handleMouseEvents
11517      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
11518      */
11519     handleMouseEvents : true,
11520
11521     /**
11522      * @cfg {String} tooltipType
11523      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
11524      */
11525     tooltipType : 'qtip',
11526
11527     /**
11528      * @cfg {String} cls
11529      * A CSS class to apply to the button's main element.
11530      */
11531     
11532     /**
11533      * @cfg {Roo.Template} template (Optional)
11534      * An {@link Roo.Template} with which to create the Button's main element. This Template must
11535      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
11536      * require code modifications if required elements (e.g. a button) aren't present.
11537      */
11538
11539     // private
11540     render : function(renderTo){
11541         var btn;
11542         if(this.hideParent){
11543             this.parentEl = Roo.get(renderTo);
11544         }
11545         if(!this.dhconfig){
11546             if(!this.template){
11547                 if(!Roo.Button.buttonTemplate){
11548                     // hideous table template
11549                     Roo.Button.buttonTemplate = new Roo.Template(
11550                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
11551                         '<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>',
11552                         "</tr></tbody></table>");
11553                 }
11554                 this.template = Roo.Button.buttonTemplate;
11555             }
11556             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
11557             var btnEl = btn.child("button:first");
11558             btnEl.on('focus', this.onFocus, this);
11559             btnEl.on('blur', this.onBlur, this);
11560             if(this.cls){
11561                 btn.addClass(this.cls);
11562             }
11563             if(this.icon){
11564                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
11565             }
11566             if(this.iconCls){
11567                 btnEl.addClass(this.iconCls);
11568                 if(!this.cls){
11569                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11570                 }
11571             }
11572             if(this.tabIndex !== undefined){
11573                 btnEl.dom.tabIndex = this.tabIndex;
11574             }
11575             if(this.tooltip){
11576                 if(typeof this.tooltip == 'object'){
11577                     Roo.QuickTips.tips(Roo.apply({
11578                           target: btnEl.id
11579                     }, this.tooltip));
11580                 } else {
11581                     btnEl.dom[this.tooltipType] = this.tooltip;
11582                 }
11583             }
11584         }else{
11585             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
11586         }
11587         this.el = btn;
11588         if(this.id){
11589             this.el.dom.id = this.el.id = this.id;
11590         }
11591         if(this.menu){
11592             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
11593             this.menu.on("show", this.onMenuShow, this);
11594             this.menu.on("hide", this.onMenuHide, this);
11595         }
11596         btn.addClass("x-btn");
11597         if(Roo.isIE && !Roo.isIE7){
11598             this.autoWidth.defer(1, this);
11599         }else{
11600             this.autoWidth();
11601         }
11602         if(this.handleMouseEvents){
11603             btn.on("mouseover", this.onMouseOver, this);
11604             btn.on("mouseout", this.onMouseOut, this);
11605             btn.on("mousedown", this.onMouseDown, this);
11606         }
11607         btn.on(this.clickEvent, this.onClick, this);
11608         //btn.on("mouseup", this.onMouseUp, this);
11609         if(this.hidden){
11610             this.hide();
11611         }
11612         if(this.disabled){
11613             this.disable();
11614         }
11615         Roo.ButtonToggleMgr.register(this);
11616         if(this.pressed){
11617             this.el.addClass("x-btn-pressed");
11618         }
11619         if(this.repeat){
11620             var repeater = new Roo.util.ClickRepeater(btn,
11621                 typeof this.repeat == "object" ? this.repeat : {}
11622             );
11623             repeater.on("click", this.onClick,  this);
11624         }
11625         this.fireEvent('render', this);
11626         
11627     },
11628     /**
11629      * Returns the button's underlying element
11630      * @return {Roo.Element} The element
11631      */
11632     getEl : function(){
11633         return this.el;  
11634     },
11635     
11636     /**
11637      * Destroys this Button and removes any listeners.
11638      */
11639     destroy : function(){
11640         Roo.ButtonToggleMgr.unregister(this);
11641         this.el.removeAllListeners();
11642         this.purgeListeners();
11643         this.el.remove();
11644     },
11645
11646     // private
11647     autoWidth : function(){
11648         if(this.el){
11649             this.el.setWidth("auto");
11650             if(Roo.isIE7 && Roo.isStrict){
11651                 var ib = this.el.child('button');
11652                 if(ib && ib.getWidth() > 20){
11653                     ib.clip();
11654                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11655                 }
11656             }
11657             if(this.minWidth){
11658                 if(this.hidden){
11659                     this.el.beginMeasure();
11660                 }
11661                 if(this.el.getWidth() < this.minWidth){
11662                     this.el.setWidth(this.minWidth);
11663                 }
11664                 if(this.hidden){
11665                     this.el.endMeasure();
11666                 }
11667             }
11668         }
11669     },
11670
11671     /**
11672      * Assigns this button's click handler
11673      * @param {Function} handler The function to call when the button is clicked
11674      * @param {Object} scope (optional) Scope for the function passed in
11675      */
11676     setHandler : function(handler, scope){
11677         this.handler = handler;
11678         this.scope = scope;  
11679     },
11680     
11681     /**
11682      * Sets this button's text
11683      * @param {String} text The button text
11684      */
11685     setText : function(text){
11686         this.text = text;
11687         if(this.el){
11688             this.el.child("td.x-btn-center button.x-btn-text").update(text);
11689         }
11690         this.autoWidth();
11691     },
11692     
11693     /**
11694      * Gets the text for this button
11695      * @return {String} The button text
11696      */
11697     getText : function(){
11698         return this.text;  
11699     },
11700     
11701     /**
11702      * Show this button
11703      */
11704     show: function(){
11705         this.hidden = false;
11706         if(this.el){
11707             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
11708         }
11709     },
11710     
11711     /**
11712      * Hide this button
11713      */
11714     hide: function(){
11715         this.hidden = true;
11716         if(this.el){
11717             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
11718         }
11719     },
11720     
11721     /**
11722      * Convenience function for boolean show/hide
11723      * @param {Boolean} visible True to show, false to hide
11724      */
11725     setVisible: function(visible){
11726         if(visible) {
11727             this.show();
11728         }else{
11729             this.hide();
11730         }
11731     },
11732     
11733     /**
11734      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
11735      * @param {Boolean} state (optional) Force a particular state
11736      */
11737     toggle : function(state){
11738         state = state === undefined ? !this.pressed : state;
11739         if(state != this.pressed){
11740             if(state){
11741                 this.el.addClass("x-btn-pressed");
11742                 this.pressed = true;
11743                 this.fireEvent("toggle", this, true);
11744             }else{
11745                 this.el.removeClass("x-btn-pressed");
11746                 this.pressed = false;
11747                 this.fireEvent("toggle", this, false);
11748             }
11749             if(this.toggleHandler){
11750                 this.toggleHandler.call(this.scope || this, this, state);
11751             }
11752         }
11753     },
11754     
11755     /**
11756      * Focus the button
11757      */
11758     focus : function(){
11759         this.el.child('button:first').focus();
11760     },
11761     
11762     /**
11763      * Disable this button
11764      */
11765     disable : function(){
11766         if(this.el){
11767             this.el.addClass("x-btn-disabled");
11768         }
11769         this.disabled = true;
11770     },
11771     
11772     /**
11773      * Enable this button
11774      */
11775     enable : function(){
11776         if(this.el){
11777             this.el.removeClass("x-btn-disabled");
11778         }
11779         this.disabled = false;
11780     },
11781
11782     /**
11783      * Convenience function for boolean enable/disable
11784      * @param {Boolean} enabled True to enable, false to disable
11785      */
11786     setDisabled : function(v){
11787         this[v !== true ? "enable" : "disable"]();
11788     },
11789
11790     // private
11791     onClick : function(e){
11792         if(e){
11793             e.preventDefault();
11794         }
11795         if(e.button != 0){
11796             return;
11797         }
11798         if(!this.disabled){
11799             if(this.enableToggle){
11800                 this.toggle();
11801             }
11802             if(this.menu && !this.menu.isVisible()){
11803                 this.menu.show(this.el, this.menuAlign);
11804             }
11805             this.fireEvent("click", this, e);
11806             if(this.handler){
11807                 this.el.removeClass("x-btn-over");
11808                 this.handler.call(this.scope || this, this, e);
11809             }
11810         }
11811     },
11812     // private
11813     onMouseOver : function(e){
11814         if(!this.disabled){
11815             this.el.addClass("x-btn-over");
11816             this.fireEvent('mouseover', this, e);
11817         }
11818     },
11819     // private
11820     onMouseOut : function(e){
11821         if(!e.within(this.el,  true)){
11822             this.el.removeClass("x-btn-over");
11823             this.fireEvent('mouseout', this, e);
11824         }
11825     },
11826     // private
11827     onFocus : function(e){
11828         if(!this.disabled){
11829             this.el.addClass("x-btn-focus");
11830         }
11831     },
11832     // private
11833     onBlur : function(e){
11834         this.el.removeClass("x-btn-focus");
11835     },
11836     // private
11837     onMouseDown : function(e){
11838         if(!this.disabled && e.button == 0){
11839             this.el.addClass("x-btn-click");
11840             Roo.get(document).on('mouseup', this.onMouseUp, this);
11841         }
11842     },
11843     // private
11844     onMouseUp : function(e){
11845         if(e.button == 0){
11846             this.el.removeClass("x-btn-click");
11847             Roo.get(document).un('mouseup', this.onMouseUp, this);
11848         }
11849     },
11850     // private
11851     onMenuShow : function(e){
11852         this.el.addClass("x-btn-menu-active");
11853     },
11854     // private
11855     onMenuHide : function(e){
11856         this.el.removeClass("x-btn-menu-active");
11857     }   
11858 });
11859
11860 // Private utility class used by Button
11861 Roo.ButtonToggleMgr = function(){
11862    var groups = {};
11863    
11864    function toggleGroup(btn, state){
11865        if(state){
11866            var g = groups[btn.toggleGroup];
11867            for(var i = 0, l = g.length; i < l; i++){
11868                if(g[i] != btn){
11869                    g[i].toggle(false);
11870                }
11871            }
11872        }
11873    }
11874    
11875    return {
11876        register : function(btn){
11877            if(!btn.toggleGroup){
11878                return;
11879            }
11880            var g = groups[btn.toggleGroup];
11881            if(!g){
11882                g = groups[btn.toggleGroup] = [];
11883            }
11884            g.push(btn);
11885            btn.on("toggle", toggleGroup);
11886        },
11887        
11888        unregister : function(btn){
11889            if(!btn.toggleGroup){
11890                return;
11891            }
11892            var g = groups[btn.toggleGroup];
11893            if(g){
11894                g.remove(btn);
11895                btn.un("toggle", toggleGroup);
11896            }
11897        }
11898    };
11899 }();/*
11900  * Based on:
11901  * Ext JS Library 1.1.1
11902  * Copyright(c) 2006-2007, Ext JS, LLC.
11903  *
11904  * Originally Released Under LGPL - original licence link has changed is not relivant.
11905  *
11906  * Fork - LGPL
11907  * <script type="text/javascript">
11908  */
11909  
11910 /**
11911  * @class Roo.SplitButton
11912  * @extends Roo.Button
11913  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
11914  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
11915  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
11916  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
11917  * @cfg {String} arrowTooltip The title attribute of the arrow
11918  * @constructor
11919  * Create a new menu button
11920  * @param {String/HTMLElement/Element} renderTo The element to append the button to
11921  * @param {Object} config The config object
11922  */
11923 Roo.SplitButton = function(renderTo, config){
11924     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
11925     /**
11926      * @event arrowclick
11927      * Fires when this button's arrow is clicked
11928      * @param {SplitButton} this
11929      * @param {EventObject} e The click event
11930      */
11931     this.addEvents({"arrowclick":true});
11932 };
11933
11934 Roo.extend(Roo.SplitButton, Roo.Button, {
11935     render : function(renderTo){
11936         // this is one sweet looking template!
11937         var tpl = new Roo.Template(
11938             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
11939             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
11940             '<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>',
11941             "</tbody></table></td><td>",
11942             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
11943             '<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>',
11944             "</tbody></table></td></tr></table>"
11945         );
11946         var btn = tpl.append(renderTo, [this.text, this.type], true);
11947         var btnEl = btn.child("button");
11948         if(this.cls){
11949             btn.addClass(this.cls);
11950         }
11951         if(this.icon){
11952             btnEl.setStyle('background-image', 'url(' +this.icon +')');
11953         }
11954         if(this.iconCls){
11955             btnEl.addClass(this.iconCls);
11956             if(!this.cls){
11957                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11958             }
11959         }
11960         this.el = btn;
11961         if(this.handleMouseEvents){
11962             btn.on("mouseover", this.onMouseOver, this);
11963             btn.on("mouseout", this.onMouseOut, this);
11964             btn.on("mousedown", this.onMouseDown, this);
11965             btn.on("mouseup", this.onMouseUp, this);
11966         }
11967         btn.on(this.clickEvent, this.onClick, this);
11968         if(this.tooltip){
11969             if(typeof this.tooltip == 'object'){
11970                 Roo.QuickTips.tips(Roo.apply({
11971                       target: btnEl.id
11972                 }, this.tooltip));
11973             } else {
11974                 btnEl.dom[this.tooltipType] = this.tooltip;
11975             }
11976         }
11977         if(this.arrowTooltip){
11978             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
11979         }
11980         if(this.hidden){
11981             this.hide();
11982         }
11983         if(this.disabled){
11984             this.disable();
11985         }
11986         if(this.pressed){
11987             this.el.addClass("x-btn-pressed");
11988         }
11989         if(Roo.isIE && !Roo.isIE7){
11990             this.autoWidth.defer(1, this);
11991         }else{
11992             this.autoWidth();
11993         }
11994         if(this.menu){
11995             this.menu.on("show", this.onMenuShow, this);
11996             this.menu.on("hide", this.onMenuHide, this);
11997         }
11998         this.fireEvent('render', this);
11999     },
12000
12001     // private
12002     autoWidth : function(){
12003         if(this.el){
12004             var tbl = this.el.child("table:first");
12005             var tbl2 = this.el.child("table:last");
12006             this.el.setWidth("auto");
12007             tbl.setWidth("auto");
12008             if(Roo.isIE7 && Roo.isStrict){
12009                 var ib = this.el.child('button:first');
12010                 if(ib && ib.getWidth() > 20){
12011                     ib.clip();
12012                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
12013                 }
12014             }
12015             if(this.minWidth){
12016                 if(this.hidden){
12017                     this.el.beginMeasure();
12018                 }
12019                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
12020                     tbl.setWidth(this.minWidth-tbl2.getWidth());
12021                 }
12022                 if(this.hidden){
12023                     this.el.endMeasure();
12024                 }
12025             }
12026             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
12027         } 
12028     },
12029     /**
12030      * Sets this button's click handler
12031      * @param {Function} handler The function to call when the button is clicked
12032      * @param {Object} scope (optional) Scope for the function passed above
12033      */
12034     setHandler : function(handler, scope){
12035         this.handler = handler;
12036         this.scope = scope;  
12037     },
12038     
12039     /**
12040      * Sets this button's arrow click handler
12041      * @param {Function} handler The function to call when the arrow is clicked
12042      * @param {Object} scope (optional) Scope for the function passed above
12043      */
12044     setArrowHandler : function(handler, scope){
12045         this.arrowHandler = handler;
12046         this.scope = scope;  
12047     },
12048     
12049     /**
12050      * Focus the button
12051      */
12052     focus : function(){
12053         if(this.el){
12054             this.el.child("button:first").focus();
12055         }
12056     },
12057
12058     // private
12059     onClick : function(e){
12060         e.preventDefault();
12061         if(!this.disabled){
12062             if(e.getTarget(".x-btn-menu-arrow-wrap")){
12063                 if(this.menu && !this.menu.isVisible()){
12064                     this.menu.show(this.el, this.menuAlign);
12065                 }
12066                 this.fireEvent("arrowclick", this, e);
12067                 if(this.arrowHandler){
12068                     this.arrowHandler.call(this.scope || this, this, e);
12069                 }
12070             }else{
12071                 this.fireEvent("click", this, e);
12072                 if(this.handler){
12073                     this.handler.call(this.scope || this, this, e);
12074                 }
12075             }
12076         }
12077     },
12078     // private
12079     onMouseDown : function(e){
12080         if(!this.disabled){
12081             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
12082         }
12083     },
12084     // private
12085     onMouseUp : function(e){
12086         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
12087     }   
12088 });
12089
12090
12091 // backwards compat
12092 Roo.MenuButton = Roo.SplitButton;/*
12093  * Based on:
12094  * Ext JS Library 1.1.1
12095  * Copyright(c) 2006-2007, Ext JS, LLC.
12096  *
12097  * Originally Released Under LGPL - original licence link has changed is not relivant.
12098  *
12099  * Fork - LGPL
12100  * <script type="text/javascript">
12101  */
12102
12103 /**
12104  * @class Roo.Toolbar
12105  * Basic Toolbar class.
12106  * @constructor
12107  * Creates a new Toolbar
12108  * @param {Object} config The config object
12109  */ 
12110 Roo.Toolbar = function(container, buttons, config)
12111 {
12112     /// old consturctor format still supported..
12113     if(container instanceof Array){ // omit the container for later rendering
12114         buttons = container;
12115         config = buttons;
12116         container = null;
12117     }
12118     if (typeof(container) == 'object' && container.xtype) {
12119         config = container;
12120         container = config.container;
12121         buttons = config.buttons; // not really - use items!!
12122     }
12123     var xitems = [];
12124     if (config && config.items) {
12125         xitems = config.items;
12126         delete config.items;
12127     }
12128     Roo.apply(this, config);
12129     this.buttons = buttons;
12130     
12131     if(container){
12132         this.render(container);
12133     }
12134     Roo.each(xitems, function(b) {
12135         this.add(b);
12136     }, this);
12137     
12138 };
12139
12140 Roo.Toolbar.prototype = {
12141     /**
12142      * @cfg {Roo.data.Store} items
12143      * array of button configs or elements to add
12144      */
12145     
12146     /**
12147      * @cfg {String/HTMLElement/Element} container
12148      * The id or element that will contain the toolbar
12149      */
12150     // private
12151     render : function(ct){
12152         this.el = Roo.get(ct);
12153         if(this.cls){
12154             this.el.addClass(this.cls);
12155         }
12156         // using a table allows for vertical alignment
12157         // 100% width is needed by Safari...
12158         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
12159         this.tr = this.el.child("tr", true);
12160         var autoId = 0;
12161         this.items = new Roo.util.MixedCollection(false, function(o){
12162             return o.id || ("item" + (++autoId));
12163         });
12164         if(this.buttons){
12165             this.add.apply(this, this.buttons);
12166             delete this.buttons;
12167         }
12168     },
12169
12170     /**
12171      * Adds element(s) to the toolbar -- this function takes a variable number of 
12172      * arguments of mixed type and adds them to the toolbar.
12173      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
12174      * <ul>
12175      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
12176      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
12177      * <li>Field: Any form field (equivalent to {@link #addField})</li>
12178      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
12179      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
12180      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
12181      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
12182      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
12183      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
12184      * </ul>
12185      * @param {Mixed} arg2
12186      * @param {Mixed} etc.
12187      */
12188     add : function(){
12189         var a = arguments, l = a.length;
12190         for(var i = 0; i < l; i++){
12191             this._add(a[i]);
12192         }
12193     },
12194     // private..
12195     _add : function(el) {
12196         
12197         if (el.xtype) {
12198             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
12199         }
12200         
12201         if (el.applyTo){ // some kind of form field
12202             return this.addField(el);
12203         } 
12204         if (el.render){ // some kind of Toolbar.Item
12205             return this.addItem(el);
12206         }
12207         if (typeof el == "string"){ // string
12208             if(el == "separator" || el == "-"){
12209                 return this.addSeparator();
12210             }
12211             if (el == " "){
12212                 return this.addSpacer();
12213             }
12214             if(el == "->"){
12215                 return this.addFill();
12216             }
12217             return this.addText(el);
12218             
12219         }
12220         if(el.tagName){ // element
12221             return this.addElement(el);
12222         }
12223         if(typeof el == "object"){ // must be button config?
12224             return this.addButton(el);
12225         }
12226         // and now what?!?!
12227         return false;
12228         
12229     },
12230     
12231     /**
12232      * Add an Xtype element
12233      * @param {Object} xtype Xtype Object
12234      * @return {Object} created Object
12235      */
12236     addxtype : function(e){
12237         return this.add(e);  
12238     },
12239     
12240     /**
12241      * Returns the Element for this toolbar.
12242      * @return {Roo.Element}
12243      */
12244     getEl : function(){
12245         return this.el;  
12246     },
12247     
12248     /**
12249      * Adds a separator
12250      * @return {Roo.Toolbar.Item} The separator item
12251      */
12252     addSeparator : function(){
12253         return this.addItem(new Roo.Toolbar.Separator());
12254     },
12255
12256     /**
12257      * Adds a spacer element
12258      * @return {Roo.Toolbar.Spacer} The spacer item
12259      */
12260     addSpacer : function(){
12261         return this.addItem(new Roo.Toolbar.Spacer());
12262     },
12263
12264     /**
12265      * Adds a fill element that forces subsequent additions to the right side of the toolbar
12266      * @return {Roo.Toolbar.Fill} The fill item
12267      */
12268     addFill : function(){
12269         return this.addItem(new Roo.Toolbar.Fill());
12270     },
12271
12272     /**
12273      * Adds any standard HTML element to the toolbar
12274      * @param {String/HTMLElement/Element} el The element or id of the element to add
12275      * @return {Roo.Toolbar.Item} The element's item
12276      */
12277     addElement : function(el){
12278         return this.addItem(new Roo.Toolbar.Item(el));
12279     },
12280     /**
12281      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
12282      * @type Roo.util.MixedCollection  
12283      */
12284     items : false,
12285      
12286     /**
12287      * Adds any Toolbar.Item or subclass
12288      * @param {Roo.Toolbar.Item} item
12289      * @return {Roo.Toolbar.Item} The item
12290      */
12291     addItem : function(item){
12292         var td = this.nextBlock();
12293         item.render(td);
12294         this.items.add(item);
12295         return item;
12296     },
12297     
12298     /**
12299      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
12300      * @param {Object/Array} config A button config or array of configs
12301      * @return {Roo.Toolbar.Button/Array}
12302      */
12303     addButton : function(config){
12304         if(config instanceof Array){
12305             var buttons = [];
12306             for(var i = 0, len = config.length; i < len; i++) {
12307                 buttons.push(this.addButton(config[i]));
12308             }
12309             return buttons;
12310         }
12311         var b = config;
12312         if(!(config instanceof Roo.Toolbar.Button)){
12313             b = config.split ?
12314                 new Roo.Toolbar.SplitButton(config) :
12315                 new Roo.Toolbar.Button(config);
12316         }
12317         var td = this.nextBlock();
12318         b.render(td);
12319         this.items.add(b);
12320         return b;
12321     },
12322     
12323     /**
12324      * Adds text to the toolbar
12325      * @param {String} text The text to add
12326      * @return {Roo.Toolbar.Item} The element's item
12327      */
12328     addText : function(text){
12329         return this.addItem(new Roo.Toolbar.TextItem(text));
12330     },
12331     
12332     /**
12333      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
12334      * @param {Number} index The index where the item is to be inserted
12335      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
12336      * @return {Roo.Toolbar.Button/Item}
12337      */
12338     insertButton : function(index, item){
12339         if(item instanceof Array){
12340             var buttons = [];
12341             for(var i = 0, len = item.length; i < len; i++) {
12342                buttons.push(this.insertButton(index + i, item[i]));
12343             }
12344             return buttons;
12345         }
12346         if (!(item instanceof Roo.Toolbar.Button)){
12347            item = new Roo.Toolbar.Button(item);
12348         }
12349         var td = document.createElement("td");
12350         this.tr.insertBefore(td, this.tr.childNodes[index]);
12351         item.render(td);
12352         this.items.insert(index, item);
12353         return item;
12354     },
12355     
12356     /**
12357      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
12358      * @param {Object} config
12359      * @return {Roo.Toolbar.Item} The element's item
12360      */
12361     addDom : function(config, returnEl){
12362         var td = this.nextBlock();
12363         Roo.DomHelper.overwrite(td, config);
12364         var ti = new Roo.Toolbar.Item(td.firstChild);
12365         ti.render(td);
12366         this.items.add(ti);
12367         return ti;
12368     },
12369
12370     /**
12371      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
12372      * @type Roo.util.MixedCollection  
12373      */
12374     fields : false,
12375     
12376     /**
12377      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc). Note: the field should not have
12378      * been rendered yet. For a field that has already been rendered, use {@link #addElement}.
12379      * @param {Roo.form.Field} field
12380      * @return {Roo.ToolbarItem}
12381      */
12382      
12383       
12384     addField : function(field) {
12385         if (!this.fields) {
12386             var autoId = 0;
12387             this.fields = new Roo.util.MixedCollection(false, function(o){
12388                 return o.id || ("item" + (++autoId));
12389             });
12390
12391         }
12392         
12393         var td = this.nextBlock();
12394         field.render(td);
12395         var ti = new Roo.Toolbar.Item(td.firstChild);
12396         ti.render(td);
12397         this.items.add(ti);
12398         this.fields.add(field);
12399         return ti;
12400     },
12401     /**
12402      * Hide the toolbar
12403      * @method hide
12404      */
12405      
12406       
12407     hide : function()
12408     {
12409         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
12410         this.el.child('div').hide();
12411     },
12412     /**
12413      * Show the toolbar
12414      * @method show
12415      */
12416     show : function()
12417     {
12418         this.el.child('div').show();
12419     },
12420       
12421     // private
12422     nextBlock : function(){
12423         var td = document.createElement("td");
12424         this.tr.appendChild(td);
12425         return td;
12426     },
12427
12428     // private
12429     destroy : function(){
12430         if(this.items){ // rendered?
12431             Roo.destroy.apply(Roo, this.items.items);
12432         }
12433         if(this.fields){ // rendered?
12434             Roo.destroy.apply(Roo, this.fields.items);
12435         }
12436         Roo.Element.uncache(this.el, this.tr);
12437     }
12438 };
12439
12440 /**
12441  * @class Roo.Toolbar.Item
12442  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
12443  * @constructor
12444  * Creates a new Item
12445  * @param {HTMLElement} el 
12446  */
12447 Roo.Toolbar.Item = function(el){
12448     this.el = Roo.getDom(el);
12449     this.id = Roo.id(this.el);
12450     this.hidden = false;
12451 };
12452
12453 Roo.Toolbar.Item.prototype = {
12454     
12455     /**
12456      * Get this item's HTML Element
12457      * @return {HTMLElement}
12458      */
12459     getEl : function(){
12460        return this.el;  
12461     },
12462
12463     // private
12464     render : function(td){
12465         this.td = td;
12466         td.appendChild(this.el);
12467     },
12468     
12469     /**
12470      * Removes and destroys this item.
12471      */
12472     destroy : function(){
12473         this.td.parentNode.removeChild(this.td);
12474     },
12475     
12476     /**
12477      * Shows this item.
12478      */
12479     show: function(){
12480         this.hidden = false;
12481         this.td.style.display = "";
12482     },
12483     
12484     /**
12485      * Hides this item.
12486      */
12487     hide: function(){
12488         this.hidden = true;
12489         this.td.style.display = "none";
12490     },
12491     
12492     /**
12493      * Convenience function for boolean show/hide.
12494      * @param {Boolean} visible true to show/false to hide
12495      */
12496     setVisible: function(visible){
12497         if(visible) {
12498             this.show();
12499         }else{
12500             this.hide();
12501         }
12502     },
12503     
12504     /**
12505      * Try to focus this item.
12506      */
12507     focus : function(){
12508         Roo.fly(this.el).focus();
12509     },
12510     
12511     /**
12512      * Disables this item.
12513      */
12514     disable : function(){
12515         Roo.fly(this.td).addClass("x-item-disabled");
12516         this.disabled = true;
12517         this.el.disabled = true;
12518     },
12519     
12520     /**
12521      * Enables this item.
12522      */
12523     enable : function(){
12524         Roo.fly(this.td).removeClass("x-item-disabled");
12525         this.disabled = false;
12526         this.el.disabled = false;
12527     }
12528 };
12529
12530
12531 /**
12532  * @class Roo.Toolbar.Separator
12533  * @extends Roo.Toolbar.Item
12534  * A simple toolbar separator class
12535  * @constructor
12536  * Creates a new Separator
12537  */
12538 Roo.Toolbar.Separator = function(){
12539     var s = document.createElement("span");
12540     s.className = "ytb-sep";
12541     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
12542 };
12543 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
12544     enable:Roo.emptyFn,
12545     disable:Roo.emptyFn,
12546     focus:Roo.emptyFn
12547 });
12548
12549 /**
12550  * @class Roo.Toolbar.Spacer
12551  * @extends Roo.Toolbar.Item
12552  * A simple element that adds extra horizontal space to a toolbar.
12553  * @constructor
12554  * Creates a new Spacer
12555  */
12556 Roo.Toolbar.Spacer = function(){
12557     var s = document.createElement("div");
12558     s.className = "ytb-spacer";
12559     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
12560 };
12561 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
12562     enable:Roo.emptyFn,
12563     disable:Roo.emptyFn,
12564     focus:Roo.emptyFn
12565 });
12566
12567 /**
12568  * @class Roo.Toolbar.Fill
12569  * @extends Roo.Toolbar.Spacer
12570  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
12571  * @constructor
12572  * Creates a new Spacer
12573  */
12574 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
12575     // private
12576     render : function(td){
12577         td.style.width = '100%';
12578         Roo.Toolbar.Fill.superclass.render.call(this, td);
12579     }
12580 });
12581
12582 /**
12583  * @class Roo.Toolbar.TextItem
12584  * @extends Roo.Toolbar.Item
12585  * A simple class that renders text directly into a toolbar.
12586  * @constructor
12587  * Creates a new TextItem
12588  * @param {String} text
12589  */
12590 Roo.Toolbar.TextItem = function(text){
12591     if (typeof(text) == 'object') {
12592         text = text.text;
12593     }
12594     var s = document.createElement("span");
12595     s.className = "ytb-text";
12596     s.innerHTML = text;
12597     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
12598 };
12599 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
12600     enable:Roo.emptyFn,
12601     disable:Roo.emptyFn,
12602     focus:Roo.emptyFn
12603 });
12604
12605 /**
12606  * @class Roo.Toolbar.Button
12607  * @extends Roo.Button
12608  * A button that renders into a toolbar.
12609  * @constructor
12610  * Creates a new Button
12611  * @param {Object} config A standard {@link Roo.Button} config object
12612  */
12613 Roo.Toolbar.Button = function(config){
12614     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
12615 };
12616 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
12617     render : function(td){
12618         this.td = td;
12619         Roo.Toolbar.Button.superclass.render.call(this, td);
12620     },
12621     
12622     /**
12623      * Removes and destroys this button
12624      */
12625     destroy : function(){
12626         Roo.Toolbar.Button.superclass.destroy.call(this);
12627         this.td.parentNode.removeChild(this.td);
12628     },
12629     
12630     /**
12631      * Shows this button
12632      */
12633     show: function(){
12634         this.hidden = false;
12635         this.td.style.display = "";
12636     },
12637     
12638     /**
12639      * Hides this button
12640      */
12641     hide: function(){
12642         this.hidden = true;
12643         this.td.style.display = "none";
12644     },
12645
12646     /**
12647      * Disables this item
12648      */
12649     disable : function(){
12650         Roo.fly(this.td).addClass("x-item-disabled");
12651         this.disabled = true;
12652     },
12653
12654     /**
12655      * Enables this item
12656      */
12657     enable : function(){
12658         Roo.fly(this.td).removeClass("x-item-disabled");
12659         this.disabled = false;
12660     }
12661 });
12662 // backwards compat
12663 Roo.ToolbarButton = Roo.Toolbar.Button;
12664
12665 /**
12666  * @class Roo.Toolbar.SplitButton
12667  * @extends Roo.SplitButton
12668  * A menu button that renders into a toolbar.
12669  * @constructor
12670  * Creates a new SplitButton
12671  * @param {Object} config A standard {@link Roo.SplitButton} config object
12672  */
12673 Roo.Toolbar.SplitButton = function(config){
12674     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
12675 };
12676 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
12677     render : function(td){
12678         this.td = td;
12679         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
12680     },
12681     
12682     /**
12683      * Removes and destroys this button
12684      */
12685     destroy : function(){
12686         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
12687         this.td.parentNode.removeChild(this.td);
12688     },
12689     
12690     /**
12691      * Shows this button
12692      */
12693     show: function(){
12694         this.hidden = false;
12695         this.td.style.display = "";
12696     },
12697     
12698     /**
12699      * Hides this button
12700      */
12701     hide: function(){
12702         this.hidden = true;
12703         this.td.style.display = "none";
12704     }
12705 });
12706
12707 // backwards compat
12708 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
12709  * Based on:
12710  * Ext JS Library 1.1.1
12711  * Copyright(c) 2006-2007, Ext JS, LLC.
12712  *
12713  * Originally Released Under LGPL - original licence link has changed is not relivant.
12714  *
12715  * Fork - LGPL
12716  * <script type="text/javascript">
12717  */
12718  
12719 /**
12720  * @class Roo.PagingToolbar
12721  * @extends Roo.Toolbar
12722  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
12723  * @constructor
12724  * Create a new PagingToolbar
12725  * @param {Object} config The config object
12726  */
12727 Roo.PagingToolbar = function(el, ds, config)
12728 {
12729     // old args format still supported... - xtype is prefered..
12730     if (typeof(el) == 'object' && el.xtype) {
12731         // created from xtype...
12732         config = el;
12733         ds = el.dataSource;
12734         el = config.container;
12735     }
12736     
12737     
12738     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
12739     this.ds = ds;
12740     this.cursor = 0;
12741     this.renderButtons(this.el);
12742     this.bind(ds);
12743 };
12744
12745 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
12746     /**
12747      * @cfg {Roo.data.Store} dataSource
12748      * The underlying data store providing the paged data
12749      */
12750     /**
12751      * @cfg {String/HTMLElement/Element} container
12752      * container The id or element that will contain the toolbar
12753      */
12754     /**
12755      * @cfg {Boolean} displayInfo
12756      * True to display the displayMsg (defaults to false)
12757      */
12758     /**
12759      * @cfg {Number} pageSize
12760      * The number of records to display per page (defaults to 20)
12761      */
12762     pageSize: 20,
12763     /**
12764      * @cfg {String} displayMsg
12765      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
12766      */
12767     displayMsg : 'Displaying {0} - {1} of {2}',
12768     /**
12769      * @cfg {String} emptyMsg
12770      * The message to display when no records are found (defaults to "No data to display")
12771      */
12772     emptyMsg : 'No data to display',
12773     /**
12774      * Customizable piece of the default paging text (defaults to "Page")
12775      * @type String
12776      */
12777     beforePageText : "Page",
12778     /**
12779      * Customizable piece of the default paging text (defaults to "of %0")
12780      * @type String
12781      */
12782     afterPageText : "of {0}",
12783     /**
12784      * Customizable piece of the default paging text (defaults to "First Page")
12785      * @type String
12786      */
12787     firstText : "First Page",
12788     /**
12789      * Customizable piece of the default paging text (defaults to "Previous Page")
12790      * @type String
12791      */
12792     prevText : "Previous Page",
12793     /**
12794      * Customizable piece of the default paging text (defaults to "Next Page")
12795      * @type String
12796      */
12797     nextText : "Next Page",
12798     /**
12799      * Customizable piece of the default paging text (defaults to "Last Page")
12800      * @type String
12801      */
12802     lastText : "Last Page",
12803     /**
12804      * Customizable piece of the default paging text (defaults to "Refresh")
12805      * @type String
12806      */
12807     refreshText : "Refresh",
12808
12809     // private
12810     renderButtons : function(el){
12811         Roo.PagingToolbar.superclass.render.call(this, el);
12812         this.first = this.addButton({
12813             tooltip: this.firstText,
12814             cls: "x-btn-icon x-grid-page-first",
12815             disabled: true,
12816             handler: this.onClick.createDelegate(this, ["first"])
12817         });
12818         this.prev = this.addButton({
12819             tooltip: this.prevText,
12820             cls: "x-btn-icon x-grid-page-prev",
12821             disabled: true,
12822             handler: this.onClick.createDelegate(this, ["prev"])
12823         });
12824         this.addSeparator();
12825         this.add(this.beforePageText);
12826         this.field = Roo.get(this.addDom({
12827            tag: "input",
12828            type: "text",
12829            size: "3",
12830            value: "1",
12831            cls: "x-grid-page-number"
12832         }).el);
12833         this.field.on("keydown", this.onPagingKeydown, this);
12834         this.field.on("focus", function(){this.dom.select();});
12835         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
12836         this.field.setHeight(18);
12837         this.addSeparator();
12838         this.next = this.addButton({
12839             tooltip: this.nextText,
12840             cls: "x-btn-icon x-grid-page-next",
12841             disabled: true,
12842             handler: this.onClick.createDelegate(this, ["next"])
12843         });
12844         this.last = this.addButton({
12845             tooltip: this.lastText,
12846             cls: "x-btn-icon x-grid-page-last",
12847             disabled: true,
12848             handler: this.onClick.createDelegate(this, ["last"])
12849         });
12850         this.addSeparator();
12851         this.loading = this.addButton({
12852             tooltip: this.refreshText,
12853             cls: "x-btn-icon x-grid-loading",
12854             handler: this.onClick.createDelegate(this, ["refresh"])
12855         });
12856
12857         if(this.displayInfo){
12858             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
12859         }
12860     },
12861
12862     // private
12863     updateInfo : function(){
12864         if(this.displayEl){
12865             var count = this.ds.getCount();
12866             var msg = count == 0 ?
12867                 this.emptyMsg :
12868                 String.format(
12869                     this.displayMsg,
12870                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
12871                 );
12872             this.displayEl.update(msg);
12873         }
12874     },
12875
12876     // private
12877     onLoad : function(ds, r, o){
12878        this.cursor = o.params ? o.params.start : 0;
12879        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
12880
12881        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
12882        this.field.dom.value = ap;
12883        this.first.setDisabled(ap == 1);
12884        this.prev.setDisabled(ap == 1);
12885        this.next.setDisabled(ap == ps);
12886        this.last.setDisabled(ap == ps);
12887        this.loading.enable();
12888        this.updateInfo();
12889     },
12890
12891     // private
12892     getPageData : function(){
12893         var total = this.ds.getTotalCount();
12894         return {
12895             total : total,
12896             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
12897             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
12898         };
12899     },
12900
12901     // private
12902     onLoadError : function(){
12903         this.loading.enable();
12904     },
12905
12906     // private
12907     onPagingKeydown : function(e){
12908         var k = e.getKey();
12909         var d = this.getPageData();
12910         if(k == e.RETURN){
12911             var v = this.field.dom.value, pageNum;
12912             if(!v || isNaN(pageNum = parseInt(v, 10))){
12913                 this.field.dom.value = d.activePage;
12914                 return;
12915             }
12916             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
12917             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
12918             e.stopEvent();
12919         }
12920         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))
12921         {
12922           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
12923           this.field.dom.value = pageNum;
12924           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
12925           e.stopEvent();
12926         }
12927         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
12928         {
12929           var v = this.field.dom.value, pageNum; 
12930           var increment = (e.shiftKey) ? 10 : 1;
12931           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
12932             increment *= -1;
12933           if(!v || isNaN(pageNum = parseInt(v, 10))) {
12934             this.field.dom.value = d.activePage;
12935             return;
12936           }
12937           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
12938           {
12939             this.field.dom.value = parseInt(v, 10) + increment;
12940             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
12941             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
12942           }
12943           e.stopEvent();
12944         }
12945     },
12946
12947     // private
12948     beforeLoad : function(){
12949         if(this.loading){
12950             this.loading.disable();
12951         }
12952     },
12953
12954     // private
12955     onClick : function(which){
12956         var ds = this.ds;
12957         switch(which){
12958             case "first":
12959                 ds.load({params:{start: 0, limit: this.pageSize}});
12960             break;
12961             case "prev":
12962                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
12963             break;
12964             case "next":
12965                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
12966             break;
12967             case "last":
12968                 var total = ds.getTotalCount();
12969                 var extra = total % this.pageSize;
12970                 var lastStart = extra ? (total - extra) : total-this.pageSize;
12971                 ds.load({params:{start: lastStart, limit: this.pageSize}});
12972             break;
12973             case "refresh":
12974                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
12975             break;
12976         }
12977     },
12978
12979     /**
12980      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
12981      * @param {Roo.data.Store} store The data store to unbind
12982      */
12983     unbind : function(ds){
12984         ds.un("beforeload", this.beforeLoad, this);
12985         ds.un("load", this.onLoad, this);
12986         ds.un("loadexception", this.onLoadError, this);
12987         ds.un("remove", this.updateInfo, this);
12988         ds.un("add", this.updateInfo, this);
12989         this.ds = undefined;
12990     },
12991
12992     /**
12993      * Binds the paging toolbar to the specified {@link Roo.data.Store}
12994      * @param {Roo.data.Store} store The data store to bind
12995      */
12996     bind : function(ds){
12997         ds.on("beforeload", this.beforeLoad, this);
12998         ds.on("load", this.onLoad, this);
12999         ds.on("loadexception", this.onLoadError, this);
13000         ds.on("remove", this.updateInfo, this);
13001         ds.on("add", this.updateInfo, this);
13002         this.ds = ds;
13003     }
13004 });/*
13005  * Based on:
13006  * Ext JS Library 1.1.1
13007  * Copyright(c) 2006-2007, Ext JS, LLC.
13008  *
13009  * Originally Released Under LGPL - original licence link has changed is not relivant.
13010  *
13011  * Fork - LGPL
13012  * <script type="text/javascript">
13013  */
13014
13015 /**
13016  * @class Roo.Resizable
13017  * @extends Roo.util.Observable
13018  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
13019  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
13020  * 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
13021  * the element will be wrapped for you automatically.</p>
13022  * <p>Here is the list of valid resize handles:</p>
13023  * <pre>
13024 Value   Description
13025 ------  -------------------
13026  'n'     north
13027  's'     south
13028  'e'     east
13029  'w'     west
13030  'nw'    northwest
13031  'sw'    southwest
13032  'se'    southeast
13033  'ne'    northeast
13034  'all'   all
13035 </pre>
13036  * <p>Here's an example showing the creation of a typical Resizable:</p>
13037  * <pre><code>
13038 var resizer = new Roo.Resizable("element-id", {
13039     handles: 'all',
13040     minWidth: 200,
13041     minHeight: 100,
13042     maxWidth: 500,
13043     maxHeight: 400,
13044     pinned: true
13045 });
13046 resizer.on("resize", myHandler);
13047 </code></pre>
13048  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
13049  * resizer.east.setDisplayed(false);</p>
13050  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
13051  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
13052  * resize operation's new size (defaults to [0, 0])
13053  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
13054  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
13055  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
13056  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
13057  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
13058  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
13059  * @cfg {Number} width The width of the element in pixels (defaults to null)
13060  * @cfg {Number} height The height of the element in pixels (defaults to null)
13061  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
13062  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
13063  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
13064  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
13065  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
13066  * in favor of the handles config option (defaults to false)
13067  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
13068  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
13069  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
13070  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
13071  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
13072  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
13073  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
13074  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
13075  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
13076  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
13077  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
13078  * @constructor
13079  * Create a new resizable component
13080  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
13081  * @param {Object} config configuration options
13082   */
13083 Roo.Resizable = function(el, config){
13084     this.el = Roo.get(el);
13085
13086     if(config && config.wrap){
13087         config.resizeChild = this.el;
13088         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
13089         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
13090         this.el.setStyle("overflow", "hidden");
13091         this.el.setPositioning(config.resizeChild.getPositioning());
13092         config.resizeChild.clearPositioning();
13093         if(!config.width || !config.height){
13094             var csize = config.resizeChild.getSize();
13095             this.el.setSize(csize.width, csize.height);
13096         }
13097         if(config.pinned && !config.adjustments){
13098             config.adjustments = "auto";
13099         }
13100     }
13101
13102     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
13103     this.proxy.unselectable();
13104     this.proxy.enableDisplayMode('block');
13105
13106     Roo.apply(this, config);
13107
13108     if(this.pinned){
13109         this.disableTrackOver = true;
13110         this.el.addClass("x-resizable-pinned");
13111     }
13112     // if the element isn't positioned, make it relative
13113     var position = this.el.getStyle("position");
13114     if(position != "absolute" && position != "fixed"){
13115         this.el.setStyle("position", "relative");
13116     }
13117     if(!this.handles){ // no handles passed, must be legacy style
13118         this.handles = 's,e,se';
13119         if(this.multiDirectional){
13120             this.handles += ',n,w';
13121         }
13122     }
13123     if(this.handles == "all"){
13124         this.handles = "n s e w ne nw se sw";
13125     }
13126     var hs = this.handles.split(/\s*?[,;]\s*?| /);
13127     var ps = Roo.Resizable.positions;
13128     for(var i = 0, len = hs.length; i < len; i++){
13129         if(hs[i] && ps[hs[i]]){
13130             var pos = ps[hs[i]];
13131             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
13132         }
13133     }
13134     // legacy
13135     this.corner = this.southeast;
13136
13137     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1){
13138         this.updateBox = true;
13139     }
13140
13141     this.activeHandle = null;
13142
13143     if(this.resizeChild){
13144         if(typeof this.resizeChild == "boolean"){
13145             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
13146         }else{
13147             this.resizeChild = Roo.get(this.resizeChild, true);
13148         }
13149     }
13150
13151     if(this.adjustments == "auto"){
13152         var rc = this.resizeChild;
13153         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
13154         if(rc && (hw || hn)){
13155             rc.position("relative");
13156             rc.setLeft(hw ? hw.el.getWidth() : 0);
13157             rc.setTop(hn ? hn.el.getHeight() : 0);
13158         }
13159         this.adjustments = [
13160             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
13161             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
13162         ];
13163     }
13164
13165     if(this.draggable){
13166         this.dd = this.dynamic ?
13167             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
13168         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
13169     }
13170
13171     // public events
13172     this.addEvents({
13173         /**
13174          * @event beforeresize
13175          * Fired before resize is allowed. Set enabled to false to cancel resize.
13176          * @param {Roo.Resizable} this
13177          * @param {Roo.EventObject} e The mousedown event
13178          */
13179         "beforeresize" : true,
13180         /**
13181          * @event resize
13182          * Fired after a resize.
13183          * @param {Roo.Resizable} this
13184          * @param {Number} width The new width
13185          * @param {Number} height The new height
13186          * @param {Roo.EventObject} e The mouseup event
13187          */
13188         "resize" : true
13189     });
13190
13191     if(this.width !== null && this.height !== null){
13192         this.resizeTo(this.width, this.height);
13193     }else{
13194         this.updateChildSize();
13195     }
13196     if(Roo.isIE){
13197         this.el.dom.style.zoom = 1;
13198     }
13199     Roo.Resizable.superclass.constructor.call(this);
13200 };
13201
13202 Roo.extend(Roo.Resizable, Roo.util.Observable, {
13203         resizeChild : false,
13204         adjustments : [0, 0],
13205         minWidth : 5,
13206         minHeight : 5,
13207         maxWidth : 10000,
13208         maxHeight : 10000,
13209         enabled : true,
13210         animate : false,
13211         duration : .35,
13212         dynamic : false,
13213         handles : false,
13214         multiDirectional : false,
13215         disableTrackOver : false,
13216         easing : 'easeOutStrong',
13217         widthIncrement : 0,
13218         heightIncrement : 0,
13219         pinned : false,
13220         width : null,
13221         height : null,
13222         preserveRatio : false,
13223         transparent: false,
13224         minX: 0,
13225         minY: 0,
13226         draggable: false,
13227
13228         /**
13229          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
13230          */
13231         constrainTo: undefined,
13232         /**
13233          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
13234          */
13235         resizeRegion: undefined,
13236
13237
13238     /**
13239      * Perform a manual resize
13240      * @param {Number} width
13241      * @param {Number} height
13242      */
13243     resizeTo : function(width, height){
13244         this.el.setSize(width, height);
13245         this.updateChildSize();
13246         this.fireEvent("resize", this, width, height, null);
13247     },
13248
13249     // private
13250     startSizing : function(e, handle){
13251         this.fireEvent("beforeresize", this, e);
13252         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
13253
13254             if(!this.overlay){
13255                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
13256                 this.overlay.unselectable();
13257                 this.overlay.enableDisplayMode("block");
13258                 this.overlay.on("mousemove", this.onMouseMove, this);
13259                 this.overlay.on("mouseup", this.onMouseUp, this);
13260             }
13261             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
13262
13263             this.resizing = true;
13264             this.startBox = this.el.getBox();
13265             this.startPoint = e.getXY();
13266             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
13267                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
13268
13269             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13270             this.overlay.show();
13271
13272             if(this.constrainTo) {
13273                 var ct = Roo.get(this.constrainTo);
13274                 this.resizeRegion = ct.getRegion().adjust(
13275                     ct.getFrameWidth('t'),
13276                     ct.getFrameWidth('l'),
13277                     -ct.getFrameWidth('b'),
13278                     -ct.getFrameWidth('r')
13279                 );
13280             }
13281
13282             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
13283             this.proxy.show();
13284             this.proxy.setBox(this.startBox);
13285             if(!this.dynamic){
13286                 this.proxy.setStyle('visibility', 'visible');
13287             }
13288         }
13289     },
13290
13291     // private
13292     onMouseDown : function(handle, e){
13293         if(this.enabled){
13294             e.stopEvent();
13295             this.activeHandle = handle;
13296             this.startSizing(e, handle);
13297         }
13298     },
13299
13300     // private
13301     onMouseUp : function(e){
13302         var size = this.resizeElement();
13303         this.resizing = false;
13304         this.handleOut();
13305         this.overlay.hide();
13306         this.proxy.hide();
13307         this.fireEvent("resize", this, size.width, size.height, e);
13308     },
13309
13310     // private
13311     updateChildSize : function(){
13312         if(this.resizeChild){
13313             var el = this.el;
13314             var child = this.resizeChild;
13315             var adj = this.adjustments;
13316             if(el.dom.offsetWidth){
13317                 var b = el.getSize(true);
13318                 child.setSize(b.width+adj[0], b.height+adj[1]);
13319             }
13320             // Second call here for IE
13321             // The first call enables instant resizing and
13322             // the second call corrects scroll bars if they
13323             // exist
13324             if(Roo.isIE){
13325                 setTimeout(function(){
13326                     if(el.dom.offsetWidth){
13327                         var b = el.getSize(true);
13328                         child.setSize(b.width+adj[0], b.height+adj[1]);
13329                     }
13330                 }, 10);
13331             }
13332         }
13333     },
13334
13335     // private
13336     snap : function(value, inc, min){
13337         if(!inc || !value) return value;
13338         var newValue = value;
13339         var m = value % inc;
13340         if(m > 0){
13341             if(m > (inc/2)){
13342                 newValue = value + (inc-m);
13343             }else{
13344                 newValue = value - m;
13345             }
13346         }
13347         return Math.max(min, newValue);
13348     },
13349
13350     // private
13351     resizeElement : function(){
13352         var box = this.proxy.getBox();
13353         if(this.updateBox){
13354             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
13355         }else{
13356             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
13357         }
13358         this.updateChildSize();
13359         if(!this.dynamic){
13360             this.proxy.hide();
13361         }
13362         return box;
13363     },
13364
13365     // private
13366     constrain : function(v, diff, m, mx){
13367         if(v - diff < m){
13368             diff = v - m;
13369         }else if(v - diff > mx){
13370             diff = mx - v;
13371         }
13372         return diff;
13373     },
13374
13375     // private
13376     onMouseMove : function(e){
13377         if(this.enabled){
13378             try{// try catch so if something goes wrong the user doesn't get hung
13379
13380             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
13381                 return;
13382             }
13383
13384             //var curXY = this.startPoint;
13385             var curSize = this.curSize || this.startBox;
13386             var x = this.startBox.x, y = this.startBox.y;
13387             var ox = x, oy = y;
13388             var w = curSize.width, h = curSize.height;
13389             var ow = w, oh = h;
13390             var mw = this.minWidth, mh = this.minHeight;
13391             var mxw = this.maxWidth, mxh = this.maxHeight;
13392             var wi = this.widthIncrement;
13393             var hi = this.heightIncrement;
13394
13395             var eventXY = e.getXY();
13396             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
13397             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
13398
13399             var pos = this.activeHandle.position;
13400
13401             switch(pos){
13402                 case "east":
13403                     w += diffX;
13404                     w = Math.min(Math.max(mw, w), mxw);
13405                     break;
13406                 case "south":
13407                     h += diffY;
13408                     h = Math.min(Math.max(mh, h), mxh);
13409                     break;
13410                 case "southeast":
13411                     w += diffX;
13412                     h += diffY;
13413                     w = Math.min(Math.max(mw, w), mxw);
13414                     h = Math.min(Math.max(mh, h), mxh);
13415                     break;
13416                 case "north":
13417                     diffY = this.constrain(h, diffY, mh, mxh);
13418                     y += diffY;
13419                     h -= diffY;
13420                     break;
13421                 case "west":
13422                     diffX = this.constrain(w, diffX, mw, mxw);
13423                     x += diffX;
13424                     w -= diffX;
13425                     break;
13426                 case "northeast":
13427                     w += diffX;
13428                     w = Math.min(Math.max(mw, w), mxw);
13429                     diffY = this.constrain(h, diffY, mh, mxh);
13430                     y += diffY;
13431                     h -= diffY;
13432                     break;
13433                 case "northwest":
13434                     diffX = this.constrain(w, diffX, mw, mxw);
13435                     diffY = this.constrain(h, diffY, mh, mxh);
13436                     y += diffY;
13437                     h -= diffY;
13438                     x += diffX;
13439                     w -= diffX;
13440                     break;
13441                case "southwest":
13442                     diffX = this.constrain(w, diffX, mw, mxw);
13443                     h += diffY;
13444                     h = Math.min(Math.max(mh, h), mxh);
13445                     x += diffX;
13446                     w -= diffX;
13447                     break;
13448             }
13449
13450             var sw = this.snap(w, wi, mw);
13451             var sh = this.snap(h, hi, mh);
13452             if(sw != w || sh != h){
13453                 switch(pos){
13454                     case "northeast":
13455                         y -= sh - h;
13456                     break;
13457                     case "north":
13458                         y -= sh - h;
13459                         break;
13460                     case "southwest":
13461                         x -= sw - w;
13462                     break;
13463                     case "west":
13464                         x -= sw - w;
13465                         break;
13466                     case "northwest":
13467                         x -= sw - w;
13468                         y -= sh - h;
13469                     break;
13470                 }
13471                 w = sw;
13472                 h = sh;
13473             }
13474
13475             if(this.preserveRatio){
13476                 switch(pos){
13477                     case "southeast":
13478                     case "east":
13479                         h = oh * (w/ow);
13480                         h = Math.min(Math.max(mh, h), mxh);
13481                         w = ow * (h/oh);
13482                        break;
13483                     case "south":
13484                         w = ow * (h/oh);
13485                         w = Math.min(Math.max(mw, w), mxw);
13486                         h = oh * (w/ow);
13487                         break;
13488                     case "northeast":
13489                         w = ow * (h/oh);
13490                         w = Math.min(Math.max(mw, w), mxw);
13491                         h = oh * (w/ow);
13492                     break;
13493                     case "north":
13494                         var tw = w;
13495                         w = ow * (h/oh);
13496                         w = Math.min(Math.max(mw, w), mxw);
13497                         h = oh * (w/ow);
13498                         x += (tw - w) / 2;
13499                         break;
13500                     case "southwest":
13501                         h = oh * (w/ow);
13502                         h = Math.min(Math.max(mh, h), mxh);
13503                         var tw = w;
13504                         w = ow * (h/oh);
13505                         x += tw - w;
13506                         break;
13507                     case "west":
13508                         var th = h;
13509                         h = oh * (w/ow);
13510                         h = Math.min(Math.max(mh, h), mxh);
13511                         y += (th - h) / 2;
13512                         var tw = w;
13513                         w = ow * (h/oh);
13514                         x += tw - w;
13515                        break;
13516                     case "northwest":
13517                         var tw = w;
13518                         var th = h;
13519                         h = oh * (w/ow);
13520                         h = Math.min(Math.max(mh, h), mxh);
13521                         w = ow * (h/oh);
13522                         y += th - h;
13523                          x += tw - w;
13524                        break;
13525
13526                 }
13527             }
13528             this.proxy.setBounds(x, y, w, h);
13529             if(this.dynamic){
13530                 this.resizeElement();
13531             }
13532             }catch(e){}
13533         }
13534     },
13535
13536     // private
13537     handleOver : function(){
13538         if(this.enabled){
13539             this.el.addClass("x-resizable-over");
13540         }
13541     },
13542
13543     // private
13544     handleOut : function(){
13545         if(!this.resizing){
13546             this.el.removeClass("x-resizable-over");
13547         }
13548     },
13549
13550     /**
13551      * Returns the element this component is bound to.
13552      * @return {Roo.Element}
13553      */
13554     getEl : function(){
13555         return this.el;
13556     },
13557
13558     /**
13559      * Returns the resizeChild element (or null).
13560      * @return {Roo.Element}
13561      */
13562     getResizeChild : function(){
13563         return this.resizeChild;
13564     },
13565
13566     /**
13567      * Destroys this resizable. If the element was wrapped and
13568      * removeEl is not true then the element remains.
13569      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
13570      */
13571     destroy : function(removeEl){
13572         this.proxy.remove();
13573         if(this.overlay){
13574             this.overlay.removeAllListeners();
13575             this.overlay.remove();
13576         }
13577         var ps = Roo.Resizable.positions;
13578         for(var k in ps){
13579             if(typeof ps[k] != "function" && this[ps[k]]){
13580                 var h = this[ps[k]];
13581                 h.el.removeAllListeners();
13582                 h.el.remove();
13583             }
13584         }
13585         if(removeEl){
13586             this.el.update("");
13587             this.el.remove();
13588         }
13589     }
13590 });
13591
13592 // private
13593 // hash to map config positions to true positions
13594 Roo.Resizable.positions = {
13595     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast"
13596 };
13597
13598 // private
13599 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
13600     if(!this.tpl){
13601         // only initialize the template if resizable is used
13602         var tpl = Roo.DomHelper.createTemplate(
13603             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
13604         );
13605         tpl.compile();
13606         Roo.Resizable.Handle.prototype.tpl = tpl;
13607     }
13608     this.position = pos;
13609     this.rz = rz;
13610     this.el = this.tpl.append(rz.el.dom, [this.position], true);
13611     this.el.unselectable();
13612     if(transparent){
13613         this.el.setOpacity(0);
13614     }
13615     this.el.on("mousedown", this.onMouseDown, this);
13616     if(!disableTrackOver){
13617         this.el.on("mouseover", this.onMouseOver, this);
13618         this.el.on("mouseout", this.onMouseOut, this);
13619     }
13620 };
13621
13622 // private
13623 Roo.Resizable.Handle.prototype = {
13624     afterResize : function(rz){
13625         // do nothing
13626     },
13627     // private
13628     onMouseDown : function(e){
13629         this.rz.onMouseDown(this, e);
13630     },
13631     // private
13632     onMouseOver : function(e){
13633         this.rz.handleOver(this, e);
13634     },
13635     // private
13636     onMouseOut : function(e){
13637         this.rz.handleOut(this, e);
13638     }
13639 };/*
13640  * Based on:
13641  * Ext JS Library 1.1.1
13642  * Copyright(c) 2006-2007, Ext JS, LLC.
13643  *
13644  * Originally Released Under LGPL - original licence link has changed is not relivant.
13645  *
13646  * Fork - LGPL
13647  * <script type="text/javascript">
13648  */
13649
13650 /**
13651  * @class Roo.Editor
13652  * @extends Roo.Component
13653  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
13654  * @constructor
13655  * Create a new Editor
13656  * @param {Roo.form.Field} field The Field object (or descendant)
13657  * @param {Object} config The config object
13658  */
13659 Roo.Editor = function(field, config){
13660     Roo.Editor.superclass.constructor.call(this, config);
13661     this.field = field;
13662     this.addEvents({
13663         /**
13664              * @event beforestartedit
13665              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
13666              * false from the handler of this event.
13667              * @param {Editor} this
13668              * @param {Roo.Element} boundEl The underlying element bound to this editor
13669              * @param {Mixed} value The field value being set
13670              */
13671         "beforestartedit" : true,
13672         /**
13673              * @event startedit
13674              * Fires when this editor is displayed
13675              * @param {Roo.Element} boundEl The underlying element bound to this editor
13676              * @param {Mixed} value The starting field value
13677              */
13678         "startedit" : true,
13679         /**
13680              * @event beforecomplete
13681              * Fires after a change has been made to the field, but before the change is reflected in the underlying
13682              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
13683              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
13684              * event will not fire since no edit actually occurred.
13685              * @param {Editor} this
13686              * @param {Mixed} value The current field value
13687              * @param {Mixed} startValue The original field value
13688              */
13689         "beforecomplete" : true,
13690         /**
13691              * @event complete
13692              * Fires after editing is complete and any changed value has been written to the underlying field.
13693              * @param {Editor} this
13694              * @param {Mixed} value The current field value
13695              * @param {Mixed} startValue The original field value
13696              */
13697         "complete" : true,
13698         /**
13699          * @event specialkey
13700          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
13701          * {@link Roo.EventObject#getKey} to determine which key was pressed.
13702          * @param {Roo.form.Field} this
13703          * @param {Roo.EventObject} e The event object
13704          */
13705         "specialkey" : true
13706     });
13707 };
13708
13709 Roo.extend(Roo.Editor, Roo.Component, {
13710     /**
13711      * @cfg {Boolean/String} autosize
13712      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
13713      * or "height" to adopt the height only (defaults to false)
13714      */
13715     /**
13716      * @cfg {Boolean} revertInvalid
13717      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
13718      * validation fails (defaults to true)
13719      */
13720     /**
13721      * @cfg {Boolean} ignoreNoChange
13722      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
13723      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
13724      * will never be ignored.
13725      */
13726     /**
13727      * @cfg {Boolean} hideEl
13728      * False to keep the bound element visible while the editor is displayed (defaults to true)
13729      */
13730     /**
13731      * @cfg {Mixed} value
13732      * The data value of the underlying field (defaults to "")
13733      */
13734     value : "",
13735     /**
13736      * @cfg {String} alignment
13737      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
13738      */
13739     alignment: "c-c?",
13740     /**
13741      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
13742      * for bottom-right shadow (defaults to "frame")
13743      */
13744     shadow : "frame",
13745     /**
13746      * @cfg {Boolean} constrain True to constrain the editor to the viewport
13747      */
13748     constrain : false,
13749     /**
13750      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
13751      */
13752     completeOnEnter : false,
13753     /**
13754      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
13755      */
13756     cancelOnEsc : false,
13757     /**
13758      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
13759      */
13760     updateEl : false,
13761
13762     // private
13763     onRender : function(ct, position){
13764         this.el = new Roo.Layer({
13765             shadow: this.shadow,
13766             cls: "x-editor",
13767             parentEl : ct,
13768             shim : this.shim,
13769             shadowOffset:4,
13770             id: this.id,
13771             constrain: this.constrain
13772         });
13773         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
13774         if(this.field.msgTarget != 'title'){
13775             this.field.msgTarget = 'qtip';
13776         }
13777         this.field.render(this.el);
13778         if(Roo.isGecko){
13779             this.field.el.dom.setAttribute('autocomplete', 'off');
13780         }
13781         this.field.on("specialkey", this.onSpecialKey, this);
13782         if(this.swallowKeys){
13783             this.field.el.swallowEvent(['keydown','keypress']);
13784         }
13785         this.field.show();
13786         this.field.on("blur", this.onBlur, this);
13787         if(this.field.grow){
13788             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
13789         }
13790     },
13791
13792     onSpecialKey : function(field, e){
13793         //Roo.log('editor onSpecialKey');
13794         if(this.completeOnEnter && e.getKey() == e.ENTER){
13795             e.stopEvent();
13796             this.completeEdit();
13797         }else if(this.cancelOnEsc && e.getKey() == e.ESC){
13798             this.cancelEdit();
13799         }else{
13800             this.fireEvent('specialkey', field, e);
13801         }
13802     },
13803
13804     /**
13805      * Starts the editing process and shows the editor.
13806      * @param {String/HTMLElement/Element} el The element to edit
13807      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
13808       * to the innerHTML of el.
13809      */
13810     startEdit : function(el, value){
13811         if(this.editing){
13812             this.completeEdit();
13813         }
13814         this.boundEl = Roo.get(el);
13815         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
13816         if(!this.rendered){
13817             this.render(this.parentEl || document.body);
13818         }
13819         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
13820             return;
13821         }
13822         this.startValue = v;
13823         this.field.setValue(v);
13824         if(this.autoSize){
13825             var sz = this.boundEl.getSize();
13826             switch(this.autoSize){
13827                 case "width":
13828                 this.setSize(sz.width,  "");
13829                 break;
13830                 case "height":
13831                 this.setSize("",  sz.height);
13832                 break;
13833                 default:
13834                 this.setSize(sz.width,  sz.height);
13835             }
13836         }
13837         this.el.alignTo(this.boundEl, this.alignment);
13838         this.editing = true;
13839         if(Roo.QuickTips){
13840             Roo.QuickTips.disable();
13841         }
13842         this.show();
13843     },
13844
13845     /**
13846      * Sets the height and width of this editor.
13847      * @param {Number} width The new width
13848      * @param {Number} height The new height
13849      */
13850     setSize : function(w, h){
13851         this.field.setSize(w, h);
13852         if(this.el){
13853             this.el.sync();
13854         }
13855     },
13856
13857     /**
13858      * Realigns the editor to the bound field based on the current alignment config value.
13859      */
13860     realign : function(){
13861         this.el.alignTo(this.boundEl, this.alignment);
13862     },
13863
13864     /**
13865      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
13866      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
13867      */
13868     completeEdit : function(remainVisible){
13869         if(!this.editing){
13870             return;
13871         }
13872         var v = this.getValue();
13873         if(this.revertInvalid !== false && !this.field.isValid()){
13874             v = this.startValue;
13875             this.cancelEdit(true);
13876         }
13877         if(String(v) === String(this.startValue) && this.ignoreNoChange){
13878             this.editing = false;
13879             this.hide();
13880             return;
13881         }
13882         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
13883             this.editing = false;
13884             if(this.updateEl && this.boundEl){
13885                 this.boundEl.update(v);
13886             }
13887             if(remainVisible !== true){
13888                 this.hide();
13889             }
13890             this.fireEvent("complete", this, v, this.startValue);
13891         }
13892     },
13893
13894     // private
13895     onShow : function(){
13896         this.el.show();
13897         if(this.hideEl !== false){
13898             this.boundEl.hide();
13899         }
13900         this.field.show();
13901         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
13902             this.fixIEFocus = true;
13903             this.deferredFocus.defer(50, this);
13904         }else{
13905             this.field.focus();
13906         }
13907         this.fireEvent("startedit", this.boundEl, this.startValue);
13908     },
13909
13910     deferredFocus : function(){
13911         if(this.editing){
13912             this.field.focus();
13913         }
13914     },
13915
13916     /**
13917      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
13918      * reverted to the original starting value.
13919      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
13920      * cancel (defaults to false)
13921      */
13922     cancelEdit : function(remainVisible){
13923         if(this.editing){
13924             this.setValue(this.startValue);
13925             if(remainVisible !== true){
13926                 this.hide();
13927             }
13928         }
13929     },
13930
13931     // private
13932     onBlur : function(){
13933         if(this.allowBlur !== true && this.editing){
13934             this.completeEdit();
13935         }
13936     },
13937
13938     // private
13939     onHide : function(){
13940         if(this.editing){
13941             this.completeEdit();
13942             return;
13943         }
13944         this.field.blur();
13945         if(this.field.collapse){
13946             this.field.collapse();
13947         }
13948         this.el.hide();
13949         if(this.hideEl !== false){
13950             this.boundEl.show();
13951         }
13952         if(Roo.QuickTips){
13953             Roo.QuickTips.enable();
13954         }
13955     },
13956
13957     /**
13958      * Sets the data value of the editor
13959      * @param {Mixed} value Any valid value supported by the underlying field
13960      */
13961     setValue : function(v){
13962         this.field.setValue(v);
13963     },
13964
13965     /**
13966      * Gets the data value of the editor
13967      * @return {Mixed} The data value
13968      */
13969     getValue : function(){
13970         return this.field.getValue();
13971     }
13972 });/*
13973  * Based on:
13974  * Ext JS Library 1.1.1
13975  * Copyright(c) 2006-2007, Ext JS, LLC.
13976  *
13977  * Originally Released Under LGPL - original licence link has changed is not relivant.
13978  *
13979  * Fork - LGPL
13980  * <script type="text/javascript">
13981  */
13982  
13983 /**
13984  * @class Roo.BasicDialog
13985  * @extends Roo.util.Observable
13986  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
13987  * <pre><code>
13988 var dlg = new Roo.BasicDialog("my-dlg", {
13989     height: 200,
13990     width: 300,
13991     minHeight: 100,
13992     minWidth: 150,
13993     modal: true,
13994     proxyDrag: true,
13995     shadow: true
13996 });
13997 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
13998 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
13999 dlg.addButton('Cancel', dlg.hide, dlg);
14000 dlg.show();
14001 </code></pre>
14002   <b>A Dialog should always be a direct child of the body element.</b>
14003  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
14004  * @cfg {String} title Default text to display in the title bar (defaults to null)
14005  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14006  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14007  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
14008  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
14009  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
14010  * (defaults to null with no animation)
14011  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
14012  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
14013  * property for valid values (defaults to 'all')
14014  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
14015  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
14016  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
14017  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
14018  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
14019  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
14020  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
14021  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
14022  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
14023  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
14024  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
14025  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
14026  * draggable = true (defaults to false)
14027  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
14028  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14029  * shadow (defaults to false)
14030  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
14031  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
14032  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
14033  * @cfg {Array} buttons Array of buttons
14034  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
14035  * @constructor
14036  * Create a new BasicDialog.
14037  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
14038  * @param {Object} config Configuration options
14039  */
14040 Roo.BasicDialog = function(el, config){
14041     this.el = Roo.get(el);
14042     var dh = Roo.DomHelper;
14043     if(!this.el && config && config.autoCreate){
14044         if(typeof config.autoCreate == "object"){
14045             if(!config.autoCreate.id){
14046                 config.autoCreate.id = el;
14047             }
14048             this.el = dh.append(document.body,
14049                         config.autoCreate, true);
14050         }else{
14051             this.el = dh.append(document.body,
14052                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
14053         }
14054     }
14055     el = this.el;
14056     el.setDisplayed(true);
14057     el.hide = this.hideAction;
14058     this.id = el.id;
14059     el.addClass("x-dlg");
14060
14061     Roo.apply(this, config);
14062
14063     this.proxy = el.createProxy("x-dlg-proxy");
14064     this.proxy.hide = this.hideAction;
14065     this.proxy.setOpacity(.5);
14066     this.proxy.hide();
14067
14068     if(config.width){
14069         el.setWidth(config.width);
14070     }
14071     if(config.height){
14072         el.setHeight(config.height);
14073     }
14074     this.size = el.getSize();
14075     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
14076         this.xy = [config.x,config.y];
14077     }else{
14078         this.xy = el.getCenterXY(true);
14079     }
14080     /** The header element @type Roo.Element */
14081     this.header = el.child("> .x-dlg-hd");
14082     /** The body element @type Roo.Element */
14083     this.body = el.child("> .x-dlg-bd");
14084     /** The footer element @type Roo.Element */
14085     this.footer = el.child("> .x-dlg-ft");
14086
14087     if(!this.header){
14088         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
14089     }
14090     if(!this.body){
14091         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
14092     }
14093
14094     this.header.unselectable();
14095     if(this.title){
14096         this.header.update(this.title);
14097     }
14098     // this element allows the dialog to be focused for keyboard event
14099     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
14100     this.focusEl.swallowEvent("click", true);
14101
14102     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
14103
14104     // wrap the body and footer for special rendering
14105     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
14106     if(this.footer){
14107         this.bwrap.dom.appendChild(this.footer.dom);
14108     }
14109
14110     this.bg = this.el.createChild({
14111         tag: "div", cls:"x-dlg-bg",
14112         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
14113     });
14114     this.centerBg = this.bg.child("div.x-dlg-bg-center");
14115
14116
14117     if(this.autoScroll !== false && !this.autoTabs){
14118         this.body.setStyle("overflow", "auto");
14119     }
14120
14121     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
14122
14123     if(this.closable !== false){
14124         this.el.addClass("x-dlg-closable");
14125         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
14126         this.close.on("click", this.closeClick, this);
14127         this.close.addClassOnOver("x-dlg-close-over");
14128     }
14129     if(this.collapsible !== false){
14130         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
14131         this.collapseBtn.on("click", this.collapseClick, this);
14132         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
14133         this.header.on("dblclick", this.collapseClick, this);
14134     }
14135     if(this.resizable !== false){
14136         this.el.addClass("x-dlg-resizable");
14137         this.resizer = new Roo.Resizable(el, {
14138             minWidth: this.minWidth || 80,
14139             minHeight:this.minHeight || 80,
14140             handles: this.resizeHandles || "all",
14141             pinned: true
14142         });
14143         this.resizer.on("beforeresize", this.beforeResize, this);
14144         this.resizer.on("resize", this.onResize, this);
14145     }
14146     if(this.draggable !== false){
14147         el.addClass("x-dlg-draggable");
14148         if (!this.proxyDrag) {
14149             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
14150         }
14151         else {
14152             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
14153         }
14154         dd.setHandleElId(this.header.id);
14155         dd.endDrag = this.endMove.createDelegate(this);
14156         dd.startDrag = this.startMove.createDelegate(this);
14157         dd.onDrag = this.onDrag.createDelegate(this);
14158         dd.scroll = false;
14159         this.dd = dd;
14160     }
14161     if(this.modal){
14162         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
14163         this.mask.enableDisplayMode("block");
14164         this.mask.hide();
14165         this.el.addClass("x-dlg-modal");
14166     }
14167     if(this.shadow){
14168         this.shadow = new Roo.Shadow({
14169             mode : typeof this.shadow == "string" ? this.shadow : "sides",
14170             offset : this.shadowOffset
14171         });
14172     }else{
14173         this.shadowOffset = 0;
14174     }
14175     if(Roo.useShims && this.shim !== false){
14176         this.shim = this.el.createShim();
14177         this.shim.hide = this.hideAction;
14178         this.shim.hide();
14179     }else{
14180         this.shim = false;
14181     }
14182     if(this.autoTabs){
14183         this.initTabs();
14184     }
14185     if (this.buttons) { 
14186         var bts= this.buttons;
14187         this.buttons = [];
14188         Roo.each(bts, function(b) {
14189             this.addButton(b);
14190         }, this);
14191     }
14192     
14193     
14194     this.addEvents({
14195         /**
14196          * @event keydown
14197          * Fires when a key is pressed
14198          * @param {Roo.BasicDialog} this
14199          * @param {Roo.EventObject} e
14200          */
14201         "keydown" : true,
14202         /**
14203          * @event move
14204          * Fires when this dialog is moved by the user.
14205          * @param {Roo.BasicDialog} this
14206          * @param {Number} x The new page X
14207          * @param {Number} y The new page Y
14208          */
14209         "move" : true,
14210         /**
14211          * @event resize
14212          * Fires when this dialog is resized by the user.
14213          * @param {Roo.BasicDialog} this
14214          * @param {Number} width The new width
14215          * @param {Number} height The new height
14216          */
14217         "resize" : true,
14218         /**
14219          * @event beforehide
14220          * Fires before this dialog is hidden.
14221          * @param {Roo.BasicDialog} this
14222          */
14223         "beforehide" : true,
14224         /**
14225          * @event hide
14226          * Fires when this dialog is hidden.
14227          * @param {Roo.BasicDialog} this
14228          */
14229         "hide" : true,
14230         /**
14231          * @event beforeshow
14232          * Fires before this dialog is shown.
14233          * @param {Roo.BasicDialog} this
14234          */
14235         "beforeshow" : true,
14236         /**
14237          * @event show
14238          * Fires when this dialog is shown.
14239          * @param {Roo.BasicDialog} this
14240          */
14241         "show" : true
14242     });
14243     el.on("keydown", this.onKeyDown, this);
14244     el.on("mousedown", this.toFront, this);
14245     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
14246     this.el.hide();
14247     Roo.DialogManager.register(this);
14248     Roo.BasicDialog.superclass.constructor.call(this);
14249 };
14250
14251 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
14252     shadowOffset: Roo.isIE ? 6 : 5,
14253     minHeight: 80,
14254     minWidth: 200,
14255     minButtonWidth: 75,
14256     defaultButton: null,
14257     buttonAlign: "right",
14258     tabTag: 'div',
14259     firstShow: true,
14260
14261     /**
14262      * Sets the dialog title text
14263      * @param {String} text The title text to display
14264      * @return {Roo.BasicDialog} this
14265      */
14266     setTitle : function(text){
14267         this.header.update(text);
14268         return this;
14269     },
14270
14271     // private
14272     closeClick : function(){
14273         this.hide();
14274     },
14275
14276     // private
14277     collapseClick : function(){
14278         this[this.collapsed ? "expand" : "collapse"]();
14279     },
14280
14281     /**
14282      * Collapses the dialog to its minimized state (only the title bar is visible).
14283      * Equivalent to the user clicking the collapse dialog button.
14284      */
14285     collapse : function(){
14286         if(!this.collapsed){
14287             this.collapsed = true;
14288             this.el.addClass("x-dlg-collapsed");
14289             this.restoreHeight = this.el.getHeight();
14290             this.resizeTo(this.el.getWidth(), this.header.getHeight());
14291         }
14292     },
14293
14294     /**
14295      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
14296      * clicking the expand dialog button.
14297      */
14298     expand : function(){
14299         if(this.collapsed){
14300             this.collapsed = false;
14301             this.el.removeClass("x-dlg-collapsed");
14302             this.resizeTo(this.el.getWidth(), this.restoreHeight);
14303         }
14304     },
14305
14306     /**
14307      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
14308      * @return {Roo.TabPanel} The tabs component
14309      */
14310     initTabs : function(){
14311         var tabs = this.getTabs();
14312         while(tabs.getTab(0)){
14313             tabs.removeTab(0);
14314         }
14315         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
14316             var dom = el.dom;
14317             tabs.addTab(Roo.id(dom), dom.title);
14318             dom.title = "";
14319         });
14320         tabs.activate(0);
14321         return tabs;
14322     },
14323
14324     // private
14325     beforeResize : function(){
14326         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
14327     },
14328
14329     // private
14330     onResize : function(){
14331         this.refreshSize();
14332         this.syncBodyHeight();
14333         this.adjustAssets();
14334         this.focus();
14335         this.fireEvent("resize", this, this.size.width, this.size.height);
14336     },
14337
14338     // private
14339     onKeyDown : function(e){
14340         if(this.isVisible()){
14341             this.fireEvent("keydown", this, e);
14342         }
14343     },
14344
14345     /**
14346      * Resizes the dialog.
14347      * @param {Number} width
14348      * @param {Number} height
14349      * @return {Roo.BasicDialog} this
14350      */
14351     resizeTo : function(width, height){
14352         this.el.setSize(width, height);
14353         this.size = {width: width, height: height};
14354         this.syncBodyHeight();
14355         if(this.fixedcenter){
14356             this.center();
14357         }
14358         if(this.isVisible()){
14359             this.constrainXY();
14360             this.adjustAssets();
14361         }
14362         this.fireEvent("resize", this, width, height);
14363         return this;
14364     },
14365
14366
14367     /**
14368      * Resizes the dialog to fit the specified content size.
14369      * @param {Number} width
14370      * @param {Number} height
14371      * @return {Roo.BasicDialog} this
14372      */
14373     setContentSize : function(w, h){
14374         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
14375         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
14376         //if(!this.el.isBorderBox()){
14377             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
14378             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
14379         //}
14380         if(this.tabs){
14381             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
14382             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
14383         }
14384         this.resizeTo(w, h);
14385         return this;
14386     },
14387
14388     /**
14389      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
14390      * executed in response to a particular key being pressed while the dialog is active.
14391      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
14392      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14393      * @param {Function} fn The function to call
14394      * @param {Object} scope (optional) The scope of the function
14395      * @return {Roo.BasicDialog} this
14396      */
14397     addKeyListener : function(key, fn, scope){
14398         var keyCode, shift, ctrl, alt;
14399         if(typeof key == "object" && !(key instanceof Array)){
14400             keyCode = key["key"];
14401             shift = key["shift"];
14402             ctrl = key["ctrl"];
14403             alt = key["alt"];
14404         }else{
14405             keyCode = key;
14406         }
14407         var handler = function(dlg, e){
14408             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14409                 var k = e.getKey();
14410                 if(keyCode instanceof Array){
14411                     for(var i = 0, len = keyCode.length; i < len; i++){
14412                         if(keyCode[i] == k){
14413                           fn.call(scope || window, dlg, k, e);
14414                           return;
14415                         }
14416                     }
14417                 }else{
14418                     if(k == keyCode){
14419                         fn.call(scope || window, dlg, k, e);
14420                     }
14421                 }
14422             }
14423         };
14424         this.on("keydown", handler);
14425         return this;
14426     },
14427
14428     /**
14429      * Returns the TabPanel component (creates it if it doesn't exist).
14430      * Note: If you wish to simply check for the existence of tabs without creating them,
14431      * check for a null 'tabs' property.
14432      * @return {Roo.TabPanel} The tabs component
14433      */
14434     getTabs : function(){
14435         if(!this.tabs){
14436             this.el.addClass("x-dlg-auto-tabs");
14437             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
14438             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
14439         }
14440         return this.tabs;
14441     },
14442
14443     /**
14444      * Adds a button to the footer section of the dialog.
14445      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
14446      * object or a valid Roo.DomHelper element config
14447      * @param {Function} handler The function called when the button is clicked
14448      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
14449      * @return {Roo.Button} The new button
14450      */
14451     addButton : function(config, handler, scope){
14452         var dh = Roo.DomHelper;
14453         if(!this.footer){
14454             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
14455         }
14456         if(!this.btnContainer){
14457             var tb = this.footer.createChild({
14458
14459                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
14460                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
14461             }, null, true);
14462             this.btnContainer = tb.firstChild.firstChild.firstChild;
14463         }
14464         var bconfig = {
14465             handler: handler,
14466             scope: scope,
14467             minWidth: this.minButtonWidth,
14468             hideParent:true
14469         };
14470         if(typeof config == "string"){
14471             bconfig.text = config;
14472         }else{
14473             if(config.tag){
14474                 bconfig.dhconfig = config;
14475             }else{
14476                 Roo.apply(bconfig, config);
14477             }
14478         }
14479         var fc = false;
14480         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
14481             bconfig.position = Math.max(0, bconfig.position);
14482             fc = this.btnContainer.childNodes[bconfig.position];
14483         }
14484          
14485         var btn = new Roo.Button(
14486             fc ? 
14487                 this.btnContainer.insertBefore(document.createElement("td"),fc)
14488                 : this.btnContainer.appendChild(document.createElement("td")),
14489             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
14490             bconfig
14491         );
14492         this.syncBodyHeight();
14493         if(!this.buttons){
14494             /**
14495              * Array of all the buttons that have been added to this dialog via addButton
14496              * @type Array
14497              */
14498             this.buttons = [];
14499         }
14500         this.buttons.push(btn);
14501         return btn;
14502     },
14503
14504     /**
14505      * Sets the default button to be focused when the dialog is displayed.
14506      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
14507      * @return {Roo.BasicDialog} this
14508      */
14509     setDefaultButton : function(btn){
14510         this.defaultButton = btn;
14511         return this;
14512     },
14513
14514     // private
14515     getHeaderFooterHeight : function(safe){
14516         var height = 0;
14517         if(this.header){
14518            height += this.header.getHeight();
14519         }
14520         if(this.footer){
14521            var fm = this.footer.getMargins();
14522             height += (this.footer.getHeight()+fm.top+fm.bottom);
14523         }
14524         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
14525         height += this.centerBg.getPadding("tb");
14526         return height;
14527     },
14528
14529     // private
14530     syncBodyHeight : function(){
14531         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
14532         var height = this.size.height - this.getHeaderFooterHeight(false);
14533         bd.setHeight(height-bd.getMargins("tb"));
14534         var hh = this.header.getHeight();
14535         var h = this.size.height-hh;
14536         cb.setHeight(h);
14537         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
14538         bw.setHeight(h-cb.getPadding("tb"));
14539         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
14540         bd.setWidth(bw.getWidth(true));
14541         if(this.tabs){
14542             this.tabs.syncHeight();
14543             if(Roo.isIE){
14544                 this.tabs.el.repaint();
14545             }
14546         }
14547     },
14548
14549     /**
14550      * Restores the previous state of the dialog if Roo.state is configured.
14551      * @return {Roo.BasicDialog} this
14552      */
14553     restoreState : function(){
14554         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
14555         if(box && box.width){
14556             this.xy = [box.x, box.y];
14557             this.resizeTo(box.width, box.height);
14558         }
14559         return this;
14560     },
14561
14562     // private
14563     beforeShow : function(){
14564         this.expand();
14565         if(this.fixedcenter){
14566             this.xy = this.el.getCenterXY(true);
14567         }
14568         if(this.modal){
14569             Roo.get(document.body).addClass("x-body-masked");
14570             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14571             this.mask.show();
14572         }
14573         this.constrainXY();
14574     },
14575
14576     // private
14577     animShow : function(){
14578         var b = Roo.get(this.animateTarget, true).getBox();
14579         this.proxy.setSize(b.width, b.height);
14580         this.proxy.setLocation(b.x, b.y);
14581         this.proxy.show();
14582         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
14583                     true, .35, this.showEl.createDelegate(this));
14584     },
14585
14586     /**
14587      * Shows the dialog.
14588      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
14589      * @return {Roo.BasicDialog} this
14590      */
14591     show : function(animateTarget){
14592         if (this.fireEvent("beforeshow", this) === false){
14593             return;
14594         }
14595         if(this.syncHeightBeforeShow){
14596             this.syncBodyHeight();
14597         }else if(this.firstShow){
14598             this.firstShow = false;
14599             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
14600         }
14601         this.animateTarget = animateTarget || this.animateTarget;
14602         if(!this.el.isVisible()){
14603             this.beforeShow();
14604             if(this.animateTarget){
14605                 this.animShow();
14606             }else{
14607                 this.showEl();
14608             }
14609         }
14610         return this;
14611     },
14612
14613     // private
14614     showEl : function(){
14615         this.proxy.hide();
14616         this.el.setXY(this.xy);
14617         this.el.show();
14618         this.adjustAssets(true);
14619         this.toFront();
14620         this.focus();
14621         // IE peekaboo bug - fix found by Dave Fenwick
14622         if(Roo.isIE){
14623             this.el.repaint();
14624         }
14625         this.fireEvent("show", this);
14626     },
14627
14628     /**
14629      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
14630      * dialog itself will receive focus.
14631      */
14632     focus : function(){
14633         if(this.defaultButton){
14634             this.defaultButton.focus();
14635         }else{
14636             this.focusEl.focus();
14637         }
14638     },
14639
14640     // private
14641     constrainXY : function(){
14642         if(this.constraintoviewport !== false){
14643             if(!this.viewSize){
14644                 if(this.container){
14645                     var s = this.container.getSize();
14646                     this.viewSize = [s.width, s.height];
14647                 }else{
14648                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
14649                 }
14650             }
14651             var s = Roo.get(this.container||document).getScroll();
14652
14653             var x = this.xy[0], y = this.xy[1];
14654             var w = this.size.width, h = this.size.height;
14655             var vw = this.viewSize[0], vh = this.viewSize[1];
14656             // only move it if it needs it
14657             var moved = false;
14658             // first validate right/bottom
14659             if(x + w > vw+s.left){
14660                 x = vw - w;
14661                 moved = true;
14662             }
14663             if(y + h > vh+s.top){
14664                 y = vh - h;
14665                 moved = true;
14666             }
14667             // then make sure top/left isn't negative
14668             if(x < s.left){
14669                 x = s.left;
14670                 moved = true;
14671             }
14672             if(y < s.top){
14673                 y = s.top;
14674                 moved = true;
14675             }
14676             if(moved){
14677                 // cache xy
14678                 this.xy = [x, y];
14679                 if(this.isVisible()){
14680                     this.el.setLocation(x, y);
14681                     this.adjustAssets();
14682                 }
14683             }
14684         }
14685     },
14686
14687     // private
14688     onDrag : function(){
14689         if(!this.proxyDrag){
14690             this.xy = this.el.getXY();
14691             this.adjustAssets();
14692         }
14693     },
14694
14695     // private
14696     adjustAssets : function(doShow){
14697         var x = this.xy[0], y = this.xy[1];
14698         var w = this.size.width, h = this.size.height;
14699         if(doShow === true){
14700             if(this.shadow){
14701                 this.shadow.show(this.el);
14702             }
14703             if(this.shim){
14704                 this.shim.show();
14705             }
14706         }
14707         if(this.shadow && this.shadow.isVisible()){
14708             this.shadow.show(this.el);
14709         }
14710         if(this.shim && this.shim.isVisible()){
14711             this.shim.setBounds(x, y, w, h);
14712         }
14713     },
14714
14715     // private
14716     adjustViewport : function(w, h){
14717         if(!w || !h){
14718             w = Roo.lib.Dom.getViewWidth();
14719             h = Roo.lib.Dom.getViewHeight();
14720         }
14721         // cache the size
14722         this.viewSize = [w, h];
14723         if(this.modal && this.mask.isVisible()){
14724             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
14725             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14726         }
14727         if(this.isVisible()){
14728             this.constrainXY();
14729         }
14730     },
14731
14732     /**
14733      * Destroys this dialog and all its supporting elements (including any tabs, shim,
14734      * shadow, proxy, mask, etc.)  Also removes all event listeners.
14735      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
14736      */
14737     destroy : function(removeEl){
14738         if(this.isVisible()){
14739             this.animateTarget = null;
14740             this.hide();
14741         }
14742         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
14743         if(this.tabs){
14744             this.tabs.destroy(removeEl);
14745         }
14746         Roo.destroy(
14747              this.shim,
14748              this.proxy,
14749              this.resizer,
14750              this.close,
14751              this.mask
14752         );
14753         if(this.dd){
14754             this.dd.unreg();
14755         }
14756         if(this.buttons){
14757            for(var i = 0, len = this.buttons.length; i < len; i++){
14758                this.buttons[i].destroy();
14759            }
14760         }
14761         this.el.removeAllListeners();
14762         if(removeEl === true){
14763             this.el.update("");
14764             this.el.remove();
14765         }
14766         Roo.DialogManager.unregister(this);
14767     },
14768
14769     // private
14770     startMove : function(){
14771         if(this.proxyDrag){
14772             this.proxy.show();
14773         }
14774         if(this.constraintoviewport !== false){
14775             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
14776         }
14777     },
14778
14779     // private
14780     endMove : function(){
14781         if(!this.proxyDrag){
14782             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
14783         }else{
14784             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
14785             this.proxy.hide();
14786         }
14787         this.refreshSize();
14788         this.adjustAssets();
14789         this.focus();
14790         this.fireEvent("move", this, this.xy[0], this.xy[1]);
14791     },
14792
14793     /**
14794      * Brings this dialog to the front of any other visible dialogs
14795      * @return {Roo.BasicDialog} this
14796      */
14797     toFront : function(){
14798         Roo.DialogManager.bringToFront(this);
14799         return this;
14800     },
14801
14802     /**
14803      * Sends this dialog to the back (under) of any other visible dialogs
14804      * @return {Roo.BasicDialog} this
14805      */
14806     toBack : function(){
14807         Roo.DialogManager.sendToBack(this);
14808         return this;
14809     },
14810
14811     /**
14812      * Centers this dialog in the viewport
14813      * @return {Roo.BasicDialog} this
14814      */
14815     center : function(){
14816         var xy = this.el.getCenterXY(true);
14817         this.moveTo(xy[0], xy[1]);
14818         return this;
14819     },
14820
14821     /**
14822      * Moves the dialog's top-left corner to the specified point
14823      * @param {Number} x
14824      * @param {Number} y
14825      * @return {Roo.BasicDialog} this
14826      */
14827     moveTo : function(x, y){
14828         this.xy = [x,y];
14829         if(this.isVisible()){
14830             this.el.setXY(this.xy);
14831             this.adjustAssets();
14832         }
14833         return this;
14834     },
14835
14836     /**
14837      * Aligns the dialog to the specified element
14838      * @param {String/HTMLElement/Roo.Element} element The element to align to.
14839      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
14840      * @param {Array} offsets (optional) Offset the positioning by [x, y]
14841      * @return {Roo.BasicDialog} this
14842      */
14843     alignTo : function(element, position, offsets){
14844         this.xy = this.el.getAlignToXY(element, position, offsets);
14845         if(this.isVisible()){
14846             this.el.setXY(this.xy);
14847             this.adjustAssets();
14848         }
14849         return this;
14850     },
14851
14852     /**
14853      * Anchors an element to another element and realigns it when the window is resized.
14854      * @param {String/HTMLElement/Roo.Element} element The element to align to.
14855      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
14856      * @param {Array} offsets (optional) Offset the positioning by [x, y]
14857      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
14858      * is a number, it is used as the buffer delay (defaults to 50ms).
14859      * @return {Roo.BasicDialog} this
14860      */
14861     anchorTo : function(el, alignment, offsets, monitorScroll){
14862         var action = function(){
14863             this.alignTo(el, alignment, offsets);
14864         };
14865         Roo.EventManager.onWindowResize(action, this);
14866         var tm = typeof monitorScroll;
14867         if(tm != 'undefined'){
14868             Roo.EventManager.on(window, 'scroll', action, this,
14869                 {buffer: tm == 'number' ? monitorScroll : 50});
14870         }
14871         action.call(this);
14872         return this;
14873     },
14874
14875     /**
14876      * Returns true if the dialog is visible
14877      * @return {Boolean}
14878      */
14879     isVisible : function(){
14880         return this.el.isVisible();
14881     },
14882
14883     // private
14884     animHide : function(callback){
14885         var b = Roo.get(this.animateTarget).getBox();
14886         this.proxy.show();
14887         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
14888         this.el.hide();
14889         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
14890                     this.hideEl.createDelegate(this, [callback]));
14891     },
14892
14893     /**
14894      * Hides the dialog.
14895      * @param {Function} callback (optional) Function to call when the dialog is hidden
14896      * @return {Roo.BasicDialog} this
14897      */
14898     hide : function(callback){
14899         if (this.fireEvent("beforehide", this) === false){
14900             return;
14901         }
14902         if(this.shadow){
14903             this.shadow.hide();
14904         }
14905         if(this.shim) {
14906           this.shim.hide();
14907         }
14908         if(this.animateTarget){
14909            this.animHide(callback);
14910         }else{
14911             this.el.hide();
14912             this.hideEl(callback);
14913         }
14914         return this;
14915     },
14916
14917     // private
14918     hideEl : function(callback){
14919         this.proxy.hide();
14920         if(this.modal){
14921             this.mask.hide();
14922             Roo.get(document.body).removeClass("x-body-masked");
14923         }
14924         this.fireEvent("hide", this);
14925         if(typeof callback == "function"){
14926             callback();
14927         }
14928     },
14929
14930     // private
14931     hideAction : function(){
14932         this.setLeft("-10000px");
14933         this.setTop("-10000px");
14934         this.setStyle("visibility", "hidden");
14935     },
14936
14937     // private
14938     refreshSize : function(){
14939         this.size = this.el.getSize();
14940         this.xy = this.el.getXY();
14941         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
14942     },
14943
14944     // private
14945     // z-index is managed by the DialogManager and may be overwritten at any time
14946     setZIndex : function(index){
14947         if(this.modal){
14948             this.mask.setStyle("z-index", index);
14949         }
14950         if(this.shim){
14951             this.shim.setStyle("z-index", ++index);
14952         }
14953         if(this.shadow){
14954             this.shadow.setZIndex(++index);
14955         }
14956         this.el.setStyle("z-index", ++index);
14957         if(this.proxy){
14958             this.proxy.setStyle("z-index", ++index);
14959         }
14960         if(this.resizer){
14961             this.resizer.proxy.setStyle("z-index", ++index);
14962         }
14963
14964         this.lastZIndex = index;
14965     },
14966
14967     /**
14968      * Returns the element for this dialog
14969      * @return {Roo.Element} The underlying dialog Element
14970      */
14971     getEl : function(){
14972         return this.el;
14973     }
14974 });
14975
14976 /**
14977  * @class Roo.DialogManager
14978  * Provides global access to BasicDialogs that have been created and
14979  * support for z-indexing (layering) multiple open dialogs.
14980  */
14981 Roo.DialogManager = function(){
14982     var list = {};
14983     var accessList = [];
14984     var front = null;
14985
14986     // private
14987     var sortDialogs = function(d1, d2){
14988         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
14989     };
14990
14991     // private
14992     var orderDialogs = function(){
14993         accessList.sort(sortDialogs);
14994         var seed = Roo.DialogManager.zseed;
14995         for(var i = 0, len = accessList.length; i < len; i++){
14996             var dlg = accessList[i];
14997             if(dlg){
14998                 dlg.setZIndex(seed + (i*10));
14999             }
15000         }
15001     };
15002
15003     return {
15004         /**
15005          * The starting z-index for BasicDialogs (defaults to 9000)
15006          * @type Number The z-index value
15007          */
15008         zseed : 9000,
15009
15010         // private
15011         register : function(dlg){
15012             list[dlg.id] = dlg;
15013             accessList.push(dlg);
15014         },
15015
15016         // private
15017         unregister : function(dlg){
15018             delete list[dlg.id];
15019             var i=0;
15020             var len=0;
15021             if(!accessList.indexOf){
15022                 for(  i = 0, len = accessList.length; i < len; i++){
15023                     if(accessList[i] == dlg){
15024                         accessList.splice(i, 1);
15025                         return;
15026                     }
15027                 }
15028             }else{
15029                  i = accessList.indexOf(dlg);
15030                 if(i != -1){
15031                     accessList.splice(i, 1);
15032                 }
15033             }
15034         },
15035
15036         /**
15037          * Gets a registered dialog by id
15038          * @param {String/Object} id The id of the dialog or a dialog
15039          * @return {Roo.BasicDialog} this
15040          */
15041         get : function(id){
15042             return typeof id == "object" ? id : list[id];
15043         },
15044
15045         /**
15046          * Brings the specified dialog to the front
15047          * @param {String/Object} dlg The id of the dialog or a dialog
15048          * @return {Roo.BasicDialog} this
15049          */
15050         bringToFront : function(dlg){
15051             dlg = this.get(dlg);
15052             if(dlg != front){
15053                 front = dlg;
15054                 dlg._lastAccess = new Date().getTime();
15055                 orderDialogs();
15056             }
15057             return dlg;
15058         },
15059
15060         /**
15061          * Sends the specified dialog to the back
15062          * @param {String/Object} dlg The id of the dialog or a dialog
15063          * @return {Roo.BasicDialog} this
15064          */
15065         sendToBack : function(dlg){
15066             dlg = this.get(dlg);
15067             dlg._lastAccess = -(new Date().getTime());
15068             orderDialogs();
15069             return dlg;
15070         },
15071
15072         /**
15073          * Hides all dialogs
15074          */
15075         hideAll : function(){
15076             for(var id in list){
15077                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
15078                     list[id].hide();
15079                 }
15080             }
15081         }
15082     };
15083 }();
15084
15085 /**
15086  * @class Roo.LayoutDialog
15087  * @extends Roo.BasicDialog
15088  * Dialog which provides adjustments for working with a layout in a Dialog.
15089  * Add your necessary layout config options to the dialog's config.<br>
15090  * Example usage (including a nested layout):
15091  * <pre><code>
15092 if(!dialog){
15093     dialog = new Roo.LayoutDialog("download-dlg", {
15094         modal: true,
15095         width:600,
15096         height:450,
15097         shadow:true,
15098         minWidth:500,
15099         minHeight:350,
15100         autoTabs:true,
15101         proxyDrag:true,
15102         // layout config merges with the dialog config
15103         center:{
15104             tabPosition: "top",
15105             alwaysShowTabs: true
15106         }
15107     });
15108     dialog.addKeyListener(27, dialog.hide, dialog);
15109     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
15110     dialog.addButton("Build It!", this.getDownload, this);
15111
15112     // we can even add nested layouts
15113     var innerLayout = new Roo.BorderLayout("dl-inner", {
15114         east: {
15115             initialSize: 200,
15116             autoScroll:true,
15117             split:true
15118         },
15119         center: {
15120             autoScroll:true
15121         }
15122     });
15123     innerLayout.beginUpdate();
15124     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
15125     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
15126     innerLayout.endUpdate(true);
15127
15128     var layout = dialog.getLayout();
15129     layout.beginUpdate();
15130     layout.add("center", new Roo.ContentPanel("standard-panel",
15131                         {title: "Download the Source", fitToFrame:true}));
15132     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
15133                {title: "Build your own roo.js"}));
15134     layout.getRegion("center").showPanel(sp);
15135     layout.endUpdate();
15136 }
15137 </code></pre>
15138     * @constructor
15139     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
15140     * @param {Object} config configuration options
15141   */
15142 Roo.LayoutDialog = function(el, cfg){
15143     
15144     var config=  cfg;
15145     if (typeof(cfg) == 'undefined') {
15146         config = Roo.apply({}, el);
15147         el = Roo.get( document.documentElement || document.body).createChild();
15148         //config.autoCreate = true;
15149     }
15150     
15151     
15152     config.autoTabs = false;
15153     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
15154     this.body.setStyle({overflow:"hidden", position:"relative"});
15155     this.layout = new Roo.BorderLayout(this.body.dom, config);
15156     this.layout.monitorWindowResize = false;
15157     this.el.addClass("x-dlg-auto-layout");
15158     // fix case when center region overwrites center function
15159     this.center = Roo.BasicDialog.prototype.center;
15160     this.on("show", this.layout.layout, this.layout, true);
15161     if (config.items) {
15162         var xitems = config.items;
15163         delete config.items;
15164         Roo.each(xitems, this.addxtype, this);
15165     }
15166     
15167     
15168 };
15169 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
15170     /**
15171      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
15172      * @deprecated
15173      */
15174     endUpdate : function(){
15175         this.layout.endUpdate();
15176     },
15177
15178     /**
15179      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
15180      *  @deprecated
15181      */
15182     beginUpdate : function(){
15183         this.layout.beginUpdate();
15184     },
15185
15186     /**
15187      * Get the BorderLayout for this dialog
15188      * @return {Roo.BorderLayout}
15189      */
15190     getLayout : function(){
15191         return this.layout;
15192     },
15193
15194     showEl : function(){
15195         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
15196         if(Roo.isIE7){
15197             this.layout.layout();
15198         }
15199     },
15200
15201     // private
15202     // Use the syncHeightBeforeShow config option to control this automatically
15203     syncBodyHeight : function(){
15204         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
15205         if(this.layout){this.layout.layout();}
15206     },
15207     
15208       /**
15209      * Add an xtype element (actually adds to the layout.)
15210      * @return {Object} xdata xtype object data.
15211      */
15212     
15213     addxtype : function(c) {
15214         return this.layout.addxtype(c);
15215     }
15216 });/*
15217  * Based on:
15218  * Ext JS Library 1.1.1
15219  * Copyright(c) 2006-2007, Ext JS, LLC.
15220  *
15221  * Originally Released Under LGPL - original licence link has changed is not relivant.
15222  *
15223  * Fork - LGPL
15224  * <script type="text/javascript">
15225  */
15226  
15227 /**
15228  * @class Roo.MessageBox
15229  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
15230  * Example usage:
15231  *<pre><code>
15232 // Basic alert:
15233 Roo.Msg.alert('Status', 'Changes saved successfully.');
15234
15235 // Prompt for user data:
15236 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
15237     if (btn == 'ok'){
15238         // process text value...
15239     }
15240 });
15241
15242 // Show a dialog using config options:
15243 Roo.Msg.show({
15244    title:'Save Changes?',
15245    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
15246    buttons: Roo.Msg.YESNOCANCEL,
15247    fn: processResult,
15248    animEl: 'elId'
15249 });
15250 </code></pre>
15251  * @singleton
15252  */
15253 Roo.MessageBox = function(){
15254     var dlg, opt, mask, waitTimer;
15255     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
15256     var buttons, activeTextEl, bwidth;
15257
15258     // private
15259     var handleButton = function(button){
15260         dlg.hide();
15261         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
15262     };
15263
15264     // private
15265     var handleHide = function(){
15266         if(opt && opt.cls){
15267             dlg.el.removeClass(opt.cls);
15268         }
15269         if(waitTimer){
15270             Roo.TaskMgr.stop(waitTimer);
15271             waitTimer = null;
15272         }
15273     };
15274
15275     // private
15276     var updateButtons = function(b){
15277         var width = 0;
15278         if(!b){
15279             buttons["ok"].hide();
15280             buttons["cancel"].hide();
15281             buttons["yes"].hide();
15282             buttons["no"].hide();
15283             dlg.footer.dom.style.display = 'none';
15284             return width;
15285         }
15286         dlg.footer.dom.style.display = '';
15287         for(var k in buttons){
15288             if(typeof buttons[k] != "function"){
15289                 if(b[k]){
15290                     buttons[k].show();
15291                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
15292                     width += buttons[k].el.getWidth()+15;
15293                 }else{
15294                     buttons[k].hide();
15295                 }
15296             }
15297         }
15298         return width;
15299     };
15300
15301     // private
15302     var handleEsc = function(d, k, e){
15303         if(opt && opt.closable !== false){
15304             dlg.hide();
15305         }
15306         if(e){
15307             e.stopEvent();
15308         }
15309     };
15310
15311     return {
15312         /**
15313          * Returns a reference to the underlying {@link Roo.BasicDialog} element
15314          * @return {Roo.BasicDialog} The BasicDialog element
15315          */
15316         getDialog : function(){
15317            if(!dlg){
15318                 dlg = new Roo.BasicDialog("x-msg-box", {
15319                     autoCreate : true,
15320                     shadow: true,
15321                     draggable: true,
15322                     resizable:false,
15323                     constraintoviewport:false,
15324                     fixedcenter:true,
15325                     collapsible : false,
15326                     shim:true,
15327                     modal: true,
15328                     width:400, height:100,
15329                     buttonAlign:"center",
15330                     closeClick : function(){
15331                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
15332                             handleButton("no");
15333                         }else{
15334                             handleButton("cancel");
15335                         }
15336                     }
15337                 });
15338                 dlg.on("hide", handleHide);
15339                 mask = dlg.mask;
15340                 dlg.addKeyListener(27, handleEsc);
15341                 buttons = {};
15342                 var bt = this.buttonText;
15343                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
15344                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
15345                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
15346                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
15347                 bodyEl = dlg.body.createChild({
15348
15349                     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>'
15350                 });
15351                 msgEl = bodyEl.dom.firstChild;
15352                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
15353                 textboxEl.enableDisplayMode();
15354                 textboxEl.addKeyListener([10,13], function(){
15355                     if(dlg.isVisible() && opt && opt.buttons){
15356                         if(opt.buttons.ok){
15357                             handleButton("ok");
15358                         }else if(opt.buttons.yes){
15359                             handleButton("yes");
15360                         }
15361                     }
15362                 });
15363                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
15364                 textareaEl.enableDisplayMode();
15365                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
15366                 progressEl.enableDisplayMode();
15367                 var pf = progressEl.dom.firstChild;
15368                 if (pf) {
15369                     pp = Roo.get(pf.firstChild);
15370                     pp.setHeight(pf.offsetHeight);
15371                 }
15372                 
15373             }
15374             return dlg;
15375         },
15376
15377         /**
15378          * Updates the message box body text
15379          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
15380          * the XHTML-compliant non-breaking space character '&amp;#160;')
15381          * @return {Roo.MessageBox} This message box
15382          */
15383         updateText : function(text){
15384             if(!dlg.isVisible() && !opt.width){
15385                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
15386             }
15387             msgEl.innerHTML = text || '&#160;';
15388             var w = Math.max(Math.min(opt.width || msgEl.offsetWidth, this.maxWidth), 
15389                         Math.max(opt.minWidth || this.minWidth, bwidth));
15390             if(opt.prompt){
15391                 activeTextEl.setWidth(w);
15392             }
15393             if(dlg.isVisible()){
15394                 dlg.fixedcenter = false;
15395             }
15396             dlg.setContentSize(w, bodyEl.getHeight());
15397             if(dlg.isVisible()){
15398                 dlg.fixedcenter = true;
15399             }
15400             return this;
15401         },
15402
15403         /**
15404          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
15405          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
15406          * @param {Number} value Any number between 0 and 1 (e.g., .5)
15407          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
15408          * @return {Roo.MessageBox} This message box
15409          */
15410         updateProgress : function(value, text){
15411             if(text){
15412                 this.updateText(text);
15413             }
15414             if (pp) { // weird bug on my firefox - for some reason this is not defined
15415                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
15416             }
15417             return this;
15418         },        
15419
15420         /**
15421          * Returns true if the message box is currently displayed
15422          * @return {Boolean} True if the message box is visible, else false
15423          */
15424         isVisible : function(){
15425             return dlg && dlg.isVisible();  
15426         },
15427
15428         /**
15429          * Hides the message box if it is displayed
15430          */
15431         hide : function(){
15432             if(this.isVisible()){
15433                 dlg.hide();
15434             }  
15435         },
15436
15437         /**
15438          * Displays a new message box, or reinitializes an existing message box, based on the config options
15439          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
15440          * The following config object properties are supported:
15441          * <pre>
15442 Property    Type             Description
15443 ----------  ---------------  ------------------------------------------------------------------------------------
15444 animEl            String/Element   An id or Element from which the message box should animate as it opens and
15445                                    closes (defaults to undefined)
15446 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
15447                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
15448 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
15449                                    progress and wait dialogs will ignore this property and always hide the
15450                                    close button as they can only be closed programmatically.
15451 cls               String           A custom CSS class to apply to the message box element
15452 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
15453                                    displayed (defaults to 75)
15454 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
15455                                    function will be btn (the name of the button that was clicked, if applicable,
15456                                    e.g. "ok"), and text (the value of the active text field, if applicable).
15457                                    Progress and wait dialogs will ignore this option since they do not respond to
15458                                    user actions and can only be closed programmatically, so any required function
15459                                    should be called by the same code after it closes the dialog.
15460 icon              String           A CSS class that provides a background image to be used as an icon for
15461                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
15462 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
15463 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
15464 modal             Boolean          False to allow user interaction with the page while the message box is
15465                                    displayed (defaults to true)
15466 msg               String           A string that will replace the existing message box body text (defaults
15467                                    to the XHTML-compliant non-breaking space character '&#160;')
15468 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
15469 progress          Boolean          True to display a progress bar (defaults to false)
15470 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
15471 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
15472 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
15473 title             String           The title text
15474 value             String           The string value to set into the active textbox element if displayed
15475 wait              Boolean          True to display a progress bar (defaults to false)
15476 width             Number           The width of the dialog in pixels
15477 </pre>
15478          *
15479          * Example usage:
15480          * <pre><code>
15481 Roo.Msg.show({
15482    title: 'Address',
15483    msg: 'Please enter your address:',
15484    width: 300,
15485    buttons: Roo.MessageBox.OKCANCEL,
15486    multiline: true,
15487    fn: saveAddress,
15488    animEl: 'addAddressBtn'
15489 });
15490 </code></pre>
15491          * @param {Object} config Configuration options
15492          * @return {Roo.MessageBox} This message box
15493          */
15494         show : function(options){
15495             if(this.isVisible()){
15496                 this.hide();
15497             }
15498             var d = this.getDialog();
15499             opt = options;
15500             d.setTitle(opt.title || "&#160;");
15501             d.close.setDisplayed(opt.closable !== false);
15502             activeTextEl = textboxEl;
15503             opt.prompt = opt.prompt || (opt.multiline ? true : false);
15504             if(opt.prompt){
15505                 if(opt.multiline){
15506                     textboxEl.hide();
15507                     textareaEl.show();
15508                     textareaEl.setHeight(typeof opt.multiline == "number" ?
15509                         opt.multiline : this.defaultTextHeight);
15510                     activeTextEl = textareaEl;
15511                 }else{
15512                     textboxEl.show();
15513                     textareaEl.hide();
15514                 }
15515             }else{
15516                 textboxEl.hide();
15517                 textareaEl.hide();
15518             }
15519             progressEl.setDisplayed(opt.progress === true);
15520             this.updateProgress(0);
15521             activeTextEl.dom.value = opt.value || "";
15522             if(opt.prompt){
15523                 dlg.setDefaultButton(activeTextEl);
15524             }else{
15525                 var bs = opt.buttons;
15526                 var db = null;
15527                 if(bs && bs.ok){
15528                     db = buttons["ok"];
15529                 }else if(bs && bs.yes){
15530                     db = buttons["yes"];
15531                 }
15532                 dlg.setDefaultButton(db);
15533             }
15534             bwidth = updateButtons(opt.buttons);
15535             this.updateText(opt.msg);
15536             if(opt.cls){
15537                 d.el.addClass(opt.cls);
15538             }
15539             d.proxyDrag = opt.proxyDrag === true;
15540             d.modal = opt.modal !== false;
15541             d.mask = opt.modal !== false ? mask : false;
15542             if(!d.isVisible()){
15543                 // force it to the end of the z-index stack so it gets a cursor in FF
15544                 document.body.appendChild(dlg.el.dom);
15545                 d.animateTarget = null;
15546                 d.show(options.animEl);
15547             }
15548             return this;
15549         },
15550
15551         /**
15552          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
15553          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
15554          * and closing the message box when the process is complete.
15555          * @param {String} title The title bar text
15556          * @param {String} msg The message box body text
15557          * @return {Roo.MessageBox} This message box
15558          */
15559         progress : function(title, msg){
15560             this.show({
15561                 title : title,
15562                 msg : msg,
15563                 buttons: false,
15564                 progress:true,
15565                 closable:false,
15566                 minWidth: this.minProgressWidth,
15567                 modal : true
15568             });
15569             return this;
15570         },
15571
15572         /**
15573          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
15574          * If a callback function is passed it will be called after the user clicks the button, and the
15575          * id of the button that was clicked will be passed as the only parameter to the callback
15576          * (could also be the top-right close button).
15577          * @param {String} title The title bar text
15578          * @param {String} msg The message box body text
15579          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15580          * @param {Object} scope (optional) The scope of the callback function
15581          * @return {Roo.MessageBox} This message box
15582          */
15583         alert : function(title, msg, fn, scope){
15584             this.show({
15585                 title : title,
15586                 msg : msg,
15587                 buttons: this.OK,
15588                 fn: fn,
15589                 scope : scope,
15590                 modal : true
15591             });
15592             return this;
15593         },
15594
15595         /**
15596          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
15597          * interaction while waiting for a long-running process to complete that does not have defined intervals.
15598          * You are responsible for closing the message box when the process is complete.
15599          * @param {String} msg The message box body text
15600          * @param {String} title (optional) The title bar text
15601          * @return {Roo.MessageBox} This message box
15602          */
15603         wait : function(msg, title){
15604             this.show({
15605                 title : title,
15606                 msg : msg,
15607                 buttons: false,
15608                 closable:false,
15609                 progress:true,
15610                 modal:true,
15611                 width:300,
15612                 wait:true
15613             });
15614             waitTimer = Roo.TaskMgr.start({
15615                 run: function(i){
15616                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
15617                 },
15618                 interval: 1000
15619             });
15620             return this;
15621         },
15622
15623         /**
15624          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
15625          * If a callback function is passed it will be called after the user clicks either button, and the id of the
15626          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
15627          * @param {String} title The title bar text
15628          * @param {String} msg The message box body text
15629          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15630          * @param {Object} scope (optional) The scope of the callback function
15631          * @return {Roo.MessageBox} This message box
15632          */
15633         confirm : function(title, msg, fn, scope){
15634             this.show({
15635                 title : title,
15636                 msg : msg,
15637                 buttons: this.YESNO,
15638                 fn: fn,
15639                 scope : scope,
15640                 modal : true
15641             });
15642             return this;
15643         },
15644
15645         /**
15646          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
15647          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
15648          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
15649          * (could also be the top-right close button) and the text that was entered will be passed as the two
15650          * parameters to the callback.
15651          * @param {String} title The title bar text
15652          * @param {String} msg The message box body text
15653          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15654          * @param {Object} scope (optional) The scope of the callback function
15655          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
15656          * property, or the height in pixels to create the textbox (defaults to false / single-line)
15657          * @return {Roo.MessageBox} This message box
15658          */
15659         prompt : function(title, msg, fn, scope, multiline){
15660             this.show({
15661                 title : title,
15662                 msg : msg,
15663                 buttons: this.OKCANCEL,
15664                 fn: fn,
15665                 minWidth:250,
15666                 scope : scope,
15667                 prompt:true,
15668                 multiline: multiline,
15669                 modal : true
15670             });
15671             return this;
15672         },
15673
15674         /**
15675          * Button config that displays a single OK button
15676          * @type Object
15677          */
15678         OK : {ok:true},
15679         /**
15680          * Button config that displays Yes and No buttons
15681          * @type Object
15682          */
15683         YESNO : {yes:true, no:true},
15684         /**
15685          * Button config that displays OK and Cancel buttons
15686          * @type Object
15687          */
15688         OKCANCEL : {ok:true, cancel:true},
15689         /**
15690          * Button config that displays Yes, No and Cancel buttons
15691          * @type Object
15692          */
15693         YESNOCANCEL : {yes:true, no:true, cancel:true},
15694
15695         /**
15696          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
15697          * @type Number
15698          */
15699         defaultTextHeight : 75,
15700         /**
15701          * The maximum width in pixels of the message box (defaults to 600)
15702          * @type Number
15703          */
15704         maxWidth : 600,
15705         /**
15706          * The minimum width in pixels of the message box (defaults to 100)
15707          * @type Number
15708          */
15709         minWidth : 100,
15710         /**
15711          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
15712          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
15713          * @type Number
15714          */
15715         minProgressWidth : 250,
15716         /**
15717          * An object containing the default button text strings that can be overriden for localized language support.
15718          * Supported properties are: ok, cancel, yes and no.
15719          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
15720          * @type Object
15721          */
15722         buttonText : {
15723             ok : "OK",
15724             cancel : "Cancel",
15725             yes : "Yes",
15726             no : "No"
15727         }
15728     };
15729 }();
15730
15731 /**
15732  * Shorthand for {@link Roo.MessageBox}
15733  */
15734 Roo.Msg = Roo.MessageBox;/*
15735  * Based on:
15736  * Ext JS Library 1.1.1
15737  * Copyright(c) 2006-2007, Ext JS, LLC.
15738  *
15739  * Originally Released Under LGPL - original licence link has changed is not relivant.
15740  *
15741  * Fork - LGPL
15742  * <script type="text/javascript">
15743  */
15744 /**
15745  * @class Roo.QuickTips
15746  * Provides attractive and customizable tooltips for any element.
15747  * @singleton
15748  */
15749 Roo.QuickTips = function(){
15750     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
15751     var ce, bd, xy, dd;
15752     var visible = false, disabled = true, inited = false;
15753     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
15754     
15755     var onOver = function(e){
15756         if(disabled){
15757             return;
15758         }
15759         var t = e.getTarget();
15760         if(!t || t.nodeType !== 1 || t == document || t == document.body){
15761             return;
15762         }
15763         if(ce && t == ce.el){
15764             clearTimeout(hideProc);
15765             return;
15766         }
15767         if(t && tagEls[t.id]){
15768             tagEls[t.id].el = t;
15769             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
15770             return;
15771         }
15772         var ttp, et = Roo.fly(t);
15773         var ns = cfg.namespace;
15774         if(tm.interceptTitles && t.title){
15775             ttp = t.title;
15776             t.qtip = ttp;
15777             t.removeAttribute("title");
15778             e.preventDefault();
15779         }else{
15780             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
15781         }
15782         if(ttp){
15783             showProc = show.defer(tm.showDelay, tm, [{
15784                 el: t, 
15785                 text: ttp, 
15786                 width: et.getAttributeNS(ns, cfg.width),
15787                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
15788                 title: et.getAttributeNS(ns, cfg.title),
15789                     cls: et.getAttributeNS(ns, cfg.cls)
15790             }]);
15791         }
15792     };
15793     
15794     var onOut = function(e){
15795         clearTimeout(showProc);
15796         var t = e.getTarget();
15797         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
15798             hideProc = setTimeout(hide, tm.hideDelay);
15799         }
15800     };
15801     
15802     var onMove = function(e){
15803         if(disabled){
15804             return;
15805         }
15806         xy = e.getXY();
15807         xy[1] += 18;
15808         if(tm.trackMouse && ce){
15809             el.setXY(xy);
15810         }
15811     };
15812     
15813     var onDown = function(e){
15814         clearTimeout(showProc);
15815         clearTimeout(hideProc);
15816         if(!e.within(el)){
15817             if(tm.hideOnClick){
15818                 hide();
15819                 tm.disable();
15820                 tm.enable.defer(100, tm);
15821             }
15822         }
15823     };
15824     
15825     var getPad = function(){
15826         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
15827     };
15828
15829     var show = function(o){
15830         if(disabled){
15831             return;
15832         }
15833         clearTimeout(dismissProc);
15834         ce = o;
15835         if(removeCls){ // in case manually hidden
15836             el.removeClass(removeCls);
15837             removeCls = null;
15838         }
15839         if(ce.cls){
15840             el.addClass(ce.cls);
15841             removeCls = ce.cls;
15842         }
15843         if(ce.title){
15844             tipTitle.update(ce.title);
15845             tipTitle.show();
15846         }else{
15847             tipTitle.update('');
15848             tipTitle.hide();
15849         }
15850         el.dom.style.width  = tm.maxWidth+'px';
15851         //tipBody.dom.style.width = '';
15852         tipBodyText.update(o.text);
15853         var p = getPad(), w = ce.width;
15854         if(!w){
15855             var td = tipBodyText.dom;
15856             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
15857             if(aw > tm.maxWidth){
15858                 w = tm.maxWidth;
15859             }else if(aw < tm.minWidth){
15860                 w = tm.minWidth;
15861             }else{
15862                 w = aw;
15863             }
15864         }
15865         //tipBody.setWidth(w);
15866         el.setWidth(parseInt(w, 10) + p);
15867         if(ce.autoHide === false){
15868             close.setDisplayed(true);
15869             if(dd){
15870                 dd.unlock();
15871             }
15872         }else{
15873             close.setDisplayed(false);
15874             if(dd){
15875                 dd.lock();
15876             }
15877         }
15878         if(xy){
15879             el.avoidY = xy[1]-18;
15880             el.setXY(xy);
15881         }
15882         if(tm.animate){
15883             el.setOpacity(.1);
15884             el.setStyle("visibility", "visible");
15885             el.fadeIn({callback: afterShow});
15886         }else{
15887             afterShow();
15888         }
15889     };
15890     
15891     var afterShow = function(){
15892         if(ce){
15893             el.show();
15894             esc.enable();
15895             if(tm.autoDismiss && ce.autoHide !== false){
15896                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
15897             }
15898         }
15899     };
15900     
15901     var hide = function(noanim){
15902         clearTimeout(dismissProc);
15903         clearTimeout(hideProc);
15904         ce = null;
15905         if(el.isVisible()){
15906             esc.disable();
15907             if(noanim !== true && tm.animate){
15908                 el.fadeOut({callback: afterHide});
15909             }else{
15910                 afterHide();
15911             } 
15912         }
15913     };
15914     
15915     var afterHide = function(){
15916         el.hide();
15917         if(removeCls){
15918             el.removeClass(removeCls);
15919             removeCls = null;
15920         }
15921     };
15922     
15923     return {
15924         /**
15925         * @cfg {Number} minWidth
15926         * The minimum width of the quick tip (defaults to 40)
15927         */
15928        minWidth : 40,
15929         /**
15930         * @cfg {Number} maxWidth
15931         * The maximum width of the quick tip (defaults to 300)
15932         */
15933        maxWidth : 300,
15934         /**
15935         * @cfg {Boolean} interceptTitles
15936         * True to automatically use the element's DOM title value if available (defaults to false)
15937         */
15938        interceptTitles : false,
15939         /**
15940         * @cfg {Boolean} trackMouse
15941         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
15942         */
15943        trackMouse : false,
15944         /**
15945         * @cfg {Boolean} hideOnClick
15946         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
15947         */
15948        hideOnClick : true,
15949         /**
15950         * @cfg {Number} showDelay
15951         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
15952         */
15953        showDelay : 500,
15954         /**
15955         * @cfg {Number} hideDelay
15956         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
15957         */
15958        hideDelay : 200,
15959         /**
15960         * @cfg {Boolean} autoHide
15961         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
15962         * Used in conjunction with hideDelay.
15963         */
15964        autoHide : true,
15965         /**
15966         * @cfg {Boolean}
15967         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
15968         * (defaults to true).  Used in conjunction with autoDismissDelay.
15969         */
15970        autoDismiss : true,
15971         /**
15972         * @cfg {Number}
15973         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
15974         */
15975        autoDismissDelay : 5000,
15976        /**
15977         * @cfg {Boolean} animate
15978         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
15979         */
15980        animate : false,
15981
15982        /**
15983         * @cfg {String} title
15984         * Title text to display (defaults to '').  This can be any valid HTML markup.
15985         */
15986         title: '',
15987        /**
15988         * @cfg {String} text
15989         * Body text to display (defaults to '').  This can be any valid HTML markup.
15990         */
15991         text : '',
15992        /**
15993         * @cfg {String} cls
15994         * A CSS class to apply to the base quick tip element (defaults to '').
15995         */
15996         cls : '',
15997        /**
15998         * @cfg {Number} width
15999         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
16000         * minWidth or maxWidth.
16001         */
16002         width : null,
16003
16004     /**
16005      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
16006      * or display QuickTips in a page.
16007      */
16008        init : function(){
16009           tm = Roo.QuickTips;
16010           cfg = tm.tagConfig;
16011           if(!inited){
16012               if(!Roo.isReady){ // allow calling of init() before onReady
16013                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
16014                   return;
16015               }
16016               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
16017               el.fxDefaults = {stopFx: true};
16018               // maximum custom styling
16019               //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>');
16020               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>');              
16021               tipTitle = el.child('h3');
16022               tipTitle.enableDisplayMode("block");
16023               tipBody = el.child('div.x-tip-bd');
16024               tipBodyText = el.child('div.x-tip-bd-inner');
16025               //bdLeft = el.child('div.x-tip-bd-left');
16026               //bdRight = el.child('div.x-tip-bd-right');
16027               close = el.child('div.x-tip-close');
16028               close.enableDisplayMode("block");
16029               close.on("click", hide);
16030               var d = Roo.get(document);
16031               d.on("mousedown", onDown);
16032               d.on("mouseover", onOver);
16033               d.on("mouseout", onOut);
16034               d.on("mousemove", onMove);
16035               esc = d.addKeyListener(27, hide);
16036               esc.disable();
16037               if(Roo.dd.DD){
16038                   dd = el.initDD("default", null, {
16039                       onDrag : function(){
16040                           el.sync();  
16041                       }
16042                   });
16043                   dd.setHandleElId(tipTitle.id);
16044                   dd.lock();
16045               }
16046               inited = true;
16047           }
16048           this.enable(); 
16049        },
16050
16051     /**
16052      * Configures a new quick tip instance and assigns it to a target element.  The following config options
16053      * are supported:
16054      * <pre>
16055 Property    Type                   Description
16056 ----------  ---------------------  ------------------------------------------------------------------------
16057 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
16058      * </ul>
16059      * @param {Object} config The config object
16060      */
16061        register : function(config){
16062            var cs = config instanceof Array ? config : arguments;
16063            for(var i = 0, len = cs.length; i < len; i++) {
16064                var c = cs[i];
16065                var target = c.target;
16066                if(target){
16067                    if(target instanceof Array){
16068                        for(var j = 0, jlen = target.length; j < jlen; j++){
16069                            tagEls[target[j]] = c;
16070                        }
16071                    }else{
16072                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
16073                    }
16074                }
16075            }
16076        },
16077
16078     /**
16079      * Removes this quick tip from its element and destroys it.
16080      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
16081      */
16082        unregister : function(el){
16083            delete tagEls[Roo.id(el)];
16084        },
16085
16086     /**
16087      * Enable this quick tip.
16088      */
16089        enable : function(){
16090            if(inited && disabled){
16091                locks.pop();
16092                if(locks.length < 1){
16093                    disabled = false;
16094                }
16095            }
16096        },
16097
16098     /**
16099      * Disable this quick tip.
16100      */
16101        disable : function(){
16102           disabled = true;
16103           clearTimeout(showProc);
16104           clearTimeout(hideProc);
16105           clearTimeout(dismissProc);
16106           if(ce){
16107               hide(true);
16108           }
16109           locks.push(1);
16110        },
16111
16112     /**
16113      * Returns true if the quick tip is enabled, else false.
16114      */
16115        isEnabled : function(){
16116             return !disabled;
16117        },
16118
16119         // private
16120        tagConfig : {
16121            namespace : "ext",
16122            attribute : "qtip",
16123            width : "width",
16124            target : "target",
16125            title : "qtitle",
16126            hide : "hide",
16127            cls : "qclass"
16128        }
16129    };
16130 }();
16131
16132 // backwards compat
16133 Roo.QuickTips.tips = Roo.QuickTips.register;/*
16134  * Based on:
16135  * Ext JS Library 1.1.1
16136  * Copyright(c) 2006-2007, Ext JS, LLC.
16137  *
16138  * Originally Released Under LGPL - original licence link has changed is not relivant.
16139  *
16140  * Fork - LGPL
16141  * <script type="text/javascript">
16142  */
16143  
16144
16145 /**
16146  * @class Roo.tree.TreePanel
16147  * @extends Roo.data.Tree
16148
16149  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
16150  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
16151  * @cfg {Boolean} enableDD true to enable drag and drop
16152  * @cfg {Boolean} enableDrag true to enable just drag
16153  * @cfg {Boolean} enableDrop true to enable just drop
16154  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
16155  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
16156  * @cfg {String} ddGroup The DD group this TreePanel belongs to
16157  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
16158  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
16159  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
16160  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
16161  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
16162  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
16163  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
16164  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
16165  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
16166  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
16167  * @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>
16168  * @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>
16169  * 
16170  * @constructor
16171  * @param {String/HTMLElement/Element} el The container element
16172  * @param {Object} config
16173  */
16174 Roo.tree.TreePanel = function(el, config){
16175     var root = false;
16176     var loader = false;
16177     if (config.root) {
16178         root = config.root;
16179         delete config.root;
16180     }
16181     if (config.loader) {
16182         loader = config.loader;
16183         delete config.loader;
16184     }
16185     
16186     Roo.apply(this, config);
16187     Roo.tree.TreePanel.superclass.constructor.call(this);
16188     this.el = Roo.get(el);
16189     this.el.addClass('x-tree');
16190     //console.log(root);
16191     if (root) {
16192         this.setRootNode( Roo.factory(root, Roo.tree));
16193     }
16194     if (loader) {
16195         this.loader = Roo.factory(loader, Roo.tree);
16196     }
16197    /**
16198     * Read-only. The id of the container element becomes this TreePanel's id.
16199     */
16200    this.id = this.el.id;
16201    this.addEvents({
16202         /**
16203         * @event beforeload
16204         * Fires before a node is loaded, return false to cancel
16205         * @param {Node} node The node being loaded
16206         */
16207         "beforeload" : true,
16208         /**
16209         * @event load
16210         * Fires when a node is loaded
16211         * @param {Node} node The node that was loaded
16212         */
16213         "load" : true,
16214         /**
16215         * @event textchange
16216         * Fires when the text for a node is changed
16217         * @param {Node} node The node
16218         * @param {String} text The new text
16219         * @param {String} oldText The old text
16220         */
16221         "textchange" : true,
16222         /**
16223         * @event beforeexpand
16224         * Fires before a node is expanded, return false to cancel.
16225         * @param {Node} node The node
16226         * @param {Boolean} deep
16227         * @param {Boolean} anim
16228         */
16229         "beforeexpand" : true,
16230         /**
16231         * @event beforecollapse
16232         * Fires before a node is collapsed, return false to cancel.
16233         * @param {Node} node The node
16234         * @param {Boolean} deep
16235         * @param {Boolean} anim
16236         */
16237         "beforecollapse" : true,
16238         /**
16239         * @event expand
16240         * Fires when a node is expanded
16241         * @param {Node} node The node
16242         */
16243         "expand" : true,
16244         /**
16245         * @event disabledchange
16246         * Fires when the disabled status of a node changes
16247         * @param {Node} node The node
16248         * @param {Boolean} disabled
16249         */
16250         "disabledchange" : true,
16251         /**
16252         * @event collapse
16253         * Fires when a node is collapsed
16254         * @param {Node} node The node
16255         */
16256         "collapse" : true,
16257         /**
16258         * @event beforeclick
16259         * Fires before click processing on a node. Return false to cancel the default action.
16260         * @param {Node} node The node
16261         * @param {Roo.EventObject} e The event object
16262         */
16263         "beforeclick":true,
16264         /**
16265         * @event checkchange
16266         * Fires when a node with a checkbox's checked property changes
16267         * @param {Node} this This node
16268         * @param {Boolean} checked
16269         */
16270         "checkchange":true,
16271         /**
16272         * @event click
16273         * Fires when a node is clicked
16274         * @param {Node} node The node
16275         * @param {Roo.EventObject} e The event object
16276         */
16277         "click":true,
16278         /**
16279         * @event dblclick
16280         * Fires when a node is double clicked
16281         * @param {Node} node The node
16282         * @param {Roo.EventObject} e The event object
16283         */
16284         "dblclick":true,
16285         /**
16286         * @event contextmenu
16287         * Fires when a node is right clicked
16288         * @param {Node} node The node
16289         * @param {Roo.EventObject} e The event object
16290         */
16291         "contextmenu":true,
16292         /**
16293         * @event beforechildrenrendered
16294         * Fires right before the child nodes for a node are rendered
16295         * @param {Node} node The node
16296         */
16297         "beforechildrenrendered":true,
16298        /**
16299              * @event startdrag
16300              * Fires when a node starts being dragged
16301              * @param {Roo.tree.TreePanel} this
16302              * @param {Roo.tree.TreeNode} node
16303              * @param {event} e The raw browser event
16304              */ 
16305             "startdrag" : true,
16306             /**
16307              * @event enddrag
16308              * Fires when a drag operation is complete
16309              * @param {Roo.tree.TreePanel} this
16310              * @param {Roo.tree.TreeNode} node
16311              * @param {event} e The raw browser event
16312              */
16313             "enddrag" : true,
16314             /**
16315              * @event dragdrop
16316              * Fires when a dragged node is dropped on a valid DD target
16317              * @param {Roo.tree.TreePanel} this
16318              * @param {Roo.tree.TreeNode} node
16319              * @param {DD} dd The dd it was dropped on
16320              * @param {event} e The raw browser event
16321              */
16322             "dragdrop" : true,
16323             /**
16324              * @event beforenodedrop
16325              * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
16326              * passed to handlers has the following properties:<br />
16327              * <ul style="padding:5px;padding-left:16px;">
16328              * <li>tree - The TreePanel</li>
16329              * <li>target - The node being targeted for the drop</li>
16330              * <li>data - The drag data from the drag source</li>
16331              * <li>point - The point of the drop - append, above or below</li>
16332              * <li>source - The drag source</li>
16333              * <li>rawEvent - Raw mouse event</li>
16334              * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
16335              * to be inserted by setting them on this object.</li>
16336              * <li>cancel - Set this to true to cancel the drop.</li>
16337              * </ul>
16338              * @param {Object} dropEvent
16339              */
16340             "beforenodedrop" : true,
16341             /**
16342              * @event nodedrop
16343              * Fires after a DD object is dropped on a node in this tree. The dropEvent
16344              * passed to handlers has the following properties:<br />
16345              * <ul style="padding:5px;padding-left:16px;">
16346              * <li>tree - The TreePanel</li>
16347              * <li>target - The node being targeted for the drop</li>
16348              * <li>data - The drag data from the drag source</li>
16349              * <li>point - The point of the drop - append, above or below</li>
16350              * <li>source - The drag source</li>
16351              * <li>rawEvent - Raw mouse event</li>
16352              * <li>dropNode - Dropped node(s).</li>
16353              * </ul>
16354              * @param {Object} dropEvent
16355              */
16356             "nodedrop" : true,
16357              /**
16358              * @event nodedragover
16359              * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
16360              * passed to handlers has the following properties:<br />
16361              * <ul style="padding:5px;padding-left:16px;">
16362              * <li>tree - The TreePanel</li>
16363              * <li>target - The node being targeted for the drop</li>
16364              * <li>data - The drag data from the drag source</li>
16365              * <li>point - The point of the drop - append, above or below</li>
16366              * <li>source - The drag source</li>
16367              * <li>rawEvent - Raw mouse event</li>
16368              * <li>dropNode - Drop node(s) provided by the source.</li>
16369              * <li>cancel - Set this to true to signal drop not allowed.</li>
16370              * </ul>
16371              * @param {Object} dragOverEvent
16372              */
16373             "nodedragover" : true
16374         
16375    });
16376    if(this.singleExpand){
16377        this.on("beforeexpand", this.restrictExpand, this);
16378    }
16379 };
16380 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
16381     rootVisible : true,
16382     animate: Roo.enableFx,
16383     lines : true,
16384     enableDD : false,
16385     hlDrop : Roo.enableFx,
16386   
16387     renderer: false,
16388     
16389     rendererTip: false,
16390     // private
16391     restrictExpand : function(node){
16392         var p = node.parentNode;
16393         if(p){
16394             if(p.expandedChild && p.expandedChild.parentNode == p){
16395                 p.expandedChild.collapse();
16396             }
16397             p.expandedChild = node;
16398         }
16399     },
16400
16401     // private override
16402     setRootNode : function(node){
16403         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
16404         if(!this.rootVisible){
16405             node.ui = new Roo.tree.RootTreeNodeUI(node);
16406         }
16407         return node;
16408     },
16409
16410     /**
16411      * Returns the container element for this TreePanel
16412      */
16413     getEl : function(){
16414         return this.el;
16415     },
16416
16417     /**
16418      * Returns the default TreeLoader for this TreePanel
16419      */
16420     getLoader : function(){
16421         return this.loader;
16422     },
16423
16424     /**
16425      * Expand all nodes
16426      */
16427     expandAll : function(){
16428         this.root.expand(true);
16429     },
16430
16431     /**
16432      * Collapse all nodes
16433      */
16434     collapseAll : function(){
16435         this.root.collapse(true);
16436     },
16437
16438     /**
16439      * Returns the selection model used by this TreePanel
16440      */
16441     getSelectionModel : function(){
16442         if(!this.selModel){
16443             this.selModel = new Roo.tree.DefaultSelectionModel();
16444         }
16445         return this.selModel;
16446     },
16447
16448     /**
16449      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
16450      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
16451      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
16452      * @return {Array}
16453      */
16454     getChecked : function(a, startNode){
16455         startNode = startNode || this.root;
16456         var r = [];
16457         var f = function(){
16458             if(this.attributes.checked){
16459                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
16460             }
16461         }
16462         startNode.cascade(f);
16463         return r;
16464     },
16465
16466     /**
16467      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16468      * @param {String} path
16469      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16470      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
16471      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
16472      */
16473     expandPath : function(path, attr, callback){
16474         attr = attr || "id";
16475         var keys = path.split(this.pathSeparator);
16476         var curNode = this.root;
16477         if(curNode.attributes[attr] != keys[1]){ // invalid root
16478             if(callback){
16479                 callback(false, null);
16480             }
16481             return;
16482         }
16483         var index = 1;
16484         var f = function(){
16485             if(++index == keys.length){
16486                 if(callback){
16487                     callback(true, curNode);
16488                 }
16489                 return;
16490             }
16491             var c = curNode.findChild(attr, keys[index]);
16492             if(!c){
16493                 if(callback){
16494                     callback(false, curNode);
16495                 }
16496                 return;
16497             }
16498             curNode = c;
16499             c.expand(false, false, f);
16500         };
16501         curNode.expand(false, false, f);
16502     },
16503
16504     /**
16505      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16506      * @param {String} path
16507      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16508      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
16509      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
16510      */
16511     selectPath : function(path, attr, callback){
16512         attr = attr || "id";
16513         var keys = path.split(this.pathSeparator);
16514         var v = keys.pop();
16515         if(keys.length > 0){
16516             var f = function(success, node){
16517                 if(success && node){
16518                     var n = node.findChild(attr, v);
16519                     if(n){
16520                         n.select();
16521                         if(callback){
16522                             callback(true, n);
16523                         }
16524                     }else if(callback){
16525                         callback(false, n);
16526                     }
16527                 }else{
16528                     if(callback){
16529                         callback(false, n);
16530                     }
16531                 }
16532             };
16533             this.expandPath(keys.join(this.pathSeparator), attr, f);
16534         }else{
16535             this.root.select();
16536             if(callback){
16537                 callback(true, this.root);
16538             }
16539         }
16540     },
16541
16542     getTreeEl : function(){
16543         return this.el;
16544     },
16545
16546     /**
16547      * Trigger rendering of this TreePanel
16548      */
16549     render : function(){
16550         if (this.innerCt) {
16551             return this; // stop it rendering more than once!!
16552         }
16553         
16554         this.innerCt = this.el.createChild({tag:"ul",
16555                cls:"x-tree-root-ct " +
16556                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
16557
16558         if(this.containerScroll){
16559             Roo.dd.ScrollManager.register(this.el);
16560         }
16561         if((this.enableDD || this.enableDrop) && !this.dropZone){
16562            /**
16563             * The dropZone used by this tree if drop is enabled
16564             * @type Roo.tree.TreeDropZone
16565             */
16566              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
16567                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
16568            });
16569         }
16570         if((this.enableDD || this.enableDrag) && !this.dragZone){
16571            /**
16572             * The dragZone used by this tree if drag is enabled
16573             * @type Roo.tree.TreeDragZone
16574             */
16575             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
16576                ddGroup: this.ddGroup || "TreeDD",
16577                scroll: this.ddScroll
16578            });
16579         }
16580         this.getSelectionModel().init(this);
16581         if (!this.root) {
16582             console.log("ROOT not set in tree");
16583             return;
16584         }
16585         this.root.render();
16586         if(!this.rootVisible){
16587             this.root.renderChildren();
16588         }
16589         return this;
16590     }
16591 });/*
16592  * Based on:
16593  * Ext JS Library 1.1.1
16594  * Copyright(c) 2006-2007, Ext JS, LLC.
16595  *
16596  * Originally Released Under LGPL - original licence link has changed is not relivant.
16597  *
16598  * Fork - LGPL
16599  * <script type="text/javascript">
16600  */
16601  
16602
16603 /**
16604  * @class Roo.tree.DefaultSelectionModel
16605  * @extends Roo.util.Observable
16606  * The default single selection for a TreePanel.
16607  */
16608 Roo.tree.DefaultSelectionModel = function(){
16609    this.selNode = null;
16610    
16611    this.addEvents({
16612        /**
16613         * @event selectionchange
16614         * Fires when the selected node changes
16615         * @param {DefaultSelectionModel} this
16616         * @param {TreeNode} node the new selection
16617         */
16618        "selectionchange" : true,
16619
16620        /**
16621         * @event beforeselect
16622         * Fires before the selected node changes, return false to cancel the change
16623         * @param {DefaultSelectionModel} this
16624         * @param {TreeNode} node the new selection
16625         * @param {TreeNode} node the old selection
16626         */
16627        "beforeselect" : true
16628    });
16629 };
16630
16631 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
16632     init : function(tree){
16633         this.tree = tree;
16634         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16635         tree.on("click", this.onNodeClick, this);
16636     },
16637     
16638     onNodeClick : function(node, e){
16639         if (e.ctrlKey && this.selNode == node)  {
16640             this.unselect(node);
16641             return;
16642         }
16643         this.select(node);
16644     },
16645     
16646     /**
16647      * Select a node.
16648      * @param {TreeNode} node The node to select
16649      * @return {TreeNode} The selected node
16650      */
16651     select : function(node){
16652         var last = this.selNode;
16653         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
16654             if(last){
16655                 last.ui.onSelectedChange(false);
16656             }
16657             this.selNode = node;
16658             node.ui.onSelectedChange(true);
16659             this.fireEvent("selectionchange", this, node, last);
16660         }
16661         return node;
16662     },
16663     
16664     /**
16665      * Deselect a node.
16666      * @param {TreeNode} node The node to unselect
16667      */
16668     unselect : function(node){
16669         if(this.selNode == node){
16670             this.clearSelections();
16671         }    
16672     },
16673     
16674     /**
16675      * Clear all selections
16676      */
16677     clearSelections : function(){
16678         var n = this.selNode;
16679         if(n){
16680             n.ui.onSelectedChange(false);
16681             this.selNode = null;
16682             this.fireEvent("selectionchange", this, null);
16683         }
16684         return n;
16685     },
16686     
16687     /**
16688      * Get the selected node
16689      * @return {TreeNode} The selected node
16690      */
16691     getSelectedNode : function(){
16692         return this.selNode;    
16693     },
16694     
16695     /**
16696      * Returns true if the node is selected
16697      * @param {TreeNode} node The node to check
16698      * @return {Boolean}
16699      */
16700     isSelected : function(node){
16701         return this.selNode == node;  
16702     },
16703
16704     /**
16705      * Selects the node above the selected node in the tree, intelligently walking the nodes
16706      * @return TreeNode The new selection
16707      */
16708     selectPrevious : function(){
16709         var s = this.selNode || this.lastSelNode;
16710         if(!s){
16711             return null;
16712         }
16713         var ps = s.previousSibling;
16714         if(ps){
16715             if(!ps.isExpanded() || ps.childNodes.length < 1){
16716                 return this.select(ps);
16717             } else{
16718                 var lc = ps.lastChild;
16719                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
16720                     lc = lc.lastChild;
16721                 }
16722                 return this.select(lc);
16723             }
16724         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
16725             return this.select(s.parentNode);
16726         }
16727         return null;
16728     },
16729
16730     /**
16731      * Selects the node above the selected node in the tree, intelligently walking the nodes
16732      * @return TreeNode The new selection
16733      */
16734     selectNext : function(){
16735         var s = this.selNode || this.lastSelNode;
16736         if(!s){
16737             return null;
16738         }
16739         if(s.firstChild && s.isExpanded()){
16740              return this.select(s.firstChild);
16741          }else if(s.nextSibling){
16742              return this.select(s.nextSibling);
16743          }else if(s.parentNode){
16744             var newS = null;
16745             s.parentNode.bubble(function(){
16746                 if(this.nextSibling){
16747                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
16748                     return false;
16749                 }
16750             });
16751             return newS;
16752          }
16753         return null;
16754     },
16755
16756     onKeyDown : function(e){
16757         var s = this.selNode || this.lastSelNode;
16758         // undesirable, but required
16759         var sm = this;
16760         if(!s){
16761             return;
16762         }
16763         var k = e.getKey();
16764         switch(k){
16765              case e.DOWN:
16766                  e.stopEvent();
16767                  this.selectNext();
16768              break;
16769              case e.UP:
16770                  e.stopEvent();
16771                  this.selectPrevious();
16772              break;
16773              case e.RIGHT:
16774                  e.preventDefault();
16775                  if(s.hasChildNodes()){
16776                      if(!s.isExpanded()){
16777                          s.expand();
16778                      }else if(s.firstChild){
16779                          this.select(s.firstChild, e);
16780                      }
16781                  }
16782              break;
16783              case e.LEFT:
16784                  e.preventDefault();
16785                  if(s.hasChildNodes() && s.isExpanded()){
16786                      s.collapse();
16787                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
16788                      this.select(s.parentNode, e);
16789                  }
16790              break;
16791         };
16792     }
16793 });
16794
16795 /**
16796  * @class Roo.tree.MultiSelectionModel
16797  * @extends Roo.util.Observable
16798  * Multi selection for a TreePanel.
16799  */
16800 Roo.tree.MultiSelectionModel = function(){
16801    this.selNodes = [];
16802    this.selMap = {};
16803    this.addEvents({
16804        /**
16805         * @event selectionchange
16806         * Fires when the selected nodes change
16807         * @param {MultiSelectionModel} this
16808         * @param {Array} nodes Array of the selected nodes
16809         */
16810        "selectionchange" : true
16811    });
16812 };
16813
16814 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
16815     init : function(tree){
16816         this.tree = tree;
16817         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16818         tree.on("click", this.onNodeClick, this);
16819     },
16820     
16821     onNodeClick : function(node, e){
16822         this.select(node, e, e.ctrlKey);
16823     },
16824     
16825     /**
16826      * Select a node.
16827      * @param {TreeNode} node The node to select
16828      * @param {EventObject} e (optional) An event associated with the selection
16829      * @param {Boolean} keepExisting True to retain existing selections
16830      * @return {TreeNode} The selected node
16831      */
16832     select : function(node, e, keepExisting){
16833         if(keepExisting !== true){
16834             this.clearSelections(true);
16835         }
16836         if(this.isSelected(node)){
16837             this.lastSelNode = node;
16838             return node;
16839         }
16840         this.selNodes.push(node);
16841         this.selMap[node.id] = node;
16842         this.lastSelNode = node;
16843         node.ui.onSelectedChange(true);
16844         this.fireEvent("selectionchange", this, this.selNodes);
16845         return node;
16846     },
16847     
16848     /**
16849      * Deselect a node.
16850      * @param {TreeNode} node The node to unselect
16851      */
16852     unselect : function(node){
16853         if(this.selMap[node.id]){
16854             node.ui.onSelectedChange(false);
16855             var sn = this.selNodes;
16856             var index = -1;
16857             if(sn.indexOf){
16858                 index = sn.indexOf(node);
16859             }else{
16860                 for(var i = 0, len = sn.length; i < len; i++){
16861                     if(sn[i] == node){
16862                         index = i;
16863                         break;
16864                     }
16865                 }
16866             }
16867             if(index != -1){
16868                 this.selNodes.splice(index, 1);
16869             }
16870             delete this.selMap[node.id];
16871             this.fireEvent("selectionchange", this, this.selNodes);
16872         }
16873     },
16874     
16875     /**
16876      * Clear all selections
16877      */
16878     clearSelections : function(suppressEvent){
16879         var sn = this.selNodes;
16880         if(sn.length > 0){
16881             for(var i = 0, len = sn.length; i < len; i++){
16882                 sn[i].ui.onSelectedChange(false);
16883             }
16884             this.selNodes = [];
16885             this.selMap = {};
16886             if(suppressEvent !== true){
16887                 this.fireEvent("selectionchange", this, this.selNodes);
16888             }
16889         }
16890     },
16891     
16892     /**
16893      * Returns true if the node is selected
16894      * @param {TreeNode} node The node to check
16895      * @return {Boolean}
16896      */
16897     isSelected : function(node){
16898         return this.selMap[node.id] ? true : false;  
16899     },
16900     
16901     /**
16902      * Returns an array of the selected nodes
16903      * @return {Array}
16904      */
16905     getSelectedNodes : function(){
16906         return this.selNodes;    
16907     },
16908
16909     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
16910
16911     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
16912
16913     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
16914 });/*
16915  * Based on:
16916  * Ext JS Library 1.1.1
16917  * Copyright(c) 2006-2007, Ext JS, LLC.
16918  *
16919  * Originally Released Under LGPL - original licence link has changed is not relivant.
16920  *
16921  * Fork - LGPL
16922  * <script type="text/javascript">
16923  */
16924  
16925 /**
16926  * @class Roo.tree.TreeNode
16927  * @extends Roo.data.Node
16928  * @cfg {String} text The text for this node
16929  * @cfg {Boolean} expanded true to start the node expanded
16930  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
16931  * @cfg {Boolean} allowDrop false if this node cannot be drop on
16932  * @cfg {Boolean} disabled true to start the node disabled
16933  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
16934  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
16935  * @cfg {String} cls A css class to be added to the node
16936  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
16937  * @cfg {String} href URL of the link used for the node (defaults to #)
16938  * @cfg {String} hrefTarget target frame for the link
16939  * @cfg {String} qtip An Ext QuickTip for the node
16940  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
16941  * @cfg {Boolean} singleClickExpand True for single click expand on this node
16942  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
16943  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
16944  * (defaults to undefined with no checkbox rendered)
16945  * @constructor
16946  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
16947  */
16948 Roo.tree.TreeNode = function(attributes){
16949     attributes = attributes || {};
16950     if(typeof attributes == "string"){
16951         attributes = {text: attributes};
16952     }
16953     this.childrenRendered = false;
16954     this.rendered = false;
16955     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
16956     this.expanded = attributes.expanded === true;
16957     this.isTarget = attributes.isTarget !== false;
16958     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
16959     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
16960
16961     /**
16962      * Read-only. The text for this node. To change it use setText().
16963      * @type String
16964      */
16965     this.text = attributes.text;
16966     /**
16967      * True if this node is disabled.
16968      * @type Boolean
16969      */
16970     this.disabled = attributes.disabled === true;
16971
16972     this.addEvents({
16973         /**
16974         * @event textchange
16975         * Fires when the text for this node is changed
16976         * @param {Node} this This node
16977         * @param {String} text The new text
16978         * @param {String} oldText The old text
16979         */
16980         "textchange" : true,
16981         /**
16982         * @event beforeexpand
16983         * Fires before this node is expanded, return false to cancel.
16984         * @param {Node} this This node
16985         * @param {Boolean} deep
16986         * @param {Boolean} anim
16987         */
16988         "beforeexpand" : true,
16989         /**
16990         * @event beforecollapse
16991         * Fires before this node is collapsed, return false to cancel.
16992         * @param {Node} this This node
16993         * @param {Boolean} deep
16994         * @param {Boolean} anim
16995         */
16996         "beforecollapse" : true,
16997         /**
16998         * @event expand
16999         * Fires when this node is expanded
17000         * @param {Node} this This node
17001         */
17002         "expand" : true,
17003         /**
17004         * @event disabledchange
17005         * Fires when the disabled status of this node changes
17006         * @param {Node} this This node
17007         * @param {Boolean} disabled
17008         */
17009         "disabledchange" : true,
17010         /**
17011         * @event collapse
17012         * Fires when this node is collapsed
17013         * @param {Node} this This node
17014         */
17015         "collapse" : true,
17016         /**
17017         * @event beforeclick
17018         * Fires before click processing. Return false to cancel the default action.
17019         * @param {Node} this This node
17020         * @param {Roo.EventObject} e The event object
17021         */
17022         "beforeclick":true,
17023         /**
17024         * @event checkchange
17025         * Fires when a node with a checkbox's checked property changes
17026         * @param {Node} this This node
17027         * @param {Boolean} checked
17028         */
17029         "checkchange":true,
17030         /**
17031         * @event click
17032         * Fires when this node is clicked
17033         * @param {Node} this This node
17034         * @param {Roo.EventObject} e The event object
17035         */
17036         "click":true,
17037         /**
17038         * @event dblclick
17039         * Fires when this node is double clicked
17040         * @param {Node} this This node
17041         * @param {Roo.EventObject} e The event object
17042         */
17043         "dblclick":true,
17044         /**
17045         * @event contextmenu
17046         * Fires when this node is right clicked
17047         * @param {Node} this This node
17048         * @param {Roo.EventObject} e The event object
17049         */
17050         "contextmenu":true,
17051         /**
17052         * @event beforechildrenrendered
17053         * Fires right before the child nodes for this node are rendered
17054         * @param {Node} this This node
17055         */
17056         "beforechildrenrendered":true
17057     });
17058
17059     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
17060
17061     /**
17062      * Read-only. The UI for this node
17063      * @type TreeNodeUI
17064      */
17065     this.ui = new uiClass(this);
17066 };
17067 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
17068     preventHScroll: true,
17069     /**
17070      * Returns true if this node is expanded
17071      * @return {Boolean}
17072      */
17073     isExpanded : function(){
17074         return this.expanded;
17075     },
17076
17077     /**
17078      * Returns the UI object for this node
17079      * @return {TreeNodeUI}
17080      */
17081     getUI : function(){
17082         return this.ui;
17083     },
17084
17085     // private override
17086     setFirstChild : function(node){
17087         var of = this.firstChild;
17088         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
17089         if(this.childrenRendered && of && node != of){
17090             of.renderIndent(true, true);
17091         }
17092         if(this.rendered){
17093             this.renderIndent(true, true);
17094         }
17095     },
17096
17097     // private override
17098     setLastChild : function(node){
17099         var ol = this.lastChild;
17100         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
17101         if(this.childrenRendered && ol && node != ol){
17102             ol.renderIndent(true, true);
17103         }
17104         if(this.rendered){
17105             this.renderIndent(true, true);
17106         }
17107     },
17108
17109     // these methods are overridden to provide lazy rendering support
17110     // private override
17111     appendChild : function(){
17112         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
17113         if(node && this.childrenRendered){
17114             node.render();
17115         }
17116         this.ui.updateExpandIcon();
17117         return node;
17118     },
17119
17120     // private override
17121     removeChild : function(node){
17122         this.ownerTree.getSelectionModel().unselect(node);
17123         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
17124         // if it's been rendered remove dom node
17125         if(this.childrenRendered){
17126             node.ui.remove();
17127         }
17128         if(this.childNodes.length < 1){
17129             this.collapse(false, false);
17130         }else{
17131             this.ui.updateExpandIcon();
17132         }
17133         if(!this.firstChild) {
17134             this.childrenRendered = false;
17135         }
17136         return node;
17137     },
17138
17139     // private override
17140     insertBefore : function(node, refNode){
17141         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
17142         if(newNode && refNode && this.childrenRendered){
17143             node.render();
17144         }
17145         this.ui.updateExpandIcon();
17146         return newNode;
17147     },
17148
17149     /**
17150      * Sets the text for this node
17151      * @param {String} text
17152      */
17153     setText : function(text){
17154         var oldText = this.text;
17155         this.text = text;
17156         this.attributes.text = text;
17157         if(this.rendered){ // event without subscribing
17158             this.ui.onTextChange(this, text, oldText);
17159         }
17160         this.fireEvent("textchange", this, text, oldText);
17161     },
17162
17163     /**
17164      * Triggers selection of this node
17165      */
17166     select : function(){
17167         this.getOwnerTree().getSelectionModel().select(this);
17168     },
17169
17170     /**
17171      * Triggers deselection of this node
17172      */
17173     unselect : function(){
17174         this.getOwnerTree().getSelectionModel().unselect(this);
17175     },
17176
17177     /**
17178      * Returns true if this node is selected
17179      * @return {Boolean}
17180      */
17181     isSelected : function(){
17182         return this.getOwnerTree().getSelectionModel().isSelected(this);
17183     },
17184
17185     /**
17186      * Expand this node.
17187      * @param {Boolean} deep (optional) True to expand all children as well
17188      * @param {Boolean} anim (optional) false to cancel the default animation
17189      * @param {Function} callback (optional) A callback to be called when
17190      * expanding this node completes (does not wait for deep expand to complete).
17191      * Called with 1 parameter, this node.
17192      */
17193     expand : function(deep, anim, callback){
17194         if(!this.expanded){
17195             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
17196                 return;
17197             }
17198             if(!this.childrenRendered){
17199                 this.renderChildren();
17200             }
17201             this.expanded = true;
17202             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
17203                 this.ui.animExpand(function(){
17204                     this.fireEvent("expand", this);
17205                     if(typeof callback == "function"){
17206                         callback(this);
17207                     }
17208                     if(deep === true){
17209                         this.expandChildNodes(true);
17210                     }
17211                 }.createDelegate(this));
17212                 return;
17213             }else{
17214                 this.ui.expand();
17215                 this.fireEvent("expand", this);
17216                 if(typeof callback == "function"){
17217                     callback(this);
17218                 }
17219             }
17220         }else{
17221            if(typeof callback == "function"){
17222                callback(this);
17223            }
17224         }
17225         if(deep === true){
17226             this.expandChildNodes(true);
17227         }
17228     },
17229
17230     isHiddenRoot : function(){
17231         return this.isRoot && !this.getOwnerTree().rootVisible;
17232     },
17233
17234     /**
17235      * Collapse this node.
17236      * @param {Boolean} deep (optional) True to collapse all children as well
17237      * @param {Boolean} anim (optional) false to cancel the default animation
17238      */
17239     collapse : function(deep, anim){
17240         if(this.expanded && !this.isHiddenRoot()){
17241             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
17242                 return;
17243             }
17244             this.expanded = false;
17245             if((this.getOwnerTree().animate && anim !== false) || anim){
17246                 this.ui.animCollapse(function(){
17247                     this.fireEvent("collapse", this);
17248                     if(deep === true){
17249                         this.collapseChildNodes(true);
17250                     }
17251                 }.createDelegate(this));
17252                 return;
17253             }else{
17254                 this.ui.collapse();
17255                 this.fireEvent("collapse", this);
17256             }
17257         }
17258         if(deep === true){
17259             var cs = this.childNodes;
17260             for(var i = 0, len = cs.length; i < len; i++) {
17261                 cs[i].collapse(true, false);
17262             }
17263         }
17264     },
17265
17266     // private
17267     delayedExpand : function(delay){
17268         if(!this.expandProcId){
17269             this.expandProcId = this.expand.defer(delay, this);
17270         }
17271     },
17272
17273     // private
17274     cancelExpand : function(){
17275         if(this.expandProcId){
17276             clearTimeout(this.expandProcId);
17277         }
17278         this.expandProcId = false;
17279     },
17280
17281     /**
17282      * Toggles expanded/collapsed state of the node
17283      */
17284     toggle : function(){
17285         if(this.expanded){
17286             this.collapse();
17287         }else{
17288             this.expand();
17289         }
17290     },
17291
17292     /**
17293      * Ensures all parent nodes are expanded
17294      */
17295     ensureVisible : function(callback){
17296         var tree = this.getOwnerTree();
17297         tree.expandPath(this.parentNode.getPath(), false, function(){
17298             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
17299             Roo.callback(callback);
17300         }.createDelegate(this));
17301     },
17302
17303     /**
17304      * Expand all child nodes
17305      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
17306      */
17307     expandChildNodes : function(deep){
17308         var cs = this.childNodes;
17309         for(var i = 0, len = cs.length; i < len; i++) {
17310                 cs[i].expand(deep);
17311         }
17312     },
17313
17314     /**
17315      * Collapse all child nodes
17316      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
17317      */
17318     collapseChildNodes : function(deep){
17319         var cs = this.childNodes;
17320         for(var i = 0, len = cs.length; i < len; i++) {
17321                 cs[i].collapse(deep);
17322         }
17323     },
17324
17325     /**
17326      * Disables this node
17327      */
17328     disable : function(){
17329         this.disabled = true;
17330         this.unselect();
17331         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17332             this.ui.onDisableChange(this, true);
17333         }
17334         this.fireEvent("disabledchange", this, true);
17335     },
17336
17337     /**
17338      * Enables this node
17339      */
17340     enable : function(){
17341         this.disabled = false;
17342         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17343             this.ui.onDisableChange(this, false);
17344         }
17345         this.fireEvent("disabledchange", this, false);
17346     },
17347
17348     // private
17349     renderChildren : function(suppressEvent){
17350         if(suppressEvent !== false){
17351             this.fireEvent("beforechildrenrendered", this);
17352         }
17353         var cs = this.childNodes;
17354         for(var i = 0, len = cs.length; i < len; i++){
17355             cs[i].render(true);
17356         }
17357         this.childrenRendered = true;
17358     },
17359
17360     // private
17361     sort : function(fn, scope){
17362         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
17363         if(this.childrenRendered){
17364             var cs = this.childNodes;
17365             for(var i = 0, len = cs.length; i < len; i++){
17366                 cs[i].render(true);
17367             }
17368         }
17369     },
17370
17371     // private
17372     render : function(bulkRender){
17373         this.ui.render(bulkRender);
17374         if(!this.rendered){
17375             this.rendered = true;
17376             if(this.expanded){
17377                 this.expanded = false;
17378                 this.expand(false, false);
17379             }
17380         }
17381     },
17382
17383     // private
17384     renderIndent : function(deep, refresh){
17385         if(refresh){
17386             this.ui.childIndent = null;
17387         }
17388         this.ui.renderIndent();
17389         if(deep === true && this.childrenRendered){
17390             var cs = this.childNodes;
17391             for(var i = 0, len = cs.length; i < len; i++){
17392                 cs[i].renderIndent(true, refresh);
17393             }
17394         }
17395     }
17396 });/*
17397  * Based on:
17398  * Ext JS Library 1.1.1
17399  * Copyright(c) 2006-2007, Ext JS, LLC.
17400  *
17401  * Originally Released Under LGPL - original licence link has changed is not relivant.
17402  *
17403  * Fork - LGPL
17404  * <script type="text/javascript">
17405  */
17406  
17407 /**
17408  * @class Roo.tree.AsyncTreeNode
17409  * @extends Roo.tree.TreeNode
17410  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
17411  * @constructor
17412  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
17413  */
17414  Roo.tree.AsyncTreeNode = function(config){
17415     this.loaded = false;
17416     this.loading = false;
17417     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
17418     /**
17419     * @event beforeload
17420     * Fires before this node is loaded, return false to cancel
17421     * @param {Node} this This node
17422     */
17423     this.addEvents({'beforeload':true, 'load': true});
17424     /**
17425     * @event load
17426     * Fires when this node is loaded
17427     * @param {Node} this This node
17428     */
17429     /**
17430      * The loader used by this node (defaults to using the tree's defined loader)
17431      * @type TreeLoader
17432      * @property loader
17433      */
17434 };
17435 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
17436     expand : function(deep, anim, callback){
17437         if(this.loading){ // if an async load is already running, waiting til it's done
17438             var timer;
17439             var f = function(){
17440                 if(!this.loading){ // done loading
17441                     clearInterval(timer);
17442                     this.expand(deep, anim, callback);
17443                 }
17444             }.createDelegate(this);
17445             timer = setInterval(f, 200);
17446             return;
17447         }
17448         if(!this.loaded){
17449             if(this.fireEvent("beforeload", this) === false){
17450                 return;
17451             }
17452             this.loading = true;
17453             this.ui.beforeLoad(this);
17454             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
17455             if(loader){
17456                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
17457                 return;
17458             }
17459         }
17460         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
17461     },
17462     
17463     /**
17464      * Returns true if this node is currently loading
17465      * @return {Boolean}
17466      */
17467     isLoading : function(){
17468         return this.loading;  
17469     },
17470     
17471     loadComplete : function(deep, anim, callback){
17472         this.loading = false;
17473         this.loaded = true;
17474         this.ui.afterLoad(this);
17475         this.fireEvent("load", this);
17476         this.expand(deep, anim, callback);
17477     },
17478     
17479     /**
17480      * Returns true if this node has been loaded
17481      * @return {Boolean}
17482      */
17483     isLoaded : function(){
17484         return this.loaded;
17485     },
17486     
17487     hasChildNodes : function(){
17488         if(!this.isLeaf() && !this.loaded){
17489             return true;
17490         }else{
17491             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
17492         }
17493     },
17494
17495     /**
17496      * Trigger a reload for this node
17497      * @param {Function} callback
17498      */
17499     reload : function(callback){
17500         this.collapse(false, false);
17501         while(this.firstChild){
17502             this.removeChild(this.firstChild);
17503         }
17504         this.childrenRendered = false;
17505         this.loaded = false;
17506         if(this.isHiddenRoot()){
17507             this.expanded = false;
17508         }
17509         this.expand(false, false, callback);
17510     }
17511 });/*
17512  * Based on:
17513  * Ext JS Library 1.1.1
17514  * Copyright(c) 2006-2007, Ext JS, LLC.
17515  *
17516  * Originally Released Under LGPL - original licence link has changed is not relivant.
17517  *
17518  * Fork - LGPL
17519  * <script type="text/javascript">
17520  */
17521  
17522 /**
17523  * @class Roo.tree.TreeNodeUI
17524  * @constructor
17525  * @param {Object} node The node to render
17526  * The TreeNode UI implementation is separate from the
17527  * tree implementation. Unless you are customizing the tree UI,
17528  * you should never have to use this directly.
17529  */
17530 Roo.tree.TreeNodeUI = function(node){
17531     this.node = node;
17532     this.rendered = false;
17533     this.animating = false;
17534     this.emptyIcon = Roo.BLANK_IMAGE_URL;
17535 };
17536
17537 Roo.tree.TreeNodeUI.prototype = {
17538     removeChild : function(node){
17539         if(this.rendered){
17540             this.ctNode.removeChild(node.ui.getEl());
17541         }
17542     },
17543
17544     beforeLoad : function(){
17545          this.addClass("x-tree-node-loading");
17546     },
17547
17548     afterLoad : function(){
17549          this.removeClass("x-tree-node-loading");
17550     },
17551
17552     onTextChange : function(node, text, oldText){
17553         if(this.rendered){
17554             this.textNode.innerHTML = text;
17555         }
17556     },
17557
17558     onDisableChange : function(node, state){
17559         this.disabled = state;
17560         if(state){
17561             this.addClass("x-tree-node-disabled");
17562         }else{
17563             this.removeClass("x-tree-node-disabled");
17564         }
17565     },
17566
17567     onSelectedChange : function(state){
17568         if(state){
17569             this.focus();
17570             this.addClass("x-tree-selected");
17571         }else{
17572             //this.blur();
17573             this.removeClass("x-tree-selected");
17574         }
17575     },
17576
17577     onMove : function(tree, node, oldParent, newParent, index, refNode){
17578         this.childIndent = null;
17579         if(this.rendered){
17580             var targetNode = newParent.ui.getContainer();
17581             if(!targetNode){//target not rendered
17582                 this.holder = document.createElement("div");
17583                 this.holder.appendChild(this.wrap);
17584                 return;
17585             }
17586             var insertBefore = refNode ? refNode.ui.getEl() : null;
17587             if(insertBefore){
17588                 targetNode.insertBefore(this.wrap, insertBefore);
17589             }else{
17590                 targetNode.appendChild(this.wrap);
17591             }
17592             this.node.renderIndent(true);
17593         }
17594     },
17595
17596     addClass : function(cls){
17597         if(this.elNode){
17598             Roo.fly(this.elNode).addClass(cls);
17599         }
17600     },
17601
17602     removeClass : function(cls){
17603         if(this.elNode){
17604             Roo.fly(this.elNode).removeClass(cls);
17605         }
17606     },
17607
17608     remove : function(){
17609         if(this.rendered){
17610             this.holder = document.createElement("div");
17611             this.holder.appendChild(this.wrap);
17612         }
17613     },
17614
17615     fireEvent : function(){
17616         return this.node.fireEvent.apply(this.node, arguments);
17617     },
17618
17619     initEvents : function(){
17620         this.node.on("move", this.onMove, this);
17621         var E = Roo.EventManager;
17622         var a = this.anchor;
17623
17624         var el = Roo.fly(a, '_treeui');
17625
17626         if(Roo.isOpera){ // opera render bug ignores the CSS
17627             el.setStyle("text-decoration", "none");
17628         }
17629
17630         el.on("click", this.onClick, this);
17631         el.on("dblclick", this.onDblClick, this);
17632
17633         if(this.checkbox){
17634             Roo.EventManager.on(this.checkbox,
17635                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
17636         }
17637
17638         el.on("contextmenu", this.onContextMenu, this);
17639
17640         var icon = Roo.fly(this.iconNode);
17641         icon.on("click", this.onClick, this);
17642         icon.on("dblclick", this.onDblClick, this);
17643         icon.on("contextmenu", this.onContextMenu, this);
17644         E.on(this.ecNode, "click", this.ecClick, this, true);
17645
17646         if(this.node.disabled){
17647             this.addClass("x-tree-node-disabled");
17648         }
17649         if(this.node.hidden){
17650             this.addClass("x-tree-node-disabled");
17651         }
17652         var ot = this.node.getOwnerTree();
17653         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
17654         if(dd && (!this.node.isRoot || ot.rootVisible)){
17655             Roo.dd.Registry.register(this.elNode, {
17656                 node: this.node,
17657                 handles: this.getDDHandles(),
17658                 isHandle: false
17659             });
17660         }
17661     },
17662
17663     getDDHandles : function(){
17664         return [this.iconNode, this.textNode];
17665     },
17666
17667     hide : function(){
17668         if(this.rendered){
17669             this.wrap.style.display = "none";
17670         }
17671     },
17672
17673     show : function(){
17674         if(this.rendered){
17675             this.wrap.style.display = "";
17676         }
17677     },
17678
17679     onContextMenu : function(e){
17680         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
17681             e.preventDefault();
17682             this.focus();
17683             this.fireEvent("contextmenu", this.node, e);
17684         }
17685     },
17686
17687     onClick : function(e){
17688         if(this.dropping){
17689             e.stopEvent();
17690             return;
17691         }
17692         if(this.fireEvent("beforeclick", this.node, e) !== false){
17693             if(!this.disabled && this.node.attributes.href){
17694                 this.fireEvent("click", this.node, e);
17695                 return;
17696             }
17697             e.preventDefault();
17698             if(this.disabled){
17699                 return;
17700             }
17701
17702             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
17703                 this.node.toggle();
17704             }
17705
17706             this.fireEvent("click", this.node, e);
17707         }else{
17708             e.stopEvent();
17709         }
17710     },
17711
17712     onDblClick : function(e){
17713         e.preventDefault();
17714         if(this.disabled){
17715             return;
17716         }
17717         if(this.checkbox){
17718             this.toggleCheck();
17719         }
17720         if(!this.animating && this.node.hasChildNodes()){
17721             this.node.toggle();
17722         }
17723         this.fireEvent("dblclick", this.node, e);
17724     },
17725
17726     onCheckChange : function(){
17727         var checked = this.checkbox.checked;
17728         this.node.attributes.checked = checked;
17729         this.fireEvent('checkchange', this.node, checked);
17730     },
17731
17732     ecClick : function(e){
17733         if(!this.animating && this.node.hasChildNodes()){
17734             this.node.toggle();
17735         }
17736     },
17737
17738     startDrop : function(){
17739         this.dropping = true;
17740     },
17741
17742     // delayed drop so the click event doesn't get fired on a drop
17743     endDrop : function(){
17744        setTimeout(function(){
17745            this.dropping = false;
17746        }.createDelegate(this), 50);
17747     },
17748
17749     expand : function(){
17750         this.updateExpandIcon();
17751         this.ctNode.style.display = "";
17752     },
17753
17754     focus : function(){
17755         if(!this.node.preventHScroll){
17756             try{this.anchor.focus();
17757             }catch(e){}
17758         }else if(!Roo.isIE){
17759             try{
17760                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
17761                 var l = noscroll.scrollLeft;
17762                 this.anchor.focus();
17763                 noscroll.scrollLeft = l;
17764             }catch(e){}
17765         }
17766     },
17767
17768     toggleCheck : function(value){
17769         var cb = this.checkbox;
17770         if(cb){
17771             cb.checked = (value === undefined ? !cb.checked : value);
17772         }
17773     },
17774
17775     blur : function(){
17776         try{
17777             this.anchor.blur();
17778         }catch(e){}
17779     },
17780
17781     animExpand : function(callback){
17782         var ct = Roo.get(this.ctNode);
17783         ct.stopFx();
17784         if(!this.node.hasChildNodes()){
17785             this.updateExpandIcon();
17786             this.ctNode.style.display = "";
17787             Roo.callback(callback);
17788             return;
17789         }
17790         this.animating = true;
17791         this.updateExpandIcon();
17792
17793         ct.slideIn('t', {
17794            callback : function(){
17795                this.animating = false;
17796                Roo.callback(callback);
17797             },
17798             scope: this,
17799             duration: this.node.ownerTree.duration || .25
17800         });
17801     },
17802
17803     highlight : function(){
17804         var tree = this.node.getOwnerTree();
17805         Roo.fly(this.wrap).highlight(
17806             tree.hlColor || "C3DAF9",
17807             {endColor: tree.hlBaseColor}
17808         );
17809     },
17810
17811     collapse : function(){
17812         this.updateExpandIcon();
17813         this.ctNode.style.display = "none";
17814     },
17815
17816     animCollapse : function(callback){
17817         var ct = Roo.get(this.ctNode);
17818         ct.enableDisplayMode('block');
17819         ct.stopFx();
17820
17821         this.animating = true;
17822         this.updateExpandIcon();
17823
17824         ct.slideOut('t', {
17825             callback : function(){
17826                this.animating = false;
17827                Roo.callback(callback);
17828             },
17829             scope: this,
17830             duration: this.node.ownerTree.duration || .25
17831         });
17832     },
17833
17834     getContainer : function(){
17835         return this.ctNode;
17836     },
17837
17838     getEl : function(){
17839         return this.wrap;
17840     },
17841
17842     appendDDGhost : function(ghostNode){
17843         ghostNode.appendChild(this.elNode.cloneNode(true));
17844     },
17845
17846     getDDRepairXY : function(){
17847         return Roo.lib.Dom.getXY(this.iconNode);
17848     },
17849
17850     onRender : function(){
17851         this.render();
17852     },
17853
17854     render : function(bulkRender){
17855         var n = this.node, a = n.attributes;
17856         var targetNode = n.parentNode ?
17857               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
17858
17859         if(!this.rendered){
17860             this.rendered = true;
17861
17862             this.renderElements(n, a, targetNode, bulkRender);
17863
17864             if(a.qtip){
17865                if(this.textNode.setAttributeNS){
17866                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
17867                    if(a.qtipTitle){
17868                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
17869                    }
17870                }else{
17871                    this.textNode.setAttribute("ext:qtip", a.qtip);
17872                    if(a.qtipTitle){
17873                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
17874                    }
17875                }
17876             }else if(a.qtipCfg){
17877                 a.qtipCfg.target = Roo.id(this.textNode);
17878                 Roo.QuickTips.register(a.qtipCfg);
17879             }
17880             this.initEvents();
17881             if(!this.node.expanded){
17882                 this.updateExpandIcon();
17883             }
17884         }else{
17885             if(bulkRender === true) {
17886                 targetNode.appendChild(this.wrap);
17887             }
17888         }
17889     },
17890
17891     renderElements : function(n, a, targetNode, bulkRender){
17892         // add some indent caching, this helps performance when rendering a large tree
17893         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
17894         var t = n.getOwnerTree();
17895         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
17896         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
17897         var cb = typeof a.checked == 'boolean';
17898         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
17899         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
17900             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
17901             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
17902             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
17903             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
17904             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
17905              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
17906                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
17907             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
17908             "</li>"];
17909
17910         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
17911             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
17912                                 n.nextSibling.ui.getEl(), buf.join(""));
17913         }else{
17914             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
17915         }
17916
17917         this.elNode = this.wrap.childNodes[0];
17918         this.ctNode = this.wrap.childNodes[1];
17919         var cs = this.elNode.childNodes;
17920         this.indentNode = cs[0];
17921         this.ecNode = cs[1];
17922         this.iconNode = cs[2];
17923         var index = 3;
17924         if(cb){
17925             this.checkbox = cs[3];
17926             index++;
17927         }
17928         this.anchor = cs[index];
17929         this.textNode = cs[index].firstChild;
17930     },
17931
17932     getAnchor : function(){
17933         return this.anchor;
17934     },
17935
17936     getTextEl : function(){
17937         return this.textNode;
17938     },
17939
17940     getIconEl : function(){
17941         return this.iconNode;
17942     },
17943
17944     isChecked : function(){
17945         return this.checkbox ? this.checkbox.checked : false;
17946     },
17947
17948     updateExpandIcon : function(){
17949         if(this.rendered){
17950             var n = this.node, c1, c2;
17951             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
17952             var hasChild = n.hasChildNodes();
17953             if(hasChild){
17954                 if(n.expanded){
17955                     cls += "-minus";
17956                     c1 = "x-tree-node-collapsed";
17957                     c2 = "x-tree-node-expanded";
17958                 }else{
17959                     cls += "-plus";
17960                     c1 = "x-tree-node-expanded";
17961                     c2 = "x-tree-node-collapsed";
17962                 }
17963                 if(this.wasLeaf){
17964                     this.removeClass("x-tree-node-leaf");
17965                     this.wasLeaf = false;
17966                 }
17967                 if(this.c1 != c1 || this.c2 != c2){
17968                     Roo.fly(this.elNode).replaceClass(c1, c2);
17969                     this.c1 = c1; this.c2 = c2;
17970                 }
17971             }else{
17972                 if(!this.wasLeaf){
17973                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
17974                     delete this.c1;
17975                     delete this.c2;
17976                     this.wasLeaf = true;
17977                 }
17978             }
17979             var ecc = "x-tree-ec-icon "+cls;
17980             if(this.ecc != ecc){
17981                 this.ecNode.className = ecc;
17982                 this.ecc = ecc;
17983             }
17984         }
17985     },
17986
17987     getChildIndent : function(){
17988         if(!this.childIndent){
17989             var buf = [];
17990             var p = this.node;
17991             while(p){
17992                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
17993                     if(!p.isLast()) {
17994                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
17995                     } else {
17996                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
17997                     }
17998                 }
17999                 p = p.parentNode;
18000             }
18001             this.childIndent = buf.join("");
18002         }
18003         return this.childIndent;
18004     },
18005
18006     renderIndent : function(){
18007         if(this.rendered){
18008             var indent = "";
18009             var p = this.node.parentNode;
18010             if(p){
18011                 indent = p.ui.getChildIndent();
18012             }
18013             if(this.indentMarkup != indent){ // don't rerender if not required
18014                 this.indentNode.innerHTML = indent;
18015                 this.indentMarkup = indent;
18016             }
18017             this.updateExpandIcon();
18018         }
18019     }
18020 };
18021
18022 Roo.tree.RootTreeNodeUI = function(){
18023     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
18024 };
18025 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
18026     render : function(){
18027         if(!this.rendered){
18028             var targetNode = this.node.ownerTree.innerCt.dom;
18029             this.node.expanded = true;
18030             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
18031             this.wrap = this.ctNode = targetNode.firstChild;
18032         }
18033     },
18034     collapse : function(){
18035     },
18036     expand : function(){
18037     }
18038 });/*
18039  * Based on:
18040  * Ext JS Library 1.1.1
18041  * Copyright(c) 2006-2007, Ext JS, LLC.
18042  *
18043  * Originally Released Under LGPL - original licence link has changed is not relivant.
18044  *
18045  * Fork - LGPL
18046  * <script type="text/javascript">
18047  */
18048 /**
18049  * @class Roo.tree.TreeLoader
18050  * @extends Roo.util.Observable
18051  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
18052  * nodes from a specified URL. The response must be a javascript Array definition
18053  * who's elements are node definition objects. eg:
18054  * <pre><code>
18055    [{ 'id': 1, 'text': 'A folder Node', 'leaf': false },
18056     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }]
18057 </code></pre>
18058  * <br><br>
18059  * A server request is sent, and child nodes are loaded only when a node is expanded.
18060  * The loading node's id is passed to the server under the parameter name "node" to
18061  * enable the server to produce the correct child nodes.
18062  * <br><br>
18063  * To pass extra parameters, an event handler may be attached to the "beforeload"
18064  * event, and the parameters specified in the TreeLoader's baseParams property:
18065  * <pre><code>
18066     myTreeLoader.on("beforeload", function(treeLoader, node) {
18067         this.baseParams.category = node.attributes.category;
18068     }, this);
18069 </code></pre><
18070  * This would pass an HTTP parameter called "category" to the server containing
18071  * the value of the Node's "category" attribute.
18072  * @constructor
18073  * Creates a new Treeloader.
18074  * @param {Object} config A config object containing config properties.
18075  */
18076 Roo.tree.TreeLoader = function(config){
18077     this.baseParams = {};
18078     this.requestMethod = "POST";
18079     Roo.apply(this, config);
18080
18081     this.addEvents({
18082     
18083         /**
18084          * @event beforeload
18085          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
18086          * @param {Object} This TreeLoader object.
18087          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18088          * @param {Object} callback The callback function specified in the {@link #load} call.
18089          */
18090         beforeload : true,
18091         /**
18092          * @event load
18093          * Fires when the node has been successfuly loaded.
18094          * @param {Object} This TreeLoader object.
18095          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18096          * @param {Object} response The response object containing the data from the server.
18097          */
18098         load : true,
18099         /**
18100          * @event loadexception
18101          * Fires if the network request failed.
18102          * @param {Object} This TreeLoader object.
18103          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18104          * @param {Object} response The response object containing the data from the server.
18105          */
18106         loadexception : true,
18107         /**
18108          * @event create
18109          * Fires before a node is created, enabling you to return custom Node types 
18110          * @param {Object} This TreeLoader object.
18111          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
18112          */
18113         create : true
18114     });
18115
18116     Roo.tree.TreeLoader.superclass.constructor.call(this);
18117 };
18118
18119 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
18120     /**
18121     * @cfg {String} dataUrl The URL from which to request a Json string which
18122     * specifies an array of node definition object representing the child nodes
18123     * to be loaded.
18124     */
18125     /**
18126     * @cfg {Object} baseParams (optional) An object containing properties which
18127     * specify HTTP parameters to be passed to each request for child nodes.
18128     */
18129     /**
18130     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
18131     * created by this loader. If the attributes sent by the server have an attribute in this object,
18132     * they take priority.
18133     */
18134     /**
18135     * @cfg {Object} uiProviders (optional) An object containing properties which
18136     * 
18137     * DEPRECIATED - use 'create' event handler to modify attributes - which affect creation.
18138     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
18139     * <i>uiProvider</i> attribute of a returned child node is a string rather
18140     * than a reference to a TreeNodeUI implementation, this that string value
18141     * is used as a property name in the uiProviders object. You can define the provider named
18142     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
18143     */
18144     uiProviders : {},
18145
18146     /**
18147     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
18148     * child nodes before loading.
18149     */
18150     clearOnLoad : true,
18151
18152     /**
18153     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
18154     * property on loading, rather than expecting an array. (eg. more compatible to a standard
18155     * Grid query { data : [ .....] }
18156     */
18157     
18158     root : false,
18159      /**
18160     * @cfg {String} queryParam (optional) 
18161     * Name of the query as it will be passed on the querystring (defaults to 'node')
18162     * eg. the request will be ?node=[id]
18163     */
18164     
18165     
18166     queryParam: false,
18167     
18168     /**
18169      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
18170      * This is called automatically when a node is expanded, but may be used to reload
18171      * a node (or append new children if the {@link #clearOnLoad} option is false.)
18172      * @param {Roo.tree.TreeNode} node
18173      * @param {Function} callback
18174      */
18175     load : function(node, callback){
18176         if(this.clearOnLoad){
18177             while(node.firstChild){
18178                 node.removeChild(node.firstChild);
18179             }
18180         }
18181         if(node.attributes.children){ // preloaded json children
18182             var cs = node.attributes.children;
18183             for(var i = 0, len = cs.length; i < len; i++){
18184                 node.appendChild(this.createNode(cs[i]));
18185             }
18186             if(typeof callback == "function"){
18187                 callback();
18188             }
18189         }else if(this.dataUrl){
18190             this.requestData(node, callback);
18191         }
18192     },
18193
18194     getParams: function(node){
18195         var buf = [], bp = this.baseParams;
18196         for(var key in bp){
18197             if(typeof bp[key] != "function"){
18198                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
18199             }
18200         }
18201         var n = this.queryParam === false ? 'node' : this.queryParam;
18202         buf.push(n + "=", encodeURIComponent(node.id));
18203         return buf.join("");
18204     },
18205
18206     requestData : function(node, callback){
18207         if(this.fireEvent("beforeload", this, node, callback) !== false){
18208             this.transId = Roo.Ajax.request({
18209                 method:this.requestMethod,
18210                 url: this.dataUrl||this.url,
18211                 success: this.handleResponse,
18212                 failure: this.handleFailure,
18213                 scope: this,
18214                 argument: {callback: callback, node: node},
18215                 params: this.getParams(node)
18216             });
18217         }else{
18218             // if the load is cancelled, make sure we notify
18219             // the node that we are done
18220             if(typeof callback == "function"){
18221                 callback();
18222             }
18223         }
18224     },
18225
18226     isLoading : function(){
18227         return this.transId ? true : false;
18228     },
18229
18230     abort : function(){
18231         if(this.isLoading()){
18232             Roo.Ajax.abort(this.transId);
18233         }
18234     },
18235
18236     // private
18237     createNode : function(attr){
18238         // apply baseAttrs, nice idea Corey!
18239         if(this.baseAttrs){
18240             Roo.applyIf(attr, this.baseAttrs);
18241         }
18242         if(this.applyLoader !== false){
18243             attr.loader = this;
18244         }
18245         // uiProvider = depreciated..
18246         
18247         if(typeof(attr.uiProvider) == 'string'){
18248            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
18249                 /**  eval:var:attr */ eval(attr.uiProvider);
18250         }
18251         if(typeof(this.uiProviders['default']) != 'undefined') {
18252             attr.uiProvider = this.uiProviders['default'];
18253         }
18254         
18255         this.fireEvent('create', this, attr);
18256         
18257         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
18258         return(attr.leaf ?
18259                         new Roo.tree.TreeNode(attr) :
18260                         new Roo.tree.AsyncTreeNode(attr));
18261     },
18262
18263     processResponse : function(response, node, callback){
18264         var json = response.responseText;
18265         try {
18266             
18267             var o = /**  eval:var:zzzzzzzzzz */ eval("("+json+")");
18268             if (this.root !== false) {
18269                 o = o[this.root];
18270             }
18271             
18272             for(var i = 0, len = o.length; i < len; i++){
18273                 var n = this.createNode(o[i]);
18274                 if(n){
18275                     node.appendChild(n);
18276                 }
18277             }
18278             if(typeof callback == "function"){
18279                 callback(this, node);
18280             }
18281         }catch(e){
18282             this.handleFailure(response);
18283         }
18284     },
18285
18286     handleResponse : function(response){
18287         this.transId = false;
18288         var a = response.argument;
18289         this.processResponse(response, a.node, a.callback);
18290         this.fireEvent("load", this, a.node, response);
18291     },
18292
18293     handleFailure : function(response){
18294         this.transId = false;
18295         var a = response.argument;
18296         this.fireEvent("loadexception", this, a.node, response);
18297         if(typeof a.callback == "function"){
18298             a.callback(this, a.node);
18299         }
18300     }
18301 });/*
18302  * Based on:
18303  * Ext JS Library 1.1.1
18304  * Copyright(c) 2006-2007, Ext JS, LLC.
18305  *
18306  * Originally Released Under LGPL - original licence link has changed is not relivant.
18307  *
18308  * Fork - LGPL
18309  * <script type="text/javascript">
18310  */
18311
18312 /**
18313 * @class Roo.tree.TreeFilter
18314 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
18315 * @param {TreePanel} tree
18316 * @param {Object} config (optional)
18317  */
18318 Roo.tree.TreeFilter = function(tree, config){
18319     this.tree = tree;
18320     this.filtered = {};
18321     Roo.apply(this, config);
18322 };
18323
18324 Roo.tree.TreeFilter.prototype = {
18325     clearBlank:false,
18326     reverse:false,
18327     autoClear:false,
18328     remove:false,
18329
18330      /**
18331      * Filter the data by a specific attribute.
18332      * @param {String/RegExp} value Either string that the attribute value
18333      * should start with or a RegExp to test against the attribute
18334      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
18335      * @param {TreeNode} startNode (optional) The node to start the filter at.
18336      */
18337     filter : function(value, attr, startNode){
18338         attr = attr || "text";
18339         var f;
18340         if(typeof value == "string"){
18341             var vlen = value.length;
18342             // auto clear empty filter
18343             if(vlen == 0 && this.clearBlank){
18344                 this.clear();
18345                 return;
18346             }
18347             value = value.toLowerCase();
18348             f = function(n){
18349                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
18350             };
18351         }else if(value.exec){ // regex?
18352             f = function(n){
18353                 return value.test(n.attributes[attr]);
18354             };
18355         }else{
18356             throw 'Illegal filter type, must be string or regex';
18357         }
18358         this.filterBy(f, null, startNode);
18359         },
18360
18361     /**
18362      * Filter by a function. The passed function will be called with each
18363      * node in the tree (or from the startNode). If the function returns true, the node is kept
18364      * otherwise it is filtered. If a node is filtered, its children are also filtered.
18365      * @param {Function} fn The filter function
18366      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
18367      */
18368     filterBy : function(fn, scope, startNode){
18369         startNode = startNode || this.tree.root;
18370         if(this.autoClear){
18371             this.clear();
18372         }
18373         var af = this.filtered, rv = this.reverse;
18374         var f = function(n){
18375             if(n == startNode){
18376                 return true;
18377             }
18378             if(af[n.id]){
18379                 return false;
18380             }
18381             var m = fn.call(scope || n, n);
18382             if(!m || rv){
18383                 af[n.id] = n;
18384                 n.ui.hide();
18385                 return false;
18386             }
18387             return true;
18388         };
18389         startNode.cascade(f);
18390         if(this.remove){
18391            for(var id in af){
18392                if(typeof id != "function"){
18393                    var n = af[id];
18394                    if(n && n.parentNode){
18395                        n.parentNode.removeChild(n);
18396                    }
18397                }
18398            }
18399         }
18400     },
18401
18402     /**
18403      * Clears the current filter. Note: with the "remove" option
18404      * set a filter cannot be cleared.
18405      */
18406     clear : function(){
18407         var t = this.tree;
18408         var af = this.filtered;
18409         for(var id in af){
18410             if(typeof id != "function"){
18411                 var n = af[id];
18412                 if(n){
18413                     n.ui.show();
18414                 }
18415             }
18416         }
18417         this.filtered = {};
18418     }
18419 };
18420 /*
18421  * Based on:
18422  * Ext JS Library 1.1.1
18423  * Copyright(c) 2006-2007, Ext JS, LLC.
18424  *
18425  * Originally Released Under LGPL - original licence link has changed is not relivant.
18426  *
18427  * Fork - LGPL
18428  * <script type="text/javascript">
18429  */
18430  
18431
18432 /**
18433  * @class Roo.tree.TreeSorter
18434  * Provides sorting of nodes in a TreePanel
18435  * 
18436  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
18437  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
18438  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
18439  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
18440  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
18441  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
18442  * @constructor
18443  * @param {TreePanel} tree
18444  * @param {Object} config
18445  */
18446 Roo.tree.TreeSorter = function(tree, config){
18447     Roo.apply(this, config);
18448     tree.on("beforechildrenrendered", this.doSort, this);
18449     tree.on("append", this.updateSort, this);
18450     tree.on("insert", this.updateSort, this);
18451     
18452     var dsc = this.dir && this.dir.toLowerCase() == "desc";
18453     var p = this.property || "text";
18454     var sortType = this.sortType;
18455     var fs = this.folderSort;
18456     var cs = this.caseSensitive === true;
18457     var leafAttr = this.leafAttr || 'leaf';
18458
18459     this.sortFn = function(n1, n2){
18460         if(fs){
18461             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
18462                 return 1;
18463             }
18464             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
18465                 return -1;
18466             }
18467         }
18468         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
18469         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
18470         if(v1 < v2){
18471                         return dsc ? +1 : -1;
18472                 }else if(v1 > v2){
18473                         return dsc ? -1 : +1;
18474         }else{
18475                 return 0;
18476         }
18477     };
18478 };
18479
18480 Roo.tree.TreeSorter.prototype = {
18481     doSort : function(node){
18482         node.sort(this.sortFn);
18483     },
18484     
18485     compareNodes : function(n1, n2){
18486         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
18487     },
18488     
18489     updateSort : function(tree, node){
18490         if(node.childrenRendered){
18491             this.doSort.defer(1, this, [node]);
18492         }
18493     }
18494 };/*
18495  * Based on:
18496  * Ext JS Library 1.1.1
18497  * Copyright(c) 2006-2007, Ext JS, LLC.
18498  *
18499  * Originally Released Under LGPL - original licence link has changed is not relivant.
18500  *
18501  * Fork - LGPL
18502  * <script type="text/javascript">
18503  */
18504
18505 if(Roo.dd.DropZone){
18506     
18507 Roo.tree.TreeDropZone = function(tree, config){
18508     this.allowParentInsert = false;
18509     this.allowContainerDrop = false;
18510     this.appendOnly = false;
18511     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
18512     this.tree = tree;
18513     this.lastInsertClass = "x-tree-no-status";
18514     this.dragOverData = {};
18515 };
18516
18517 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
18518     ddGroup : "TreeDD",
18519     
18520     expandDelay : 1000,
18521     
18522     expandNode : function(node){
18523         if(node.hasChildNodes() && !node.isExpanded()){
18524             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
18525         }
18526     },
18527     
18528     queueExpand : function(node){
18529         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
18530     },
18531     
18532     cancelExpand : function(){
18533         if(this.expandProcId){
18534             clearTimeout(this.expandProcId);
18535             this.expandProcId = false;
18536         }
18537     },
18538     
18539     isValidDropPoint : function(n, pt, dd, e, data){
18540         if(!n || !data){ return false; }
18541         var targetNode = n.node;
18542         var dropNode = data.node;
18543         // default drop rules
18544         if(!(targetNode && targetNode.isTarget && pt)){
18545             return false;
18546         }
18547         if(pt == "append" && targetNode.allowChildren === false){
18548             return false;
18549         }
18550         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
18551             return false;
18552         }
18553         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
18554             return false;
18555         }
18556         // reuse the object
18557         var overEvent = this.dragOverData;
18558         overEvent.tree = this.tree;
18559         overEvent.target = targetNode;
18560         overEvent.data = data;
18561         overEvent.point = pt;
18562         overEvent.source = dd;
18563         overEvent.rawEvent = e;
18564         overEvent.dropNode = dropNode;
18565         overEvent.cancel = false;  
18566         var result = this.tree.fireEvent("nodedragover", overEvent);
18567         return overEvent.cancel === false && result !== false;
18568     },
18569     
18570     getDropPoint : function(e, n, dd){
18571         var tn = n.node;
18572         if(tn.isRoot){
18573             return tn.allowChildren !== false ? "append" : false; // always append for root
18574         }
18575         var dragEl = n.ddel;
18576         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
18577         var y = Roo.lib.Event.getPageY(e);
18578         //var noAppend = tn.allowChildren === false || tn.isLeaf();
18579         
18580         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
18581         var noAppend = tn.allowChildren === false;
18582         if(this.appendOnly || tn.parentNode.allowChildren === false){
18583             return noAppend ? false : "append";
18584         }
18585         var noBelow = false;
18586         if(!this.allowParentInsert){
18587             noBelow = tn.hasChildNodes() && tn.isExpanded();
18588         }
18589         var q = (b - t) / (noAppend ? 2 : 3);
18590         if(y >= t && y < (t + q)){
18591             return "above";
18592         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
18593             return "below";
18594         }else{
18595             return "append";
18596         }
18597     },
18598     
18599     onNodeEnter : function(n, dd, e, data){
18600         this.cancelExpand();
18601     },
18602     
18603     onNodeOver : function(n, dd, e, data){
18604         var pt = this.getDropPoint(e, n, dd);
18605         var node = n.node;
18606         
18607         // auto node expand check
18608         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
18609             this.queueExpand(node);
18610         }else if(pt != "append"){
18611             this.cancelExpand();
18612         }
18613         
18614         // set the insert point style on the target node
18615         var returnCls = this.dropNotAllowed;
18616         if(this.isValidDropPoint(n, pt, dd, e, data)){
18617            if(pt){
18618                var el = n.ddel;
18619                var cls;
18620                if(pt == "above"){
18621                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
18622                    cls = "x-tree-drag-insert-above";
18623                }else if(pt == "below"){
18624                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
18625                    cls = "x-tree-drag-insert-below";
18626                }else{
18627                    returnCls = "x-tree-drop-ok-append";
18628                    cls = "x-tree-drag-append";
18629                }
18630                if(this.lastInsertClass != cls){
18631                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
18632                    this.lastInsertClass = cls;
18633                }
18634            }
18635        }
18636        return returnCls;
18637     },
18638     
18639     onNodeOut : function(n, dd, e, data){
18640         this.cancelExpand();
18641         this.removeDropIndicators(n);
18642     },
18643     
18644     onNodeDrop : function(n, dd, e, data){
18645         var point = this.getDropPoint(e, n, dd);
18646         var targetNode = n.node;
18647         targetNode.ui.startDrop();
18648         if(!this.isValidDropPoint(n, point, dd, e, data)){
18649             targetNode.ui.endDrop();
18650             return false;
18651         }
18652         // first try to find the drop node
18653         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
18654         var dropEvent = {
18655             tree : this.tree,
18656             target: targetNode,
18657             data: data,
18658             point: point,
18659             source: dd,
18660             rawEvent: e,
18661             dropNode: dropNode,
18662             cancel: !dropNode   
18663         };
18664         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
18665         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
18666             targetNode.ui.endDrop();
18667             return false;
18668         }
18669         // allow target changing
18670         targetNode = dropEvent.target;
18671         if(point == "append" && !targetNode.isExpanded()){
18672             targetNode.expand(false, null, function(){
18673                 this.completeDrop(dropEvent);
18674             }.createDelegate(this));
18675         }else{
18676             this.completeDrop(dropEvent);
18677         }
18678         return true;
18679     },
18680     
18681     completeDrop : function(de){
18682         var ns = de.dropNode, p = de.point, t = de.target;
18683         if(!(ns instanceof Array)){
18684             ns = [ns];
18685         }
18686         var n;
18687         for(var i = 0, len = ns.length; i < len; i++){
18688             n = ns[i];
18689             if(p == "above"){
18690                 t.parentNode.insertBefore(n, t);
18691             }else if(p == "below"){
18692                 t.parentNode.insertBefore(n, t.nextSibling);
18693             }else{
18694                 t.appendChild(n);
18695             }
18696         }
18697         n.ui.focus();
18698         if(this.tree.hlDrop){
18699             n.ui.highlight();
18700         }
18701         t.ui.endDrop();
18702         this.tree.fireEvent("nodedrop", de);
18703     },
18704     
18705     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
18706         if(this.tree.hlDrop){
18707             dropNode.ui.focus();
18708             dropNode.ui.highlight();
18709         }
18710         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
18711     },
18712     
18713     getTree : function(){
18714         return this.tree;
18715     },
18716     
18717     removeDropIndicators : function(n){
18718         if(n && n.ddel){
18719             var el = n.ddel;
18720             Roo.fly(el).removeClass([
18721                     "x-tree-drag-insert-above",
18722                     "x-tree-drag-insert-below",
18723                     "x-tree-drag-append"]);
18724             this.lastInsertClass = "_noclass";
18725         }
18726     },
18727     
18728     beforeDragDrop : function(target, e, id){
18729         this.cancelExpand();
18730         return true;
18731     },
18732     
18733     afterRepair : function(data){
18734         if(data && Roo.enableFx){
18735             data.node.ui.highlight();
18736         }
18737         this.hideProxy();
18738     }    
18739 });
18740
18741 }
18742 /*
18743  * Based on:
18744  * Ext JS Library 1.1.1
18745  * Copyright(c) 2006-2007, Ext JS, LLC.
18746  *
18747  * Originally Released Under LGPL - original licence link has changed is not relivant.
18748  *
18749  * Fork - LGPL
18750  * <script type="text/javascript">
18751  */
18752  
18753
18754 if(Roo.dd.DragZone){
18755 Roo.tree.TreeDragZone = function(tree, config){
18756     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
18757     this.tree = tree;
18758 };
18759
18760 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
18761     ddGroup : "TreeDD",
18762     
18763     onBeforeDrag : function(data, e){
18764         var n = data.node;
18765         return n && n.draggable && !n.disabled;
18766     },
18767     
18768     onInitDrag : function(e){
18769         var data = this.dragData;
18770         this.tree.getSelectionModel().select(data.node);
18771         this.proxy.update("");
18772         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
18773         this.tree.fireEvent("startdrag", this.tree, data.node, e);
18774     },
18775     
18776     getRepairXY : function(e, data){
18777         return data.node.ui.getDDRepairXY();
18778     },
18779     
18780     onEndDrag : function(data, e){
18781         this.tree.fireEvent("enddrag", this.tree, data.node, e);
18782     },
18783     
18784     onValidDrop : function(dd, e, id){
18785         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
18786         this.hideProxy();
18787     },
18788     
18789     beforeInvalidDrop : function(e, id){
18790         // this scrolls the original position back into view
18791         var sm = this.tree.getSelectionModel();
18792         sm.clearSelections();
18793         sm.select(this.dragData.node);
18794     }
18795 });
18796 }/*
18797  * Based on:
18798  * Ext JS Library 1.1.1
18799  * Copyright(c) 2006-2007, Ext JS, LLC.
18800  *
18801  * Originally Released Under LGPL - original licence link has changed is not relivant.
18802  *
18803  * Fork - LGPL
18804  * <script type="text/javascript">
18805  */
18806 /**
18807  * @class Roo.tree.TreeEditor
18808  * @extends Roo.Editor
18809  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
18810  * as the editor field.
18811  * @constructor
18812  * @param {TreePanel} tree
18813  * @param {Object} config Either a prebuilt {@link Roo.form.Field} instance or a Field config object
18814  */
18815 Roo.tree.TreeEditor = function(tree, config){
18816     config = config || {};
18817     var field = config.events ? config : new Roo.form.TextField(config);
18818     Roo.tree.TreeEditor.superclass.constructor.call(this, field);
18819
18820     this.tree = tree;
18821
18822     tree.on('beforeclick', this.beforeNodeClick, this);
18823     tree.getTreeEl().on('mousedown', this.hide, this);
18824     this.on('complete', this.updateNode, this);
18825     this.on('beforestartedit', this.fitToTree, this);
18826     this.on('startedit', this.bindScroll, this, {delay:10});
18827     this.on('specialkey', this.onSpecialKey, this);
18828 };
18829
18830 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
18831     /**
18832      * @cfg {String} alignment
18833      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
18834      */
18835     alignment: "l-l",
18836     // inherit
18837     autoSize: false,
18838     /**
18839      * @cfg {Boolean} hideEl
18840      * True to hide the bound element while the editor is displayed (defaults to false)
18841      */
18842     hideEl : false,
18843     /**
18844      * @cfg {String} cls
18845      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
18846      */
18847     cls: "x-small-editor x-tree-editor",
18848     /**
18849      * @cfg {Boolean} shim
18850      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
18851      */
18852     shim:false,
18853     // inherit
18854     shadow:"frame",
18855     /**
18856      * @cfg {Number} maxWidth
18857      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
18858      * the containing tree element's size, it will be automatically limited for you to the container width, taking
18859      * scroll and client offsets into account prior to each edit.
18860      */
18861     maxWidth: 250,
18862
18863     editDelay : 350,
18864
18865     // private
18866     fitToTree : function(ed, el){
18867         var td = this.tree.getTreeEl().dom, nd = el.dom;
18868         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
18869             td.scrollLeft = nd.offsetLeft;
18870         }
18871         var w = Math.min(
18872                 this.maxWidth,
18873                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
18874         this.setSize(w, '');
18875     },
18876
18877     // private
18878     triggerEdit : function(node){
18879         this.completeEdit();
18880         this.editNode = node;
18881         this.startEdit(node.ui.textNode, node.text);
18882     },
18883
18884     // private
18885     bindScroll : function(){
18886         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
18887     },
18888
18889     // private
18890     beforeNodeClick : function(node, e){
18891         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
18892         this.lastClick = new Date();
18893         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
18894             e.stopEvent();
18895             this.triggerEdit(node);
18896             return false;
18897         }
18898     },
18899
18900     // private
18901     updateNode : function(ed, value){
18902         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
18903         this.editNode.setText(value);
18904     },
18905
18906     // private
18907     onHide : function(){
18908         Roo.tree.TreeEditor.superclass.onHide.call(this);
18909         if(this.editNode){
18910             this.editNode.ui.focus();
18911         }
18912     },
18913
18914     // private
18915     onSpecialKey : function(field, e){
18916         var k = e.getKey();
18917         if(k == e.ESC){
18918             e.stopEvent();
18919             this.cancelEdit();
18920         }else if(k == e.ENTER && !e.hasModifier()){
18921             e.stopEvent();
18922             this.completeEdit();
18923         }
18924     }
18925 });//<Script type="text/javascript">
18926 /*
18927  * Based on:
18928  * Ext JS Library 1.1.1
18929  * Copyright(c) 2006-2007, Ext JS, LLC.
18930  *
18931  * Originally Released Under LGPL - original licence link has changed is not relivant.
18932  *
18933  * Fork - LGPL
18934  * <script type="text/javascript">
18935  */
18936  
18937 /**
18938  * Not documented??? - probably should be...
18939  */
18940
18941 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
18942     //focus: Roo.emptyFn, // prevent odd scrolling behavior
18943     
18944     renderElements : function(n, a, targetNode, bulkRender){
18945         //consel.log("renderElements?");
18946         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
18947
18948         var t = n.getOwnerTree();
18949         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
18950         
18951         var cols = t.columns;
18952         var bw = t.borderWidth;
18953         var c = cols[0];
18954         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
18955          var cb = typeof a.checked == "boolean";
18956         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
18957         var colcls = 'x-t-' + tid + '-c0';
18958         var buf = [
18959             '<li class="x-tree-node">',
18960             
18961                 
18962                 '<div class="x-tree-node-el ', a.cls,'">',
18963                     // extran...
18964                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
18965                 
18966                 
18967                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
18968                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
18969                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
18970                            (a.icon ? ' x-tree-node-inline-icon' : ''),
18971                            (a.iconCls ? ' '+a.iconCls : ''),
18972                            '" unselectable="on" />',
18973                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
18974                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
18975                              
18976                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
18977                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
18978                             '<span unselectable="on" qtip="' + tx + '">',
18979                              tx,
18980                              '</span></a>' ,
18981                     '</div>',
18982                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
18983                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
18984                  ];
18985         for(var i = 1, len = cols.length; i < len; i++){
18986             c = cols[i];
18987             colcls = 'x-t-' + tid + '-c' +i;
18988             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
18989             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
18990                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
18991                       "</div>");
18992          }
18993          
18994          buf.push(
18995             '</a>',
18996             '<div class="x-clear"></div></div>',
18997             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
18998             "</li>");
18999         
19000         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
19001             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
19002                                 n.nextSibling.ui.getEl(), buf.join(""));
19003         }else{
19004             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
19005         }
19006         var el = this.wrap.firstChild;
19007         this.elRow = el;
19008         this.elNode = el.firstChild;
19009         this.ranchor = el.childNodes[1];
19010         this.ctNode = this.wrap.childNodes[1];
19011         var cs = el.firstChild.childNodes;
19012         this.indentNode = cs[0];
19013         this.ecNode = cs[1];
19014         this.iconNode = cs[2];
19015         var index = 3;
19016         if(cb){
19017             this.checkbox = cs[3];
19018             index++;
19019         }
19020         this.anchor = cs[index];
19021         
19022         this.textNode = cs[index].firstChild;
19023         
19024         //el.on("click", this.onClick, this);
19025         //el.on("dblclick", this.onDblClick, this);
19026         
19027         
19028        // console.log(this);
19029     },
19030     initEvents : function(){
19031         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
19032         
19033             
19034         var a = this.ranchor;
19035
19036         var el = Roo.get(a);
19037
19038         if(Roo.isOpera){ // opera render bug ignores the CSS
19039             el.setStyle("text-decoration", "none");
19040         }
19041
19042         el.on("click", this.onClick, this);
19043         el.on("dblclick", this.onDblClick, this);
19044         el.on("contextmenu", this.onContextMenu, this);
19045         
19046     },
19047     
19048     /*onSelectedChange : function(state){
19049         if(state){
19050             this.focus();
19051             this.addClass("x-tree-selected");
19052         }else{
19053             //this.blur();
19054             this.removeClass("x-tree-selected");
19055         }
19056     },*/
19057     addClass : function(cls){
19058         if(this.elRow){
19059             Roo.fly(this.elRow).addClass(cls);
19060         }
19061         
19062     },
19063     
19064     
19065     removeClass : function(cls){
19066         if(this.elRow){
19067             Roo.fly(this.elRow).removeClass(cls);
19068         }
19069     }
19070
19071     
19072     
19073 });//<Script type="text/javascript">
19074
19075 /*
19076  * Based on:
19077  * Ext JS Library 1.1.1
19078  * Copyright(c) 2006-2007, Ext JS, LLC.
19079  *
19080  * Originally Released Under LGPL - original licence link has changed is not relivant.
19081  *
19082  * Fork - LGPL
19083  * <script type="text/javascript">
19084  */
19085  
19086
19087 /**
19088  * @class Roo.tree.ColumnTree
19089  * @extends Roo.data.TreePanel
19090  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
19091  * @cfg {int} borderWidth  compined right/left border allowance
19092  * @constructor
19093  * @param {String/HTMLElement/Element} el The container element
19094  * @param {Object} config
19095  */
19096 Roo.tree.ColumnTree =  function(el, config)
19097 {
19098    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
19099    this.addEvents({
19100         /**
19101         * @event resize
19102         * Fire this event on a container when it resizes
19103         * @param {int} w Width
19104         * @param {int} h Height
19105         */
19106        "resize" : true
19107     });
19108     this.on('resize', this.onResize, this);
19109 };
19110
19111 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
19112     //lines:false,
19113     
19114     
19115     borderWidth: Roo.isBorderBox ? 0 : 2, 
19116     headEls : false,
19117     
19118     render : function(){
19119         // add the header.....
19120        
19121         Roo.tree.ColumnTree.superclass.render.apply(this);
19122         
19123         this.el.addClass('x-column-tree');
19124         
19125         this.headers = this.el.createChild(
19126             {cls:'x-tree-headers'},this.innerCt.dom);
19127    
19128         var cols = this.columns, c;
19129         var totalWidth = 0;
19130         this.headEls = [];
19131         var  len = cols.length;
19132         for(var i = 0; i < len; i++){
19133              c = cols[i];
19134              totalWidth += c.width;
19135             this.headEls.push(this.headers.createChild({
19136                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
19137                  cn: {
19138                      cls:'x-tree-hd-text',
19139                      html: c.header
19140                  },
19141                  style:'width:'+(c.width-this.borderWidth)+'px;'
19142              }));
19143         }
19144         this.headers.createChild({cls:'x-clear'});
19145         // prevent floats from wrapping when clipped
19146         this.headers.setWidth(totalWidth);
19147         //this.innerCt.setWidth(totalWidth);
19148         this.innerCt.setStyle({ overflow: 'auto' });
19149         this.onResize(this.width, this.height);
19150              
19151         
19152     },
19153     onResize : function(w,h)
19154     {
19155         this.height = h;
19156         this.width = w;
19157         // resize cols..
19158         this.innerCt.setWidth(this.width);
19159         this.innerCt.setHeight(this.height-20);
19160         
19161         // headers...
19162         var cols = this.columns, c;
19163         var totalWidth = 0;
19164         var expEl = false;
19165         var len = cols.length;
19166         for(var i = 0; i < len; i++){
19167             c = cols[i];
19168             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
19169                 // it's the expander..
19170                 expEl  = this.headEls[i];
19171                 continue;
19172             }
19173             totalWidth += c.width;
19174             
19175         }
19176         if (expEl) {
19177             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
19178         }
19179         this.headers.setWidth(w-20);
19180
19181         
19182         
19183         
19184     }
19185 });
19186 /*
19187  * Based on:
19188  * Ext JS Library 1.1.1
19189  * Copyright(c) 2006-2007, Ext JS, LLC.
19190  *
19191  * Originally Released Under LGPL - original licence link has changed is not relivant.
19192  *
19193  * Fork - LGPL
19194  * <script type="text/javascript">
19195  */
19196  
19197 /**
19198  * @class Roo.menu.Menu
19199  * @extends Roo.util.Observable
19200  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
19201  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
19202  * @constructor
19203  * Creates a new Menu
19204  * @param {Object} config Configuration options
19205  */
19206 Roo.menu.Menu = function(config){
19207     Roo.apply(this, config);
19208     this.id = this.id || Roo.id();
19209     this.addEvents({
19210         /**
19211          * @event beforeshow
19212          * Fires before this menu is displayed
19213          * @param {Roo.menu.Menu} this
19214          */
19215         beforeshow : true,
19216         /**
19217          * @event beforehide
19218          * Fires before this menu is hidden
19219          * @param {Roo.menu.Menu} this
19220          */
19221         beforehide : true,
19222         /**
19223          * @event show
19224          * Fires after this menu is displayed
19225          * @param {Roo.menu.Menu} this
19226          */
19227         show : true,
19228         /**
19229          * @event hide
19230          * Fires after this menu is hidden
19231          * @param {Roo.menu.Menu} this
19232          */
19233         hide : true,
19234         /**
19235          * @event click
19236          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19237          * @param {Roo.menu.Menu} this
19238          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19239          * @param {Roo.EventObject} e
19240          */
19241         click : true,
19242         /**
19243          * @event mouseover
19244          * Fires when the mouse is hovering over this menu
19245          * @param {Roo.menu.Menu} this
19246          * @param {Roo.EventObject} e
19247          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19248          */
19249         mouseover : true,
19250         /**
19251          * @event mouseout
19252          * Fires when the mouse exits this menu
19253          * @param {Roo.menu.Menu} this
19254          * @param {Roo.EventObject} e
19255          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19256          */
19257         mouseout : true,
19258         /**
19259          * @event itemclick
19260          * Fires when a menu item contained in this menu is clicked
19261          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
19262          * @param {Roo.EventObject} e
19263          */
19264         itemclick: true
19265     });
19266     if (this.registerMenu) {
19267         Roo.menu.MenuMgr.register(this);
19268     }
19269     
19270     var mis = this.items;
19271     this.items = new Roo.util.MixedCollection();
19272     if(mis){
19273         this.add.apply(this, mis);
19274     }
19275 };
19276
19277 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
19278     /**
19279      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
19280      */
19281     minWidth : 120,
19282     /**
19283      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
19284      * for bottom-right shadow (defaults to "sides")
19285      */
19286     shadow : "sides",
19287     /**
19288      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
19289      * this menu (defaults to "tl-tr?")
19290      */
19291     subMenuAlign : "tl-tr?",
19292     /**
19293      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
19294      * relative to its element of origin (defaults to "tl-bl?")
19295      */
19296     defaultAlign : "tl-bl?",
19297     /**
19298      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
19299      */
19300     allowOtherMenus : false,
19301     /**
19302      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
19303      */
19304     registerMenu : true,
19305
19306     hidden:true,
19307
19308     // private
19309     render : function(){
19310         if(this.el){
19311             return;
19312         }
19313         var el = this.el = new Roo.Layer({
19314             cls: "x-menu",
19315             shadow:this.shadow,
19316             constrain: false,
19317             parentEl: this.parentEl || document.body,
19318             zindex:15000
19319         });
19320
19321         this.keyNav = new Roo.menu.MenuNav(this);
19322
19323         if(this.plain){
19324             el.addClass("x-menu-plain");
19325         }
19326         if(this.cls){
19327             el.addClass(this.cls);
19328         }
19329         // generic focus element
19330         this.focusEl = el.createChild({
19331             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
19332         });
19333         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
19334         ul.on("click", this.onClick, this);
19335         ul.on("mouseover", this.onMouseOver, this);
19336         ul.on("mouseout", this.onMouseOut, this);
19337         this.items.each(function(item){
19338             var li = document.createElement("li");
19339             li.className = "x-menu-list-item";
19340             ul.dom.appendChild(li);
19341             item.render(li, this);
19342         }, this);
19343         this.ul = ul;
19344         this.autoWidth();
19345     },
19346
19347     // private
19348     autoWidth : function(){
19349         var el = this.el, ul = this.ul;
19350         if(!el){
19351             return;
19352         }
19353         var w = this.width;
19354         if(w){
19355             el.setWidth(w);
19356         }else if(Roo.isIE){
19357             el.setWidth(this.minWidth);
19358             var t = el.dom.offsetWidth; // force recalc
19359             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
19360         }
19361     },
19362
19363     // private
19364     delayAutoWidth : function(){
19365         if(this.rendered){
19366             if(!this.awTask){
19367                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
19368             }
19369             this.awTask.delay(20);
19370         }
19371     },
19372
19373     // private
19374     findTargetItem : function(e){
19375         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
19376         if(t && t.menuItemId){
19377             return this.items.get(t.menuItemId);
19378         }
19379     },
19380
19381     // private
19382     onClick : function(e){
19383         var t;
19384         if(t = this.findTargetItem(e)){
19385             t.onClick(e);
19386             this.fireEvent("click", this, t, e);
19387         }
19388     },
19389
19390     // private
19391     setActiveItem : function(item, autoExpand){
19392         if(item != this.activeItem){
19393             if(this.activeItem){
19394                 this.activeItem.deactivate();
19395             }
19396             this.activeItem = item;
19397             item.activate(autoExpand);
19398         }else if(autoExpand){
19399             item.expandMenu();
19400         }
19401     },
19402
19403     // private
19404     tryActivate : function(start, step){
19405         var items = this.items;
19406         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
19407             var item = items.get(i);
19408             if(!item.disabled && item.canActivate){
19409                 this.setActiveItem(item, false);
19410                 return item;
19411             }
19412         }
19413         return false;
19414     },
19415
19416     // private
19417     onMouseOver : function(e){
19418         var t;
19419         if(t = this.findTargetItem(e)){
19420             if(t.canActivate && !t.disabled){
19421                 this.setActiveItem(t, true);
19422             }
19423         }
19424         this.fireEvent("mouseover", this, e, t);
19425     },
19426
19427     // private
19428     onMouseOut : function(e){
19429         var t;
19430         if(t = this.findTargetItem(e)){
19431             if(t == this.activeItem && t.shouldDeactivate(e)){
19432                 this.activeItem.deactivate();
19433                 delete this.activeItem;
19434             }
19435         }
19436         this.fireEvent("mouseout", this, e, t);
19437     },
19438
19439     /**
19440      * Read-only.  Returns true if the menu is currently displayed, else false.
19441      * @type Boolean
19442      */
19443     isVisible : function(){
19444         return this.el && !this.hidden;
19445     },
19446
19447     /**
19448      * Displays this menu relative to another element
19449      * @param {String/HTMLElement/Roo.Element} element The element to align to
19450      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
19451      * the element (defaults to this.defaultAlign)
19452      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19453      */
19454     show : function(el, pos, parentMenu){
19455         this.parentMenu = parentMenu;
19456         if(!this.el){
19457             this.render();
19458         }
19459         this.fireEvent("beforeshow", this);
19460         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
19461     },
19462
19463     /**
19464      * Displays this menu at a specific xy position
19465      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
19466      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19467      */
19468     showAt : function(xy, parentMenu, /* private: */_e){
19469         this.parentMenu = parentMenu;
19470         if(!this.el){
19471             this.render();
19472         }
19473         if(_e !== false){
19474             this.fireEvent("beforeshow", this);
19475             xy = this.el.adjustForConstraints(xy);
19476         }
19477         this.el.setXY(xy);
19478         this.el.show();
19479         this.hidden = false;
19480         this.focus();
19481         this.fireEvent("show", this);
19482     },
19483
19484     focus : function(){
19485         if(!this.hidden){
19486             this.doFocus.defer(50, this);
19487         }
19488     },
19489
19490     doFocus : function(){
19491         if(!this.hidden){
19492             this.focusEl.focus();
19493         }
19494     },
19495
19496     /**
19497      * Hides this menu and optionally all parent menus
19498      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
19499      */
19500     hide : function(deep){
19501         if(this.el && this.isVisible()){
19502             this.fireEvent("beforehide", this);
19503             if(this.activeItem){
19504                 this.activeItem.deactivate();
19505                 this.activeItem = null;
19506             }
19507             this.el.hide();
19508             this.hidden = true;
19509             this.fireEvent("hide", this);
19510         }
19511         if(deep === true && this.parentMenu){
19512             this.parentMenu.hide(true);
19513         }
19514     },
19515
19516     /**
19517      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
19518      * Any of the following are valid:
19519      * <ul>
19520      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
19521      * <li>An HTMLElement object which will be converted to a menu item</li>
19522      * <li>A menu item config object that will be created as a new menu item</li>
19523      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
19524      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
19525      * </ul>
19526      * Usage:
19527      * <pre><code>
19528 // Create the menu
19529 var menu = new Roo.menu.Menu();
19530
19531 // Create a menu item to add by reference
19532 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
19533
19534 // Add a bunch of items at once using different methods.
19535 // Only the last item added will be returned.
19536 var item = menu.add(
19537     menuItem,                // add existing item by ref
19538     'Dynamic Item',          // new TextItem
19539     '-',                     // new separator
19540     { text: 'Config Item' }  // new item by config
19541 );
19542 </code></pre>
19543      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
19544      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
19545      */
19546     add : function(){
19547         var a = arguments, l = a.length, item;
19548         for(var i = 0; i < l; i++){
19549             var el = a[i];
19550             if ((typeof(el) == "object") && el.xtype && el.xns) {
19551                 el = Roo.factory(el, Roo.menu);
19552             }
19553             
19554             if(el.render){ // some kind of Item
19555                 item = this.addItem(el);
19556             }else if(typeof el == "string"){ // string
19557                 if(el == "separator" || el == "-"){
19558                     item = this.addSeparator();
19559                 }else{
19560                     item = this.addText(el);
19561                 }
19562             }else if(el.tagName || el.el){ // element
19563                 item = this.addElement(el);
19564             }else if(typeof el == "object"){ // must be menu item config?
19565                 item = this.addMenuItem(el);
19566             }
19567         }
19568         return item;
19569     },
19570
19571     /**
19572      * Returns this menu's underlying {@link Roo.Element} object
19573      * @return {Roo.Element} The element
19574      */
19575     getEl : function(){
19576         if(!this.el){
19577             this.render();
19578         }
19579         return this.el;
19580     },
19581
19582     /**
19583      * Adds a separator bar to the menu
19584      * @return {Roo.menu.Item} The menu item that was added
19585      */
19586     addSeparator : function(){
19587         return this.addItem(new Roo.menu.Separator());
19588     },
19589
19590     /**
19591      * Adds an {@link Roo.Element} object to the menu
19592      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
19593      * @return {Roo.menu.Item} The menu item that was added
19594      */
19595     addElement : function(el){
19596         return this.addItem(new Roo.menu.BaseItem(el));
19597     },
19598
19599     /**
19600      * Adds an existing object based on {@link Roo.menu.Item} to the menu
19601      * @param {Roo.menu.Item} item The menu item to add
19602      * @return {Roo.menu.Item} The menu item that was added
19603      */
19604     addItem : function(item){
19605         this.items.add(item);
19606         if(this.ul){
19607             var li = document.createElement("li");
19608             li.className = "x-menu-list-item";
19609             this.ul.dom.appendChild(li);
19610             item.render(li, this);
19611             this.delayAutoWidth();
19612         }
19613         return item;
19614     },
19615
19616     /**
19617      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
19618      * @param {Object} config A MenuItem config object
19619      * @return {Roo.menu.Item} The menu item that was added
19620      */
19621     addMenuItem : function(config){
19622         if(!(config instanceof Roo.menu.Item)){
19623             if(typeof config.checked == "boolean"){ // must be check menu item config?
19624                 config = new Roo.menu.CheckItem(config);
19625             }else{
19626                 config = new Roo.menu.Item(config);
19627             }
19628         }
19629         return this.addItem(config);
19630     },
19631
19632     /**
19633      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
19634      * @param {String} text The text to display in the menu item
19635      * @return {Roo.menu.Item} The menu item that was added
19636      */
19637     addText : function(text){
19638         return this.addItem(new Roo.menu.TextItem({ text : text }));
19639     },
19640
19641     /**
19642      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
19643      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
19644      * @param {Roo.menu.Item} item The menu item to add
19645      * @return {Roo.menu.Item} The menu item that was added
19646      */
19647     insert : function(index, item){
19648         this.items.insert(index, item);
19649         if(this.ul){
19650             var li = document.createElement("li");
19651             li.className = "x-menu-list-item";
19652             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
19653             item.render(li, this);
19654             this.delayAutoWidth();
19655         }
19656         return item;
19657     },
19658
19659     /**
19660      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
19661      * @param {Roo.menu.Item} item The menu item to remove
19662      */
19663     remove : function(item){
19664         this.items.removeKey(item.id);
19665         item.destroy();
19666     },
19667
19668     /**
19669      * Removes and destroys all items in the menu
19670      */
19671     removeAll : function(){
19672         var f;
19673         while(f = this.items.first()){
19674             this.remove(f);
19675         }
19676     }
19677 });
19678
19679 // MenuNav is a private utility class used internally by the Menu
19680 Roo.menu.MenuNav = function(menu){
19681     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
19682     this.scope = this.menu = menu;
19683 };
19684
19685 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
19686     doRelay : function(e, h){
19687         var k = e.getKey();
19688         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
19689             this.menu.tryActivate(0, 1);
19690             return false;
19691         }
19692         return h.call(this.scope || this, e, this.menu);
19693     },
19694
19695     up : function(e, m){
19696         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
19697             m.tryActivate(m.items.length-1, -1);
19698         }
19699     },
19700
19701     down : function(e, m){
19702         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
19703             m.tryActivate(0, 1);
19704         }
19705     },
19706
19707     right : function(e, m){
19708         if(m.activeItem){
19709             m.activeItem.expandMenu(true);
19710         }
19711     },
19712
19713     left : function(e, m){
19714         m.hide();
19715         if(m.parentMenu && m.parentMenu.activeItem){
19716             m.parentMenu.activeItem.activate();
19717         }
19718     },
19719
19720     enter : function(e, m){
19721         if(m.activeItem){
19722             e.stopPropagation();
19723             m.activeItem.onClick(e);
19724             m.fireEvent("click", this, m.activeItem);
19725             return true;
19726         }
19727     }
19728 });/*
19729  * Based on:
19730  * Ext JS Library 1.1.1
19731  * Copyright(c) 2006-2007, Ext JS, LLC.
19732  *
19733  * Originally Released Under LGPL - original licence link has changed is not relivant.
19734  *
19735  * Fork - LGPL
19736  * <script type="text/javascript">
19737  */
19738  
19739 /**
19740  * @class Roo.menu.MenuMgr
19741  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
19742  * @singleton
19743  */
19744 Roo.menu.MenuMgr = function(){
19745    var menus, active, groups = {}, attached = false, lastShow = new Date();
19746
19747    // private - called when first menu is created
19748    function init(){
19749        menus = {};
19750        active = new Roo.util.MixedCollection();
19751        Roo.get(document).addKeyListener(27, function(){
19752            if(active.length > 0){
19753                hideAll();
19754            }
19755        });
19756    }
19757
19758    // private
19759    function hideAll(){
19760        if(active && active.length > 0){
19761            var c = active.clone();
19762            c.each(function(m){
19763                m.hide();
19764            });
19765        }
19766    }
19767
19768    // private
19769    function onHide(m){
19770        active.remove(m);
19771        if(active.length < 1){
19772            Roo.get(document).un("mousedown", onMouseDown);
19773            attached = false;
19774        }
19775    }
19776
19777    // private
19778    function onShow(m){
19779        var last = active.last();
19780        lastShow = new Date();
19781        active.add(m);
19782        if(!attached){
19783            Roo.get(document).on("mousedown", onMouseDown);
19784            attached = true;
19785        }
19786        if(m.parentMenu){
19787           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
19788           m.parentMenu.activeChild = m;
19789        }else if(last && last.isVisible()){
19790           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
19791        }
19792    }
19793
19794    // private
19795    function onBeforeHide(m){
19796        if(m.activeChild){
19797            m.activeChild.hide();
19798        }
19799        if(m.autoHideTimer){
19800            clearTimeout(m.autoHideTimer);
19801            delete m.autoHideTimer;
19802        }
19803    }
19804
19805    // private
19806    function onBeforeShow(m){
19807        var pm = m.parentMenu;
19808        if(!pm && !m.allowOtherMenus){
19809            hideAll();
19810        }else if(pm && pm.activeChild && active != m){
19811            pm.activeChild.hide();
19812        }
19813    }
19814
19815    // private
19816    function onMouseDown(e){
19817        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
19818            hideAll();
19819        }
19820    }
19821
19822    // private
19823    function onBeforeCheck(mi, state){
19824        if(state){
19825            var g = groups[mi.group];
19826            for(var i = 0, l = g.length; i < l; i++){
19827                if(g[i] != mi){
19828                    g[i].setChecked(false);
19829                }
19830            }
19831        }
19832    }
19833
19834    return {
19835
19836        /**
19837         * Hides all menus that are currently visible
19838         */
19839        hideAll : function(){
19840             hideAll();  
19841        },
19842
19843        // private
19844        register : function(menu){
19845            if(!menus){
19846                init();
19847            }
19848            menus[menu.id] = menu;
19849            menu.on("beforehide", onBeforeHide);
19850            menu.on("hide", onHide);
19851            menu.on("beforeshow", onBeforeShow);
19852            menu.on("show", onShow);
19853            var g = menu.group;
19854            if(g && menu.events["checkchange"]){
19855                if(!groups[g]){
19856                    groups[g] = [];
19857                }
19858                groups[g].push(menu);
19859                menu.on("checkchange", onCheck);
19860            }
19861        },
19862
19863         /**
19864          * Returns a {@link Roo.menu.Menu} object
19865          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
19866          * be used to generate and return a new Menu instance.
19867          */
19868        get : function(menu){
19869            if(typeof menu == "string"){ // menu id
19870                return menus[menu];
19871            }else if(menu.events){  // menu instance
19872                return menu;
19873            }else if(typeof menu.length == 'number'){ // array of menu items?
19874                return new Roo.menu.Menu({items:menu});
19875            }else{ // otherwise, must be a config
19876                return new Roo.menu.Menu(menu);
19877            }
19878        },
19879
19880        // private
19881        unregister : function(menu){
19882            delete menus[menu.id];
19883            menu.un("beforehide", onBeforeHide);
19884            menu.un("hide", onHide);
19885            menu.un("beforeshow", onBeforeShow);
19886            menu.un("show", onShow);
19887            var g = menu.group;
19888            if(g && menu.events["checkchange"]){
19889                groups[g].remove(menu);
19890                menu.un("checkchange", onCheck);
19891            }
19892        },
19893
19894        // private
19895        registerCheckable : function(menuItem){
19896            var g = menuItem.group;
19897            if(g){
19898                if(!groups[g]){
19899                    groups[g] = [];
19900                }
19901                groups[g].push(menuItem);
19902                menuItem.on("beforecheckchange", onBeforeCheck);
19903            }
19904        },
19905
19906        // private
19907        unregisterCheckable : function(menuItem){
19908            var g = menuItem.group;
19909            if(g){
19910                groups[g].remove(menuItem);
19911                menuItem.un("beforecheckchange", onBeforeCheck);
19912            }
19913        }
19914    };
19915 }();/*
19916  * Based on:
19917  * Ext JS Library 1.1.1
19918  * Copyright(c) 2006-2007, Ext JS, LLC.
19919  *
19920  * Originally Released Under LGPL - original licence link has changed is not relivant.
19921  *
19922  * Fork - LGPL
19923  * <script type="text/javascript">
19924  */
19925  
19926
19927 /**
19928  * @class Roo.menu.BaseItem
19929  * @extends Roo.Component
19930  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
19931  * management and base configuration options shared by all menu components.
19932  * @constructor
19933  * Creates a new BaseItem
19934  * @param {Object} config Configuration options
19935  */
19936 Roo.menu.BaseItem = function(config){
19937     Roo.menu.BaseItem.superclass.constructor.call(this, config);
19938
19939     this.addEvents({
19940         /**
19941          * @event click
19942          * Fires when this item is clicked
19943          * @param {Roo.menu.BaseItem} this
19944          * @param {Roo.EventObject} e
19945          */
19946         click: true,
19947         /**
19948          * @event activate
19949          * Fires when this item is activated
19950          * @param {Roo.menu.BaseItem} this
19951          */
19952         activate : true,
19953         /**
19954          * @event deactivate
19955          * Fires when this item is deactivated
19956          * @param {Roo.menu.BaseItem} this
19957          */
19958         deactivate : true
19959     });
19960
19961     if(this.handler){
19962         this.on("click", this.handler, this.scope, true);
19963     }
19964 };
19965
19966 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
19967     /**
19968      * @cfg {Function} handler
19969      * A function that will handle the click event of this menu item (defaults to undefined)
19970      */
19971     /**
19972      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
19973      */
19974     canActivate : false,
19975     /**
19976      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
19977      */
19978     activeClass : "x-menu-item-active",
19979     /**
19980      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
19981      */
19982     hideOnClick : true,
19983     /**
19984      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
19985      */
19986     hideDelay : 100,
19987
19988     // private
19989     ctype: "Roo.menu.BaseItem",
19990
19991     // private
19992     actionMode : "container",
19993
19994     // private
19995     render : function(container, parentMenu){
19996         this.parentMenu = parentMenu;
19997         Roo.menu.BaseItem.superclass.render.call(this, container);
19998         this.container.menuItemId = this.id;
19999     },
20000
20001     // private
20002     onRender : function(container, position){
20003         this.el = Roo.get(this.el);
20004         container.dom.appendChild(this.el.dom);
20005     },
20006
20007     // private
20008     onClick : function(e){
20009         if(!this.disabled && this.fireEvent("click", this, e) !== false
20010                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
20011             this.handleClick(e);
20012         }else{
20013             e.stopEvent();
20014         }
20015     },
20016
20017     // private
20018     activate : function(){
20019         if(this.disabled){
20020             return false;
20021         }
20022         var li = this.container;
20023         li.addClass(this.activeClass);
20024         this.region = li.getRegion().adjust(2, 2, -2, -2);
20025         this.fireEvent("activate", this);
20026         return true;
20027     },
20028
20029     // private
20030     deactivate : function(){
20031         this.container.removeClass(this.activeClass);
20032         this.fireEvent("deactivate", this);
20033     },
20034
20035     // private
20036     shouldDeactivate : function(e){
20037         return !this.region || !this.region.contains(e.getPoint());
20038     },
20039
20040     // private
20041     handleClick : function(e){
20042         if(this.hideOnClick){
20043             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
20044         }
20045     },
20046
20047     // private
20048     expandMenu : function(autoActivate){
20049         // do nothing
20050     },
20051
20052     // private
20053     hideMenu : function(){
20054         // do nothing
20055     }
20056 });/*
20057  * Based on:
20058  * Ext JS Library 1.1.1
20059  * Copyright(c) 2006-2007, Ext JS, LLC.
20060  *
20061  * Originally Released Under LGPL - original licence link has changed is not relivant.
20062  *
20063  * Fork - LGPL
20064  * <script type="text/javascript">
20065  */
20066  
20067 /**
20068  * @class Roo.menu.Adapter
20069  * @extends Roo.menu.BaseItem
20070  * 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.
20071  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
20072  * @constructor
20073  * Creates a new Adapter
20074  * @param {Object} config Configuration options
20075  */
20076 Roo.menu.Adapter = function(component, config){
20077     Roo.menu.Adapter.superclass.constructor.call(this, config);
20078     this.component = component;
20079 };
20080 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
20081     // private
20082     canActivate : true,
20083
20084     // private
20085     onRender : function(container, position){
20086         this.component.render(container);
20087         this.el = this.component.getEl();
20088     },
20089
20090     // private
20091     activate : function(){
20092         if(this.disabled){
20093             return false;
20094         }
20095         this.component.focus();
20096         this.fireEvent("activate", this);
20097         return true;
20098     },
20099
20100     // private
20101     deactivate : function(){
20102         this.fireEvent("deactivate", this);
20103     },
20104
20105     // private
20106     disable : function(){
20107         this.component.disable();
20108         Roo.menu.Adapter.superclass.disable.call(this);
20109     },
20110
20111     // private
20112     enable : function(){
20113         this.component.enable();
20114         Roo.menu.Adapter.superclass.enable.call(this);
20115     }
20116 });/*
20117  * Based on:
20118  * Ext JS Library 1.1.1
20119  * Copyright(c) 2006-2007, Ext JS, LLC.
20120  *
20121  * Originally Released Under LGPL - original licence link has changed is not relivant.
20122  *
20123  * Fork - LGPL
20124  * <script type="text/javascript">
20125  */
20126
20127 /**
20128  * @class Roo.menu.TextItem
20129  * @extends Roo.menu.BaseItem
20130  * Adds a static text string to a menu, usually used as either a heading or group separator.
20131  * Note: old style constructor with text is still supported.
20132  * 
20133  * @constructor
20134  * Creates a new TextItem
20135  * @param {Object} cfg Configuration
20136  */
20137 Roo.menu.TextItem = function(cfg){
20138     if (typeof(cfg) == 'string') {
20139         this.text = cfg;
20140     } else {
20141         Roo.apply(this,cfg);
20142     }
20143     
20144     Roo.menu.TextItem.superclass.constructor.call(this);
20145 };
20146
20147 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
20148     /**
20149      * @cfg {Boolean} text Text to show on item.
20150      */
20151     text : '',
20152     
20153     /**
20154      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20155      */
20156     hideOnClick : false,
20157     /**
20158      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
20159      */
20160     itemCls : "x-menu-text",
20161
20162     // private
20163     onRender : function(){
20164         var s = document.createElement("span");
20165         s.className = this.itemCls;
20166         s.innerHTML = this.text;
20167         this.el = s;
20168         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
20169     }
20170 });/*
20171  * Based on:
20172  * Ext JS Library 1.1.1
20173  * Copyright(c) 2006-2007, Ext JS, LLC.
20174  *
20175  * Originally Released Under LGPL - original licence link has changed is not relivant.
20176  *
20177  * Fork - LGPL
20178  * <script type="text/javascript">
20179  */
20180
20181 /**
20182  * @class Roo.menu.Separator
20183  * @extends Roo.menu.BaseItem
20184  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
20185  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
20186  * @constructor
20187  * @param {Object} config Configuration options
20188  */
20189 Roo.menu.Separator = function(config){
20190     Roo.menu.Separator.superclass.constructor.call(this, config);
20191 };
20192
20193 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
20194     /**
20195      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
20196      */
20197     itemCls : "x-menu-sep",
20198     /**
20199      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20200      */
20201     hideOnClick : false,
20202
20203     // private
20204     onRender : function(li){
20205         var s = document.createElement("span");
20206         s.className = this.itemCls;
20207         s.innerHTML = "&#160;";
20208         this.el = s;
20209         li.addClass("x-menu-sep-li");
20210         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
20211     }
20212 });/*
20213  * Based on:
20214  * Ext JS Library 1.1.1
20215  * Copyright(c) 2006-2007, Ext JS, LLC.
20216  *
20217  * Originally Released Under LGPL - original licence link has changed is not relivant.
20218  *
20219  * Fork - LGPL
20220  * <script type="text/javascript">
20221  */
20222 /**
20223  * @class Roo.menu.Item
20224  * @extends Roo.menu.BaseItem
20225  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
20226  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
20227  * activation and click handling.
20228  * @constructor
20229  * Creates a new Item
20230  * @param {Object} config Configuration options
20231  */
20232 Roo.menu.Item = function(config){
20233     Roo.menu.Item.superclass.constructor.call(this, config);
20234     if(this.menu){
20235         this.menu = Roo.menu.MenuMgr.get(this.menu);
20236     }
20237 };
20238 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
20239     
20240     /**
20241      * @cfg {String} text
20242      * The text to show on the menu item.
20243      */
20244     text: '',
20245      /**
20246      * @cfg {String} HTML to render in menu
20247      * The text to show on the menu item (HTML version).
20248      */
20249     html: '',
20250     /**
20251      * @cfg {String} icon
20252      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
20253      */
20254     icon: undefined,
20255     /**
20256      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
20257      */
20258     itemCls : "x-menu-item",
20259     /**
20260      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
20261      */
20262     canActivate : true,
20263     /**
20264      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
20265      */
20266     showDelay: 200,
20267     // doc'd in BaseItem
20268     hideDelay: 200,
20269
20270     // private
20271     ctype: "Roo.menu.Item",
20272     
20273     // private
20274     onRender : function(container, position){
20275         var el = document.createElement("a");
20276         el.hideFocus = true;
20277         el.unselectable = "on";
20278         el.href = this.href || "#";
20279         if(this.hrefTarget){
20280             el.target = this.hrefTarget;
20281         }
20282         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
20283         
20284         var html = this.html.length ? this.html  : String.format('{0}',this.text);
20285         
20286         el.innerHTML = String.format(
20287                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
20288                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
20289         this.el = el;
20290         Roo.menu.Item.superclass.onRender.call(this, container, position);
20291     },
20292
20293     /**
20294      * Sets the text to display in this menu item
20295      * @param {String} text The text to display
20296      * @param {Boolean} isHTML true to indicate text is pure html.
20297      */
20298     setText : function(text, isHTML){
20299         if (isHTML) {
20300             this.html = text;
20301         } else {
20302             this.text = text;
20303             this.html = '';
20304         }
20305         if(this.rendered){
20306             var html = this.html.length ? this.html  : String.format('{0}',this.text);
20307      
20308             this.el.update(String.format(
20309                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
20310                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
20311             this.parentMenu.autoWidth();
20312         }
20313     },
20314
20315     // private
20316     handleClick : function(e){
20317         if(!this.href){ // if no link defined, stop the event automatically
20318             e.stopEvent();
20319         }
20320         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
20321     },
20322
20323     // private
20324     activate : function(autoExpand){
20325         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
20326             this.focus();
20327             if(autoExpand){
20328                 this.expandMenu();
20329             }
20330         }
20331         return true;
20332     },
20333
20334     // private
20335     shouldDeactivate : function(e){
20336         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
20337             if(this.menu && this.menu.isVisible()){
20338                 return !this.menu.getEl().getRegion().contains(e.getPoint());
20339             }
20340             return true;
20341         }
20342         return false;
20343     },
20344
20345     // private
20346     deactivate : function(){
20347         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
20348         this.hideMenu();
20349     },
20350
20351     // private
20352     expandMenu : function(autoActivate){
20353         if(!this.disabled && this.menu){
20354             clearTimeout(this.hideTimer);
20355             delete this.hideTimer;
20356             if(!this.menu.isVisible() && !this.showTimer){
20357                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
20358             }else if (this.menu.isVisible() && autoActivate){
20359                 this.menu.tryActivate(0, 1);
20360             }
20361         }
20362     },
20363
20364     // private
20365     deferExpand : function(autoActivate){
20366         delete this.showTimer;
20367         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
20368         if(autoActivate){
20369             this.menu.tryActivate(0, 1);
20370         }
20371     },
20372
20373     // private
20374     hideMenu : function(){
20375         clearTimeout(this.showTimer);
20376         delete this.showTimer;
20377         if(!this.hideTimer && this.menu && this.menu.isVisible()){
20378             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
20379         }
20380     },
20381
20382     // private
20383     deferHide : function(){
20384         delete this.hideTimer;
20385         this.menu.hide();
20386     }
20387 });/*
20388  * Based on:
20389  * Ext JS Library 1.1.1
20390  * Copyright(c) 2006-2007, Ext JS, LLC.
20391  *
20392  * Originally Released Under LGPL - original licence link has changed is not relivant.
20393  *
20394  * Fork - LGPL
20395  * <script type="text/javascript">
20396  */
20397  
20398 /**
20399  * @class Roo.menu.CheckItem
20400  * @extends Roo.menu.Item
20401  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
20402  * @constructor
20403  * Creates a new CheckItem
20404  * @param {Object} config Configuration options
20405  */
20406 Roo.menu.CheckItem = function(config){
20407     Roo.menu.CheckItem.superclass.constructor.call(this, config);
20408     this.addEvents({
20409         /**
20410          * @event beforecheckchange
20411          * Fires before the checked value is set, providing an opportunity to cancel if needed
20412          * @param {Roo.menu.CheckItem} this
20413          * @param {Boolean} checked The new checked value that will be set
20414          */
20415         "beforecheckchange" : true,
20416         /**
20417          * @event checkchange
20418          * Fires after the checked value has been set
20419          * @param {Roo.menu.CheckItem} this
20420          * @param {Boolean} checked The checked value that was set
20421          */
20422         "checkchange" : true
20423     });
20424     if(this.checkHandler){
20425         this.on('checkchange', this.checkHandler, this.scope);
20426     }
20427 };
20428 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
20429     /**
20430      * @cfg {String} group
20431      * All check items with the same group name will automatically be grouped into a single-select
20432      * radio button group (defaults to '')
20433      */
20434     /**
20435      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
20436      */
20437     itemCls : "x-menu-item x-menu-check-item",
20438     /**
20439      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
20440      */
20441     groupClass : "x-menu-group-item",
20442
20443     /**
20444      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
20445      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
20446      * initialized with checked = true will be rendered as checked.
20447      */
20448     checked: false,
20449
20450     // private
20451     ctype: "Roo.menu.CheckItem",
20452
20453     // private
20454     onRender : function(c){
20455         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
20456         if(this.group){
20457             this.el.addClass(this.groupClass);
20458         }
20459         Roo.menu.MenuMgr.registerCheckable(this);
20460         if(this.checked){
20461             this.checked = false;
20462             this.setChecked(true, true);
20463         }
20464     },
20465
20466     // private
20467     destroy : function(){
20468         if(this.rendered){
20469             Roo.menu.MenuMgr.unregisterCheckable(this);
20470         }
20471         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
20472     },
20473
20474     /**
20475      * Set the checked state of this item
20476      * @param {Boolean} checked The new checked value
20477      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
20478      */
20479     setChecked : function(state, suppressEvent){
20480         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
20481             if(this.container){
20482                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
20483             }
20484             this.checked = state;
20485             if(suppressEvent !== true){
20486                 this.fireEvent("checkchange", this, state);
20487             }
20488         }
20489     },
20490
20491     // private
20492     handleClick : function(e){
20493        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
20494            this.setChecked(!this.checked);
20495        }
20496        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
20497     }
20498 });/*
20499  * Based on:
20500  * Ext JS Library 1.1.1
20501  * Copyright(c) 2006-2007, Ext JS, LLC.
20502  *
20503  * Originally Released Under LGPL - original licence link has changed is not relivant.
20504  *
20505  * Fork - LGPL
20506  * <script type="text/javascript">
20507  */
20508  
20509 /**
20510  * @class Roo.menu.DateItem
20511  * @extends Roo.menu.Adapter
20512  * A menu item that wraps the {@link Roo.DatPicker} component.
20513  * @constructor
20514  * Creates a new DateItem
20515  * @param {Object} config Configuration options
20516  */
20517 Roo.menu.DateItem = function(config){
20518     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
20519     /** The Roo.DatePicker object @type Roo.DatePicker */
20520     this.picker = this.component;
20521     this.addEvents({select: true});
20522     
20523     this.picker.on("render", function(picker){
20524         picker.getEl().swallowEvent("click");
20525         picker.container.addClass("x-menu-date-item");
20526     });
20527
20528     this.picker.on("select", this.onSelect, this);
20529 };
20530
20531 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
20532     // private
20533     onSelect : function(picker, date){
20534         this.fireEvent("select", this, date, picker);
20535         Roo.menu.DateItem.superclass.handleClick.call(this);
20536     }
20537 });/*
20538  * Based on:
20539  * Ext JS Library 1.1.1
20540  * Copyright(c) 2006-2007, Ext JS, LLC.
20541  *
20542  * Originally Released Under LGPL - original licence link has changed is not relivant.
20543  *
20544  * Fork - LGPL
20545  * <script type="text/javascript">
20546  */
20547  
20548 /**
20549  * @class Roo.menu.ColorItem
20550  * @extends Roo.menu.Adapter
20551  * A menu item that wraps the {@link Roo.ColorPalette} component.
20552  * @constructor
20553  * Creates a new ColorItem
20554  * @param {Object} config Configuration options
20555  */
20556 Roo.menu.ColorItem = function(config){
20557     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
20558     /** The Roo.ColorPalette object @type Roo.ColorPalette */
20559     this.palette = this.component;
20560     this.relayEvents(this.palette, ["select"]);
20561     if(this.selectHandler){
20562         this.on('select', this.selectHandler, this.scope);
20563     }
20564 };
20565 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
20566  * Based on:
20567  * Ext JS Library 1.1.1
20568  * Copyright(c) 2006-2007, Ext JS, LLC.
20569  *
20570  * Originally Released Under LGPL - original licence link has changed is not relivant.
20571  *
20572  * Fork - LGPL
20573  * <script type="text/javascript">
20574  */
20575  
20576
20577 /**
20578  * @class Roo.menu.DateMenu
20579  * @extends Roo.menu.Menu
20580  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
20581  * @constructor
20582  * Creates a new DateMenu
20583  * @param {Object} config Configuration options
20584  */
20585 Roo.menu.DateMenu = function(config){
20586     Roo.menu.DateMenu.superclass.constructor.call(this, config);
20587     this.plain = true;
20588     var di = new Roo.menu.DateItem(config);
20589     this.add(di);
20590     /**
20591      * The {@link Roo.DatePicker} instance for this DateMenu
20592      * @type DatePicker
20593      */
20594     this.picker = di.picker;
20595     /**
20596      * @event select
20597      * @param {DatePicker} picker
20598      * @param {Date} date
20599      */
20600     this.relayEvents(di, ["select"]);
20601
20602     this.on('beforeshow', function(){
20603         if(this.picker){
20604             this.picker.hideMonthPicker(true);
20605         }
20606     }, this);
20607 };
20608 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
20609     cls:'x-date-menu'
20610 });/*
20611  * Based on:
20612  * Ext JS Library 1.1.1
20613  * Copyright(c) 2006-2007, Ext JS, LLC.
20614  *
20615  * Originally Released Under LGPL - original licence link has changed is not relivant.
20616  *
20617  * Fork - LGPL
20618  * <script type="text/javascript">
20619  */
20620  
20621
20622 /**
20623  * @class Roo.menu.ColorMenu
20624  * @extends Roo.menu.Menu
20625  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
20626  * @constructor
20627  * Creates a new ColorMenu
20628  * @param {Object} config Configuration options
20629  */
20630 Roo.menu.ColorMenu = function(config){
20631     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
20632     this.plain = true;
20633     var ci = new Roo.menu.ColorItem(config);
20634     this.add(ci);
20635     /**
20636      * The {@link Roo.ColorPalette} instance for this ColorMenu
20637      * @type ColorPalette
20638      */
20639     this.palette = ci.palette;
20640     /**
20641      * @event select
20642      * @param {ColorPalette} palette
20643      * @param {String} color
20644      */
20645     this.relayEvents(ci, ["select"]);
20646 };
20647 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
20648  * Based on:
20649  * Ext JS Library 1.1.1
20650  * Copyright(c) 2006-2007, Ext JS, LLC.
20651  *
20652  * Originally Released Under LGPL - original licence link has changed is not relivant.
20653  *
20654  * Fork - LGPL
20655  * <script type="text/javascript">
20656  */
20657  
20658 /**
20659  * @class Roo.form.Field
20660  * @extends Roo.BoxComponent
20661  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
20662  * @constructor
20663  * Creates a new Field
20664  * @param {Object} config Configuration options
20665  */
20666 Roo.form.Field = function(config){
20667     Roo.form.Field.superclass.constructor.call(this, config);
20668 };
20669
20670 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
20671     /**
20672      * @cfg {String} fieldLabel Label to use when rendering a form.
20673      */
20674        /**
20675      * @cfg {String} qtip Mouse over tip
20676      */
20677      
20678     /**
20679      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
20680      */
20681     invalidClass : "x-form-invalid",
20682     /**
20683      * @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")
20684      */
20685     invalidText : "The value in this field is invalid",
20686     /**
20687      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
20688      */
20689     focusClass : "x-form-focus",
20690     /**
20691      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
20692       automatic validation (defaults to "keyup").
20693      */
20694     validationEvent : "keyup",
20695     /**
20696      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
20697      */
20698     validateOnBlur : true,
20699     /**
20700      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
20701      */
20702     validationDelay : 250,
20703     /**
20704      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20705      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
20706      */
20707     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
20708     /**
20709      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
20710      */
20711     fieldClass : "x-form-field",
20712     /**
20713      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
20714      *<pre>
20715 Value         Description
20716 -----------   ----------------------------------------------------------------------
20717 qtip          Display a quick tip when the user hovers over the field
20718 title         Display a default browser title attribute popup
20719 under         Add a block div beneath the field containing the error text
20720 side          Add an error icon to the right of the field with a popup on hover
20721 [element id]  Add the error text directly to the innerHTML of the specified element
20722 </pre>
20723      */
20724     msgTarget : 'qtip',
20725     /**
20726      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
20727      */
20728     msgFx : 'normal',
20729
20730     /**
20731      * @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.
20732      */
20733     readOnly : false,
20734
20735     /**
20736      * @cfg {Boolean} disabled True to disable the field (defaults to false).
20737      */
20738     disabled : false,
20739
20740     /**
20741      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
20742      */
20743     inputType : undefined,
20744     
20745     /**
20746      * @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).
20747          */
20748         tabIndex : undefined,
20749         
20750     // private
20751     isFormField : true,
20752
20753     // private
20754     hasFocus : false,
20755     /**
20756      * @property {Roo.Element} fieldEl
20757      * Element Containing the rendered Field (with label etc.)
20758      */
20759     /**
20760      * @cfg {Mixed} value A value to initialize this field with.
20761      */
20762     value : undefined,
20763
20764     /**
20765      * @cfg {String} name The field's HTML name attribute.
20766      */
20767     /**
20768      * @cfg {String} cls A CSS class to apply to the field's underlying element.
20769      */
20770
20771         // private ??
20772         initComponent : function(){
20773         Roo.form.Field.superclass.initComponent.call(this);
20774         this.addEvents({
20775             /**
20776              * @event focus
20777              * Fires when this field receives input focus.
20778              * @param {Roo.form.Field} this
20779              */
20780             focus : true,
20781             /**
20782              * @event blur
20783              * Fires when this field loses input focus.
20784              * @param {Roo.form.Field} this
20785              */
20786             blur : true,
20787             /**
20788              * @event specialkey
20789              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
20790              * {@link Roo.EventObject#getKey} to determine which key was pressed.
20791              * @param {Roo.form.Field} this
20792              * @param {Roo.EventObject} e The event object
20793              */
20794             specialkey : true,
20795             /**
20796              * @event change
20797              * Fires just before the field blurs if the field value has changed.
20798              * @param {Roo.form.Field} this
20799              * @param {Mixed} newValue The new value
20800              * @param {Mixed} oldValue The original value
20801              */
20802             change : true,
20803             /**
20804              * @event invalid
20805              * Fires after the field has been marked as invalid.
20806              * @param {Roo.form.Field} this
20807              * @param {String} msg The validation message
20808              */
20809             invalid : true,
20810             /**
20811              * @event valid
20812              * Fires after the field has been validated with no errors.
20813              * @param {Roo.form.Field} this
20814              */
20815             valid : true
20816         });
20817     },
20818
20819     /**
20820      * Returns the name attribute of the field if available
20821      * @return {String} name The field name
20822      */
20823     getName: function(){
20824          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
20825     },
20826
20827     // private
20828     onRender : function(ct, position){
20829         Roo.form.Field.superclass.onRender.call(this, ct, position);
20830         if(!this.el){
20831             var cfg = this.getAutoCreate();
20832             if(!cfg.name){
20833                 cfg.name = this.name || this.id;
20834             }
20835             if(this.inputType){
20836                 cfg.type = this.inputType;
20837             }
20838             this.el = ct.createChild(cfg, position);
20839         }
20840         var type = this.el.dom.type;
20841         if(type){
20842             if(type == 'password'){
20843                 type = 'text';
20844             }
20845             this.el.addClass('x-form-'+type);
20846         }
20847         if(this.readOnly){
20848             this.el.dom.readOnly = true;
20849         }
20850         if(this.tabIndex !== undefined){
20851             this.el.dom.setAttribute('tabIndex', this.tabIndex);
20852         }
20853
20854         this.el.addClass([this.fieldClass, this.cls]);
20855         this.initValue();
20856     },
20857
20858     /**
20859      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
20860      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
20861      * @return {Roo.form.Field} this
20862      */
20863     applyTo : function(target){
20864         this.allowDomMove = false;
20865         this.el = Roo.get(target);
20866         this.render(this.el.dom.parentNode);
20867         return this;
20868     },
20869
20870     // private
20871     initValue : function(){
20872         if(this.value !== undefined){
20873             this.setValue(this.value);
20874         }else if(this.el.dom.value.length > 0){
20875             this.setValue(this.el.dom.value);
20876         }
20877     },
20878
20879     /**
20880      * Returns true if this field has been changed since it was originally loaded and is not disabled.
20881      */
20882     isDirty : function() {
20883         if(this.disabled) {
20884             return false;
20885         }
20886         return String(this.getValue()) !== String(this.originalValue);
20887     },
20888
20889     // private
20890     afterRender : function(){
20891         Roo.form.Field.superclass.afterRender.call(this);
20892         this.initEvents();
20893     },
20894
20895     // private
20896     fireKey : function(e){
20897         //Roo.log('field ' + e.getKey());
20898         if(e.isNavKeyPress()){
20899             this.fireEvent("specialkey", this, e);
20900         }
20901     },
20902
20903     /**
20904      * Resets the current field value to the originally loaded value and clears any validation messages
20905      */
20906     reset : function(){
20907         this.setValue(this.originalValue);
20908         this.clearInvalid();
20909     },
20910
20911     // private
20912     initEvents : function(){
20913         // safari killled keypress - so keydown is now used..
20914         this.el.on("keydown" , this.fireKey,  this);
20915         this.el.on("focus", this.onFocus,  this);
20916         this.el.on("blur", this.onBlur,  this);
20917
20918         // reference to original value for reset
20919         this.originalValue = this.getValue();
20920     },
20921
20922     // private
20923     onFocus : function(){
20924         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
20925             this.el.addClass(this.focusClass);
20926         }
20927         if(!this.hasFocus){
20928             this.hasFocus = true;
20929             this.startValue = this.getValue();
20930             this.fireEvent("focus", this);
20931         }
20932     },
20933
20934     beforeBlur : Roo.emptyFn,
20935
20936     // private
20937     onBlur : function(){
20938         this.beforeBlur();
20939         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
20940             this.el.removeClass(this.focusClass);
20941         }
20942         this.hasFocus = false;
20943         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
20944             this.validate();
20945         }
20946         var v = this.getValue();
20947         if(String(v) !== String(this.startValue)){
20948             this.fireEvent('change', this, v, this.startValue);
20949         }
20950         this.fireEvent("blur", this);
20951     },
20952
20953     /**
20954      * Returns whether or not the field value is currently valid
20955      * @param {Boolean} preventMark True to disable marking the field invalid
20956      * @return {Boolean} True if the value is valid, else false
20957      */
20958     isValid : function(preventMark){
20959         if(this.disabled){
20960             return true;
20961         }
20962         var restore = this.preventMark;
20963         this.preventMark = preventMark === true;
20964         var v = this.validateValue(this.processValue(this.getRawValue()));
20965         this.preventMark = restore;
20966         return v;
20967     },
20968
20969     /**
20970      * Validates the field value
20971      * @return {Boolean} True if the value is valid, else false
20972      */
20973     validate : function(){
20974         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
20975             this.clearInvalid();
20976             return true;
20977         }
20978         return false;
20979     },
20980
20981     processValue : function(value){
20982         return value;
20983     },
20984
20985     // private
20986     // Subclasses should provide the validation implementation by overriding this
20987     validateValue : function(value){
20988         return true;
20989     },
20990
20991     /**
20992      * Mark this field as invalid
20993      * @param {String} msg The validation message
20994      */
20995     markInvalid : function(msg){
20996         if(!this.rendered || this.preventMark){ // not rendered
20997             return;
20998         }
20999         this.el.addClass(this.invalidClass);
21000         msg = msg || this.invalidText;
21001         switch(this.msgTarget){
21002             case 'qtip':
21003                 this.el.dom.qtip = msg;
21004                 this.el.dom.qclass = 'x-form-invalid-tip';
21005                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
21006                     Roo.QuickTips.enable();
21007                 }
21008                 break;
21009             case 'title':
21010                 this.el.dom.title = msg;
21011                 break;
21012             case 'under':
21013                 if(!this.errorEl){
21014                     var elp = this.el.findParent('.x-form-element', 5, true);
21015                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
21016                     this.errorEl.setWidth(elp.getWidth(true)-20);
21017                 }
21018                 this.errorEl.update(msg);
21019                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
21020                 break;
21021             case 'side':
21022                 if(!this.errorIcon){
21023                     var elp = this.el.findParent('.x-form-element', 5, true);
21024                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
21025                 }
21026                 this.alignErrorIcon();
21027                 this.errorIcon.dom.qtip = msg;
21028                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
21029                 this.errorIcon.show();
21030                 this.on('resize', this.alignErrorIcon, this);
21031                 break;
21032             default:
21033                 var t = Roo.getDom(this.msgTarget);
21034                 t.innerHTML = msg;
21035                 t.style.display = this.msgDisplay;
21036                 break;
21037         }
21038         this.fireEvent('invalid', this, msg);
21039     },
21040
21041     // private
21042     alignErrorIcon : function(){
21043         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
21044     },
21045
21046     /**
21047      * Clear any invalid styles/messages for this field
21048      */
21049     clearInvalid : function(){
21050         if(!this.rendered || this.preventMark){ // not rendered
21051             return;
21052         }
21053         this.el.removeClass(this.invalidClass);
21054         switch(this.msgTarget){
21055             case 'qtip':
21056                 this.el.dom.qtip = '';
21057                 break;
21058             case 'title':
21059                 this.el.dom.title = '';
21060                 break;
21061             case 'under':
21062                 if(this.errorEl){
21063                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
21064                 }
21065                 break;
21066             case 'side':
21067                 if(this.errorIcon){
21068                     this.errorIcon.dom.qtip = '';
21069                     this.errorIcon.hide();
21070                     this.un('resize', this.alignErrorIcon, this);
21071                 }
21072                 break;
21073             default:
21074                 var t = Roo.getDom(this.msgTarget);
21075                 t.innerHTML = '';
21076                 t.style.display = 'none';
21077                 break;
21078         }
21079         this.fireEvent('valid', this);
21080     },
21081
21082     /**
21083      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
21084      * @return {Mixed} value The field value
21085      */
21086     getRawValue : function(){
21087         var v = this.el.getValue();
21088         if(v === this.emptyText){
21089             v = '';
21090         }
21091         return v;
21092     },
21093
21094     /**
21095      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
21096      * @return {Mixed} value The field value
21097      */
21098     getValue : function(){
21099         var v = this.el.getValue();
21100         if(v === this.emptyText || v === undefined){
21101             v = '';
21102         }
21103         return v;
21104     },
21105
21106     /**
21107      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
21108      * @param {Mixed} value The value to set
21109      */
21110     setRawValue : function(v){
21111         return this.el.dom.value = (v === null || v === undefined ? '' : v);
21112     },
21113
21114     /**
21115      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
21116      * @param {Mixed} value The value to set
21117      */
21118     setValue : function(v){
21119         this.value = v;
21120         if(this.rendered){
21121             this.el.dom.value = (v === null || v === undefined ? '' : v);
21122             this.validate();
21123         }
21124     },
21125
21126     adjustSize : function(w, h){
21127         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
21128         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
21129         return s;
21130     },
21131
21132     adjustWidth : function(tag, w){
21133         tag = tag.toLowerCase();
21134         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
21135             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
21136                 if(tag == 'input'){
21137                     return w + 2;
21138                 }
21139                 if(tag = 'textarea'){
21140                     return w-2;
21141                 }
21142             }else if(Roo.isOpera){
21143                 if(tag == 'input'){
21144                     return w + 2;
21145                 }
21146                 if(tag = 'textarea'){
21147                     return w-2;
21148                 }
21149             }
21150         }
21151         return w;
21152     }
21153 });
21154
21155
21156 // anything other than normal should be considered experimental
21157 Roo.form.Field.msgFx = {
21158     normal : {
21159         show: function(msgEl, f){
21160             msgEl.setDisplayed('block');
21161         },
21162
21163         hide : function(msgEl, f){
21164             msgEl.setDisplayed(false).update('');
21165         }
21166     },
21167
21168     slide : {
21169         show: function(msgEl, f){
21170             msgEl.slideIn('t', {stopFx:true});
21171         },
21172
21173         hide : function(msgEl, f){
21174             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
21175         }
21176     },
21177
21178     slideRight : {
21179         show: function(msgEl, f){
21180             msgEl.fixDisplay();
21181             msgEl.alignTo(f.el, 'tl-tr');
21182             msgEl.slideIn('l', {stopFx:true});
21183         },
21184
21185         hide : function(msgEl, f){
21186             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
21187         }
21188     }
21189 };/*
21190  * Based on:
21191  * Ext JS Library 1.1.1
21192  * Copyright(c) 2006-2007, Ext JS, LLC.
21193  *
21194  * Originally Released Under LGPL - original licence link has changed is not relivant.
21195  *
21196  * Fork - LGPL
21197  * <script type="text/javascript">
21198  */
21199  
21200
21201 /**
21202  * @class Roo.form.TextField
21203  * @extends Roo.form.Field
21204  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
21205  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
21206  * @constructor
21207  * Creates a new TextField
21208  * @param {Object} config Configuration options
21209  */
21210 Roo.form.TextField = function(config){
21211     Roo.form.TextField.superclass.constructor.call(this, config);
21212     this.addEvents({
21213         /**
21214          * @event autosize
21215          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
21216          * according to the default logic, but this event provides a hook for the developer to apply additional
21217          * logic at runtime to resize the field if needed.
21218              * @param {Roo.form.Field} this This text field
21219              * @param {Number} width The new field width
21220              */
21221         autosize : true
21222     });
21223 };
21224
21225 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
21226     /**
21227      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
21228      */
21229     grow : false,
21230     /**
21231      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
21232      */
21233     growMin : 30,
21234     /**
21235      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
21236      */
21237     growMax : 800,
21238     /**
21239      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
21240      */
21241     vtype : null,
21242     /**
21243      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
21244      */
21245     maskRe : null,
21246     /**
21247      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
21248      */
21249     disableKeyFilter : false,
21250     /**
21251      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
21252      */
21253     allowBlank : true,
21254     /**
21255      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
21256      */
21257     minLength : 0,
21258     /**
21259      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
21260      */
21261     maxLength : Number.MAX_VALUE,
21262     /**
21263      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
21264      */
21265     minLengthText : "The minimum length for this field is {0}",
21266     /**
21267      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
21268      */
21269     maxLengthText : "The maximum length for this field is {0}",
21270     /**
21271      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
21272      */
21273     selectOnFocus : false,
21274     /**
21275      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
21276      */
21277     blankText : "This field is required",
21278     /**
21279      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
21280      * If available, this function will be called only after the basic validators all return true, and will be passed the
21281      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
21282      */
21283     validator : null,
21284     /**
21285      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
21286      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
21287      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
21288      */
21289     regex : null,
21290     /**
21291      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
21292      */
21293     regexText : "",
21294     /**
21295      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
21296      */
21297     emptyText : null,
21298     /**
21299      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
21300      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
21301      */
21302     emptyClass : 'x-form-empty-field',
21303
21304     // private
21305     initEvents : function(){
21306         Roo.form.TextField.superclass.initEvents.call(this);
21307         if(this.validationEvent == 'keyup'){
21308             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
21309             this.el.on('keyup', this.filterValidation, this);
21310         }
21311         else if(this.validationEvent !== false){
21312             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
21313         }
21314         if(this.selectOnFocus || this.emptyText){
21315             this.on("focus", this.preFocus, this);
21316             if(this.emptyText){
21317                 this.on('blur', this.postBlur, this);
21318                 this.applyEmptyText();
21319             }
21320         }
21321         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
21322             this.el.on("keypress", this.filterKeys, this);
21323         }
21324         if(this.grow){
21325             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
21326             this.el.on("click", this.autoSize,  this);
21327         }
21328     },
21329
21330     processValue : function(value){
21331         if(this.stripCharsRe){
21332             var newValue = value.replace(this.stripCharsRe, '');
21333             if(newValue !== value){
21334                 this.setRawValue(newValue);
21335                 return newValue;
21336             }
21337         }
21338         return value;
21339     },
21340
21341     filterValidation : function(e){
21342         if(!e.isNavKeyPress()){
21343             this.validationTask.delay(this.validationDelay);
21344         }
21345     },
21346
21347     // private
21348     onKeyUp : function(e){
21349         if(!e.isNavKeyPress()){
21350             this.autoSize();
21351         }
21352     },
21353
21354     /**
21355      * Resets the current field value to the originally-loaded value and clears any validation messages.
21356      * Also adds emptyText and emptyClass if the original value was blank.
21357      */
21358     reset : function(){
21359         Roo.form.TextField.superclass.reset.call(this);
21360         this.applyEmptyText();
21361     },
21362
21363     applyEmptyText : function(){
21364         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
21365             this.setRawValue(this.emptyText);
21366             this.el.addClass(this.emptyClass);
21367         }
21368     },
21369
21370     // private
21371     preFocus : function(){
21372         if(this.emptyText){
21373             if(this.el.dom.value == this.emptyText){
21374                 this.setRawValue('');
21375             }
21376             this.el.removeClass(this.emptyClass);
21377         }
21378         if(this.selectOnFocus){
21379             this.el.dom.select();
21380         }
21381     },
21382
21383     // private
21384     postBlur : function(){
21385         this.applyEmptyText();
21386     },
21387
21388     // private
21389     filterKeys : function(e){
21390         var k = e.getKey();
21391         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
21392             return;
21393         }
21394         var c = e.getCharCode(), cc = String.fromCharCode(c);
21395         if(Roo.isIE && (e.isSpecialKey() || !cc)){
21396             return;
21397         }
21398         if(!this.maskRe.test(cc)){
21399             e.stopEvent();
21400         }
21401     },
21402
21403     setValue : function(v){
21404         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
21405             this.el.removeClass(this.emptyClass);
21406         }
21407         Roo.form.TextField.superclass.setValue.apply(this, arguments);
21408         this.applyEmptyText();
21409         this.autoSize();
21410     },
21411
21412     /**
21413      * Validates a value according to the field's validation rules and marks the field as invalid
21414      * if the validation fails
21415      * @param {Mixed} value The value to validate
21416      * @return {Boolean} True if the value is valid, else false
21417      */
21418     validateValue : function(value){
21419         if(value.length < 1 || value === this.emptyText){ // if it's blank
21420              if(this.allowBlank){
21421                 this.clearInvalid();
21422                 return true;
21423              }else{
21424                 this.markInvalid(this.blankText);
21425                 return false;
21426              }
21427         }
21428         if(value.length < this.minLength){
21429             this.markInvalid(String.format(this.minLengthText, this.minLength));
21430             return false;
21431         }
21432         if(value.length > this.maxLength){
21433             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
21434             return false;
21435         }
21436         if(this.vtype){
21437             var vt = Roo.form.VTypes;
21438             if(!vt[this.vtype](value, this)){
21439                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
21440                 return false;
21441             }
21442         }
21443         if(typeof this.validator == "function"){
21444             var msg = this.validator(value);
21445             if(msg !== true){
21446                 this.markInvalid(msg);
21447                 return false;
21448             }
21449         }
21450         if(this.regex && !this.regex.test(value)){
21451             this.markInvalid(this.regexText);
21452             return false;
21453         }
21454         return true;
21455     },
21456
21457     /**
21458      * Selects text in this field
21459      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
21460      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
21461      */
21462     selectText : function(start, end){
21463         var v = this.getRawValue();
21464         if(v.length > 0){
21465             start = start === undefined ? 0 : start;
21466             end = end === undefined ? v.length : end;
21467             var d = this.el.dom;
21468             if(d.setSelectionRange){
21469                 d.setSelectionRange(start, end);
21470             }else if(d.createTextRange){
21471                 var range = d.createTextRange();
21472                 range.moveStart("character", start);
21473                 range.moveEnd("character", v.length-end);
21474                 range.select();
21475             }
21476         }
21477     },
21478
21479     /**
21480      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
21481      * This only takes effect if grow = true, and fires the autosize event.
21482      */
21483     autoSize : function(){
21484         if(!this.grow || !this.rendered){
21485             return;
21486         }
21487         if(!this.metrics){
21488             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
21489         }
21490         var el = this.el;
21491         var v = el.dom.value;
21492         var d = document.createElement('div');
21493         d.appendChild(document.createTextNode(v));
21494         v = d.innerHTML;
21495         d = null;
21496         v += "&#160;";
21497         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
21498         this.el.setWidth(w);
21499         this.fireEvent("autosize", this, w);
21500     }
21501 });/*
21502  * Based on:
21503  * Ext JS Library 1.1.1
21504  * Copyright(c) 2006-2007, Ext JS, LLC.
21505  *
21506  * Originally Released Under LGPL - original licence link has changed is not relivant.
21507  *
21508  * Fork - LGPL
21509  * <script type="text/javascript">
21510  */
21511  
21512 /**
21513  * @class Roo.form.Hidden
21514  * @extends Roo.form.TextField
21515  * Simple Hidden element used on forms 
21516  * 
21517  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
21518  * 
21519  * @constructor
21520  * Creates a new Hidden form element.
21521  * @param {Object} config Configuration options
21522  */
21523
21524
21525
21526 // easy hidden field...
21527 Roo.form.Hidden = function(config){
21528     Roo.form.Hidden.superclass.constructor.call(this, config);
21529 };
21530   
21531 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
21532     fieldLabel:      '',
21533     inputType:      'hidden',
21534     width:          50,
21535     allowBlank:     true,
21536     labelSeparator: '',
21537     hidden:         true,
21538     itemCls :       'x-form-item-display-none'
21539
21540
21541 });
21542
21543
21544 /*
21545  * Based on:
21546  * Ext JS Library 1.1.1
21547  * Copyright(c) 2006-2007, Ext JS, LLC.
21548  *
21549  * Originally Released Under LGPL - original licence link has changed is not relivant.
21550  *
21551  * Fork - LGPL
21552  * <script type="text/javascript">
21553  */
21554  
21555 /**
21556  * @class Roo.form.TriggerField
21557  * @extends Roo.form.TextField
21558  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
21559  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
21560  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
21561  * for which you can provide a custom implementation.  For example:
21562  * <pre><code>
21563 var trigger = new Roo.form.TriggerField();
21564 trigger.onTriggerClick = myTriggerFn;
21565 trigger.applyTo('my-field');
21566 </code></pre>
21567  *
21568  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
21569  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
21570  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
21571  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
21572  * @constructor
21573  * Create a new TriggerField.
21574  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
21575  * to the base TextField)
21576  */
21577 Roo.form.TriggerField = function(config){
21578     this.mimicing = false;
21579     Roo.form.TriggerField.superclass.constructor.call(this, config);
21580 };
21581
21582 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
21583     /**
21584      * @cfg {String} triggerClass A CSS class to apply to the trigger
21585      */
21586     /**
21587      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21588      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
21589      */
21590     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
21591     /**
21592      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
21593      */
21594     hideTrigger:false,
21595
21596     /** @cfg {Boolean} grow @hide */
21597     /** @cfg {Number} growMin @hide */
21598     /** @cfg {Number} growMax @hide */
21599
21600     /**
21601      * @hide 
21602      * @method
21603      */
21604     autoSize: Roo.emptyFn,
21605     // private
21606     monitorTab : true,
21607     // private
21608     deferHeight : true,
21609
21610     
21611     actionMode : 'wrap',
21612     // private
21613     onResize : function(w, h){
21614         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
21615         if(typeof w == 'number'){
21616             var x = w - this.trigger.getWidth();
21617             this.el.setWidth(this.adjustWidth('input', x));
21618             this.trigger.setStyle('left', x+'px');
21619         }
21620     },
21621
21622     // private
21623     adjustSize : Roo.BoxComponent.prototype.adjustSize,
21624
21625     // private
21626     getResizeEl : function(){
21627         return this.wrap;
21628     },
21629
21630     // private
21631     getPositionEl : function(){
21632         return this.wrap;
21633     },
21634
21635     // private
21636     alignErrorIcon : function(){
21637         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
21638     },
21639
21640     // private
21641     onRender : function(ct, position){
21642         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
21643         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
21644         this.trigger = this.wrap.createChild(this.triggerConfig ||
21645                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
21646         if(this.hideTrigger){
21647             this.trigger.setDisplayed(false);
21648         }
21649         this.initTrigger();
21650         if(!this.width){
21651             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
21652         }
21653     },
21654
21655     // private
21656     initTrigger : function(){
21657         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
21658         this.trigger.addClassOnOver('x-form-trigger-over');
21659         this.trigger.addClassOnClick('x-form-trigger-click');
21660     },
21661
21662     // private
21663     onDestroy : function(){
21664         if(this.trigger){
21665             this.trigger.removeAllListeners();
21666             this.trigger.remove();
21667         }
21668         if(this.wrap){
21669             this.wrap.remove();
21670         }
21671         Roo.form.TriggerField.superclass.onDestroy.call(this);
21672     },
21673
21674     // private
21675     onFocus : function(){
21676         Roo.form.TriggerField.superclass.onFocus.call(this);
21677         if(!this.mimicing){
21678             this.wrap.addClass('x-trigger-wrap-focus');
21679             this.mimicing = true;
21680             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
21681             if(this.monitorTab){
21682                 this.el.on("keydown", this.checkTab, this);
21683             }
21684         }
21685     },
21686
21687     // private
21688     checkTab : function(e){
21689         if(e.getKey() == e.TAB){
21690             this.triggerBlur();
21691         }
21692     },
21693
21694     // private
21695     onBlur : function(){
21696         // do nothing
21697     },
21698
21699     // private
21700     mimicBlur : function(e, t){
21701         if(!this.wrap.contains(t) && this.validateBlur()){
21702             this.triggerBlur();
21703         }
21704     },
21705
21706     // private
21707     triggerBlur : function(){
21708         this.mimicing = false;
21709         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
21710         if(this.monitorTab){
21711             this.el.un("keydown", this.checkTab, this);
21712         }
21713         this.wrap.removeClass('x-trigger-wrap-focus');
21714         Roo.form.TriggerField.superclass.onBlur.call(this);
21715     },
21716
21717     // private
21718     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
21719     validateBlur : function(e, t){
21720         return true;
21721     },
21722
21723     // private
21724     onDisable : function(){
21725         Roo.form.TriggerField.superclass.onDisable.call(this);
21726         if(this.wrap){
21727             this.wrap.addClass('x-item-disabled');
21728         }
21729     },
21730
21731     // private
21732     onEnable : function(){
21733         Roo.form.TriggerField.superclass.onEnable.call(this);
21734         if(this.wrap){
21735             this.wrap.removeClass('x-item-disabled');
21736         }
21737     },
21738
21739     // private
21740     onShow : function(){
21741         var ae = this.getActionEl();
21742         
21743         if(ae){
21744             ae.dom.style.display = '';
21745             ae.dom.style.visibility = 'visible';
21746         }
21747     },
21748
21749     // private
21750     
21751     onHide : function(){
21752         var ae = this.getActionEl();
21753         ae.dom.style.display = 'none';
21754     },
21755
21756     /**
21757      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
21758      * by an implementing function.
21759      * @method
21760      * @param {EventObject} e
21761      */
21762     onTriggerClick : Roo.emptyFn
21763 });
21764
21765 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
21766 // to be extended by an implementing class.  For an example of implementing this class, see the custom
21767 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
21768 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
21769     initComponent : function(){
21770         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
21771
21772         this.triggerConfig = {
21773             tag:'span', cls:'x-form-twin-triggers', cn:[
21774             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
21775             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
21776         ]};
21777     },
21778
21779     getTrigger : function(index){
21780         return this.triggers[index];
21781     },
21782
21783     initTrigger : function(){
21784         var ts = this.trigger.select('.x-form-trigger', true);
21785         this.wrap.setStyle('overflow', 'hidden');
21786         var triggerField = this;
21787         ts.each(function(t, all, index){
21788             t.hide = function(){
21789                 var w = triggerField.wrap.getWidth();
21790                 this.dom.style.display = 'none';
21791                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21792             };
21793             t.show = function(){
21794                 var w = triggerField.wrap.getWidth();
21795                 this.dom.style.display = '';
21796                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21797             };
21798             var triggerIndex = 'Trigger'+(index+1);
21799
21800             if(this['hide'+triggerIndex]){
21801                 t.dom.style.display = 'none';
21802             }
21803             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
21804             t.addClassOnOver('x-form-trigger-over');
21805             t.addClassOnClick('x-form-trigger-click');
21806         }, this);
21807         this.triggers = ts.elements;
21808     },
21809
21810     onTrigger1Click : Roo.emptyFn,
21811     onTrigger2Click : Roo.emptyFn
21812 });/*
21813  * Based on:
21814  * Ext JS Library 1.1.1
21815  * Copyright(c) 2006-2007, Ext JS, LLC.
21816  *
21817  * Originally Released Under LGPL - original licence link has changed is not relivant.
21818  *
21819  * Fork - LGPL
21820  * <script type="text/javascript">
21821  */
21822  
21823 /**
21824  * @class Roo.form.TextArea
21825  * @extends Roo.form.TextField
21826  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
21827  * support for auto-sizing.
21828  * @constructor
21829  * Creates a new TextArea
21830  * @param {Object} config Configuration options
21831  */
21832 Roo.form.TextArea = function(config){
21833     Roo.form.TextArea.superclass.constructor.call(this, config);
21834     // these are provided exchanges for backwards compat
21835     // minHeight/maxHeight were replaced by growMin/growMax to be
21836     // compatible with TextField growing config values
21837     if(this.minHeight !== undefined){
21838         this.growMin = this.minHeight;
21839     }
21840     if(this.maxHeight !== undefined){
21841         this.growMax = this.maxHeight;
21842     }
21843 };
21844
21845 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
21846     /**
21847      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
21848      */
21849     growMin : 60,
21850     /**
21851      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
21852      */
21853     growMax: 1000,
21854     /**
21855      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
21856      * in the field (equivalent to setting overflow: hidden, defaults to false)
21857      */
21858     preventScrollbars: false,
21859     /**
21860      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21861      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
21862      */
21863
21864     // private
21865     onRender : function(ct, position){
21866         if(!this.el){
21867             this.defaultAutoCreate = {
21868                 tag: "textarea",
21869                 style:"width:300px;height:60px;",
21870                 autocomplete: "off"
21871             };
21872         }
21873         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
21874         if(this.grow){
21875             this.textSizeEl = Roo.DomHelper.append(document.body, {
21876                 tag: "pre", cls: "x-form-grow-sizer"
21877             });
21878             if(this.preventScrollbars){
21879                 this.el.setStyle("overflow", "hidden");
21880             }
21881             this.el.setHeight(this.growMin);
21882         }
21883     },
21884
21885     onDestroy : function(){
21886         if(this.textSizeEl){
21887             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
21888         }
21889         Roo.form.TextArea.superclass.onDestroy.call(this);
21890     },
21891
21892     // private
21893     onKeyUp : function(e){
21894         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
21895             this.autoSize();
21896         }
21897     },
21898
21899     /**
21900      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
21901      * This only takes effect if grow = true, and fires the autosize event if the height changes.
21902      */
21903     autoSize : function(){
21904         if(!this.grow || !this.textSizeEl){
21905             return;
21906         }
21907         var el = this.el;
21908         var v = el.dom.value;
21909         var ts = this.textSizeEl;
21910
21911         ts.innerHTML = '';
21912         ts.appendChild(document.createTextNode(v));
21913         v = ts.innerHTML;
21914
21915         Roo.fly(ts).setWidth(this.el.getWidth());
21916         if(v.length < 1){
21917             v = "&#160;&#160;";
21918         }else{
21919             if(Roo.isIE){
21920                 v = v.replace(/\n/g, '<p>&#160;</p>');
21921             }
21922             v += "&#160;\n&#160;";
21923         }
21924         ts.innerHTML = v;
21925         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
21926         if(h != this.lastHeight){
21927             this.lastHeight = h;
21928             this.el.setHeight(h);
21929             this.fireEvent("autosize", this, h);
21930         }
21931     }
21932 });/*
21933  * Based on:
21934  * Ext JS Library 1.1.1
21935  * Copyright(c) 2006-2007, Ext JS, LLC.
21936  *
21937  * Originally Released Under LGPL - original licence link has changed is not relivant.
21938  *
21939  * Fork - LGPL
21940  * <script type="text/javascript">
21941  */
21942  
21943
21944 /**
21945  * @class Roo.form.NumberField
21946  * @extends Roo.form.TextField
21947  * Numeric text field that provides automatic keystroke filtering and numeric validation.
21948  * @constructor
21949  * Creates a new NumberField
21950  * @param {Object} config Configuration options
21951  */
21952 Roo.form.NumberField = function(config){
21953     Roo.form.NumberField.superclass.constructor.call(this, config);
21954 };
21955
21956 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
21957     /**
21958      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
21959      */
21960     fieldClass: "x-form-field x-form-num-field",
21961     /**
21962      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
21963      */
21964     allowDecimals : true,
21965     /**
21966      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
21967      */
21968     decimalSeparator : ".",
21969     /**
21970      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
21971      */
21972     decimalPrecision : 2,
21973     /**
21974      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
21975      */
21976     allowNegative : true,
21977     /**
21978      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
21979      */
21980     minValue : Number.NEGATIVE_INFINITY,
21981     /**
21982      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
21983      */
21984     maxValue : Number.MAX_VALUE,
21985     /**
21986      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
21987      */
21988     minText : "The minimum value for this field is {0}",
21989     /**
21990      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
21991      */
21992     maxText : "The maximum value for this field is {0}",
21993     /**
21994      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
21995      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
21996      */
21997     nanText : "{0} is not a valid number",
21998
21999     // private
22000     initEvents : function(){
22001         Roo.form.NumberField.superclass.initEvents.call(this);
22002         var allowed = "0123456789";
22003         if(this.allowDecimals){
22004             allowed += this.decimalSeparator;
22005         }
22006         if(this.allowNegative){
22007             allowed += "-";
22008         }
22009         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
22010         var keyPress = function(e){
22011             var k = e.getKey();
22012             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
22013                 return;
22014             }
22015             var c = e.getCharCode();
22016             if(allowed.indexOf(String.fromCharCode(c)) === -1){
22017                 e.stopEvent();
22018             }
22019         };
22020         this.el.on("keypress", keyPress, this);
22021     },
22022
22023     // private
22024     validateValue : function(value){
22025         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
22026             return false;
22027         }
22028         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22029              return true;
22030         }
22031         var num = this.parseValue(value);
22032         if(isNaN(num)){
22033             this.markInvalid(String.format(this.nanText, value));
22034             return false;
22035         }
22036         if(num < this.minValue){
22037             this.markInvalid(String.format(this.minText, this.minValue));
22038             return false;
22039         }
22040         if(num > this.maxValue){
22041             this.markInvalid(String.format(this.maxText, this.maxValue));
22042             return false;
22043         }
22044         return true;
22045     },
22046
22047     getValue : function(){
22048         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
22049     },
22050
22051     // private
22052     parseValue : function(value){
22053         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
22054         return isNaN(value) ? '' : value;
22055     },
22056
22057     // private
22058     fixPrecision : function(value){
22059         var nan = isNaN(value);
22060         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
22061             return nan ? '' : value;
22062         }
22063         return parseFloat(value).toFixed(this.decimalPrecision);
22064     },
22065
22066     setValue : function(v){
22067         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
22068     },
22069
22070     // private
22071     decimalPrecisionFcn : function(v){
22072         return Math.floor(v);
22073     },
22074
22075     beforeBlur : function(){
22076         var v = this.parseValue(this.getRawValue());
22077         if(v){
22078             this.setValue(this.fixPrecision(v));
22079         }
22080     }
22081 });/*
22082  * Based on:
22083  * Ext JS Library 1.1.1
22084  * Copyright(c) 2006-2007, Ext JS, LLC.
22085  *
22086  * Originally Released Under LGPL - original licence link has changed is not relivant.
22087  *
22088  * Fork - LGPL
22089  * <script type="text/javascript">
22090  */
22091  
22092 /**
22093  * @class Roo.form.DateField
22094  * @extends Roo.form.TriggerField
22095  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22096 * @constructor
22097 * Create a new DateField
22098 * @param {Object} config
22099  */
22100 Roo.form.DateField = function(config){
22101     Roo.form.DateField.superclass.constructor.call(this, config);
22102     
22103       this.addEvents({
22104          
22105         /**
22106          * @event select
22107          * Fires when a date is selected
22108              * @param {Roo.form.DateField} combo This combo box
22109              * @param {Date} date The date selected
22110              */
22111         'select' : true
22112          
22113     });
22114     
22115     
22116     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22117     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22118     this.ddMatch = null;
22119     if(this.disabledDates){
22120         var dd = this.disabledDates;
22121         var re = "(?:";
22122         for(var i = 0; i < dd.length; i++){
22123             re += dd[i];
22124             if(i != dd.length-1) re += "|";
22125         }
22126         this.ddMatch = new RegExp(re + ")");
22127     }
22128 };
22129
22130 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
22131     /**
22132      * @cfg {String} format
22133      * The default date format string which can be overriden for localization support.  The format must be
22134      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22135      */
22136     format : "m/d/y",
22137     /**
22138      * @cfg {String} altFormats
22139      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22140      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22141      */
22142     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22143     /**
22144      * @cfg {Array} disabledDays
22145      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22146      */
22147     disabledDays : null,
22148     /**
22149      * @cfg {String} disabledDaysText
22150      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22151      */
22152     disabledDaysText : "Disabled",
22153     /**
22154      * @cfg {Array} disabledDates
22155      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22156      * expression so they are very powerful. Some examples:
22157      * <ul>
22158      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22159      * <li>["03/08", "09/16"] would disable those days for every year</li>
22160      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22161      * <li>["03/../2006"] would disable every day in March 2006</li>
22162      * <li>["^03"] would disable every day in every March</li>
22163      * </ul>
22164      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22165      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22166      */
22167     disabledDates : null,
22168     /**
22169      * @cfg {String} disabledDatesText
22170      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22171      */
22172     disabledDatesText : "Disabled",
22173     /**
22174      * @cfg {Date/String} minValue
22175      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22176      * valid format (defaults to null).
22177      */
22178     minValue : null,
22179     /**
22180      * @cfg {Date/String} maxValue
22181      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22182      * valid format (defaults to null).
22183      */
22184     maxValue : null,
22185     /**
22186      * @cfg {String} minText
22187      * The error text to display when the date in the cell is before minValue (defaults to
22188      * 'The date in this field must be after {minValue}').
22189      */
22190     minText : "The date in this field must be equal to or after {0}",
22191     /**
22192      * @cfg {String} maxText
22193      * The error text to display when the date in the cell is after maxValue (defaults to
22194      * 'The date in this field must be before {maxValue}').
22195      */
22196     maxText : "The date in this field must be equal to or before {0}",
22197     /**
22198      * @cfg {String} invalidText
22199      * The error text to display when the date in the field is invalid (defaults to
22200      * '{value} is not a valid date - it must be in the format {format}').
22201      */
22202     invalidText : "{0} is not a valid date - it must be in the format {1}",
22203     /**
22204      * @cfg {String} triggerClass
22205      * An additional CSS class used to style the trigger button.  The trigger will always get the
22206      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22207      * which displays a calendar icon).
22208      */
22209     triggerClass : 'x-form-date-trigger',
22210     
22211
22212     /**
22213      * @cfg {bool} useIso
22214      * if enabled, then the date field will use a hidden field to store the 
22215      * real value as iso formated date. default (false)
22216      */ 
22217     useIso : false,
22218     /**
22219      * @cfg {String/Object} autoCreate
22220      * A DomHelper element spec, or true for a default element spec (defaults to
22221      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22222      */ 
22223     // private
22224     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
22225     
22226     // private
22227     hiddenField: false,
22228     
22229     onRender : function(ct, position)
22230     {
22231         Roo.form.DateField.superclass.onRender.call(this, ct, position);
22232         if (this.useIso) {
22233             this.el.dom.removeAttribute('name'); 
22234             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22235                     'before', true);
22236             this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
22237             // prevent input submission
22238             this.hiddenName = this.name;
22239         }
22240             
22241             
22242     },
22243     
22244     // private
22245     validateValue : function(value)
22246     {
22247         value = this.formatDate(value);
22248         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
22249             return false;
22250         }
22251         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22252              return true;
22253         }
22254         var svalue = value;
22255         value = this.parseDate(value);
22256         if(!value){
22257             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22258             return false;
22259         }
22260         var time = value.getTime();
22261         if(this.minValue && time < this.minValue.getTime()){
22262             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22263             return false;
22264         }
22265         if(this.maxValue && time > this.maxValue.getTime()){
22266             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22267             return false;
22268         }
22269         if(this.disabledDays){
22270             var day = value.getDay();
22271             for(var i = 0; i < this.disabledDays.length; i++) {
22272                 if(day === this.disabledDays[i]){
22273                     this.markInvalid(this.disabledDaysText);
22274                     return false;
22275                 }
22276             }
22277         }
22278         var fvalue = this.formatDate(value);
22279         if(this.ddMatch && this.ddMatch.test(fvalue)){
22280             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22281             return false;
22282         }
22283         return true;
22284     },
22285
22286     // private
22287     // Provides logic to override the default TriggerField.validateBlur which just returns true
22288     validateBlur : function(){
22289         return !this.menu || !this.menu.isVisible();
22290     },
22291
22292     /**
22293      * Returns the current date value of the date field.
22294      * @return {Date} The date value
22295      */
22296     getValue : function(){
22297         
22298         return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
22299     },
22300
22301     /**
22302      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22303      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
22304      * (the default format used is "m/d/y").
22305      * <br />Usage:
22306      * <pre><code>
22307 //All of these calls set the same date value (May 4, 2006)
22308
22309 //Pass a date object:
22310 var dt = new Date('5/4/06');
22311 dateField.setValue(dt);
22312
22313 //Pass a date string (default format):
22314 dateField.setValue('5/4/06');
22315
22316 //Pass a date string (custom format):
22317 dateField.format = 'Y-m-d';
22318 dateField.setValue('2006-5-4');
22319 </code></pre>
22320      * @param {String/Date} date The date or valid date string
22321      */
22322     setValue : function(date){
22323         if (this.hiddenField) {
22324             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22325         }
22326         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22327     },
22328
22329     // private
22330     parseDate : function(value){
22331         if(!value || value instanceof Date){
22332             return value;
22333         }
22334         var v = Date.parseDate(value, this.format);
22335         if(!v && this.altFormats){
22336             if(!this.altFormatsArray){
22337                 this.altFormatsArray = this.altFormats.split("|");
22338             }
22339             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22340                 v = Date.parseDate(value, this.altFormatsArray[i]);
22341             }
22342         }
22343         return v;
22344     },
22345
22346     // private
22347     formatDate : function(date, fmt){
22348         return (!date || !(date instanceof Date)) ?
22349                date : date.dateFormat(fmt || this.format);
22350     },
22351
22352     // private
22353     menuListeners : {
22354         select: function(m, d){
22355             this.setValue(d);
22356             this.fireEvent('select', this, d);
22357         },
22358         show : function(){ // retain focus styling
22359             this.onFocus();
22360         },
22361         hide : function(){
22362             this.focus.defer(10, this);
22363             var ml = this.menuListeners;
22364             this.menu.un("select", ml.select,  this);
22365             this.menu.un("show", ml.show,  this);
22366             this.menu.un("hide", ml.hide,  this);
22367         }
22368     },
22369
22370     // private
22371     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22372     onTriggerClick : function(){
22373         if(this.disabled){
22374             return;
22375         }
22376         if(this.menu == null){
22377             this.menu = new Roo.menu.DateMenu();
22378         }
22379         Roo.apply(this.menu.picker,  {
22380             showClear: this.allowBlank,
22381             minDate : this.minValue,
22382             maxDate : this.maxValue,
22383             disabledDatesRE : this.ddMatch,
22384             disabledDatesText : this.disabledDatesText,
22385             disabledDays : this.disabledDays,
22386             disabledDaysText : this.disabledDaysText,
22387             format : this.format,
22388             minText : String.format(this.minText, this.formatDate(this.minValue)),
22389             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22390         });
22391         this.menu.on(Roo.apply({}, this.menuListeners, {
22392             scope:this
22393         }));
22394         this.menu.picker.setValue(this.getValue() || new Date());
22395         this.menu.show(this.el, "tl-bl?");
22396     },
22397
22398     beforeBlur : function(){
22399         var v = this.parseDate(this.getRawValue());
22400         if(v){
22401             this.setValue(v);
22402         }
22403     }
22404
22405     /** @cfg {Boolean} grow @hide */
22406     /** @cfg {Number} growMin @hide */
22407     /** @cfg {Number} growMax @hide */
22408     /**
22409      * @hide
22410      * @method autoSize
22411      */
22412 });/*
22413  * Based on:
22414  * Ext JS Library 1.1.1
22415  * Copyright(c) 2006-2007, Ext JS, LLC.
22416  *
22417  * Originally Released Under LGPL - original licence link has changed is not relivant.
22418  *
22419  * Fork - LGPL
22420  * <script type="text/javascript">
22421  */
22422  
22423
22424 /**
22425  * @class Roo.form.ComboBox
22426  * @extends Roo.form.TriggerField
22427  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
22428  * @constructor
22429  * Create a new ComboBox.
22430  * @param {Object} config Configuration options
22431  */
22432 Roo.form.ComboBox = function(config){
22433     Roo.form.ComboBox.superclass.constructor.call(this, config);
22434     this.addEvents({
22435         /**
22436          * @event expand
22437          * Fires when the dropdown list is expanded
22438              * @param {Roo.form.ComboBox} combo This combo box
22439              */
22440         'expand' : true,
22441         /**
22442          * @event collapse
22443          * Fires when the dropdown list is collapsed
22444              * @param {Roo.form.ComboBox} combo This combo box
22445              */
22446         'collapse' : true,
22447         /**
22448          * @event beforeselect
22449          * Fires before a list item is selected. Return false to cancel the selection.
22450              * @param {Roo.form.ComboBox} combo This combo box
22451              * @param {Roo.data.Record} record The data record returned from the underlying store
22452              * @param {Number} index The index of the selected item in the dropdown list
22453              */
22454         'beforeselect' : true,
22455         /**
22456          * @event select
22457          * Fires when a list item is selected
22458              * @param {Roo.form.ComboBox} combo This combo box
22459              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
22460              * @param {Number} index The index of the selected item in the dropdown list
22461              */
22462         'select' : true,
22463         /**
22464          * @event beforequery
22465          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
22466          * The event object passed has these properties:
22467              * @param {Roo.form.ComboBox} combo This combo box
22468              * @param {String} query The query
22469              * @param {Boolean} forceAll true to force "all" query
22470              * @param {Boolean} cancel true to cancel the query
22471              * @param {Object} e The query event object
22472              */
22473         'beforequery': true,
22474          /**
22475          * @event add
22476          * Fires when the 'add' icon is pressed (add a listener to enable add button)
22477              * @param {Roo.form.ComboBox} combo This combo box
22478              */
22479         'add' : true,
22480         /**
22481          * @event edit
22482          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
22483              * @param {Roo.form.ComboBox} combo This combo box
22484              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
22485              */
22486         'edit' : true
22487         
22488         
22489     });
22490     if(this.transform){
22491         this.allowDomMove = false;
22492         var s = Roo.getDom(this.transform);
22493         if(!this.hiddenName){
22494             this.hiddenName = s.name;
22495         }
22496         if(!this.store){
22497             this.mode = 'local';
22498             var d = [], opts = s.options;
22499             for(var i = 0, len = opts.length;i < len; i++){
22500                 var o = opts[i];
22501                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
22502                 if(o.selected) {
22503                     this.value = value;
22504                 }
22505                 d.push([value, o.text]);
22506             }
22507             this.store = new Roo.data.SimpleStore({
22508                 'id': 0,
22509                 fields: ['value', 'text'],
22510                 data : d
22511             });
22512             this.valueField = 'value';
22513             this.displayField = 'text';
22514         }
22515         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
22516         if(!this.lazyRender){
22517             this.target = true;
22518             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
22519             s.parentNode.removeChild(s); // remove it
22520             this.render(this.el.parentNode);
22521         }else{
22522             s.parentNode.removeChild(s); // remove it
22523         }
22524
22525     }
22526     if (this.store) {
22527         this.store = Roo.factory(this.store, Roo.data);
22528     }
22529     
22530     this.selectedIndex = -1;
22531     if(this.mode == 'local'){
22532         if(config.queryDelay === undefined){
22533             this.queryDelay = 10;
22534         }
22535         if(config.minChars === undefined){
22536             this.minChars = 0;
22537         }
22538     }
22539 };
22540
22541 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
22542     /**
22543      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
22544      */
22545     /**
22546      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
22547      * rendering into an Roo.Editor, defaults to false)
22548      */
22549     /**
22550      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
22551      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
22552      */
22553     /**
22554      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
22555      */
22556     /**
22557      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
22558      * the dropdown list (defaults to undefined, with no header element)
22559      */
22560
22561      /**
22562      * @cfg {String/Roo.Template} tpl The template to use to render the output
22563      */
22564      
22565     // private
22566     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
22567     /**
22568      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
22569      */
22570     listWidth: undefined,
22571     /**
22572      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
22573      * mode = 'remote' or 'text' if mode = 'local')
22574      */
22575     displayField: undefined,
22576     /**
22577      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
22578      * mode = 'remote' or 'value' if mode = 'local'). 
22579      * Note: use of a valueField requires the user make a selection
22580      * in order for a value to be mapped.
22581      */
22582     valueField: undefined,
22583     /**
22584      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
22585      * field's data value (defaults to the underlying DOM element's name)
22586      */
22587     hiddenName: undefined,
22588     /**
22589      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
22590      */
22591     listClass: '',
22592     /**
22593      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
22594      */
22595     selectedClass: 'x-combo-selected',
22596     /**
22597      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
22598      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
22599      * which displays a downward arrow icon).
22600      */
22601     triggerClass : 'x-form-arrow-trigger',
22602     /**
22603      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
22604      */
22605     shadow:'sides',
22606     /**
22607      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
22608      * anchor positions (defaults to 'tl-bl')
22609      */
22610     listAlign: 'tl-bl?',
22611     /**
22612      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
22613      */
22614     maxHeight: 300,
22615     /**
22616      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
22617      * query specified by the allQuery config option (defaults to 'query')
22618      */
22619     triggerAction: 'query',
22620     /**
22621      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
22622      * (defaults to 4, does not apply if editable = false)
22623      */
22624     minChars : 4,
22625     /**
22626      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
22627      * delay (typeAheadDelay) if it matches a known value (defaults to false)
22628      */
22629     typeAhead: false,
22630     /**
22631      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
22632      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
22633      */
22634     queryDelay: 500,
22635     /**
22636      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
22637      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
22638      */
22639     pageSize: 0,
22640     /**
22641      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
22642      * when editable = true (defaults to false)
22643      */
22644     selectOnFocus:false,
22645     /**
22646      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
22647      */
22648     queryParam: 'query',
22649     /**
22650      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
22651      * when mode = 'remote' (defaults to 'Loading...')
22652      */
22653     loadingText: 'Loading...',
22654     /**
22655      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
22656      */
22657     resizable: false,
22658     /**
22659      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
22660      */
22661     handleHeight : 8,
22662     /**
22663      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
22664      * traditional select (defaults to true)
22665      */
22666     editable: true,
22667     /**
22668      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
22669      */
22670     allQuery: '',
22671     /**
22672      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
22673      */
22674     mode: 'remote',
22675     /**
22676      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
22677      * listWidth has a higher value)
22678      */
22679     minListWidth : 70,
22680     /**
22681      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
22682      * allow the user to set arbitrary text into the field (defaults to false)
22683      */
22684     forceSelection:false,
22685     /**
22686      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
22687      * if typeAhead = true (defaults to 250)
22688      */
22689     typeAheadDelay : 250,
22690     /**
22691      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
22692      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
22693      */
22694     valueNotFoundText : undefined,
22695     /**
22696      * @cfg {bool} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
22697      */
22698     blockFocus : false,
22699     
22700     /**
22701      * @cfg {bool} disableClear Disable showing of clear button.
22702      */
22703     disableClear : false,
22704     
22705     //private
22706     addicon : false,
22707     editicon: false,
22708     
22709     
22710     // private
22711     onRender : function(ct, position){
22712         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
22713         if(this.hiddenName){
22714             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
22715                     'before', true);
22716             this.hiddenField.value =
22717                 this.hiddenValue !== undefined ? this.hiddenValue :
22718                 this.value !== undefined ? this.value : '';
22719
22720             // prevent input submission
22721             this.el.dom.removeAttribute('name');
22722         }
22723         if(Roo.isGecko){
22724             this.el.dom.setAttribute('autocomplete', 'off');
22725         }
22726
22727         var cls = 'x-combo-list';
22728
22729         this.list = new Roo.Layer({
22730             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
22731         });
22732
22733         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
22734         this.list.setWidth(lw);
22735         this.list.swallowEvent('mousewheel');
22736         this.assetHeight = 0;
22737
22738         if(this.title){
22739             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
22740             this.assetHeight += this.header.getHeight();
22741         }
22742
22743         this.innerList = this.list.createChild({cls:cls+'-inner'});
22744         this.innerList.on('mouseover', this.onViewOver, this);
22745         this.innerList.on('mousemove', this.onViewMove, this);
22746         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
22747         
22748         if(this.allowBlank && !this.pageSize && !this.disableClear){
22749             this.footer = this.list.createChild({cls:cls+'-ft'});
22750             this.pageTb = new Roo.Toolbar(this.footer);
22751            
22752         }
22753         if(this.pageSize){
22754             this.footer = this.list.createChild({cls:cls+'-ft'});
22755             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
22756                     {pageSize: this.pageSize});
22757             
22758         }
22759         
22760         if (this.pageTb && this.allowBlank && !this.disableClear) {
22761             var _this = this;
22762             this.pageTb.add(new Roo.Toolbar.Fill(), {
22763                 cls: 'x-btn-icon x-btn-clear',
22764                 text: '&#160;',
22765                 handler: function()
22766                 {
22767                     _this.collapse();
22768                     _this.clearValue();
22769                     _this.onSelect(false, -1);
22770                 }
22771             });
22772         }
22773         if (this.footer) {
22774             this.assetHeight += this.footer.getHeight();
22775         }
22776         
22777
22778         if(!this.tpl){
22779             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
22780         }
22781
22782         this.view = new Roo.View(this.innerList, this.tpl, {
22783             singleSelect:true, store: this.store, selectedClass: this.selectedClass
22784         });
22785
22786         this.view.on('click', this.onViewClick, this);
22787
22788         this.store.on('beforeload', this.onBeforeLoad, this);
22789         this.store.on('load', this.onLoad, this);
22790         this.store.on('loadexception', this.collapse, this);
22791
22792         if(this.resizable){
22793             this.resizer = new Roo.Resizable(this.list,  {
22794                pinned:true, handles:'se'
22795             });
22796             this.resizer.on('resize', function(r, w, h){
22797                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
22798                 this.listWidth = w;
22799                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
22800                 this.restrictHeight();
22801             }, this);
22802             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
22803         }
22804         if(!this.editable){
22805             this.editable = true;
22806             this.setEditable(false);
22807         }  
22808         
22809         
22810         if (typeof(this.events.add.listeners) != 'undefined') {
22811             
22812             this.addicon = this.wrap.createChild(
22813                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
22814        
22815             this.addicon.on('click', function(e) {
22816                 this.fireEvent('add', this);
22817             }, this);
22818         }
22819         if (typeof(this.events.edit.listeners) != 'undefined') {
22820             
22821             this.editicon = this.wrap.createChild(
22822                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
22823             if (this.addicon) {
22824                 this.editicon.setStyle('margin-left', '40px');
22825             }
22826             this.editicon.on('click', function(e) {
22827                 
22828                 // we fire even  if inothing is selected..
22829                 this.fireEvent('edit', this, this.lastData );
22830                 
22831             }, this);
22832         }
22833         
22834         
22835         
22836     },
22837
22838     // private
22839     initEvents : function(){
22840         Roo.form.ComboBox.superclass.initEvents.call(this);
22841
22842         this.keyNav = new Roo.KeyNav(this.el, {
22843             "up" : function(e){
22844                 this.inKeyMode = true;
22845                 this.selectPrev();
22846             },
22847
22848             "down" : function(e){
22849                 if(!this.isExpanded()){
22850                     this.onTriggerClick();
22851                 }else{
22852                     this.inKeyMode = true;
22853                     this.selectNext();
22854                 }
22855             },
22856
22857             "enter" : function(e){
22858                 this.onViewClick();
22859                 //return true;
22860             },
22861
22862             "esc" : function(e){
22863                 this.collapse();
22864             },
22865
22866             "tab" : function(e){
22867                 this.onViewClick(false);
22868                 return true;
22869             },
22870
22871             scope : this,
22872
22873             doRelay : function(foo, bar, hname){
22874                 if(hname == 'down' || this.scope.isExpanded()){
22875                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
22876                 }
22877                 return true;
22878             },
22879
22880             forceKeyDown: true
22881         });
22882         this.queryDelay = Math.max(this.queryDelay || 10,
22883                 this.mode == 'local' ? 10 : 250);
22884         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
22885         if(this.typeAhead){
22886             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
22887         }
22888         if(this.editable !== false){
22889             this.el.on("keyup", this.onKeyUp, this);
22890         }
22891         if(this.forceSelection){
22892             this.on('blur', this.doForce, this);
22893         }
22894     },
22895
22896     onDestroy : function(){
22897         if(this.view){
22898             this.view.setStore(null);
22899             this.view.el.removeAllListeners();
22900             this.view.el.remove();
22901             this.view.purgeListeners();
22902         }
22903         if(this.list){
22904             this.list.destroy();
22905         }
22906         if(this.store){
22907             this.store.un('beforeload', this.onBeforeLoad, this);
22908             this.store.un('load', this.onLoad, this);
22909             this.store.un('loadexception', this.collapse, this);
22910         }
22911         Roo.form.ComboBox.superclass.onDestroy.call(this);
22912     },
22913
22914     // private
22915     fireKey : function(e){
22916         if(e.isNavKeyPress() && !this.list.isVisible()){
22917             this.fireEvent("specialkey", this, e);
22918         }
22919     },
22920
22921     // private
22922     onResize: function(w, h){
22923         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
22924         
22925         if(typeof w != 'number'){
22926             // we do not handle it!?!?
22927             return;
22928         }
22929         var tw = this.trigger.getWidth();
22930         tw += this.addicon ? this.addicon.getWidth() : 0;
22931         tw += this.editicon ? this.editicon.getWidth() : 0;
22932         var x = w - tw;
22933         this.el.setWidth( this.adjustWidth('input', x));
22934             
22935         this.trigger.setStyle('left', x+'px');
22936         
22937         if(this.list && this.listWidth === undefined){
22938             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
22939             this.list.setWidth(lw);
22940             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
22941         }
22942         
22943     
22944         
22945     },
22946
22947     /**
22948      * Allow or prevent the user from directly editing the field text.  If false is passed,
22949      * the user will only be able to select from the items defined in the dropdown list.  This method
22950      * is the runtime equivalent of setting the 'editable' config option at config time.
22951      * @param {Boolean} value True to allow the user to directly edit the field text
22952      */
22953     setEditable : function(value){
22954         if(value == this.editable){
22955             return;
22956         }
22957         this.editable = value;
22958         if(!value){
22959             this.el.dom.setAttribute('readOnly', true);
22960             this.el.on('mousedown', this.onTriggerClick,  this);
22961             this.el.addClass('x-combo-noedit');
22962         }else{
22963             this.el.dom.setAttribute('readOnly', false);
22964             this.el.un('mousedown', this.onTriggerClick,  this);
22965             this.el.removeClass('x-combo-noedit');
22966         }
22967     },
22968
22969     // private
22970     onBeforeLoad : function(){
22971         if(!this.hasFocus){
22972             return;
22973         }
22974         this.innerList.update(this.loadingText ?
22975                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
22976         this.restrictHeight();
22977         this.selectedIndex = -1;
22978     },
22979
22980     // private
22981     onLoad : function(){
22982         if(!this.hasFocus){
22983             return;
22984         }
22985         if(this.store.getCount() > 0){
22986             this.expand();
22987             this.restrictHeight();
22988             if(this.lastQuery == this.allQuery){
22989                 if(this.editable){
22990                     this.el.dom.select();
22991                 }
22992                 if(!this.selectByValue(this.value, true)){
22993                     this.select(0, true);
22994                 }
22995             }else{
22996                 this.selectNext();
22997                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
22998                     this.taTask.delay(this.typeAheadDelay);
22999                 }
23000             }
23001         }else{
23002             this.onEmptyResults();
23003         }
23004         //this.el.focus();
23005     },
23006
23007     // private
23008     onTypeAhead : function(){
23009         if(this.store.getCount() > 0){
23010             var r = this.store.getAt(0);
23011             var newValue = r.data[this.displayField];
23012             var len = newValue.length;
23013             var selStart = this.getRawValue().length;
23014             if(selStart != len){
23015                 this.setRawValue(newValue);
23016                 this.selectText(selStart, newValue.length);
23017             }
23018         }
23019     },
23020
23021     // private
23022     onSelect : function(record, index){
23023         if(this.fireEvent('beforeselect', this, record, index) !== false){
23024             this.setFromData(index > -1 ? record.data : false);
23025             this.collapse();
23026             this.fireEvent('select', this, record, index);
23027         }
23028     },
23029
23030     /**
23031      * Returns the currently selected field value or empty string if no value is set.
23032      * @return {String} value The selected value
23033      */
23034     getValue : function(){
23035         if(this.valueField){
23036             return typeof this.value != 'undefined' ? this.value : '';
23037         }else{
23038             return Roo.form.ComboBox.superclass.getValue.call(this);
23039         }
23040     },
23041
23042     /**
23043      * Clears any text/value currently set in the field
23044      */
23045     clearValue : function(){
23046         if(this.hiddenField){
23047             this.hiddenField.value = '';
23048         }
23049         this.value = '';
23050         this.setRawValue('');
23051         this.lastSelectionText = '';
23052         this.applyEmptyText();
23053     },
23054
23055     /**
23056      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
23057      * will be displayed in the field.  If the value does not match the data value of an existing item,
23058      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
23059      * Otherwise the field will be blank (although the value will still be set).
23060      * @param {String} value The value to match
23061      */
23062     setValue : function(v){
23063         var text = v;
23064         if(this.valueField){
23065             var r = this.findRecord(this.valueField, v);
23066             if(r){
23067                 text = r.data[this.displayField];
23068             }else if(this.valueNotFoundText !== undefined){
23069                 text = this.valueNotFoundText;
23070             }
23071         }
23072         this.lastSelectionText = text;
23073         if(this.hiddenField){
23074             this.hiddenField.value = v;
23075         }
23076         Roo.form.ComboBox.superclass.setValue.call(this, text);
23077         this.value = v;
23078     },
23079     /**
23080      * @property {Object} the last set data for the element
23081      */
23082     
23083     lastData : false,
23084     /**
23085      * Sets the value of the field based on a object which is related to the record format for the store.
23086      * @param {Object} value the value to set as. or false on reset?
23087      */
23088     setFromData : function(o){
23089         var dv = ''; // display value
23090         var vv = ''; // value value..
23091         this.lastData = o;
23092         if (this.displayField) {
23093             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
23094         } else {
23095             // this is an error condition!!!
23096             console.log('no value field set for '+ this.name);
23097         }
23098         
23099         if(this.valueField){
23100             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
23101         }
23102         if(this.hiddenField){
23103             this.hiddenField.value = vv;
23104             
23105             this.lastSelectionText = dv;
23106             Roo.form.ComboBox.superclass.setValue.call(this, dv);
23107             this.value = vv;
23108             return;
23109         }
23110         // no hidden field.. - we store the value in 'value', but still display
23111         // display field!!!!
23112         this.lastSelectionText = dv;
23113         Roo.form.ComboBox.superclass.setValue.call(this, dv);
23114         this.value = vv;
23115         
23116         
23117     },
23118     // private
23119     reset : function(){
23120         // overridden so that last data is reset..
23121         this.setValue(this.originalValue);
23122         this.clearInvalid();
23123         this.lastData = false;
23124     },
23125     // private
23126     findRecord : function(prop, value){
23127         var record;
23128         if(this.store.getCount() > 0){
23129             this.store.each(function(r){
23130                 if(r.data[prop] == value){
23131                     record = r;
23132                     return false;
23133                 }
23134             });
23135         }
23136         return record;
23137     },
23138
23139     // private
23140     onViewMove : function(e, t){
23141         this.inKeyMode = false;
23142     },
23143
23144     // private
23145     onViewOver : function(e, t){
23146         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
23147             return;
23148         }
23149         var item = this.view.findItemFromChild(t);
23150         if(item){
23151             var index = this.view.indexOf(item);
23152             this.select(index, false);
23153         }
23154     },
23155
23156     // private
23157     onViewClick : function(doFocus){
23158         var index = this.view.getSelectedIndexes()[0];
23159         var r = this.store.getAt(index);
23160         if(r){
23161             this.onSelect(r, index);
23162         }
23163         if(doFocus !== false && !this.blockFocus){
23164             this.el.focus();
23165         }
23166     },
23167
23168     // private
23169     restrictHeight : function(){
23170         this.innerList.dom.style.height = '';
23171         var inner = this.innerList.dom;
23172         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
23173         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
23174         this.list.beginUpdate();
23175         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
23176         this.list.alignTo(this.el, this.listAlign);
23177         this.list.endUpdate();
23178     },
23179
23180     // private
23181     onEmptyResults : function(){
23182         this.collapse();
23183     },
23184
23185     /**
23186      * Returns true if the dropdown list is expanded, else false.
23187      */
23188     isExpanded : function(){
23189         return this.list.isVisible();
23190     },
23191
23192     /**
23193      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
23194      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23195      * @param {String} value The data value of the item to select
23196      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23197      * selected item if it is not currently in view (defaults to true)
23198      * @return {Boolean} True if the value matched an item in the list, else false
23199      */
23200     selectByValue : function(v, scrollIntoView){
23201         if(v !== undefined && v !== null){
23202             var r = this.findRecord(this.valueField || this.displayField, v);
23203             if(r){
23204                 this.select(this.store.indexOf(r), scrollIntoView);
23205                 return true;
23206             }
23207         }
23208         return false;
23209     },
23210
23211     /**
23212      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
23213      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23214      * @param {Number} index The zero-based index of the list item to select
23215      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23216      * selected item if it is not currently in view (defaults to true)
23217      */
23218     select : function(index, scrollIntoView){
23219         this.selectedIndex = index;
23220         this.view.select(index);
23221         if(scrollIntoView !== false){
23222             var el = this.view.getNode(index);
23223             if(el){
23224                 this.innerList.scrollChildIntoView(el, false);
23225             }
23226         }
23227     },
23228
23229     // private
23230     selectNext : function(){
23231         var ct = this.store.getCount();
23232         if(ct > 0){
23233             if(this.selectedIndex == -1){
23234                 this.select(0);
23235             }else if(this.selectedIndex < ct-1){
23236                 this.select(this.selectedIndex+1);
23237             }
23238         }
23239     },
23240
23241     // private
23242     selectPrev : function(){
23243         var ct = this.store.getCount();
23244         if(ct > 0){
23245             if(this.selectedIndex == -1){
23246                 this.select(0);
23247             }else if(this.selectedIndex != 0){
23248                 this.select(this.selectedIndex-1);
23249             }
23250         }
23251     },
23252
23253     // private
23254     onKeyUp : function(e){
23255         if(this.editable !== false && !e.isSpecialKey()){
23256             this.lastKey = e.getKey();
23257             this.dqTask.delay(this.queryDelay);
23258         }
23259     },
23260
23261     // private
23262     validateBlur : function(){
23263         return !this.list || !this.list.isVisible();   
23264     },
23265
23266     // private
23267     initQuery : function(){
23268         this.doQuery(this.getRawValue());
23269     },
23270
23271     // private
23272     doForce : function(){
23273         if(this.el.dom.value.length > 0){
23274             this.el.dom.value =
23275                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
23276             this.applyEmptyText();
23277         }
23278     },
23279
23280     /**
23281      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
23282      * query allowing the query action to be canceled if needed.
23283      * @param {String} query The SQL query to execute
23284      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
23285      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
23286      * saved in the current store (defaults to false)
23287      */
23288     doQuery : function(q, forceAll){
23289         if(q === undefined || q === null){
23290             q = '';
23291         }
23292         var qe = {
23293             query: q,
23294             forceAll: forceAll,
23295             combo: this,
23296             cancel:false
23297         };
23298         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
23299             return false;
23300         }
23301         q = qe.query;
23302         forceAll = qe.forceAll;
23303         if(forceAll === true || (q.length >= this.minChars)){
23304             if(this.lastQuery != q){
23305                 this.lastQuery = q;
23306                 if(this.mode == 'local'){
23307                     this.selectedIndex = -1;
23308                     if(forceAll){
23309                         this.store.clearFilter();
23310                     }else{
23311                         this.store.filter(this.displayField, q);
23312                     }
23313                     this.onLoad();
23314                 }else{
23315                     this.store.baseParams[this.queryParam] = q;
23316                     this.store.load({
23317                         params: this.getParams(q)
23318                     });
23319                     this.expand();
23320                 }
23321             }else{
23322                 this.selectedIndex = -1;
23323                 this.onLoad();   
23324             }
23325         }
23326     },
23327
23328     // private
23329     getParams : function(q){
23330         var p = {};
23331         //p[this.queryParam] = q;
23332         if(this.pageSize){
23333             p.start = 0;
23334             p.limit = this.pageSize;
23335         }
23336         return p;
23337     },
23338
23339     /**
23340      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
23341      */
23342     collapse : function(){
23343         if(!this.isExpanded()){
23344             return;
23345         }
23346         this.list.hide();
23347         Roo.get(document).un('mousedown', this.collapseIf, this);
23348         Roo.get(document).un('mousewheel', this.collapseIf, this);
23349         this.fireEvent('collapse', this);
23350     },
23351
23352     // private
23353     collapseIf : function(e){
23354         if(!e.within(this.wrap) && !e.within(this.list)){
23355             this.collapse();
23356         }
23357     },
23358
23359     /**
23360      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
23361      */
23362     expand : function(){
23363         if(this.isExpanded() || !this.hasFocus){
23364             return;
23365         }
23366         this.list.alignTo(this.el, this.listAlign);
23367         this.list.show();
23368         Roo.get(document).on('mousedown', this.collapseIf, this);
23369         Roo.get(document).on('mousewheel', this.collapseIf, this);
23370         this.fireEvent('expand', this);
23371     },
23372
23373     // private
23374     // Implements the default empty TriggerField.onTriggerClick function
23375     onTriggerClick : function(){
23376         if(this.disabled){
23377             return;
23378         }
23379         if(this.isExpanded()){
23380             this.collapse();
23381             if (!this.blockFocus) {
23382                 this.el.focus();
23383             }
23384             
23385         }else {
23386             this.hasFocus = true;
23387             if(this.triggerAction == 'all') {
23388                 this.doQuery(this.allQuery, true);
23389             } else {
23390                 this.doQuery(this.getRawValue());
23391             }
23392             if (!this.blockFocus) {
23393                 this.el.focus();
23394             }
23395         }
23396     }
23397
23398     /** 
23399     * @cfg {Boolean} grow 
23400     * @hide 
23401     */
23402     /** 
23403     * @cfg {Number} growMin 
23404     * @hide 
23405     */
23406     /** 
23407     * @cfg {Number} growMax 
23408     * @hide 
23409     */
23410     /**
23411      * @hide
23412      * @method autoSize
23413      */
23414 });/*
23415  * Based on:
23416  * Ext JS Library 1.1.1
23417  * Copyright(c) 2006-2007, Ext JS, LLC.
23418  *
23419  * Originally Released Under LGPL - original licence link has changed is not relivant.
23420  *
23421  * Fork - LGPL
23422  * <script type="text/javascript">
23423  */
23424 /**
23425  * @class Roo.form.Checkbox
23426  * @extends Roo.form.Field
23427  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
23428  * @constructor
23429  * Creates a new Checkbox
23430  * @param {Object} config Configuration options
23431  */
23432 Roo.form.Checkbox = function(config){
23433     Roo.form.Checkbox.superclass.constructor.call(this, config);
23434     this.addEvents({
23435         /**
23436          * @event check
23437          * Fires when the checkbox is checked or unchecked.
23438              * @param {Roo.form.Checkbox} this This checkbox
23439              * @param {Boolean} checked The new checked value
23440              */
23441         check : true
23442     });
23443 };
23444
23445 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
23446     /**
23447      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
23448      */
23449     focusClass : undefined,
23450     /**
23451      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
23452      */
23453     fieldClass: "x-form-field",
23454     /**
23455      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
23456      */
23457     checked: false,
23458     /**
23459      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
23460      * {tag: "input", type: "checkbox", autocomplete: "off"})
23461      */
23462     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
23463     /**
23464      * @cfg {String} boxLabel The text that appears beside the checkbox
23465      */
23466     boxLabel : "",
23467     /**
23468      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
23469      */  
23470     inputValue : '1',
23471     /**
23472      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23473      */
23474      valueOff: '0', // value when not checked..
23475
23476     actionMode : 'viewEl', 
23477     //
23478     // private
23479     itemCls : 'x-menu-check-item x-form-item',
23480     groupClass : 'x-menu-group-item',
23481     inputType : 'hidden',
23482     
23483     
23484     inSetChecked: false, // check that we are not calling self...
23485     
23486     inputElement: false, // real input element?
23487     basedOn: false, // ????
23488     
23489     isFormField: true, // not sure where this is needed!!!!
23490
23491     onResize : function(){
23492         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
23493         if(!this.boxLabel){
23494             this.el.alignTo(this.wrap, 'c-c');
23495         }
23496     },
23497
23498     initEvents : function(){
23499         Roo.form.Checkbox.superclass.initEvents.call(this);
23500         this.el.on("click", this.onClick,  this);
23501         this.el.on("change", this.onClick,  this);
23502     },
23503
23504
23505     getResizeEl : function(){
23506         return this.wrap;
23507     },
23508
23509     getPositionEl : function(){
23510         return this.wrap;
23511     },
23512
23513     // private
23514     onRender : function(ct, position){
23515         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
23516         /*
23517         if(this.inputValue !== undefined){
23518             this.el.dom.value = this.inputValue;
23519         }
23520         */
23521         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
23522         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
23523         var viewEl = this.wrap.createChild({ 
23524             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
23525         this.viewEl = viewEl;   
23526         this.wrap.on('click', this.onClick,  this); 
23527         
23528         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
23529         this.el.on('propertychange', this.setFromHidden,  this);  //ie
23530         
23531         
23532         
23533         if(this.boxLabel){
23534             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
23535         //    viewEl.on('click', this.onClick,  this); 
23536         }
23537         //if(this.checked){
23538             this.setChecked(this.checked);
23539         //}else{
23540             //this.checked = this.el.dom;
23541         //}
23542
23543     },
23544
23545     // private
23546     initValue : Roo.emptyFn,
23547
23548     /**
23549      * Returns the checked state of the checkbox.
23550      * @return {Boolean} True if checked, else false
23551      */
23552     getValue : function(){
23553         if(this.el){
23554             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
23555         }
23556         return this.valueOff;
23557         
23558     },
23559
23560         // private
23561     onClick : function(){ 
23562         this.setChecked(!this.checked);
23563
23564         //if(this.el.dom.checked != this.checked){
23565         //    this.setValue(this.el.dom.checked);
23566        // }
23567     },
23568
23569     /**
23570      * Sets the checked state of the checkbox.
23571      * On is always based on a string comparison between inputValue and the param.
23572      * @param {Boolean/String} value - the value to set 
23573      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
23574      */
23575     setValue : function(v,suppressEvent){
23576         
23577         
23578         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
23579         //if(this.el && this.el.dom){
23580         //    this.el.dom.checked = this.checked;
23581         //    this.el.dom.defaultChecked = this.checked;
23582         //}
23583         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
23584         //this.fireEvent("check", this, this.checked);
23585     },
23586     // private..
23587     setChecked : function(state,suppressEvent)
23588     {
23589         if (this.inSetChecked) {
23590             this.checked = state;
23591             return;
23592         }
23593         
23594     
23595         if(this.wrap){
23596             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
23597         }
23598         this.checked = state;
23599         if(suppressEvent !== true){
23600             this.fireEvent('checkchange', this, state);
23601         }
23602         this.inSetChecked = true;
23603         this.el.dom.value = state ? this.inputValue : this.valueOff;
23604         this.inSetChecked = false;
23605         
23606     },
23607     // handle setting of hidden value by some other method!!?!?
23608     setFromHidden: function()
23609     {
23610         if(!this.el){
23611             return;
23612         }
23613         //console.log("SET FROM HIDDEN");
23614         //alert('setFrom hidden');
23615         this.setValue(this.el.dom.value);
23616     },
23617     
23618     onDestroy : function()
23619     {
23620         if(this.viewEl){
23621             Roo.get(this.viewEl).remove();
23622         }
23623          
23624         Roo.form.Checkbox.superclass.onDestroy.call(this);
23625     }
23626
23627 });/*
23628  * Based on:
23629  * Ext JS Library 1.1.1
23630  * Copyright(c) 2006-2007, Ext JS, LLC.
23631  *
23632  * Originally Released Under LGPL - original licence link has changed is not relivant.
23633  *
23634  * Fork - LGPL
23635  * <script type="text/javascript">
23636  */
23637  
23638 /**
23639  * @class Roo.form.Radio
23640  * @extends Roo.form.Checkbox
23641  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
23642  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
23643  * @constructor
23644  * Creates a new Radio
23645  * @param {Object} config Configuration options
23646  */
23647 Roo.form.Radio = function(){
23648     Roo.form.Radio.superclass.constructor.apply(this, arguments);
23649 };
23650 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
23651     inputType: 'radio',
23652
23653     /**
23654      * If this radio is part of a group, it will return the selected value
23655      * @return {String}
23656      */
23657     getGroupValue : function(){
23658         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
23659     }
23660 });//<script type="text/javascript">
23661
23662 /*
23663  * Ext JS Library 1.1.1
23664  * Copyright(c) 2006-2007, Ext JS, LLC.
23665  * licensing@extjs.com
23666  * 
23667  * http://www.extjs.com/license
23668  */
23669  
23670  /*
23671   * 
23672   * Known bugs:
23673   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
23674   * - IE ? - no idea how much works there.
23675   * 
23676   * 
23677   * 
23678   */
23679  
23680
23681 /**
23682  * @class Ext.form.HtmlEditor
23683  * @extends Ext.form.Field
23684  * Provides a lightweight HTML Editor component.
23685  * WARNING - THIS CURRENTlY ONLY WORKS ON FIREFOX - USE FCKeditor for a cross platform version
23686  * 
23687  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
23688  * supported by this editor.</b><br/><br/>
23689  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
23690  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23691  */
23692 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
23693       /**
23694      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23695      */
23696     toolbars : false,
23697     /**
23698      * @cfg {String} createLinkText The default text for the create link prompt
23699      */
23700     createLinkText : 'Please enter the URL for the link:',
23701     /**
23702      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
23703      */
23704     defaultLinkValue : 'http:/'+'/',
23705    
23706     
23707     // id of frame..
23708     frameId: false,
23709     
23710     // private properties
23711     validationEvent : false,
23712     deferHeight: true,
23713     initialized : false,
23714     activated : false,
23715     sourceEditMode : false,
23716     onFocus : Roo.emptyFn,
23717     iframePad:3,
23718     hideMode:'offsets',
23719     defaultAutoCreate : {
23720         tag: "textarea",
23721         style:"width:500px;height:300px;",
23722         autocomplete: "off"
23723     },
23724
23725     // private
23726     initComponent : function(){
23727         this.addEvents({
23728             /**
23729              * @event initialize
23730              * Fires when the editor is fully initialized (including the iframe)
23731              * @param {HtmlEditor} this
23732              */
23733             initialize: true,
23734             /**
23735              * @event activate
23736              * Fires when the editor is first receives the focus. Any insertion must wait
23737              * until after this event.
23738              * @param {HtmlEditor} this
23739              */
23740             activate: true,
23741              /**
23742              * @event beforesync
23743              * Fires before the textarea is updated with content from the editor iframe. Return false
23744              * to cancel the sync.
23745              * @param {HtmlEditor} this
23746              * @param {String} html
23747              */
23748             beforesync: true,
23749              /**
23750              * @event beforepush
23751              * Fires before the iframe editor is updated with content from the textarea. Return false
23752              * to cancel the push.
23753              * @param {HtmlEditor} this
23754              * @param {String} html
23755              */
23756             beforepush: true,
23757              /**
23758              * @event sync
23759              * Fires when the textarea is updated with content from the editor iframe.
23760              * @param {HtmlEditor} this
23761              * @param {String} html
23762              */
23763             sync: true,
23764              /**
23765              * @event push
23766              * Fires when the iframe editor is updated with content from the textarea.
23767              * @param {HtmlEditor} this
23768              * @param {String} html
23769              */
23770             push: true,
23771              /**
23772              * @event editmodechange
23773              * Fires when the editor switches edit modes
23774              * @param {HtmlEditor} this
23775              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23776              */
23777             editmodechange: true,
23778             /**
23779              * @event editorevent
23780              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23781              * @param {HtmlEditor} this
23782              */
23783             editorevent: true
23784         })
23785     },
23786
23787     /**
23788      * Protected method that will not generally be called directly. It
23789      * is called when the editor creates its toolbar. Override this method if you need to
23790      * add custom toolbar buttons.
23791      * @param {HtmlEditor} editor
23792      */
23793     createToolbar : function(editor){
23794         if (!editor.toolbars || !editor.toolbars.length) {
23795             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
23796         }
23797         
23798         for (var i =0 ; i < editor.toolbars.length;i++) {
23799             editor.toolbars[i] = Roo.factory(editor.toolbars[i], Roo.form.HtmlEditor);
23800             editor.toolbars[i].init(editor);
23801         }
23802          
23803         
23804     },
23805
23806     /**
23807      * Protected method that will not generally be called directly. It
23808      * is called when the editor initializes the iframe with HTML contents. Override this method if you
23809      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
23810      */
23811     getDocMarkup : function(){
23812         return '<html><head><style type="text/css">body{border:0;margin:0;padding:3px;height:98%;cursor:text;}</style></head><body></body></html>';
23813     },
23814
23815     // private
23816     onRender : function(ct, position){
23817         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
23818         this.el.dom.style.border = '0 none';
23819         this.el.dom.setAttribute('tabIndex', -1);
23820         this.el.addClass('x-hidden');
23821         if(Roo.isIE){ // fix IE 1px bogus margin
23822             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
23823         }
23824         this.wrap = this.el.wrap({
23825             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23826         });
23827
23828         this.frameId = Roo.id();
23829         this.createToolbar(this);
23830         
23831         
23832         
23833         
23834       
23835         
23836         var iframe = this.wrap.createChild({
23837             tag: 'iframe',
23838             id: this.frameId,
23839             name: this.frameId,
23840             frameBorder : 'no',
23841             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
23842         });
23843         
23844        // console.log(iframe);
23845         //this.wrap.dom.appendChild(iframe);
23846
23847         this.iframe = iframe.dom;
23848
23849          this.assignDocWin();
23850         
23851         this.doc.designMode = 'on';
23852        
23853         this.doc.open();
23854         this.doc.write(this.getDocMarkup());
23855         this.doc.close();
23856
23857         
23858         var task = { // must defer to wait for browser to be ready
23859             run : function(){
23860                 //console.log("run task?" + this.doc.readyState);
23861                 this.assignDocWin();
23862                 if(this.doc.body || this.doc.readyState == 'complete'){
23863                     try {
23864                         this.doc.designMode="on";
23865                     } catch (e) {
23866                         return;
23867                     }
23868                     Roo.TaskMgr.stop(task);
23869                     this.initEditor.defer(10, this);
23870                 }
23871             },
23872             interval : 10,
23873             duration:10000,
23874             scope: this
23875         };
23876         Roo.TaskMgr.start(task);
23877
23878         if(!this.width){
23879             this.setSize(this.el.getSize());
23880         }
23881     },
23882
23883     // private
23884     onResize : function(w, h){
23885         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
23886         if(this.el && this.iframe){
23887             if(typeof w == 'number'){
23888                 var aw = w - this.wrap.getFrameWidth('lr');
23889                 this.el.setWidth(this.adjustWidth('textarea', aw));
23890                 this.iframe.style.width = aw + 'px';
23891             }
23892             if(typeof h == 'number'){
23893                 var tbh = 0;
23894                 for (var i =0; i < this.toolbars.length;i++) {
23895                     // fixme - ask toolbars for heights?
23896                     tbh += this.toolbars[i].tb.el.getHeight();
23897                 }
23898                 
23899                 
23900                 
23901                 
23902                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23903                 this.el.setHeight(this.adjustWidth('textarea', ah));
23904                 this.iframe.style.height = ah + 'px';
23905                 if(this.doc){
23906                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
23907                 }
23908             }
23909         }
23910     },
23911
23912     /**
23913      * Toggles the editor between standard and source edit mode.
23914      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23915      */
23916     toggleSourceEdit : function(sourceEditMode){
23917         
23918         this.sourceEditMode = sourceEditMode === true;
23919         
23920         if(this.sourceEditMode){
23921           
23922             this.syncValue();
23923             this.iframe.className = 'x-hidden';
23924             this.el.removeClass('x-hidden');
23925             this.el.dom.removeAttribute('tabIndex');
23926             this.el.focus();
23927         }else{
23928              
23929             this.pushValue();
23930             this.iframe.className = '';
23931             this.el.addClass('x-hidden');
23932             this.el.dom.setAttribute('tabIndex', -1);
23933             this.deferFocus();
23934         }
23935         this.setSize(this.wrap.getSize());
23936         this.fireEvent('editmodechange', this, this.sourceEditMode);
23937     },
23938
23939     // private used internally
23940     createLink : function(){
23941         var url = prompt(this.createLinkText, this.defaultLinkValue);
23942         if(url && url != 'http:/'+'/'){
23943             this.relayCmd('createlink', url);
23944         }
23945     },
23946
23947     // private (for BoxComponent)
23948     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23949
23950     // private (for BoxComponent)
23951     getResizeEl : function(){
23952         return this.wrap;
23953     },
23954
23955     // private (for BoxComponent)
23956     getPositionEl : function(){
23957         return this.wrap;
23958     },
23959
23960     // private
23961     initEvents : function(){
23962         this.originalValue = this.getValue();
23963     },
23964
23965     /**
23966      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23967      * @method
23968      */
23969     markInvalid : Roo.emptyFn,
23970     /**
23971      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23972      * @method
23973      */
23974     clearInvalid : Roo.emptyFn,
23975
23976     setValue : function(v){
23977         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
23978         this.pushValue();
23979     },
23980
23981     /**
23982      * Protected method that will not generally be called directly. If you need/want
23983      * custom HTML cleanup, this is the method you should override.
23984      * @param {String} html The HTML to be cleaned
23985      * return {String} The cleaned HTML
23986      */
23987     cleanHtml : function(html){
23988         html = String(html);
23989         if(html.length > 5){
23990             if(Roo.isSafari){ // strip safari nonsense
23991                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
23992             }
23993         }
23994         if(html == '&nbsp;'){
23995             html = '';
23996         }
23997         return html;
23998     },
23999
24000     /**
24001      * Protected method that will not generally be called directly. Syncs the contents
24002      * of the editor iframe with the textarea.
24003      */
24004     syncValue : function(){
24005         if(this.initialized){
24006             var bd = (this.doc.body || this.doc.documentElement);
24007             var html = bd.innerHTML;
24008             if(Roo.isSafari){
24009                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24010                 var m = bs.match(/text-align:(.*?);/i);
24011                 if(m && m[1]){
24012                     html = '<div style="'+m[0]+'">' + html + '</div>';
24013                 }
24014             }
24015             html = this.cleanHtml(html);
24016             if(this.fireEvent('beforesync', this, html) !== false){
24017                 this.el.dom.value = html;
24018                 this.fireEvent('sync', this, html);
24019             }
24020         }
24021     },
24022
24023     /**
24024      * Protected method that will not generally be called directly. Pushes the value of the textarea
24025      * into the iframe editor.
24026      */
24027     pushValue : function(){
24028         if(this.initialized){
24029             var v = this.el.dom.value;
24030             if(v.length < 1){
24031                 v = '&#160;';
24032             }
24033             if(this.fireEvent('beforepush', this, v) !== false){
24034                 (this.doc.body || this.doc.documentElement).innerHTML = v;
24035                 this.fireEvent('push', this, v);
24036             }
24037         }
24038     },
24039
24040     // private
24041     deferFocus : function(){
24042         this.focus.defer(10, this);
24043     },
24044
24045     // doc'ed in Field
24046     focus : function(){
24047         if(this.win && !this.sourceEditMode){
24048             this.win.focus();
24049         }else{
24050             this.el.focus();
24051         }
24052     },
24053     
24054     assignDocWin: function()
24055     {
24056         var iframe = this.iframe;
24057         
24058          if(Roo.isIE){
24059             this.doc = iframe.contentWindow.document;
24060             this.win = iframe.contentWindow;
24061         } else {
24062             if (!Roo.get(this.frameId)) {
24063                 return;
24064             }
24065             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24066             this.win = Roo.get(this.frameId).dom.contentWindow;
24067         }
24068     },
24069     
24070     // private
24071     initEditor : function(){
24072         //console.log("INIT EDITOR");
24073         this.assignDocWin();
24074         
24075         
24076         
24077         this.doc.designMode="on";
24078         this.doc.open();
24079         this.doc.write(this.getDocMarkup());
24080         this.doc.close();
24081         
24082         var dbody = (this.doc.body || this.doc.documentElement);
24083         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24084         // this copies styles from the containing element into thsi one..
24085         // not sure why we need all of this..
24086         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24087         ss['background-attachment'] = 'fixed'; // w3c
24088         dbody.bgProperties = 'fixed'; // ie
24089         Roo.DomHelper.applyStyles(dbody, ss);
24090         Roo.EventManager.on(this.doc, {
24091             'mousedown': this.onEditorEvent,
24092             'dblclick': this.onEditorEvent,
24093             'click': this.onEditorEvent,
24094             'keyup': this.onEditorEvent,
24095             buffer:100,
24096             scope: this
24097         });
24098         if(Roo.isGecko){
24099             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24100         }
24101         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24102             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24103         }
24104         this.initialized = true;
24105
24106         this.fireEvent('initialize', this);
24107         this.pushValue();
24108     },
24109
24110     // private
24111     onDestroy : function(){
24112         
24113         
24114         
24115         if(this.rendered){
24116             
24117             for (var i =0; i < this.toolbars.length;i++) {
24118                 // fixme - ask toolbars for heights?
24119                 this.toolbars[i].onDestroy();
24120             }
24121             
24122             this.wrap.dom.innerHTML = '';
24123             this.wrap.remove();
24124         }
24125     },
24126
24127     // private
24128     onFirstFocus : function(){
24129         
24130         this.assignDocWin();
24131         
24132         
24133         this.activated = true;
24134         for (var i =0; i < this.toolbars.length;i++) {
24135             this.toolbars[i].onFirstFocus();
24136         }
24137        
24138         if(Roo.isGecko){ // prevent silly gecko errors
24139             this.win.focus();
24140             var s = this.win.getSelection();
24141             if(!s.focusNode || s.focusNode.nodeType != 3){
24142                 var r = s.getRangeAt(0);
24143                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24144                 r.collapse(true);
24145                 this.deferFocus();
24146             }
24147             try{
24148                 this.execCmd('useCSS', true);
24149                 this.execCmd('styleWithCSS', false);
24150             }catch(e){}
24151         }
24152         this.fireEvent('activate', this);
24153     },
24154
24155     // private
24156     adjustFont: function(btn){
24157         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24158         //if(Roo.isSafari){ // safari
24159         //    adjust *= 2;
24160        // }
24161         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24162         if(Roo.isSafari){ // safari
24163             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24164             v =  (v < 10) ? 10 : v;
24165             v =  (v > 48) ? 48 : v;
24166             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24167             
24168         }
24169         
24170         
24171         v = Math.max(1, v+adjust);
24172         
24173         this.execCmd('FontSize', v  );
24174     },
24175
24176     onEditorEvent : function(e){
24177         this.fireEvent('editorevent', this, e);
24178       //  this.updateToolbar();
24179         this.syncValue();
24180     },
24181
24182     insertTag : function(tg)
24183     {
24184         // could be a bit smarter... -> wrap the current selected tRoo..
24185         
24186         this.execCmd("formatblock",   tg);
24187         
24188     },
24189     
24190     insertText : function(txt)
24191     {
24192         
24193         
24194         range = this.createRange();
24195         range.deleteContents();
24196                //alert(Sender.getAttribute('label'));
24197                
24198         range.insertNode(this.doc.createTextNode(txt));
24199     } ,
24200     
24201     // private
24202     relayBtnCmd : function(btn){
24203         this.relayCmd(btn.cmd);
24204     },
24205
24206     /**
24207      * Executes a Midas editor command on the editor document and performs necessary focus and
24208      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24209      * @param {String} cmd The Midas command
24210      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24211      */
24212     relayCmd : function(cmd, value){
24213         this.win.focus();
24214         this.execCmd(cmd, value);
24215         this.fireEvent('editorevent', this);
24216         //this.updateToolbar();
24217         this.deferFocus();
24218     },
24219
24220     /**
24221      * Executes a Midas editor command directly on the editor document.
24222      * For visual commands, you should use {@link #relayCmd} instead.
24223      * <b>This should only be called after the editor is initialized.</b>
24224      * @param {String} cmd The Midas command
24225      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24226      */
24227     execCmd : function(cmd, value){
24228         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24229         this.syncValue();
24230     },
24231
24232    
24233     /**
24234      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24235      * to insert tRoo.
24236      * @param {String} text
24237      */
24238     insertAtCursor : function(text){
24239         if(!this.activated){
24240             return;
24241         }
24242         if(Roo.isIE){
24243             this.win.focus();
24244             var r = this.doc.selection.createRange();
24245             if(r){
24246                 r.collapse(true);
24247                 r.pasteHTML(text);
24248                 this.syncValue();
24249                 this.deferFocus();
24250             }
24251         }else if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24252             this.win.focus();
24253             this.execCmd('InsertHTML', text);
24254             this.deferFocus();
24255         }
24256     },
24257  // private
24258     mozKeyPress : function(e){
24259         if(e.ctrlKey){
24260             var c = e.getCharCode(), cmd;
24261           
24262             if(c > 0){
24263                 c = String.fromCharCode(c).toLowerCase();
24264                 switch(c){
24265                     case 'b':
24266                         cmd = 'bold';
24267                     break;
24268                     case 'i':
24269                         cmd = 'italic';
24270                     break;
24271                     case 'u':
24272                         cmd = 'underline';
24273                     case 'v':
24274                         this.cleanUpPaste.defer(100, this);
24275                         return;
24276                     break;
24277                 }
24278                 if(cmd){
24279                     this.win.focus();
24280                     this.execCmd(cmd);
24281                     this.deferFocus();
24282                     e.preventDefault();
24283                 }
24284                 
24285             }
24286         }
24287     },
24288
24289     // private
24290     fixKeys : function(){ // load time branching for fastest keydown performance
24291         if(Roo.isIE){
24292             return function(e){
24293                 var k = e.getKey(), r;
24294                 if(k == e.TAB){
24295                     e.stopEvent();
24296                     r = this.doc.selection.createRange();
24297                     if(r){
24298                         r.collapse(true);
24299                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24300                         this.deferFocus();
24301                     }
24302                     return;
24303                 }
24304                 
24305                 if(k == e.ENTER){
24306                     r = this.doc.selection.createRange();
24307                     if(r){
24308                         var target = r.parentElement();
24309                         if(!target || target.tagName.toLowerCase() != 'li'){
24310                             e.stopEvent();
24311                             r.pasteHTML('<br />');
24312                             r.collapse(false);
24313                             r.select();
24314                         }
24315                     }
24316                 }
24317                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24318                     this.cleanUpPaste.defer(100, this);
24319                     return;
24320                 }
24321                 
24322                 
24323             };
24324         }else if(Roo.isOpera){
24325             return function(e){
24326                 var k = e.getKey();
24327                 if(k == e.TAB){
24328                     e.stopEvent();
24329                     this.win.focus();
24330                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24331                     this.deferFocus();
24332                 }
24333                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24334                     this.cleanUpPaste.defer(100, this);
24335                     return;
24336                 }
24337                 
24338             };
24339         }else if(Roo.isSafari){
24340             return function(e){
24341                 var k = e.getKey();
24342                 
24343                 if(k == e.TAB){
24344                     e.stopEvent();
24345                     this.execCmd('InsertText','\t');
24346                     this.deferFocus();
24347                     return;
24348                 }
24349                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24350                     this.cleanUpPaste.defer(100, this);
24351                     return;
24352                 }
24353                 
24354              };
24355         }
24356     }(),
24357     
24358     getAllAncestors: function()
24359     {
24360         var p = this.getSelectedNode();
24361         var a = [];
24362         if (!p) {
24363             a.push(p); // push blank onto stack..
24364             p = this.getParentElement();
24365         }
24366         
24367         
24368         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24369             a.push(p);
24370             p = p.parentNode;
24371         }
24372         a.push(this.doc.body);
24373         return a;
24374     },
24375     lastSel : false,
24376     lastSelNode : false,
24377     
24378     
24379     getSelection : function() 
24380     {
24381         this.assignDocWin();
24382         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24383     },
24384     
24385     getSelectedNode: function() 
24386     {
24387         // this may only work on Gecko!!!
24388         
24389         // should we cache this!!!!
24390         
24391         
24392         
24393          
24394         var range = this.createRange(this.getSelection());
24395         
24396         if (Roo.isIE) {
24397             var parent = range.parentElement();
24398             while (true) {
24399                 var testRange = range.duplicate();
24400                 testRange.moveToElementText(parent);
24401                 if (testRange.inRange(range)) {
24402                     break;
24403                 }
24404                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24405                     break;
24406                 }
24407                 parent = parent.parentElement;
24408             }
24409             return parent;
24410         }
24411         
24412         
24413         var ar = range.endContainer.childNodes;
24414         if (!ar.length) {
24415             ar = range.commonAncestorContainer.childNodes;
24416             //alert(ar.length);
24417         }
24418         var nodes = [];
24419         var other_nodes = [];
24420         var has_other_nodes = false;
24421         for (var i=0;i<ar.length;i++) {
24422             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24423                 continue;
24424             }
24425             // fullly contained node.
24426             
24427             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24428                 nodes.push(ar[i]);
24429                 continue;
24430             }
24431             
24432             // probably selected..
24433             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24434                 other_nodes.push(ar[i]);
24435                 continue;
24436             }
24437             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24438                 continue;
24439             }
24440             
24441             
24442             has_other_nodes = true;
24443         }
24444         if (!nodes.length && other_nodes.length) {
24445             nodes= other_nodes;
24446         }
24447         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24448             return false;
24449         }
24450         
24451         return nodes[0];
24452     },
24453     createRange: function(sel)
24454     {
24455         // this has strange effects when using with 
24456         // top toolbar - not sure if it's a great idea.
24457         //this.editor.contentWindow.focus();
24458         if (typeof sel != "undefined") {
24459             try {
24460                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24461             } catch(e) {
24462                 return this.doc.createRange();
24463             }
24464         } else {
24465             return this.doc.createRange();
24466         }
24467     },
24468     getParentElement: function()
24469     {
24470         
24471         this.assignDocWin();
24472         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24473         
24474         var range = this.createRange(sel);
24475          
24476         try {
24477             var p = range.commonAncestorContainer;
24478             while (p.nodeType == 3) { // text node
24479                 p = p.parentNode;
24480             }
24481             return p;
24482         } catch (e) {
24483             return null;
24484         }
24485     
24486     },
24487     
24488     
24489     
24490     // BC Hacks - cause I cant work out what i was trying to do..
24491     rangeIntersectsNode : function(range, node)
24492     {
24493         var nodeRange = node.ownerDocument.createRange();
24494         try {
24495             nodeRange.selectNode(node);
24496         }
24497         catch (e) {
24498             nodeRange.selectNodeContents(node);
24499         }
24500
24501         return range.compareBoundaryPoints(Range.END_TO_START, nodeRange) == -1 &&
24502                  range.compareBoundaryPoints(Range.START_TO_END, nodeRange) == 1;
24503     },
24504     rangeCompareNode : function(range, node) {
24505         var nodeRange = node.ownerDocument.createRange();
24506         try {
24507             nodeRange.selectNode(node);
24508         } catch (e) {
24509             nodeRange.selectNodeContents(node);
24510         }
24511         var nodeIsBefore = range.compareBoundaryPoints(Range.START_TO_START, nodeRange) == 1;
24512         var nodeIsAfter = range.compareBoundaryPoints(Range.END_TO_END, nodeRange) == -1;
24513
24514         if (nodeIsBefore && !nodeIsAfter)
24515             return 0;
24516         if (!nodeIsBefore && nodeIsAfter)
24517             return 1;
24518         if (nodeIsBefore && nodeIsAfter)
24519             return 2;
24520
24521         return 3;
24522     },
24523
24524     // private? - in a new class?
24525     cleanUpPaste :  function()
24526     {
24527         // cleans up the whole document..
24528       //  console.log('cleanuppaste');
24529         this.cleanUpChildren(this.doc.body)
24530         
24531         
24532     },
24533     cleanUpChildren : function (n)
24534     {
24535         if (!n.childNodes.length) {
24536             return;
24537         }
24538         for (var i = n.childNodes.length-1; i > -1 ; i--) {
24539            this.cleanUpChild(n.childNodes[i]);
24540         }
24541     },
24542     
24543     
24544         
24545     
24546     cleanUpChild : function (node)
24547     {
24548         //console.log(node);
24549         if (node.nodeName == "#text") {
24550             // clean up silly Windows -- stuff?
24551             return; 
24552         }
24553         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
24554             // remove node.
24555             node.parentNode.removeChild(node);
24556             return;
24557             
24558         }
24559         if (!node.attributes || !node.attributes.length) {
24560             this.cleanUpChildren(node);
24561             return;
24562         }
24563         
24564         function cleanAttr(n,v)
24565         {
24566             
24567             if (v.match(/^\./) || v.match(/^\//)) {
24568                 return;
24569             }
24570             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
24571                 return;
24572             }
24573             Roo.log("(REMOVE)"+ node.tagName +'.' + n + '=' + v);
24574             node.removeAttribute(n);
24575             
24576         }
24577         
24578         function cleanStyle(n,v)
24579         {
24580             if (v.match(/expression/)) { //XSS?? should we even bother..
24581                 node.removeAttribute(n);
24582                 return;
24583             }
24584             var parts = v.split(/;/);
24585             Roo.each(parts, function(p) {
24586                 p = p.replace(/\s+/g,'');
24587                 if (!p.length) {
24588                     return;
24589                 }
24590                 var l = p.split(':').shift().replace(/\s+/g,'');
24591                 
24592                 if (Roo.form.HtmlEditor.cwhite.indexOf(l) < 0) {
24593                     Roo.log('(REMOVE)' + node.tagName +'.' + n + ':'+l + '=' + v);
24594                     node.removeAttribute(n);
24595                     return false;
24596                 }
24597             });
24598             
24599             
24600         }
24601         
24602         
24603         for (var i = node.attributes.length-1; i > -1 ; i--) {
24604             var a = node.attributes[i];
24605             //console.log(a);
24606             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
24607                 node.removeAttribute(a.name);
24608                 return;
24609             }
24610             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
24611                 cleanAttr(a.name,a.value); // fixme..
24612                 return;
24613             }
24614             if (a.name == 'style') {
24615                 cleanStyle(a.name,a.value);
24616             }
24617             
24618             
24619             // style cleanup!?
24620             // class cleanup?
24621             
24622         }
24623         
24624         
24625         this.cleanUpChildren(node);
24626         
24627         
24628     }
24629     
24630     
24631     // hide stuff that is not compatible
24632     /**
24633      * @event blur
24634      * @hide
24635      */
24636     /**
24637      * @event change
24638      * @hide
24639      */
24640     /**
24641      * @event focus
24642      * @hide
24643      */
24644     /**
24645      * @event specialkey
24646      * @hide
24647      */
24648     /**
24649      * @cfg {String} fieldClass @hide
24650      */
24651     /**
24652      * @cfg {String} focusClass @hide
24653      */
24654     /**
24655      * @cfg {String} autoCreate @hide
24656      */
24657     /**
24658      * @cfg {String} inputType @hide
24659      */
24660     /**
24661      * @cfg {String} invalidClass @hide
24662      */
24663     /**
24664      * @cfg {String} invalidText @hide
24665      */
24666     /**
24667      * @cfg {String} msgFx @hide
24668      */
24669     /**
24670      * @cfg {String} validateOnBlur @hide
24671      */
24672 });
24673
24674 Roo.form.HtmlEditor.white = [
24675         'area', 'br', 'img', 'input', 'hr', 'wbr',
24676         
24677        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
24678        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
24679        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
24680        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
24681        'table',   'ul',         'xmp', 
24682        
24683        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
24684       'thead',   'tr', 
24685      
24686       'dir', 'menu', 'ol', 'ul', 'dl'
24687        
24688 ];
24689
24690
24691 Roo.form.HtmlEditor.black = [
24692         'embed',  'object', // eventually enable for flash?
24693         'applet', // 
24694         'base',   'basefont', 'bgsound', 'blink',  'body', 
24695         'frame',  'frameset', 'head',    'html',   'ilayer', 
24696         'iframe', 'layer',  'link',     'meta',    'object', 
24697         
24698         'script', 'style' ,'title',  'xml' // clean later..
24699 ];
24700 Roo.form.HtmlEditor.clean = [
24701     'script', 'style', 'title', 'xml'
24702 ];
24703
24704 // attributes..
24705
24706 Roo.form.HtmlEditor.ablack = [
24707     'on'
24708 ];
24709     
24710 Roo.form.HtmlEditor.aclean = [ 
24711     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
24712 ];
24713
24714 // protocols..
24715 Roo.form.HtmlEditor.pwhite= [
24716         'http',  'https',  'mailto'
24717 ];
24718
24719 Roo.form.HtmlEditor.cwhite= [
24720         'text-align',
24721         'font-size'
24722 ];
24723
24724 // <script type="text/javascript">
24725 /*
24726  * Based on
24727  * Ext JS Library 1.1.1
24728  * Copyright(c) 2006-2007, Ext JS, LLC.
24729  *  
24730  
24731  */
24732
24733 /**
24734  * @class Roo.form.HtmlEditorToolbar1
24735  * Basic Toolbar
24736  * 
24737  * Usage:
24738  *
24739  new Roo.form.HtmlEditor({
24740     ....
24741     toolbars : [
24742         new Roo.form.HtmlEditorToolbar1({
24743             disable : { fonts: 1 , format: 1, ..., ... , ...],
24744             btns : [ .... ]
24745         })
24746     }
24747      
24748  * 
24749  * @cfg {Object} disable List of elements to disable..
24750  * @cfg {Array} btns List of additional buttons.
24751  * 
24752  * 
24753  * NEEDS Extra CSS? 
24754  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
24755  */
24756  
24757 Roo.form.HtmlEditor.ToolbarStandard = function(config)
24758 {
24759     
24760     Roo.apply(this, config);
24761     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
24762     // dont call parent... till later.
24763 }
24764
24765 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
24766     
24767     tb: false,
24768     
24769     rendered: false,
24770     
24771     editor : false,
24772     /**
24773      * @cfg {Object} disable  List of toolbar elements to disable
24774          
24775      */
24776     disable : false,
24777       /**
24778      * @cfg {Array} fontFamilies An array of available font families
24779      */
24780     fontFamilies : [
24781         'Arial',
24782         'Courier New',
24783         'Tahoma',
24784         'Times New Roman',
24785         'Verdana'
24786     ],
24787     
24788     specialChars : [
24789            "&#169;",
24790           "&#174;",     
24791           "&#8482;",    
24792           "&#163;" ,    
24793          // "&#8212;",    
24794           "&#8230;",    
24795           "&#247;" ,    
24796         //  "&#225;" ,     ?? a acute?
24797            "&#8364;"    , //Euro
24798        //   "&#8220;"    ,
24799         //  "&#8221;"    ,
24800         //  "&#8226;"    ,
24801           "&#176;"  //   , // degrees
24802
24803          // "&#233;"     , // e ecute
24804          // "&#250;"     , // u ecute?
24805     ],
24806     inputElements : [ 
24807             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
24808             "input:submit", "input:button", "select", "textarea", "label" ],
24809     formats : [
24810         ["p"] ,  
24811         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
24812         ["pre"],[ "code"], 
24813         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
24814     ],
24815      /**
24816      * @cfg {String} defaultFont default font to use.
24817      */
24818     defaultFont: 'tahoma',
24819    
24820     fontSelect : false,
24821     
24822     
24823     formatCombo : false,
24824     
24825     init : function(editor)
24826     {
24827         this.editor = editor;
24828         
24829         
24830         var fid = editor.frameId;
24831         var etb = this;
24832         function btn(id, toggle, handler){
24833             var xid = fid + '-'+ id ;
24834             return {
24835                 id : xid,
24836                 cmd : id,
24837                 cls : 'x-btn-icon x-edit-'+id,
24838                 enableToggle:toggle !== false,
24839                 scope: editor, // was editor...
24840                 handler:handler||editor.relayBtnCmd,
24841                 clickEvent:'mousedown',
24842                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
24843                 tabIndex:-1
24844             };
24845         }
24846         
24847         
24848         
24849         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
24850         this.tb = tb;
24851          // stop form submits
24852         tb.el.on('click', function(e){
24853             e.preventDefault(); // what does this do?
24854         });
24855
24856         if(!this.disable.font && !Roo.isSafari){
24857             /* why no safari for fonts
24858             editor.fontSelect = tb.el.createChild({
24859                 tag:'select',
24860                 tabIndex: -1,
24861                 cls:'x-font-select',
24862                 html: editor.createFontOptions()
24863             });
24864             editor.fontSelect.on('change', function(){
24865                 var font = editor.fontSelect.dom.value;
24866                 editor.relayCmd('fontname', font);
24867                 editor.deferFocus();
24868             }, editor);
24869             tb.add(
24870                 editor.fontSelect.dom,
24871                 '-'
24872             );
24873             */
24874         };
24875         if(!this.disable.formats){
24876             this.formatCombo = new Roo.form.ComboBox({
24877                 store: new Roo.data.SimpleStore({
24878                     id : 'tag',
24879                     fields: ['tag'],
24880                     data : this.formats // from states.js
24881                 }),
24882                 blockFocus : true,
24883                 //autoCreate : {tag: "div",  size: "20"},
24884                 displayField:'tag',
24885                 typeAhead: false,
24886                 mode: 'local',
24887                 editable : false,
24888                 triggerAction: 'all',
24889                 emptyText:'Add tag',
24890                 selectOnFocus:true,
24891                 width:135,
24892                 listeners : {
24893                     'select': function(c, r, i) {
24894                         editor.insertTag(r.get('tag'));
24895                         editor.focus();
24896                     }
24897                 }
24898
24899             });
24900             tb.addField(this.formatCombo);
24901             
24902         }
24903         
24904         if(!this.disable.format){
24905             tb.add(
24906                 btn('bold'),
24907                 btn('italic'),
24908                 btn('underline')
24909             );
24910         };
24911         if(!this.disable.fontSize){
24912             tb.add(
24913                 '-',
24914                 
24915                 
24916                 btn('increasefontsize', false, editor.adjustFont),
24917                 btn('decreasefontsize', false, editor.adjustFont)
24918             );
24919         };
24920         
24921         
24922         if(this.disable.colors){
24923             tb.add(
24924                 '-', {
24925                     id:editor.frameId +'-forecolor',
24926                     cls:'x-btn-icon x-edit-forecolor',
24927                     clickEvent:'mousedown',
24928                     tooltip: this.buttonTips['forecolor'] || undefined,
24929                     tabIndex:-1,
24930                     menu : new Roo.menu.ColorMenu({
24931                         allowReselect: true,
24932                         focus: Roo.emptyFn,
24933                         value:'000000',
24934                         plain:true,
24935                         selectHandler: function(cp, color){
24936                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
24937                             editor.deferFocus();
24938                         },
24939                         scope: editor,
24940                         clickEvent:'mousedown'
24941                     })
24942                 }, {
24943                     id:editor.frameId +'backcolor',
24944                     cls:'x-btn-icon x-edit-backcolor',
24945                     clickEvent:'mousedown',
24946                     tooltip: this.buttonTips['backcolor'] || undefined,
24947                     tabIndex:-1,
24948                     menu : new Roo.menu.ColorMenu({
24949                         focus: Roo.emptyFn,
24950                         value:'FFFFFF',
24951                         plain:true,
24952                         allowReselect: true,
24953                         selectHandler: function(cp, color){
24954                             if(Roo.isGecko){
24955                                 editor.execCmd('useCSS', false);
24956                                 editor.execCmd('hilitecolor', color);
24957                                 editor.execCmd('useCSS', true);
24958                                 editor.deferFocus();
24959                             }else{
24960                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
24961                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
24962                                 editor.deferFocus();
24963                             }
24964                         },
24965                         scope:editor,
24966                         clickEvent:'mousedown'
24967                     })
24968                 }
24969             );
24970         };
24971         // now add all the items...
24972         
24973
24974         if(!this.disable.alignments){
24975             tb.add(
24976                 '-',
24977                 btn('justifyleft'),
24978                 btn('justifycenter'),
24979                 btn('justifyright')
24980             );
24981         };
24982
24983         //if(!Roo.isSafari){
24984             if(!this.disable.links){
24985                 tb.add(
24986                     '-',
24987                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
24988                 );
24989             };
24990
24991             if(!this.disable.lists){
24992                 tb.add(
24993                     '-',
24994                     btn('insertorderedlist'),
24995                     btn('insertunorderedlist')
24996                 );
24997             }
24998             if(!this.disable.sourceEdit){
24999                 tb.add(
25000                     '-',
25001                     btn('sourceedit', true, function(btn){
25002                         this.toggleSourceEdit(btn.pressed);
25003                     })
25004                 );
25005             }
25006         //}
25007         
25008         var smenu = { };
25009         // special menu.. - needs to be tidied up..
25010         if (!this.disable.special) {
25011             smenu = {
25012                 text: "&#169;",
25013                 cls: 'x-edit-none',
25014                 menu : {
25015                     items : []
25016                    }
25017             };
25018             for (var i =0; i < this.specialChars.length; i++) {
25019                 smenu.menu.items.push({
25020                     
25021                     html: this.specialChars[i],
25022                     handler: function(a,b) {
25023                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
25024                         
25025                     },
25026                     tabIndex:-1
25027                 });
25028             }
25029             
25030             
25031             tb.add(smenu);
25032             
25033             
25034         }
25035         if (this.btns) {
25036             for(var i =0; i< this.btns.length;i++) {
25037                 var b = this.btns[i];
25038                 b.cls =  'x-edit-none';
25039                 b.scope = editor;
25040                 tb.add(b);
25041             }
25042         
25043         }
25044         
25045         
25046         
25047         // disable everything...
25048         
25049         this.tb.items.each(function(item){
25050            if(item.id != editor.frameId+ '-sourceedit'){
25051                 item.disable();
25052             }
25053         });
25054         this.rendered = true;
25055         
25056         // the all the btns;
25057         editor.on('editorevent', this.updateToolbar, this);
25058         // other toolbars need to implement this..
25059         //editor.on('editmodechange', this.updateToolbar, this);
25060     },
25061     
25062     
25063     
25064     /**
25065      * Protected method that will not generally be called directly. It triggers
25066      * a toolbar update by reading the markup state of the current selection in the editor.
25067      */
25068     updateToolbar: function(){
25069
25070         if(!this.editor.activated){
25071             this.editor.onFirstFocus();
25072             return;
25073         }
25074
25075         var btns = this.tb.items.map, 
25076             doc = this.editor.doc,
25077             frameId = this.editor.frameId;
25078
25079         if(!this.disable.font && !Roo.isSafari){
25080             /*
25081             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
25082             if(name != this.fontSelect.dom.value){
25083                 this.fontSelect.dom.value = name;
25084             }
25085             */
25086         }
25087         if(!this.disable.format){
25088             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
25089             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
25090             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
25091         }
25092         if(!this.disable.alignments){
25093             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
25094             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
25095             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
25096         }
25097         if(!Roo.isSafari && !this.disable.lists){
25098             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
25099             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
25100         }
25101         
25102         var ans = this.editor.getAllAncestors();
25103         if (this.formatCombo) {
25104             
25105             
25106             var store = this.formatCombo.store;
25107             this.formatCombo.setValue("");
25108             for (var i =0; i < ans.length;i++) {
25109                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
25110                     // select it..
25111                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
25112                     break;
25113                 }
25114             }
25115         }
25116         
25117         
25118         
25119         // hides menus... - so this cant be on a menu...
25120         Roo.menu.MenuMgr.hideAll();
25121
25122         //this.editorsyncValue();
25123     },
25124    
25125     
25126     createFontOptions : function(){
25127         var buf = [], fs = this.fontFamilies, ff, lc;
25128         for(var i = 0, len = fs.length; i< len; i++){
25129             ff = fs[i];
25130             lc = ff.toLowerCase();
25131             buf.push(
25132                 '<option value="',lc,'" style="font-family:',ff,';"',
25133                     (this.defaultFont == lc ? ' selected="true">' : '>'),
25134                     ff,
25135                 '</option>'
25136             );
25137         }
25138         return buf.join('');
25139     },
25140     
25141     toggleSourceEdit : function(sourceEditMode){
25142         if(sourceEditMode === undefined){
25143             sourceEditMode = !this.sourceEditMode;
25144         }
25145         this.sourceEditMode = sourceEditMode === true;
25146         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
25147         // just toggle the button?
25148         if(btn.pressed !== this.editor.sourceEditMode){
25149             btn.toggle(this.editor.sourceEditMode);
25150             return;
25151         }
25152         
25153         if(this.sourceEditMode){
25154             this.tb.items.each(function(item){
25155                 if(item.cmd != 'sourceedit'){
25156                     item.disable();
25157                 }
25158             });
25159           
25160         }else{
25161             if(this.initialized){
25162                 this.tb.items.each(function(item){
25163                     item.enable();
25164                 });
25165             }
25166             
25167         }
25168         // tell the editor that it's been pressed..
25169         this.editor.toggleSourceEdit(sourceEditMode);
25170        
25171     },
25172      /**
25173      * Object collection of toolbar tooltips for the buttons in the editor. The key
25174      * is the command id associated with that button and the value is a valid QuickTips object.
25175      * For example:
25176 <pre><code>
25177 {
25178     bold : {
25179         title: 'Bold (Ctrl+B)',
25180         text: 'Make the selected text bold.',
25181         cls: 'x-html-editor-tip'
25182     },
25183     italic : {
25184         title: 'Italic (Ctrl+I)',
25185         text: 'Make the selected text italic.',
25186         cls: 'x-html-editor-tip'
25187     },
25188     ...
25189 </code></pre>
25190     * @type Object
25191      */
25192     buttonTips : {
25193         bold : {
25194             title: 'Bold (Ctrl+B)',
25195             text: 'Make the selected text bold.',
25196             cls: 'x-html-editor-tip'
25197         },
25198         italic : {
25199             title: 'Italic (Ctrl+I)',
25200             text: 'Make the selected text italic.',
25201             cls: 'x-html-editor-tip'
25202         },
25203         underline : {
25204             title: 'Underline (Ctrl+U)',
25205             text: 'Underline the selected text.',
25206             cls: 'x-html-editor-tip'
25207         },
25208         increasefontsize : {
25209             title: 'Grow Text',
25210             text: 'Increase the font size.',
25211             cls: 'x-html-editor-tip'
25212         },
25213         decreasefontsize : {
25214             title: 'Shrink Text',
25215             text: 'Decrease the font size.',
25216             cls: 'x-html-editor-tip'
25217         },
25218         backcolor : {
25219             title: 'Text Highlight Color',
25220             text: 'Change the background color of the selected text.',
25221             cls: 'x-html-editor-tip'
25222         },
25223         forecolor : {
25224             title: 'Font Color',
25225             text: 'Change the color of the selected text.',
25226             cls: 'x-html-editor-tip'
25227         },
25228         justifyleft : {
25229             title: 'Align Text Left',
25230             text: 'Align text to the left.',
25231             cls: 'x-html-editor-tip'
25232         },
25233         justifycenter : {
25234             title: 'Center Text',
25235             text: 'Center text in the editor.',
25236             cls: 'x-html-editor-tip'
25237         },
25238         justifyright : {
25239             title: 'Align Text Right',
25240             text: 'Align text to the right.',
25241             cls: 'x-html-editor-tip'
25242         },
25243         insertunorderedlist : {
25244             title: 'Bullet List',
25245             text: 'Start a bulleted list.',
25246             cls: 'x-html-editor-tip'
25247         },
25248         insertorderedlist : {
25249             title: 'Numbered List',
25250             text: 'Start a numbered list.',
25251             cls: 'x-html-editor-tip'
25252         },
25253         createlink : {
25254             title: 'Hyperlink',
25255             text: 'Make the selected text a hyperlink.',
25256             cls: 'x-html-editor-tip'
25257         },
25258         sourceedit : {
25259             title: 'Source Edit',
25260             text: 'Switch to source editing mode.',
25261             cls: 'x-html-editor-tip'
25262         }
25263     },
25264     // private
25265     onDestroy : function(){
25266         if(this.rendered){
25267             
25268             this.tb.items.each(function(item){
25269                 if(item.menu){
25270                     item.menu.removeAll();
25271                     if(item.menu.el){
25272                         item.menu.el.destroy();
25273                     }
25274                 }
25275                 item.destroy();
25276             });
25277              
25278         }
25279     },
25280     onFirstFocus: function() {
25281         this.tb.items.each(function(item){
25282            item.enable();
25283         });
25284     }
25285 });
25286
25287
25288
25289
25290 // <script type="text/javascript">
25291 /*
25292  * Based on
25293  * Ext JS Library 1.1.1
25294  * Copyright(c) 2006-2007, Ext JS, LLC.
25295  *  
25296  
25297  */
25298
25299  
25300 /**
25301  * @class Roo.form.HtmlEditor.ToolbarContext
25302  * Context Toolbar
25303  * 
25304  * Usage:
25305  *
25306  new Roo.form.HtmlEditor({
25307     ....
25308     toolbars : [
25309         new Roo.form.HtmlEditor.ToolbarStandard(),
25310         new Roo.form.HtmlEditor.ToolbarContext()
25311         })
25312     }
25313      
25314  * 
25315  * @config : {Object} disable List of elements to disable.. (not done yet.)
25316  * 
25317  * 
25318  */
25319
25320 Roo.form.HtmlEditor.ToolbarContext = function(config)
25321 {
25322     
25323     Roo.apply(this, config);
25324     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
25325     // dont call parent... till later.
25326 }
25327 Roo.form.HtmlEditor.ToolbarContext.types = {
25328     'IMG' : {
25329         width : {
25330             title: "Width",
25331             width: 40
25332         },
25333         height:  {
25334             title: "Height",
25335             width: 40
25336         },
25337         align: {
25338             title: "Align",
25339             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
25340             width : 80
25341             
25342         },
25343         border: {
25344             title: "Border",
25345             width: 40
25346         },
25347         alt: {
25348             title: "Alt",
25349             width: 120
25350         },
25351         src : {
25352             title: "Src",
25353             width: 220
25354         }
25355         
25356     },
25357     'A' : {
25358         name : {
25359             title: "Name",
25360             width: 50
25361         },
25362         href:  {
25363             title: "Href",
25364             width: 220
25365         } // border?
25366         
25367     },
25368     'TABLE' : {
25369         rows : {
25370             title: "Rows",
25371             width: 20
25372         },
25373         cols : {
25374             title: "Cols",
25375             width: 20
25376         },
25377         width : {
25378             title: "Width",
25379             width: 40
25380         },
25381         height : {
25382             title: "Height",
25383             width: 40
25384         },
25385         border : {
25386             title: "Border",
25387             width: 20
25388         }
25389     },
25390     'TD' : {
25391         width : {
25392             title: "Width",
25393             width: 40
25394         },
25395         height : {
25396             title: "Height",
25397             width: 40
25398         },   
25399         align: {
25400             title: "Align",
25401             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
25402             width: 40
25403         },
25404         valign: {
25405             title: "Valign",
25406             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
25407             width: 40
25408         },
25409         colspan: {
25410             title: "Colspan",
25411             width: 20
25412             
25413         }
25414     },
25415     'INPUT' : {
25416         name : {
25417             title: "name",
25418             width: 120
25419         },
25420         value : {
25421             title: "Value",
25422             width: 120
25423         },
25424         width : {
25425             title: "Width",
25426             width: 40
25427         }
25428     },
25429     'LABEL' : {
25430         'for' : {
25431             title: "For",
25432             width: 120
25433         }
25434     },
25435     'TEXTAREA' : {
25436           name : {
25437             title: "name",
25438             width: 120
25439         },
25440         rows : {
25441             title: "Rows",
25442             width: 20
25443         },
25444         cols : {
25445             title: "Cols",
25446             width: 20
25447         }
25448     },
25449     'SELECT' : {
25450         name : {
25451             title: "name",
25452             width: 120
25453         },
25454         selectoptions : {
25455             title: "Options",
25456             width: 200
25457         }
25458     },
25459     'BODY' : {
25460         title : {
25461             title: "title",
25462             width: 120,
25463             disabled : true
25464         }
25465     }
25466 };
25467
25468
25469
25470 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
25471     
25472     tb: false,
25473     
25474     rendered: false,
25475     
25476     editor : false,
25477     /**
25478      * @cfg {Object} disable  List of toolbar elements to disable
25479          
25480      */
25481     disable : false,
25482     
25483     
25484     
25485     toolbars : false,
25486     
25487     init : function(editor)
25488     {
25489         this.editor = editor;
25490         
25491         
25492         var fid = editor.frameId;
25493         var etb = this;
25494         function btn(id, toggle, handler){
25495             var xid = fid + '-'+ id ;
25496             return {
25497                 id : xid,
25498                 cmd : id,
25499                 cls : 'x-btn-icon x-edit-'+id,
25500                 enableToggle:toggle !== false,
25501                 scope: editor, // was editor...
25502                 handler:handler||editor.relayBtnCmd,
25503                 clickEvent:'mousedown',
25504                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
25505                 tabIndex:-1
25506             };
25507         }
25508         // create a new element.
25509         var wdiv = editor.wrap.createChild({
25510                 tag: 'div'
25511             }, editor.wrap.dom.firstChild.nextSibling, true);
25512         
25513         // can we do this more than once??
25514         
25515          // stop form submits
25516       
25517  
25518         // disable everything...
25519         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
25520         this.toolbars = {};
25521            
25522         for (var i in  ty) {
25523             this.toolbars[i] = this.buildToolbar(ty[i],i);
25524         }
25525         this.tb = this.toolbars.BODY;
25526         this.tb.el.show();
25527         
25528          
25529         this.rendered = true;
25530         
25531         // the all the btns;
25532         editor.on('editorevent', this.updateToolbar, this);
25533         // other toolbars need to implement this..
25534         //editor.on('editmodechange', this.updateToolbar, this);
25535     },
25536     
25537     
25538     
25539     /**
25540      * Protected method that will not generally be called directly. It triggers
25541      * a toolbar update by reading the markup state of the current selection in the editor.
25542      */
25543     updateToolbar: function(){
25544
25545         if(!this.editor.activated){
25546             this.editor.onFirstFocus();
25547             return;
25548         }
25549
25550         
25551         var ans = this.editor.getAllAncestors();
25552         
25553         // pick
25554         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
25555         var sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
25556         sel = sel ? sel : this.editor.doc.body;
25557         sel = sel.tagName.length ? sel : this.editor.doc.body;
25558         var tn = sel.tagName.toUpperCase();
25559         sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
25560         tn = sel.tagName.toUpperCase();
25561         if (this.tb.name  == tn) {
25562             return; // no change
25563         }
25564         this.tb.el.hide();
25565         ///console.log("show: " + tn);
25566         this.tb =  this.toolbars[tn];
25567         this.tb.el.show();
25568         this.tb.fields.each(function(e) {
25569             e.setValue(sel.getAttribute(e.name));
25570         });
25571         this.tb.selectedNode = sel;
25572         
25573         
25574         Roo.menu.MenuMgr.hideAll();
25575
25576         //this.editorsyncValue();
25577     },
25578    
25579        
25580     // private
25581     onDestroy : function(){
25582         if(this.rendered){
25583             
25584             this.tb.items.each(function(item){
25585                 if(item.menu){
25586                     item.menu.removeAll();
25587                     if(item.menu.el){
25588                         item.menu.el.destroy();
25589                     }
25590                 }
25591                 item.destroy();
25592             });
25593              
25594         }
25595     },
25596     onFirstFocus: function() {
25597         // need to do this for all the toolbars..
25598         this.tb.items.each(function(item){
25599            item.enable();
25600         });
25601     },
25602     buildToolbar: function(tlist, nm)
25603     {
25604         var editor = this.editor;
25605          // create a new element.
25606         var wdiv = editor.wrap.createChild({
25607                 tag: 'div'
25608             }, editor.wrap.dom.firstChild.nextSibling, true);
25609         
25610        
25611         var tb = new Roo.Toolbar(wdiv);
25612         tb.add(nm+ ":&nbsp;");
25613         for (var i in tlist) {
25614             var item = tlist[i];
25615             tb.add(item.title + ":&nbsp;");
25616             if (item.opts) {
25617                 // fixme
25618                 
25619               
25620                 tb.addField( new Roo.form.ComboBox({
25621                     store: new Roo.data.SimpleStore({
25622                         id : 'val',
25623                         fields: ['val'],
25624                         data : item.opts // from states.js
25625                     }),
25626                     name : i,
25627                     displayField:'val',
25628                     typeAhead: false,
25629                     mode: 'local',
25630                     editable : false,
25631                     triggerAction: 'all',
25632                     emptyText:'Select',
25633                     selectOnFocus:true,
25634                     width: item.width ? item.width  : 130,
25635                     listeners : {
25636                         'select': function(c, r, i) {
25637                             tb.selectedNode.setAttribute(c.name, r.get('val'));
25638                         }
25639                     }
25640
25641                 }));
25642                 continue;
25643                     
25644                 
25645                 
25646                 
25647                 
25648                 tb.addField( new Roo.form.TextField({
25649                     name: i,
25650                     width: 100,
25651                     //allowBlank:false,
25652                     value: ''
25653                 }));
25654                 continue;
25655             }
25656             tb.addField( new Roo.form.TextField({
25657                 name: i,
25658                 width: item.width,
25659                 //allowBlank:true,
25660                 value: '',
25661                 listeners: {
25662                     'change' : function(f, nv, ov) {
25663                         tb.selectedNode.setAttribute(f.name, nv);
25664                     }
25665                 }
25666             }));
25667              
25668         }
25669         tb.el.on('click', function(e){
25670             e.preventDefault(); // what does this do?
25671         });
25672         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
25673         tb.el.hide();
25674         tb.name = nm;
25675         // dont need to disable them... as they will get hidden
25676         return tb;
25677          
25678         
25679     }
25680     
25681     
25682     
25683     
25684 });
25685
25686
25687
25688
25689
25690 /*
25691  * Based on:
25692  * Ext JS Library 1.1.1
25693  * Copyright(c) 2006-2007, Ext JS, LLC.
25694  *
25695  * Originally Released Under LGPL - original licence link has changed is not relivant.
25696  *
25697  * Fork - LGPL
25698  * <script type="text/javascript">
25699  */
25700  
25701 /**
25702  * @class Roo.form.BasicForm
25703  * @extends Roo.util.Observable
25704  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
25705  * @constructor
25706  * @param {String/HTMLElement/Roo.Element} el The form element or its id
25707  * @param {Object} config Configuration options
25708  */
25709 Roo.form.BasicForm = function(el, config){
25710     this.allItems = [];
25711     this.childForms = [];
25712     Roo.apply(this, config);
25713     /*
25714      * The Roo.form.Field items in this form.
25715      * @type MixedCollection
25716      */
25717      
25718      
25719     this.items = new Roo.util.MixedCollection(false, function(o){
25720         return o.id || (o.id = Roo.id());
25721     });
25722     this.addEvents({
25723         /**
25724          * @event beforeaction
25725          * Fires before any action is performed. Return false to cancel the action.
25726          * @param {Form} this
25727          * @param {Action} action The action to be performed
25728          */
25729         beforeaction: true,
25730         /**
25731          * @event actionfailed
25732          * Fires when an action fails.
25733          * @param {Form} this
25734          * @param {Action} action The action that failed
25735          */
25736         actionfailed : true,
25737         /**
25738          * @event actioncomplete
25739          * Fires when an action is completed.
25740          * @param {Form} this
25741          * @param {Action} action The action that completed
25742          */
25743         actioncomplete : true
25744     });
25745     if(el){
25746         this.initEl(el);
25747     }
25748     Roo.form.BasicForm.superclass.constructor.call(this);
25749 };
25750
25751 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
25752     /**
25753      * @cfg {String} method
25754      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
25755      */
25756     /**
25757      * @cfg {DataReader} reader
25758      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
25759      * This is optional as there is built-in support for processing JSON.
25760      */
25761     /**
25762      * @cfg {DataReader} errorReader
25763      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
25764      * This is completely optional as there is built-in support for processing JSON.
25765      */
25766     /**
25767      * @cfg {String} url
25768      * The URL to use for form actions if one isn't supplied in the action options.
25769      */
25770     /**
25771      * @cfg {Boolean} fileUpload
25772      * Set to true if this form is a file upload.
25773      */
25774     /**
25775      * @cfg {Object} baseParams
25776      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
25777      */
25778     /**
25779      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
25780      */
25781     timeout: 30,
25782
25783     // private
25784     activeAction : null,
25785
25786     /**
25787      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
25788      * or setValues() data instead of when the form was first created.
25789      */
25790     trackResetOnLoad : false,
25791     
25792     
25793     /**
25794      * childForms - used for multi-tab forms
25795      * @type {Array}
25796      */
25797     childForms : false,
25798     
25799     /**
25800      * allItems - full list of fields.
25801      * @type {Array}
25802      */
25803     allItems : false,
25804     
25805     /**
25806      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
25807      * element by passing it or its id or mask the form itself by passing in true.
25808      * @type Mixed
25809      */
25810     waitMsgTarget : undefined,
25811
25812     // private
25813     initEl : function(el){
25814         this.el = Roo.get(el);
25815         this.id = this.el.id || Roo.id();
25816         this.el.on('submit', this.onSubmit, this);
25817         this.el.addClass('x-form');
25818     },
25819
25820     // private
25821     onSubmit : function(e){
25822         e.stopEvent();
25823     },
25824
25825     /**
25826      * Returns true if client-side validation on the form is successful.
25827      * @return Boolean
25828      */
25829     isValid : function(){
25830         var valid = true;
25831         this.items.each(function(f){
25832            if(!f.validate()){
25833                valid = false;
25834            }
25835         });
25836         return valid;
25837     },
25838
25839     /**
25840      * Returns true if any fields in this form have changed since their original load.
25841      * @return Boolean
25842      */
25843     isDirty : function(){
25844         var dirty = false;
25845         this.items.each(function(f){
25846            if(f.isDirty()){
25847                dirty = true;
25848                return false;
25849            }
25850         });
25851         return dirty;
25852     },
25853
25854     /**
25855      * Performs a predefined action (submit or load) or custom actions you define on this form.
25856      * @param {String} actionName The name of the action type
25857      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
25858      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
25859      * accept other config options):
25860      * <pre>
25861 Property          Type             Description
25862 ----------------  ---------------  ----------------------------------------------------------------------------------
25863 url               String           The url for the action (defaults to the form's url)
25864 method            String           The form method to use (defaults to the form's method, or POST if not defined)
25865 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
25866 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
25867                                    validate the form on the client (defaults to false)
25868      * </pre>
25869      * @return {BasicForm} this
25870      */
25871     doAction : function(action, options){
25872         if(typeof action == 'string'){
25873             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
25874         }
25875         if(this.fireEvent('beforeaction', this, action) !== false){
25876             this.beforeAction(action);
25877             action.run.defer(100, action);
25878         }
25879         return this;
25880     },
25881
25882     /**
25883      * Shortcut to do a submit action.
25884      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
25885      * @return {BasicForm} this
25886      */
25887     submit : function(options){
25888         this.doAction('submit', options);
25889         return this;
25890     },
25891
25892     /**
25893      * Shortcut to do a load action.
25894      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
25895      * @return {BasicForm} this
25896      */
25897     load : function(options){
25898         this.doAction('load', options);
25899         return this;
25900     },
25901
25902     /**
25903      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
25904      * @param {Record} record The record to edit
25905      * @return {BasicForm} this
25906      */
25907     updateRecord : function(record){
25908         record.beginEdit();
25909         var fs = record.fields;
25910         fs.each(function(f){
25911             var field = this.findField(f.name);
25912             if(field){
25913                 record.set(f.name, field.getValue());
25914             }
25915         }, this);
25916         record.endEdit();
25917         return this;
25918     },
25919
25920     /**
25921      * Loads an Roo.data.Record into this form.
25922      * @param {Record} record The record to load
25923      * @return {BasicForm} this
25924      */
25925     loadRecord : function(record){
25926         this.setValues(record.data);
25927         return this;
25928     },
25929
25930     // private
25931     beforeAction : function(action){
25932         var o = action.options;
25933         if(o.waitMsg){
25934             if(this.waitMsgTarget === true){
25935                 this.el.mask(o.waitMsg, 'x-mask-loading');
25936             }else if(this.waitMsgTarget){
25937                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
25938                 this.waitMsgTarget.mask(o.waitMsg, 'x-mask-loading');
25939             }else{
25940                 Roo.MessageBox.wait(o.waitMsg, o.waitTitle || this.waitTitle || 'Please Wait...');
25941             }
25942         }
25943     },
25944
25945     // private
25946     afterAction : function(action, success){
25947         this.activeAction = null;
25948         var o = action.options;
25949         if(o.waitMsg){
25950             if(this.waitMsgTarget === true){
25951                 this.el.unmask();
25952             }else if(this.waitMsgTarget){
25953                 this.waitMsgTarget.unmask();
25954             }else{
25955                 Roo.MessageBox.updateProgress(1);
25956                 Roo.MessageBox.hide();
25957             }
25958         }
25959         if(success){
25960             if(o.reset){
25961                 this.reset();
25962             }
25963             Roo.callback(o.success, o.scope, [this, action]);
25964             this.fireEvent('actioncomplete', this, action);
25965         }else{
25966             Roo.callback(o.failure, o.scope, [this, action]);
25967             this.fireEvent('actionfailed', this, action);
25968         }
25969     },
25970
25971     /**
25972      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
25973      * @param {String} id The value to search for
25974      * @return Field
25975      */
25976     findField : function(id){
25977         var field = this.items.get(id);
25978         if(!field){
25979             this.items.each(function(f){
25980                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
25981                     field = f;
25982                     return false;
25983                 }
25984             });
25985         }
25986         return field || null;
25987     },
25988
25989     /**
25990      * Add a secondary form to this one, 
25991      * Used to provide tabbed forms. One form is primary, with hidden values 
25992      * which mirror the elements from the other forms.
25993      * 
25994      * @param {Roo.form.Form} form to add.
25995      * 
25996      */
25997     addForm : function(form)
25998     {
25999        
26000         if (this.childForms.indexOf(form) > -1) {
26001             // already added..
26002             return;
26003         }
26004         this.childForms.push(form);
26005         var n = '';
26006         Roo.each(form.allItems, function (fe) {
26007             
26008             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
26009             if (this.findField(n)) { // already added..
26010                 return;
26011             }
26012             var add = new Roo.form.Hidden({
26013                 name : n
26014             });
26015             add.render(this.el);
26016             
26017             this.add( add );
26018         }, this);
26019         
26020     },
26021     /**
26022      * Mark fields in this form invalid in bulk.
26023      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
26024      * @return {BasicForm} this
26025      */
26026     markInvalid : function(errors){
26027         if(errors instanceof Array){
26028             for(var i = 0, len = errors.length; i < len; i++){
26029                 var fieldError = errors[i];
26030                 var f = this.findField(fieldError.id);
26031                 if(f){
26032                     f.markInvalid(fieldError.msg);
26033                 }
26034             }
26035         }else{
26036             var field, id;
26037             for(id in errors){
26038                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
26039                     field.markInvalid(errors[id]);
26040                 }
26041             }
26042         }
26043         Roo.each(this.childForms || [], function (f) {
26044             f.markInvalid(errors);
26045         });
26046         
26047         return this;
26048     },
26049
26050     /**
26051      * Set values for fields in this form in bulk.
26052      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
26053      * @return {BasicForm} this
26054      */
26055     setValues : function(values){
26056         if(values instanceof Array){ // array of objects
26057             for(var i = 0, len = values.length; i < len; i++){
26058                 var v = values[i];
26059                 var f = this.findField(v.id);
26060                 if(f){
26061                     f.setValue(v.value);
26062                     if(this.trackResetOnLoad){
26063                         f.originalValue = f.getValue();
26064                     }
26065                 }
26066             }
26067         }else{ // object hash
26068             var field, id;
26069             for(id in values){
26070                 if(typeof values[id] != 'function' && (field = this.findField(id))){
26071                     
26072                     if (field.setFromData && 
26073                         field.valueField && 
26074                         field.displayField &&
26075                         // combos' with local stores can 
26076                         // be queried via setValue()
26077                         // to set their value..
26078                         (field.store && !field.store.isLocal)
26079                         ) {
26080                         // it's a combo
26081                         var sd = { };
26082                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
26083                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
26084                         field.setFromData(sd);
26085                         
26086                     } else {
26087                         field.setValue(values[id]);
26088                     }
26089                     
26090                     
26091                     if(this.trackResetOnLoad){
26092                         field.originalValue = field.getValue();
26093                     }
26094                 }
26095             }
26096         }
26097          
26098         Roo.each(this.childForms || [], function (f) {
26099             f.setValues(values);
26100         });
26101                 
26102         return this;
26103     },
26104
26105     /**
26106      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
26107      * they are returned as an array.
26108      * @param {Boolean} asString
26109      * @return {Object}
26110      */
26111     getValues : function(asString){
26112         if (this.childForms) {
26113             // copy values from the child forms
26114             Roo.each(this.childForms, function (f) {
26115                 this.setValues(f.getValues());
26116             }, this);
26117         }
26118         
26119         
26120         
26121         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
26122         if(asString === true){
26123             return fs;
26124         }
26125         return Roo.urlDecode(fs);
26126     },
26127
26128     /**
26129      * Clears all invalid messages in this form.
26130      * @return {BasicForm} this
26131      */
26132     clearInvalid : function(){
26133         this.items.each(function(f){
26134            f.clearInvalid();
26135         });
26136         
26137         Roo.each(this.childForms || [], function (f) {
26138             f.clearInvalid();
26139         });
26140         
26141         
26142         return this;
26143     },
26144
26145     /**
26146      * Resets this form.
26147      * @return {BasicForm} this
26148      */
26149     reset : function(){
26150         this.items.each(function(f){
26151             f.reset();
26152         });
26153         
26154         Roo.each(this.childForms || [], function (f) {
26155             f.reset();
26156         });
26157        
26158         
26159         return this;
26160     },
26161
26162     /**
26163      * Add Roo.form components to this form.
26164      * @param {Field} field1
26165      * @param {Field} field2 (optional)
26166      * @param {Field} etc (optional)
26167      * @return {BasicForm} this
26168      */
26169     add : function(){
26170         this.items.addAll(Array.prototype.slice.call(arguments, 0));
26171         return this;
26172     },
26173
26174
26175     /**
26176      * Removes a field from the items collection (does NOT remove its markup).
26177      * @param {Field} field
26178      * @return {BasicForm} this
26179      */
26180     remove : function(field){
26181         this.items.remove(field);
26182         return this;
26183     },
26184
26185     /**
26186      * Looks at the fields in this form, checks them for an id attribute,
26187      * and calls applyTo on the existing dom element with that id.
26188      * @return {BasicForm} this
26189      */
26190     render : function(){
26191         this.items.each(function(f){
26192             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
26193                 f.applyTo(f.id);
26194             }
26195         });
26196         return this;
26197     },
26198
26199     /**
26200      * Calls {@link Ext#apply} for all fields in this form with the passed object.
26201      * @param {Object} values
26202      * @return {BasicForm} this
26203      */
26204     applyToFields : function(o){
26205         this.items.each(function(f){
26206            Roo.apply(f, o);
26207         });
26208         return this;
26209     },
26210
26211     /**
26212      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
26213      * @param {Object} values
26214      * @return {BasicForm} this
26215      */
26216     applyIfToFields : function(o){
26217         this.items.each(function(f){
26218            Roo.applyIf(f, o);
26219         });
26220         return this;
26221     }
26222 });
26223
26224 // back compat
26225 Roo.BasicForm = Roo.form.BasicForm;/*
26226  * Based on:
26227  * Ext JS Library 1.1.1
26228  * Copyright(c) 2006-2007, Ext JS, LLC.
26229  *
26230  * Originally Released Under LGPL - original licence link has changed is not relivant.
26231  *
26232  * Fork - LGPL
26233  * <script type="text/javascript">
26234  */
26235
26236 /**
26237  * @class Roo.form.Form
26238  * @extends Roo.form.BasicForm
26239  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
26240  * @constructor
26241  * @param {Object} config Configuration options
26242  */
26243 Roo.form.Form = function(config){
26244     var xitems =  [];
26245     if (config.items) {
26246         xitems = config.items;
26247         delete config.items;
26248     }
26249    
26250     
26251     Roo.form.Form.superclass.constructor.call(this, null, config);
26252     this.url = this.url || this.action;
26253     if(!this.root){
26254         this.root = new Roo.form.Layout(Roo.applyIf({
26255             id: Roo.id()
26256         }, config));
26257     }
26258     this.active = this.root;
26259     /**
26260      * Array of all the buttons that have been added to this form via {@link addButton}
26261      * @type Array
26262      */
26263     this.buttons = [];
26264     this.allItems = [];
26265     this.addEvents({
26266         /**
26267          * @event clientvalidation
26268          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
26269          * @param {Form} this
26270          * @param {Boolean} valid true if the form has passed client-side validation
26271          */
26272         clientvalidation: true,
26273         /**
26274          * @event rendered
26275          * Fires when the form is rendered
26276          * @param {Roo.form.Form} form
26277          */
26278         rendered : true
26279     });
26280     
26281     Roo.each(xitems, this.addxtype, this);
26282     
26283     
26284     
26285 };
26286
26287 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
26288     /**
26289      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
26290      */
26291     /**
26292      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
26293      */
26294     /**
26295      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
26296      */
26297     buttonAlign:'center',
26298
26299     /**
26300      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
26301      */
26302     minButtonWidth:75,
26303
26304     /**
26305      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
26306      * This property cascades to child containers if not set.
26307      */
26308     labelAlign:'left',
26309
26310     /**
26311      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
26312      * fires a looping event with that state. This is required to bind buttons to the valid
26313      * state using the config value formBind:true on the button.
26314      */
26315     monitorValid : false,
26316
26317     /**
26318      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
26319      */
26320     monitorPoll : 200,
26321
26322   
26323     /**
26324      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
26325      * fields are added and the column is closed. If no fields are passed the column remains open
26326      * until end() is called.
26327      * @param {Object} config The config to pass to the column
26328      * @param {Field} field1 (optional)
26329      * @param {Field} field2 (optional)
26330      * @param {Field} etc (optional)
26331      * @return Column The column container object
26332      */
26333     column : function(c){
26334         var col = new Roo.form.Column(c);
26335         this.start(col);
26336         if(arguments.length > 1){ // duplicate code required because of Opera
26337             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
26338             this.end();
26339         }
26340         return col;
26341     },
26342
26343     /**
26344      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
26345      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
26346      * until end() is called.
26347      * @param {Object} config The config to pass to the fieldset
26348      * @param {Field} field1 (optional)
26349      * @param {Field} field2 (optional)
26350      * @param {Field} etc (optional)
26351      * @return FieldSet The fieldset container object
26352      */
26353     fieldset : function(c){
26354         var fs = new Roo.form.FieldSet(c);
26355         this.start(fs);
26356         if(arguments.length > 1){ // duplicate code required because of Opera
26357             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
26358             this.end();
26359         }
26360         return fs;
26361     },
26362
26363     /**
26364      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
26365      * fields are added and the container is closed. If no fields are passed the container remains open
26366      * until end() is called.
26367      * @param {Object} config The config to pass to the Layout
26368      * @param {Field} field1 (optional)
26369      * @param {Field} field2 (optional)
26370      * @param {Field} etc (optional)
26371      * @return Layout The container object
26372      */
26373     container : function(c){
26374         var l = new Roo.form.Layout(c);
26375         this.start(l);
26376         if(arguments.length > 1){ // duplicate code required because of Opera
26377             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
26378             this.end();
26379         }
26380         return l;
26381     },
26382
26383     /**
26384      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
26385      * @param {Object} container A Roo.form.Layout or subclass of Layout
26386      * @return {Form} this
26387      */
26388     start : function(c){
26389         // cascade label info
26390         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
26391         this.active.stack.push(c);
26392         c.ownerCt = this.active;
26393         this.active = c;
26394         return this;
26395     },
26396
26397     /**
26398      * Closes the current open container
26399      * @return {Form} this
26400      */
26401     end : function(){
26402         if(this.active == this.root){
26403             return this;
26404         }
26405         this.active = this.active.ownerCt;
26406         return this;
26407     },
26408
26409     /**
26410      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
26411      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
26412      * as the label of the field.
26413      * @param {Field} field1
26414      * @param {Field} field2 (optional)
26415      * @param {Field} etc. (optional)
26416      * @return {Form} this
26417      */
26418     add : function(){
26419         this.active.stack.push.apply(this.active.stack, arguments);
26420         this.allItems.push.apply(this.allItems,arguments);
26421         var r = [];
26422         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
26423             if(a[i].isFormField){
26424                 r.push(a[i]);
26425             }
26426         }
26427         if(r.length > 0){
26428             Roo.form.Form.superclass.add.apply(this, r);
26429         }
26430         return this;
26431     },
26432     
26433
26434     
26435     
26436     
26437      /**
26438      * Find any element that has been added to a form, using it's ID or name
26439      * This can include framesets, columns etc. along with regular fields..
26440      * @param {String} id - id or name to find.
26441      
26442      * @return {Element} e - or false if nothing found.
26443      */
26444     findbyId : function(id)
26445     {
26446         var ret = false;
26447         if (!id) {
26448             return ret;
26449         }
26450         Ext.each(this.allItems, function(f){
26451             if (f.id == id || f.name == id ){
26452                 ret = f;
26453                 return false;
26454             }
26455         });
26456         return ret;
26457     },
26458
26459     
26460     
26461     /**
26462      * Render this form into the passed container. This should only be called once!
26463      * @param {String/HTMLElement/Element} container The element this component should be rendered into
26464      * @return {Form} this
26465      */
26466     render : function(ct){
26467         ct = Roo.get(ct);
26468         var o = this.autoCreate || {
26469             tag: 'form',
26470             method : this.method || 'POST',
26471             id : this.id || Roo.id()
26472         };
26473         this.initEl(ct.createChild(o));
26474
26475         this.root.render(this.el);
26476
26477         this.items.each(function(f){
26478             f.render('x-form-el-'+f.id);
26479         });
26480
26481         if(this.buttons.length > 0){
26482             // tables are required to maintain order and for correct IE layout
26483             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
26484                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
26485                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
26486             }}, null, true);
26487             var tr = tb.getElementsByTagName('tr')[0];
26488             for(var i = 0, len = this.buttons.length; i < len; i++) {
26489                 var b = this.buttons[i];
26490                 var td = document.createElement('td');
26491                 td.className = 'x-form-btn-td';
26492                 b.render(tr.appendChild(td));
26493             }
26494         }
26495         if(this.monitorValid){ // initialize after render
26496             this.startMonitoring();
26497         }
26498         this.fireEvent('rendered', this);
26499         return this;
26500     },
26501
26502     /**
26503      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
26504      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
26505      * object or a valid Roo.DomHelper element config
26506      * @param {Function} handler The function called when the button is clicked
26507      * @param {Object} scope (optional) The scope of the handler function
26508      * @return {Roo.Button}
26509      */
26510     addButton : function(config, handler, scope){
26511         var bc = {
26512             handler: handler,
26513             scope: scope,
26514             minWidth: this.minButtonWidth,
26515             hideParent:true
26516         };
26517         if(typeof config == "string"){
26518             bc.text = config;
26519         }else{
26520             Roo.apply(bc, config);
26521         }
26522         var btn = new Roo.Button(null, bc);
26523         this.buttons.push(btn);
26524         return btn;
26525     },
26526
26527      /**
26528      * Adds a series of form elements (using the xtype property as the factory method.
26529      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
26530      * @param {Object} config 
26531      */
26532     
26533     addxtype : function()
26534     {
26535         var ar = Array.prototype.slice.call(arguments, 0);
26536         var ret = false;
26537         for(var i = 0; i < ar.length; i++) {
26538             if (!ar[i]) {
26539                 continue; // skip -- if this happends something invalid got sent, we 
26540                 // should ignore it, as basically that interface element will not show up
26541                 // and that should be pretty obvious!!
26542             }
26543             
26544             if (Roo.form[ar[i].xtype]) {
26545                 ar[i].form = this;
26546                 var fe = Roo.factory(ar[i], Roo.form);
26547                 if (!ret) {
26548                     ret = fe;
26549                 }
26550                 fe.form = this;
26551                 if (fe.store) {
26552                     fe.store.form = this;
26553                 }
26554                 if (fe.isLayout) {  
26555                          
26556                     this.start(fe);
26557                     this.allItems.push(fe);
26558                     if (fe.items && fe.addxtype) {
26559                         fe.addxtype.apply(fe, fe.items);
26560                         delete fe.items;
26561                     }
26562                      this.end();
26563                     continue;
26564                 }
26565                 
26566                 
26567                  
26568                 this.add(fe);
26569               //  console.log('adding ' + ar[i].xtype);
26570             }
26571             if (ar[i].xtype == 'Button') {  
26572                 //console.log('adding button');
26573                 //console.log(ar[i]);
26574                 this.addButton(ar[i]);
26575                 this.allItems.push(fe);
26576                 continue;
26577             }
26578             
26579             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
26580                 alert('end is not supported on xtype any more, use items');
26581             //    this.end();
26582             //    //console.log('adding end');
26583             }
26584             
26585         }
26586         return ret;
26587     },
26588     
26589     /**
26590      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
26591      * option "monitorValid"
26592      */
26593     startMonitoring : function(){
26594         if(!this.bound){
26595             this.bound = true;
26596             Roo.TaskMgr.start({
26597                 run : this.bindHandler,
26598                 interval : this.monitorPoll || 200,
26599                 scope: this
26600             });
26601         }
26602     },
26603
26604     /**
26605      * Stops monitoring of the valid state of this form
26606      */
26607     stopMonitoring : function(){
26608         this.bound = false;
26609     },
26610
26611     // private
26612     bindHandler : function(){
26613         if(!this.bound){
26614             return false; // stops binding
26615         }
26616         var valid = true;
26617         this.items.each(function(f){
26618             if(!f.isValid(true)){
26619                 valid = false;
26620                 return false;
26621             }
26622         });
26623         for(var i = 0, len = this.buttons.length; i < len; i++){
26624             var btn = this.buttons[i];
26625             if(btn.formBind === true && btn.disabled === valid){
26626                 btn.setDisabled(!valid);
26627             }
26628         }
26629         this.fireEvent('clientvalidation', this, valid);
26630     }
26631     
26632     
26633     
26634     
26635     
26636     
26637     
26638     
26639 });
26640
26641
26642 // back compat
26643 Roo.Form = Roo.form.Form;
26644 /*
26645  * Based on:
26646  * Ext JS Library 1.1.1
26647  * Copyright(c) 2006-2007, Ext JS, LLC.
26648  *
26649  * Originally Released Under LGPL - original licence link has changed is not relivant.
26650  *
26651  * Fork - LGPL
26652  * <script type="text/javascript">
26653  */
26654  
26655  /**
26656  * @class Roo.form.Action
26657  * Internal Class used to handle form actions
26658  * @constructor
26659  * @param {Roo.form.BasicForm} el The form element or its id
26660  * @param {Object} config Configuration options
26661  */
26662  
26663  
26664 // define the action interface
26665 Roo.form.Action = function(form, options){
26666     this.form = form;
26667     this.options = options || {};
26668 };
26669 /**
26670  * Client Validation Failed
26671  * @const 
26672  */
26673 Roo.form.Action.CLIENT_INVALID = 'client';
26674 /**
26675  * Server Validation Failed
26676  * @const 
26677  */
26678  Roo.form.Action.SERVER_INVALID = 'server';
26679  /**
26680  * Connect to Server Failed
26681  * @const 
26682  */
26683 Roo.form.Action.CONNECT_FAILURE = 'connect';
26684 /**
26685  * Reading Data from Server Failed
26686  * @const 
26687  */
26688 Roo.form.Action.LOAD_FAILURE = 'load';
26689
26690 Roo.form.Action.prototype = {
26691     type : 'default',
26692     failureType : undefined,
26693     response : undefined,
26694     result : undefined,
26695
26696     // interface method
26697     run : function(options){
26698
26699     },
26700
26701     // interface method
26702     success : function(response){
26703
26704     },
26705
26706     // interface method
26707     handleResponse : function(response){
26708
26709     },
26710
26711     // default connection failure
26712     failure : function(response){
26713         this.response = response;
26714         this.failureType = Roo.form.Action.CONNECT_FAILURE;
26715         this.form.afterAction(this, false);
26716     },
26717
26718     processResponse : function(response){
26719         this.response = response;
26720         if(!response.responseText){
26721             return true;
26722         }
26723         this.result = this.handleResponse(response);
26724         return this.result;
26725     },
26726
26727     // utility functions used internally
26728     getUrl : function(appendParams){
26729         var url = this.options.url || this.form.url || this.form.el.dom.action;
26730         if(appendParams){
26731             var p = this.getParams();
26732             if(p){
26733                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
26734             }
26735         }
26736         return url;
26737     },
26738
26739     getMethod : function(){
26740         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
26741     },
26742
26743     getParams : function(){
26744         var bp = this.form.baseParams;
26745         var p = this.options.params;
26746         if(p){
26747             if(typeof p == "object"){
26748                 p = Roo.urlEncode(Roo.applyIf(p, bp));
26749             }else if(typeof p == 'string' && bp){
26750                 p += '&' + Roo.urlEncode(bp);
26751             }
26752         }else if(bp){
26753             p = Roo.urlEncode(bp);
26754         }
26755         return p;
26756     },
26757
26758     createCallback : function(){
26759         return {
26760             success: this.success,
26761             failure: this.failure,
26762             scope: this,
26763             timeout: (this.form.timeout*1000),
26764             upload: this.form.fileUpload ? this.success : undefined
26765         };
26766     }
26767 };
26768
26769 Roo.form.Action.Submit = function(form, options){
26770     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
26771 };
26772
26773 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
26774     type : 'submit',
26775
26776     run : function()
26777     {
26778         // run get Values on the form, so it syncs any secondary forms.
26779         this.form.getValues();
26780         
26781         var o = this.options;
26782         var method = this.getMethod();
26783         var isPost = method == 'POST';
26784         if(o.clientValidation === false || this.form.isValid()){
26785             Roo.Ajax.request(Roo.apply(this.createCallback(), {
26786                 form:this.form.el.dom,
26787                 url:this.getUrl(!isPost),
26788                 method: method,
26789                 params:isPost ? this.getParams() : null,
26790                 isUpload: this.form.fileUpload
26791             }));
26792
26793         }else if (o.clientValidation !== false){ // client validation failed
26794             this.failureType = Roo.form.Action.CLIENT_INVALID;
26795             this.form.afterAction(this, false);
26796         }
26797     },
26798
26799     success : function(response){
26800         var result = this.processResponse(response);
26801         if(result === true || result.success){
26802             this.form.afterAction(this, true);
26803             return;
26804         }
26805         if(result.errors){
26806             this.form.markInvalid(result.errors);
26807             this.failureType = Roo.form.Action.SERVER_INVALID;
26808         }
26809         this.form.afterAction(this, false);
26810     },
26811
26812     handleResponse : function(response){
26813         if(this.form.errorReader){
26814             var rs = this.form.errorReader.read(response);
26815             var errors = [];
26816             if(rs.records){
26817                 for(var i = 0, len = rs.records.length; i < len; i++) {
26818                     var r = rs.records[i];
26819                     errors[i] = r.data;
26820                 }
26821             }
26822             if(errors.length < 1){
26823                 errors = null;
26824             }
26825             return {
26826                 success : rs.success,
26827                 errors : errors
26828             };
26829         }
26830         var ret = false;
26831         try {
26832             ret = Roo.decode(response.responseText);
26833         } catch (e) {
26834             ret = {
26835                 success: false,
26836                 errorMsg: "Failed to read server message: " + response.responseText,
26837                 errors : []
26838             };
26839         }
26840         return ret;
26841         
26842     }
26843 });
26844
26845
26846 Roo.form.Action.Load = function(form, options){
26847     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
26848     this.reader = this.form.reader;
26849 };
26850
26851 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
26852     type : 'load',
26853
26854     run : function(){
26855         Roo.Ajax.request(Roo.apply(
26856                 this.createCallback(), {
26857                     method:this.getMethod(),
26858                     url:this.getUrl(false),
26859                     params:this.getParams()
26860         }));
26861     },
26862
26863     success : function(response){
26864         var result = this.processResponse(response);
26865         if(result === true || !result.success || !result.data){
26866             this.failureType = Roo.form.Action.LOAD_FAILURE;
26867             this.form.afterAction(this, false);
26868             return;
26869         }
26870         this.form.clearInvalid();
26871         this.form.setValues(result.data);
26872         this.form.afterAction(this, true);
26873     },
26874
26875     handleResponse : function(response){
26876         if(this.form.reader){
26877             var rs = this.form.reader.read(response);
26878             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
26879             return {
26880                 success : rs.success,
26881                 data : data
26882             };
26883         }
26884         return Roo.decode(response.responseText);
26885     }
26886 });
26887
26888 Roo.form.Action.ACTION_TYPES = {
26889     'load' : Roo.form.Action.Load,
26890     'submit' : Roo.form.Action.Submit
26891 };/*
26892  * Based on:
26893  * Ext JS Library 1.1.1
26894  * Copyright(c) 2006-2007, Ext JS, LLC.
26895  *
26896  * Originally Released Under LGPL - original licence link has changed is not relivant.
26897  *
26898  * Fork - LGPL
26899  * <script type="text/javascript">
26900  */
26901  
26902 /**
26903  * @class Roo.form.Layout
26904  * @extends Roo.Component
26905  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
26906  * @constructor
26907  * @param {Object} config Configuration options
26908  */
26909 Roo.form.Layout = function(config){
26910     var xitems = [];
26911     if (config.items) {
26912         xitems = config.items;
26913         delete config.items;
26914     }
26915     Roo.form.Layout.superclass.constructor.call(this, config);
26916     this.stack = [];
26917     Roo.each(xitems, this.addxtype, this);
26918      
26919 };
26920
26921 Roo.extend(Roo.form.Layout, Roo.Component, {
26922     /**
26923      * @cfg {String/Object} autoCreate
26924      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
26925      */
26926     /**
26927      * @cfg {String/Object/Function} style
26928      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
26929      * a function which returns such a specification.
26930      */
26931     /**
26932      * @cfg {String} labelAlign
26933      * Valid values are "left," "top" and "right" (defaults to "left")
26934      */
26935     /**
26936      * @cfg {Number} labelWidth
26937      * Fixed width in pixels of all field labels (defaults to undefined)
26938      */
26939     /**
26940      * @cfg {Boolean} clear
26941      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
26942      */
26943     clear : true,
26944     /**
26945      * @cfg {String} labelSeparator
26946      * The separator to use after field labels (defaults to ':')
26947      */
26948     labelSeparator : ':',
26949     /**
26950      * @cfg {Boolean} hideLabels
26951      * True to suppress the display of field labels in this layout (defaults to false)
26952      */
26953     hideLabels : false,
26954
26955     // private
26956     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
26957     
26958     isLayout : true,
26959     
26960     // private
26961     onRender : function(ct, position){
26962         if(this.el){ // from markup
26963             this.el = Roo.get(this.el);
26964         }else {  // generate
26965             var cfg = this.getAutoCreate();
26966             this.el = ct.createChild(cfg, position);
26967         }
26968         if(this.style){
26969             this.el.applyStyles(this.style);
26970         }
26971         if(this.labelAlign){
26972             this.el.addClass('x-form-label-'+this.labelAlign);
26973         }
26974         if(this.hideLabels){
26975             this.labelStyle = "display:none";
26976             this.elementStyle = "padding-left:0;";
26977         }else{
26978             if(typeof this.labelWidth == 'number'){
26979                 this.labelStyle = "width:"+this.labelWidth+"px;";
26980                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
26981             }
26982             if(this.labelAlign == 'top'){
26983                 this.labelStyle = "width:auto;";
26984                 this.elementStyle = "padding-left:0;";
26985             }
26986         }
26987         var stack = this.stack;
26988         var slen = stack.length;
26989         if(slen > 0){
26990             if(!this.fieldTpl){
26991                 var t = new Roo.Template(
26992                     '<div class="x-form-item {5}">',
26993                         '<label for="{0}" style="{2}">{1}{4}</label>',
26994                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
26995                         '</div>',
26996                     '</div><div class="x-form-clear-left"></div>'
26997                 );
26998                 t.disableFormats = true;
26999                 t.compile();
27000                 Roo.form.Layout.prototype.fieldTpl = t;
27001             }
27002             for(var i = 0; i < slen; i++) {
27003                 if(stack[i].isFormField){
27004                     this.renderField(stack[i]);
27005                 }else{
27006                     this.renderComponent(stack[i]);
27007                 }
27008             }
27009         }
27010         if(this.clear){
27011             this.el.createChild({cls:'x-form-clear'});
27012         }
27013     },
27014
27015     // private
27016     renderField : function(f){
27017         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
27018                f.id, //0
27019                f.fieldLabel, //1
27020                f.labelStyle||this.labelStyle||'', //2
27021                this.elementStyle||'', //3
27022                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
27023                f.itemCls||this.itemCls||''  //5
27024        ], true).getPrevSibling());
27025     },
27026
27027     // private
27028     renderComponent : function(c){
27029         c.render(c.isLayout ? this.el : this.el.createChild());    
27030     },
27031     /**
27032      * Adds a object form elements (using the xtype property as the factory method.)
27033      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
27034      * @param {Object} config 
27035      */
27036     addxtype : function(o)
27037     {
27038         // create the lement.
27039         o.form = this.form;
27040         var fe = Roo.factory(o, Roo.form);
27041         this.form.allItems.push(fe);
27042         this.stack.push(fe);
27043         
27044         if (fe.isFormField) {
27045             this.form.items.add(fe);
27046         }
27047          
27048         return fe;
27049     }
27050 });
27051
27052 /**
27053  * @class Roo.form.Column
27054  * @extends Roo.form.Layout
27055  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
27056  * @constructor
27057  * @param {Object} config Configuration options
27058  */
27059 Roo.form.Column = function(config){
27060     Roo.form.Column.superclass.constructor.call(this, config);
27061 };
27062
27063 Roo.extend(Roo.form.Column, Roo.form.Layout, {
27064     /**
27065      * @cfg {Number/String} width
27066      * The fixed width of the column in pixels or CSS value (defaults to "auto")
27067      */
27068     /**
27069      * @cfg {String/Object} autoCreate
27070      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
27071      */
27072
27073     // private
27074     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
27075
27076     // private
27077     onRender : function(ct, position){
27078         Roo.form.Column.superclass.onRender.call(this, ct, position);
27079         if(this.width){
27080             this.el.setWidth(this.width);
27081         }
27082     }
27083 });
27084
27085
27086 /**
27087  * @class Roo.form.Row
27088  * @extends Roo.form.Layout
27089  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
27090  * @constructor
27091  * @param {Object} config Configuration options
27092  */
27093
27094  
27095 Roo.form.Row = function(config){
27096     Roo.form.Row.superclass.constructor.call(this, config);
27097 };
27098  
27099 Roo.extend(Roo.form.Row, Roo.form.Layout, {
27100       /**
27101      * @cfg {Number/String} width
27102      * The fixed width of the column in pixels or CSS value (defaults to "auto")
27103      */
27104     /**
27105      * @cfg {Number/String} height
27106      * The fixed height of the column in pixels or CSS value (defaults to "auto")
27107      */
27108     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
27109     
27110     padWidth : 20,
27111     // private
27112     onRender : function(ct, position){
27113         //console.log('row render');
27114         if(!this.rowTpl){
27115             var t = new Roo.Template(
27116                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
27117                     '<label for="{0}" style="{2}">{1}{4}</label>',
27118                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
27119                     '</div>',
27120                 '</div>'
27121             );
27122             t.disableFormats = true;
27123             t.compile();
27124             Roo.form.Layout.prototype.rowTpl = t;
27125         }
27126         this.fieldTpl = this.rowTpl;
27127         
27128         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
27129         var labelWidth = 100;
27130         
27131         if ((this.labelAlign != 'top')) {
27132             if (typeof this.labelWidth == 'number') {
27133                 labelWidth = this.labelWidth
27134             }
27135             this.padWidth =  20 + labelWidth;
27136             
27137         }
27138         
27139         Roo.form.Column.superclass.onRender.call(this, ct, position);
27140         if(this.width){
27141             this.el.setWidth(this.width);
27142         }
27143         if(this.height){
27144             this.el.setHeight(this.height);
27145         }
27146     },
27147     
27148     // private
27149     renderField : function(f){
27150         f.fieldEl = this.fieldTpl.append(this.el, [
27151                f.id, f.fieldLabel,
27152                f.labelStyle||this.labelStyle||'',
27153                this.elementStyle||'',
27154                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
27155                f.itemCls||this.itemCls||'',
27156                f.width ? f.width + this.padWidth : 160 + this.padWidth
27157        ],true);
27158     }
27159 });
27160  
27161
27162 /**
27163  * @class Roo.form.FieldSet
27164  * @extends Roo.form.Layout
27165  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
27166  * @constructor
27167  * @param {Object} config Configuration options
27168  */
27169 Roo.form.FieldSet = function(config){
27170     Roo.form.FieldSet.superclass.constructor.call(this, config);
27171 };
27172
27173 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
27174     /**
27175      * @cfg {String} legend
27176      * The text to display as the legend for the FieldSet (defaults to '')
27177      */
27178     /**
27179      * @cfg {String/Object} autoCreate
27180      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
27181      */
27182
27183     // private
27184     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
27185
27186     // private
27187     onRender : function(ct, position){
27188         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
27189         if(this.legend){
27190             this.setLegend(this.legend);
27191         }
27192     },
27193
27194     // private
27195     setLegend : function(text){
27196         if(this.rendered){
27197             this.el.child('legend').update(text);
27198         }
27199     }
27200 });/*
27201  * Based on:
27202  * Ext JS Library 1.1.1
27203  * Copyright(c) 2006-2007, Ext JS, LLC.
27204  *
27205  * Originally Released Under LGPL - original licence link has changed is not relivant.
27206  *
27207  * Fork - LGPL
27208  * <script type="text/javascript">
27209  */
27210 /**
27211  * @class Roo.form.VTypes
27212  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
27213  * @singleton
27214  */
27215 Roo.form.VTypes = function(){
27216     // closure these in so they are only created once.
27217     var alpha = /^[a-zA-Z_]+$/;
27218     var alphanum = /^[a-zA-Z0-9_]+$/;
27219     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
27220     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
27221
27222     // All these messages and functions are configurable
27223     return {
27224         /**
27225          * The function used to validate email addresses
27226          * @param {String} value The email address
27227          */
27228         'email' : function(v){
27229             return email.test(v);
27230         },
27231         /**
27232          * The error text to display when the email validation function returns false
27233          * @type String
27234          */
27235         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
27236         /**
27237          * The keystroke filter mask to be applied on email input
27238          * @type RegExp
27239          */
27240         'emailMask' : /[a-z0-9_\.\-@]/i,
27241
27242         /**
27243          * The function used to validate URLs
27244          * @param {String} value The URL
27245          */
27246         'url' : function(v){
27247             return url.test(v);
27248         },
27249         /**
27250          * The error text to display when the url validation function returns false
27251          * @type String
27252          */
27253         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
27254         
27255         /**
27256          * The function used to validate alpha values
27257          * @param {String} value The value
27258          */
27259         'alpha' : function(v){
27260             return alpha.test(v);
27261         },
27262         /**
27263          * The error text to display when the alpha validation function returns false
27264          * @type String
27265          */
27266         'alphaText' : 'This field should only contain letters and _',
27267         /**
27268          * The keystroke filter mask to be applied on alpha input
27269          * @type RegExp
27270          */
27271         'alphaMask' : /[a-z_]/i,
27272
27273         /**
27274          * The function used to validate alphanumeric values
27275          * @param {String} value The value
27276          */
27277         'alphanum' : function(v){
27278             return alphanum.test(v);
27279         },
27280         /**
27281          * The error text to display when the alphanumeric validation function returns false
27282          * @type String
27283          */
27284         'alphanumText' : 'This field should only contain letters, numbers and _',
27285         /**
27286          * The keystroke filter mask to be applied on alphanumeric input
27287          * @type RegExp
27288          */
27289         'alphanumMask' : /[a-z0-9_]/i
27290     };
27291 }();//<script type="text/javascript">
27292
27293 /**
27294  * @class Roo.form.FCKeditor
27295  * @extends Roo.form.TextArea
27296  * Wrapper around the FCKEditor http://www.fckeditor.net
27297  * @constructor
27298  * Creates a new FCKeditor
27299  * @param {Object} config Configuration options
27300  */
27301 Roo.form.FCKeditor = function(config){
27302     Roo.form.FCKeditor.superclass.constructor.call(this, config);
27303     this.addEvents({
27304          /**
27305          * @event editorinit
27306          * Fired when the editor is initialized - you can add extra handlers here..
27307          * @param {FCKeditor} this
27308          * @param {Object} the FCK object.
27309          */
27310         editorinit : true
27311     });
27312     
27313     
27314 };
27315 Roo.form.FCKeditor.editors = { };
27316 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
27317 {
27318     //defaultAutoCreate : {
27319     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
27320     //},
27321     // private
27322     /**
27323      * @cfg {Object} fck options - see fck manual for details.
27324      */
27325     fckconfig : false,
27326     
27327     /**
27328      * @cfg {Object} fck toolbar set (Basic or Default)
27329      */
27330     toolbarSet : 'Basic',
27331     /**
27332      * @cfg {Object} fck BasePath
27333      */ 
27334     basePath : '/fckeditor/',
27335     
27336     
27337     frame : false,
27338     
27339     value : '',
27340     
27341    
27342     onRender : function(ct, position)
27343     {
27344         if(!this.el){
27345             this.defaultAutoCreate = {
27346                 tag: "textarea",
27347                 style:"width:300px;height:60px;",
27348                 autocomplete: "off"
27349             };
27350         }
27351         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
27352         /*
27353         if(this.grow){
27354             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
27355             if(this.preventScrollbars){
27356                 this.el.setStyle("overflow", "hidden");
27357             }
27358             this.el.setHeight(this.growMin);
27359         }
27360         */
27361         //console.log('onrender' + this.getId() );
27362         Roo.form.FCKeditor.editors[this.getId()] = this;
27363          
27364
27365         this.replaceTextarea() ;
27366         
27367     },
27368     
27369     getEditor : function() {
27370         return this.fckEditor;
27371     },
27372     /**
27373      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
27374      * @param {Mixed} value The value to set
27375      */
27376     
27377     
27378     setValue : function(value)
27379     {
27380         //console.log('setValue: ' + value);
27381         
27382         if(typeof(value) == 'undefined') { // not sure why this is happending...
27383             return;
27384         }
27385         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
27386         
27387         //if(!this.el || !this.getEditor()) {
27388         //    this.value = value;
27389             //this.setValue.defer(100,this,[value]);    
27390         //    return;
27391         //} 
27392         
27393         if(!this.getEditor()) {
27394             return;
27395         }
27396         
27397         this.getEditor().SetData(value);
27398         
27399         //
27400
27401     },
27402
27403     /**
27404      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
27405      * @return {Mixed} value The field value
27406      */
27407     getValue : function()
27408     {
27409         
27410         if (this.frame && this.frame.dom.style.display == 'none') {
27411             return Roo.form.FCKeditor.superclass.getValue.call(this);
27412         }
27413         
27414         if(!this.el || !this.getEditor()) {
27415            
27416            // this.getValue.defer(100,this); 
27417             return this.value;
27418         }
27419        
27420         
27421         var value=this.getEditor().GetData();
27422         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
27423         return Roo.form.FCKeditor.superclass.getValue.call(this);
27424         
27425
27426     },
27427
27428     /**
27429      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
27430      * @return {Mixed} value The field value
27431      */
27432     getRawValue : function()
27433     {
27434         if (this.frame && this.frame.dom.style.display == 'none') {
27435             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
27436         }
27437         
27438         if(!this.el || !this.getEditor()) {
27439             //this.getRawValue.defer(100,this); 
27440             return this.value;
27441             return;
27442         }
27443         
27444         
27445         
27446         var value=this.getEditor().GetData();
27447         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
27448         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
27449          
27450     },
27451     
27452     setSize : function(w,h) {
27453         
27454         
27455         
27456         //if (this.frame && this.frame.dom.style.display == 'none') {
27457         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
27458         //    return;
27459         //}
27460         //if(!this.el || !this.getEditor()) {
27461         //    this.setSize.defer(100,this, [w,h]); 
27462         //    return;
27463         //}
27464         
27465         
27466         
27467         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
27468         
27469         this.frame.dom.setAttribute('width', w);
27470         this.frame.dom.setAttribute('height', h);
27471         this.frame.setSize(w,h);
27472         
27473     },
27474     
27475     toggleSourceEdit : function(value) {
27476         
27477       
27478          
27479         this.el.dom.style.display = value ? '' : 'none';
27480         this.frame.dom.style.display = value ?  'none' : '';
27481         
27482     },
27483     
27484     
27485     focus: function(tag)
27486     {
27487         if (this.frame.dom.style.display == 'none') {
27488             return Roo.form.FCKeditor.superclass.focus.call(this);
27489         }
27490         if(!this.el || !this.getEditor()) {
27491             this.focus.defer(100,this, [tag]); 
27492             return;
27493         }
27494         
27495         
27496         
27497         
27498         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
27499         this.getEditor().Focus();
27500         if (tgs.length) {
27501             if (!this.getEditor().Selection.GetSelection()) {
27502                 this.focus.defer(100,this, [tag]); 
27503                 return;
27504             }
27505             
27506             
27507             var r = this.getEditor().EditorDocument.createRange();
27508             r.setStart(tgs[0],0);
27509             r.setEnd(tgs[0],0);
27510             this.getEditor().Selection.GetSelection().removeAllRanges();
27511             this.getEditor().Selection.GetSelection().addRange(r);
27512             this.getEditor().Focus();
27513         }
27514         
27515     },
27516     
27517     
27518     
27519     replaceTextarea : function()
27520     {
27521         if ( document.getElementById( this.getId() + '___Frame' ) )
27522             return ;
27523         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
27524         //{
27525             // We must check the elements firstly using the Id and then the name.
27526         var oTextarea = document.getElementById( this.getId() );
27527         
27528         var colElementsByName = document.getElementsByName( this.getId() ) ;
27529          
27530         oTextarea.style.display = 'none' ;
27531
27532         if ( oTextarea.tabIndex ) {            
27533             this.TabIndex = oTextarea.tabIndex ;
27534         }
27535         
27536         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
27537         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
27538         this.frame = Roo.get(this.getId() + '___Frame')
27539     },
27540     
27541     _getConfigHtml : function()
27542     {
27543         var sConfig = '' ;
27544
27545         for ( var o in this.fckconfig ) {
27546             sConfig += sConfig.length > 0  ? '&amp;' : '';
27547             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
27548         }
27549
27550         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
27551     },
27552     
27553     
27554     _getIFrameHtml : function()
27555     {
27556         var sFile = 'fckeditor.html' ;
27557         /* no idea what this is about..
27558         try
27559         {
27560             if ( (/fcksource=true/i).test( window.top.location.search ) )
27561                 sFile = 'fckeditor.original.html' ;
27562         }
27563         catch (e) { 
27564         */
27565
27566         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
27567         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
27568         
27569         
27570         var html = '<iframe id="' + this.getId() +
27571             '___Frame" src="' + sLink +
27572             '" width="' + this.width +
27573             '" height="' + this.height + '"' +
27574             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
27575             ' frameborder="0" scrolling="no"></iframe>' ;
27576
27577         return html ;
27578     },
27579     
27580     _insertHtmlBefore : function( html, element )
27581     {
27582         if ( element.insertAdjacentHTML )       {
27583             // IE
27584             element.insertAdjacentHTML( 'beforeBegin', html ) ;
27585         } else { // Gecko
27586             var oRange = document.createRange() ;
27587             oRange.setStartBefore( element ) ;
27588             var oFragment = oRange.createContextualFragment( html );
27589             element.parentNode.insertBefore( oFragment, element ) ;
27590         }
27591     }
27592     
27593     
27594   
27595     
27596     
27597     
27598     
27599
27600 });
27601
27602 //Roo.reg('fckeditor', Roo.form.FCKeditor);
27603
27604 function FCKeditor_OnComplete(editorInstance){
27605     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
27606     f.fckEditor = editorInstance;
27607     //console.log("loaded");
27608     f.fireEvent('editorinit', f, editorInstance);
27609
27610   
27611
27612  
27613
27614
27615
27616
27617
27618
27619
27620
27621
27622
27623
27624
27625
27626
27627
27628 //<script type="text/javascript">
27629 /**
27630  * @class Roo.form.GridField
27631  * @extends Roo.form.Field
27632  * Embed a grid (or editable grid into a form)
27633  * STATUS ALPHA
27634  * @constructor
27635  * Creates a new GridField
27636  * @param {Object} config Configuration options
27637  */
27638 Roo.form.GridField = function(config){
27639     Roo.form.GridField.superclass.constructor.call(this, config);
27640      
27641 };
27642
27643 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
27644     /**
27645      * @cfg {Number} width  - used to restrict width of grid..
27646      */
27647     width : 100,
27648     /**
27649      * @cfg {Number} height - used to restrict height of grid..
27650      */
27651     height : 50,
27652      /**
27653      * @cfg {Object} xgrid (xtype'd description of grid) Grid or EditorGrid
27654      */
27655     xgrid : false, 
27656     /**
27657      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
27658      * {tag: "input", type: "checkbox", autocomplete: "off"})
27659      */
27660    // defaultAutoCreate : { tag: 'div' },
27661     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
27662     /**
27663      * @cfg {String} addTitle Text to include for adding a title.
27664      */
27665     addTitle : false,
27666     //
27667     onResize : function(){
27668         Roo.form.Field.superclass.onResize.apply(this, arguments);
27669     },
27670
27671     initEvents : function(){
27672         // Roo.form.Checkbox.superclass.initEvents.call(this);
27673         // has no events...
27674        
27675     },
27676
27677
27678     getResizeEl : function(){
27679         return this.wrap;
27680     },
27681
27682     getPositionEl : function(){
27683         return this.wrap;
27684     },
27685
27686     // private
27687     onRender : function(ct, position){
27688         
27689         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
27690         var style = this.style;
27691         delete this.style;
27692         
27693         Roo.form.DisplayImage.superclass.onRender.call(this, ct, position);
27694         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
27695         this.viewEl = this.wrap.createChild({ tag: 'div' });
27696         if (style) {
27697             this.viewEl.applyStyles(style);
27698         }
27699         if (this.width) {
27700             this.viewEl.setWidth(this.width);
27701         }
27702         if (this.height) {
27703             this.viewEl.setHeight(this.height);
27704         }
27705         //if(this.inputValue !== undefined){
27706         //this.setValue(this.value);
27707         
27708         
27709         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
27710         
27711         
27712         this.grid.render();
27713         this.grid.getDataSource().on('remove', this.refreshValue, this);
27714         this.grid.getDataSource().on('update', this.refreshValue, this);
27715         this.grid.on('afteredit', this.refreshValue, this);
27716  
27717     },
27718      
27719     
27720     /**
27721      * Sets the value of the item. 
27722      * @param {String} either an object  or a string..
27723      */
27724     setValue : function(v){
27725         //this.value = v;
27726         v = v || []; // empty set..
27727         // this does not seem smart - it really only affects memoryproxy grids..
27728         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
27729             var ds = this.grid.getDataSource();
27730             // assumes a json reader..
27731             var data = {}
27732             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
27733             ds.loadData( data);
27734         }
27735         Roo.form.GridField.superclass.setValue.call(this, v);
27736         this.refreshValue();
27737         // should load data in the grid really....
27738     },
27739     
27740     // private
27741     refreshValue: function() {
27742          var val = [];
27743         this.grid.getDataSource().each(function(r) {
27744             val.push(r.data);
27745         });
27746         this.el.dom.value = Roo.encode(val);
27747     }
27748     
27749      
27750     
27751     
27752 });/*
27753  * Based on:
27754  * Ext JS Library 1.1.1
27755  * Copyright(c) 2006-2007, Ext JS, LLC.
27756  *
27757  * Originally Released Under LGPL - original licence link has changed is not relivant.
27758  *
27759  * Fork - LGPL
27760  * <script type="text/javascript">
27761  */
27762 /**
27763  * @class Roo.form.DisplayField
27764  * @extends Roo.form.Field
27765  * A generic Field to display non-editable data.
27766  * @constructor
27767  * Creates a new Display Field item.
27768  * @param {Object} config Configuration options
27769  */
27770 Roo.form.DisplayField = function(config){
27771     Roo.form.DisplayField.superclass.constructor.call(this, config);
27772     
27773 };
27774
27775 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
27776     inputType:      'hidden',
27777     allowBlank:     true,
27778     readOnly:         true,
27779     
27780  
27781     /**
27782      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
27783      */
27784     focusClass : undefined,
27785     /**
27786      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
27787      */
27788     fieldClass: 'x-form-field',
27789     
27790      /**
27791      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
27792      */
27793     valueRenderer: undefined,
27794     
27795     width: 100,
27796     /**
27797      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
27798      * {tag: "input", type: "checkbox", autocomplete: "off"})
27799      */
27800      
27801  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
27802
27803     onResize : function(){
27804         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
27805         
27806     },
27807
27808     initEvents : function(){
27809         // Roo.form.Checkbox.superclass.initEvents.call(this);
27810         // has no events...
27811        
27812     },
27813
27814
27815     getResizeEl : function(){
27816         return this.wrap;
27817     },
27818
27819     getPositionEl : function(){
27820         return this.wrap;
27821     },
27822
27823     // private
27824     onRender : function(ct, position){
27825         
27826         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
27827         //if(this.inputValue !== undefined){
27828         this.wrap = this.el.wrap();
27829         
27830         this.viewEl = this.wrap.createChild({ tag: 'div'});
27831         
27832         if (this.bodyStyle) {
27833             this.viewEl.applyStyles(this.bodyStyle);
27834         }
27835         //this.viewEl.setStyle('padding', '2px');
27836         
27837         this.setValue(this.value);
27838         
27839     },
27840 /*
27841     // private
27842     initValue : Roo.emptyFn,
27843
27844   */
27845
27846         // private
27847     onClick : function(){
27848         
27849     },
27850
27851     /**
27852      * Sets the checked state of the checkbox.
27853      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
27854      */
27855     setValue : function(v){
27856         this.value = v;
27857         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
27858         // this might be called before we have a dom element..
27859         if (!this.viewEl) {
27860             return;
27861         }
27862         this.viewEl.dom.innerHTML = html;
27863         Roo.form.DisplayField.superclass.setValue.call(this, v);
27864
27865     }
27866 });//<script type="text/javasscript">
27867  
27868
27869 /**
27870  * @class Roo.DDView
27871  * A DnD enabled version of Roo.View.
27872  * @param {Element/String} container The Element in which to create the View.
27873  * @param {String} tpl The template string used to create the markup for each element of the View
27874  * @param {Object} config The configuration properties. These include all the config options of
27875  * {@link Roo.View} plus some specific to this class.<br>
27876  * <p>
27877  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
27878  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
27879  * <p>
27880  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
27881 .x-view-drag-insert-above {
27882         border-top:1px dotted #3366cc;
27883 }
27884 .x-view-drag-insert-below {
27885         border-bottom:1px dotted #3366cc;
27886 }
27887 </code></pre>
27888  * 
27889  */
27890  
27891 Roo.DDView = function(container, tpl, config) {
27892     Roo.DDView.superclass.constructor.apply(this, arguments);
27893     this.getEl().setStyle("outline", "0px none");
27894     this.getEl().unselectable();
27895     if (this.dragGroup) {
27896                 this.setDraggable(this.dragGroup.split(","));
27897     }
27898     if (this.dropGroup) {
27899                 this.setDroppable(this.dropGroup.split(","));
27900     }
27901     if (this.deletable) {
27902         this.setDeletable();
27903     }
27904     this.isDirtyFlag = false;
27905         this.addEvents({
27906                 "drop" : true
27907         });
27908 };
27909
27910 Roo.extend(Roo.DDView, Roo.View, {
27911 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
27912 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
27913 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
27914 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
27915
27916         isFormField: true,
27917
27918         reset: Roo.emptyFn,
27919         
27920         clearInvalid: Roo.form.Field.prototype.clearInvalid,
27921
27922         validate: function() {
27923                 return true;
27924         },
27925         
27926         destroy: function() {
27927                 this.purgeListeners();
27928                 this.getEl.removeAllListeners();
27929                 this.getEl().remove();
27930                 if (this.dragZone) {
27931                         if (this.dragZone.destroy) {
27932                                 this.dragZone.destroy();
27933                         }
27934                 }
27935                 if (this.dropZone) {
27936                         if (this.dropZone.destroy) {
27937                                 this.dropZone.destroy();
27938                         }
27939                 }
27940         },
27941
27942 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
27943         getName: function() {
27944                 return this.name;
27945         },
27946
27947 /**     Loads the View from a JSON string representing the Records to put into the Store. */
27948         setValue: function(v) {
27949                 if (!this.store) {
27950                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
27951                 }
27952                 var data = {};
27953                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
27954                 this.store.proxy = new Roo.data.MemoryProxy(data);
27955                 this.store.load();
27956         },
27957
27958 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
27959         getValue: function() {
27960                 var result = '(';
27961                 this.store.each(function(rec) {
27962                         result += rec.id + ',';
27963                 });
27964                 return result.substr(0, result.length - 1) + ')';
27965         },
27966         
27967         getIds: function() {
27968                 var i = 0, result = new Array(this.store.getCount());
27969                 this.store.each(function(rec) {
27970                         result[i++] = rec.id;
27971                 });
27972                 return result;
27973         },
27974         
27975         isDirty: function() {
27976                 return this.isDirtyFlag;
27977         },
27978
27979 /**
27980  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
27981  *      whole Element becomes the target, and this causes the drop gesture to append.
27982  */
27983     getTargetFromEvent : function(e) {
27984                 var target = e.getTarget();
27985                 while ((target !== null) && (target.parentNode != this.el.dom)) {
27986                 target = target.parentNode;
27987                 }
27988                 if (!target) {
27989                         target = this.el.dom.lastChild || this.el.dom;
27990                 }
27991                 return target;
27992     },
27993
27994 /**
27995  *      Create the drag data which consists of an object which has the property "ddel" as
27996  *      the drag proxy element. 
27997  */
27998     getDragData : function(e) {
27999         var target = this.findItemFromChild(e.getTarget());
28000                 if(target) {
28001                         this.handleSelection(e);
28002                         var selNodes = this.getSelectedNodes();
28003             var dragData = {
28004                 source: this,
28005                 copy: this.copy || (this.allowCopy && e.ctrlKey),
28006                 nodes: selNodes,
28007                 records: []
28008                         };
28009                         var selectedIndices = this.getSelectedIndexes();
28010                         for (var i = 0; i < selectedIndices.length; i++) {
28011                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
28012                         }
28013                         if (selNodes.length == 1) {
28014                                 dragData.ddel = target.cloneNode(true); // the div element
28015                         } else {
28016                                 var div = document.createElement('div'); // create the multi element drag "ghost"
28017                                 div.className = 'multi-proxy';
28018                                 for (var i = 0, len = selNodes.length; i < len; i++) {
28019                                         div.appendChild(selNodes[i].cloneNode(true));
28020                                 }
28021                                 dragData.ddel = div;
28022                         }
28023             //console.log(dragData)
28024             //console.log(dragData.ddel.innerHTML)
28025                         return dragData;
28026                 }
28027         //console.log('nodragData')
28028                 return false;
28029     },
28030     
28031 /**     Specify to which ddGroup items in this DDView may be dragged. */
28032     setDraggable: function(ddGroup) {
28033         if (ddGroup instanceof Array) {
28034                 Roo.each(ddGroup, this.setDraggable, this);
28035                 return;
28036         }
28037         if (this.dragZone) {
28038                 this.dragZone.addToGroup(ddGroup);
28039         } else {
28040                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
28041                                 containerScroll: true,
28042                                 ddGroup: ddGroup 
28043
28044                         });
28045 //                      Draggability implies selection. DragZone's mousedown selects the element.
28046                         if (!this.multiSelect) { this.singleSelect = true; }
28047
28048 //                      Wire the DragZone's handlers up to methods in *this*
28049                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
28050                 }
28051     },
28052
28053 /**     Specify from which ddGroup this DDView accepts drops. */
28054     setDroppable: function(ddGroup) {
28055         if (ddGroup instanceof Array) {
28056                 Roo.each(ddGroup, this.setDroppable, this);
28057                 return;
28058         }
28059         if (this.dropZone) {
28060                 this.dropZone.addToGroup(ddGroup);
28061         } else {
28062                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
28063                                 containerScroll: true,
28064                                 ddGroup: ddGroup
28065                         });
28066
28067 //                      Wire the DropZone's handlers up to methods in *this*
28068                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
28069                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
28070                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
28071                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
28072                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
28073                 }
28074     },
28075
28076 /**     Decide whether to drop above or below a View node. */
28077     getDropPoint : function(e, n, dd){
28078         if (n == this.el.dom) { return "above"; }
28079                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
28080                 var c = t + (b - t) / 2;
28081                 var y = Roo.lib.Event.getPageY(e);
28082                 if(y <= c) {
28083                         return "above";
28084                 }else{
28085                         return "below";
28086                 }
28087     },
28088
28089     onNodeEnter : function(n, dd, e, data){
28090                 return false;
28091     },
28092     
28093     onNodeOver : function(n, dd, e, data){
28094                 var pt = this.getDropPoint(e, n, dd);
28095                 // set the insert point style on the target node
28096                 var dragElClass = this.dropNotAllowed;
28097                 if (pt) {
28098                         var targetElClass;
28099                         if (pt == "above"){
28100                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
28101                                 targetElClass = "x-view-drag-insert-above";
28102                         } else {
28103                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
28104                                 targetElClass = "x-view-drag-insert-below";
28105                         }
28106                         if (this.lastInsertClass != targetElClass){
28107                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
28108                                 this.lastInsertClass = targetElClass;
28109                         }
28110                 }
28111                 return dragElClass;
28112         },
28113
28114     onNodeOut : function(n, dd, e, data){
28115                 this.removeDropIndicators(n);
28116     },
28117
28118     onNodeDrop : function(n, dd, e, data){
28119         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
28120                 return false;
28121         }
28122         var pt = this.getDropPoint(e, n, dd);
28123                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
28124                 if (pt == "below") { insertAt++; }
28125                 for (var i = 0; i < data.records.length; i++) {
28126                         var r = data.records[i];
28127                         var dup = this.store.getById(r.id);
28128                         if (dup && (dd != this.dragZone)) {
28129                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
28130                         } else {
28131                                 if (data.copy) {
28132                                         this.store.insert(insertAt++, r.copy());
28133                                 } else {
28134                                         data.source.isDirtyFlag = true;
28135                                         r.store.remove(r);
28136                                         this.store.insert(insertAt++, r);
28137                                 }
28138                                 this.isDirtyFlag = true;
28139                         }
28140                 }
28141                 this.dragZone.cachedTarget = null;
28142                 return true;
28143     },
28144
28145     removeDropIndicators : function(n){
28146                 if(n){
28147                         Roo.fly(n).removeClass([
28148                                 "x-view-drag-insert-above",
28149                                 "x-view-drag-insert-below"]);
28150                         this.lastInsertClass = "_noclass";
28151                 }
28152     },
28153
28154 /**
28155  *      Utility method. Add a delete option to the DDView's context menu.
28156  *      @param {String} imageUrl The URL of the "delete" icon image.
28157  */
28158         setDeletable: function(imageUrl) {
28159                 if (!this.singleSelect && !this.multiSelect) {
28160                         this.singleSelect = true;
28161                 }
28162                 var c = this.getContextMenu();
28163                 this.contextMenu.on("itemclick", function(item) {
28164                         switch (item.id) {
28165                                 case "delete":
28166                                         this.remove(this.getSelectedIndexes());
28167                                         break;
28168                         }
28169                 }, this);
28170                 this.contextMenu.add({
28171                         icon: imageUrl,
28172                         id: "delete",
28173                         text: 'Delete'
28174                 });
28175         },
28176         
28177 /**     Return the context menu for this DDView. */
28178         getContextMenu: function() {
28179                 if (!this.contextMenu) {
28180 //                      Create the View's context menu
28181                         this.contextMenu = new Roo.menu.Menu({
28182                                 id: this.id + "-contextmenu"
28183                         });
28184                         this.el.on("contextmenu", this.showContextMenu, this);
28185                 }
28186                 return this.contextMenu;
28187         },
28188         
28189         disableContextMenu: function() {
28190                 if (this.contextMenu) {
28191                         this.el.un("contextmenu", this.showContextMenu, this);
28192                 }
28193         },
28194
28195         showContextMenu: function(e, item) {
28196         item = this.findItemFromChild(e.getTarget());
28197                 if (item) {
28198                         e.stopEvent();
28199                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
28200                         this.contextMenu.showAt(e.getXY());
28201             }
28202     },
28203
28204 /**
28205  *      Remove {@link Roo.data.Record}s at the specified indices.
28206  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
28207  */
28208     remove: function(selectedIndices) {
28209                 selectedIndices = [].concat(selectedIndices);
28210                 for (var i = 0; i < selectedIndices.length; i++) {
28211                         var rec = this.store.getAt(selectedIndices[i]);
28212                         this.store.remove(rec);
28213                 }
28214     },
28215
28216 /**
28217  *      Double click fires the event, but also, if this is draggable, and there is only one other
28218  *      related DropZone, it transfers the selected node.
28219  */
28220     onDblClick : function(e){
28221         var item = this.findItemFromChild(e.getTarget());
28222         if(item){
28223             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
28224                 return false;
28225             }
28226             if (this.dragGroup) {
28227                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
28228                     while (targets.indexOf(this.dropZone) > -1) {
28229                             targets.remove(this.dropZone);
28230                                 }
28231                     if (targets.length == 1) {
28232                                         this.dragZone.cachedTarget = null;
28233                         var el = Roo.get(targets[0].getEl());
28234                         var box = el.getBox(true);
28235                         targets[0].onNodeDrop(el.dom, {
28236                                 target: el.dom,
28237                                 xy: [box.x, box.y + box.height - 1]
28238                         }, null, this.getDragData(e));
28239                     }
28240                 }
28241         }
28242     },
28243     
28244     handleSelection: function(e) {
28245                 this.dragZone.cachedTarget = null;
28246         var item = this.findItemFromChild(e.getTarget());
28247         if (!item) {
28248                 this.clearSelections(true);
28249                 return;
28250         }
28251                 if (item && (this.multiSelect || this.singleSelect)){
28252                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
28253                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
28254                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
28255                                 this.unselect(item);
28256                         } else {
28257                                 this.select(item, this.multiSelect && e.ctrlKey);
28258                                 this.lastSelection = item;
28259                         }
28260                 }
28261     },
28262
28263     onItemClick : function(item, index, e){
28264                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
28265                         return false;
28266                 }
28267                 return true;
28268     },
28269
28270     unselect : function(nodeInfo, suppressEvent){
28271                 var node = this.getNode(nodeInfo);
28272                 if(node && this.isSelected(node)){
28273                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28274                                 Roo.fly(node).removeClass(this.selectedClass);
28275                                 this.selections.remove(node);
28276                                 if(!suppressEvent){
28277                                         this.fireEvent("selectionchange", this, this.selections);
28278                                 }
28279                         }
28280                 }
28281     }
28282 });
28283 /*
28284  * Based on:
28285  * Ext JS Library 1.1.1
28286  * Copyright(c) 2006-2007, Ext JS, LLC.
28287  *
28288  * Originally Released Under LGPL - original licence link has changed is not relivant.
28289  *
28290  * Fork - LGPL
28291  * <script type="text/javascript">
28292  */
28293  
28294 /**
28295  * @class Roo.LayoutManager
28296  * @extends Roo.util.Observable
28297  * Base class for layout managers.
28298  */
28299 Roo.LayoutManager = function(container, config){
28300     Roo.LayoutManager.superclass.constructor.call(this);
28301     this.el = Roo.get(container);
28302     // ie scrollbar fix
28303     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
28304         document.body.scroll = "no";
28305     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
28306         this.el.position('relative');
28307     }
28308     this.id = this.el.id;
28309     this.el.addClass("x-layout-container");
28310     /** false to disable window resize monitoring @type Boolean */
28311     this.monitorWindowResize = true;
28312     this.regions = {};
28313     this.addEvents({
28314         /**
28315          * @event layout
28316          * Fires when a layout is performed. 
28317          * @param {Roo.LayoutManager} this
28318          */
28319         "layout" : true,
28320         /**
28321          * @event regionresized
28322          * Fires when the user resizes a region. 
28323          * @param {Roo.LayoutRegion} region The resized region
28324          * @param {Number} newSize The new size (width for east/west, height for north/south)
28325          */
28326         "regionresized" : true,
28327         /**
28328          * @event regioncollapsed
28329          * Fires when a region is collapsed. 
28330          * @param {Roo.LayoutRegion} region The collapsed region
28331          */
28332         "regioncollapsed" : true,
28333         /**
28334          * @event regionexpanded
28335          * Fires when a region is expanded.  
28336          * @param {Roo.LayoutRegion} region The expanded region
28337          */
28338         "regionexpanded" : true
28339     });
28340     this.updating = false;
28341     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
28342 };
28343
28344 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
28345     /**
28346      * Returns true if this layout is currently being updated
28347      * @return {Boolean}
28348      */
28349     isUpdating : function(){
28350         return this.updating; 
28351     },
28352     
28353     /**
28354      * Suspend the LayoutManager from doing auto-layouts while
28355      * making multiple add or remove calls
28356      */
28357     beginUpdate : function(){
28358         this.updating = true;    
28359     },
28360     
28361     /**
28362      * Restore auto-layouts and optionally disable the manager from performing a layout
28363      * @param {Boolean} noLayout true to disable a layout update 
28364      */
28365     endUpdate : function(noLayout){
28366         this.updating = false;
28367         if(!noLayout){
28368             this.layout();
28369         }    
28370     },
28371     
28372     layout: function(){
28373         
28374     },
28375     
28376     onRegionResized : function(region, newSize){
28377         this.fireEvent("regionresized", region, newSize);
28378         this.layout();
28379     },
28380     
28381     onRegionCollapsed : function(region){
28382         this.fireEvent("regioncollapsed", region);
28383     },
28384     
28385     onRegionExpanded : function(region){
28386         this.fireEvent("regionexpanded", region);
28387     },
28388         
28389     /**
28390      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
28391      * performs box-model adjustments.
28392      * @return {Object} The size as an object {width: (the width), height: (the height)}
28393      */
28394     getViewSize : function(){
28395         var size;
28396         if(this.el.dom != document.body){
28397             size = this.el.getSize();
28398         }else{
28399             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
28400         }
28401         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
28402         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
28403         return size;
28404     },
28405     
28406     /**
28407      * Returns the Element this layout is bound to.
28408      * @return {Roo.Element}
28409      */
28410     getEl : function(){
28411         return this.el;
28412     },
28413     
28414     /**
28415      * Returns the specified region.
28416      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
28417      * @return {Roo.LayoutRegion}
28418      */
28419     getRegion : function(target){
28420         return this.regions[target.toLowerCase()];
28421     },
28422     
28423     onWindowResize : function(){
28424         if(this.monitorWindowResize){
28425             this.layout();
28426         }
28427     }
28428 });/*
28429  * Based on:
28430  * Ext JS Library 1.1.1
28431  * Copyright(c) 2006-2007, Ext JS, LLC.
28432  *
28433  * Originally Released Under LGPL - original licence link has changed is not relivant.
28434  *
28435  * Fork - LGPL
28436  * <script type="text/javascript">
28437  */
28438 /**
28439  * @class Roo.BorderLayout
28440  * @extends Roo.LayoutManager
28441  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
28442  * please see: <br><br>
28443  * <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>
28444  * <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>
28445  * Example:
28446  <pre><code>
28447  var layout = new Roo.BorderLayout(document.body, {
28448     north: {
28449         initialSize: 25,
28450         titlebar: false
28451     },
28452     west: {
28453         split:true,
28454         initialSize: 200,
28455         minSize: 175,
28456         maxSize: 400,
28457         titlebar: true,
28458         collapsible: true
28459     },
28460     east: {
28461         split:true,
28462         initialSize: 202,
28463         minSize: 175,
28464         maxSize: 400,
28465         titlebar: true,
28466         collapsible: true
28467     },
28468     south: {
28469         split:true,
28470         initialSize: 100,
28471         minSize: 100,
28472         maxSize: 200,
28473         titlebar: true,
28474         collapsible: true
28475     },
28476     center: {
28477         titlebar: true,
28478         autoScroll:true,
28479         resizeTabs: true,
28480         minTabWidth: 50,
28481         preferredTabWidth: 150
28482     }
28483 });
28484
28485 // shorthand
28486 var CP = Roo.ContentPanel;
28487
28488 layout.beginUpdate();
28489 layout.add("north", new CP("north", "North"));
28490 layout.add("south", new CP("south", {title: "South", closable: true}));
28491 layout.add("west", new CP("west", {title: "West"}));
28492 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
28493 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
28494 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
28495 layout.getRegion("center").showPanel("center1");
28496 layout.endUpdate();
28497 </code></pre>
28498
28499 <b>The container the layout is rendered into can be either the body element or any other element.
28500 If it is not the body element, the container needs to either be an absolute positioned element,
28501 or you will need to add "position:relative" to the css of the container.  You will also need to specify
28502 the container size if it is not the body element.</b>
28503
28504 * @constructor
28505 * Create a new BorderLayout
28506 * @param {String/HTMLElement/Element} container The container this layout is bound to
28507 * @param {Object} config Configuration options
28508  */
28509 Roo.BorderLayout = function(container, config){
28510     config = config || {};
28511     Roo.BorderLayout.superclass.constructor.call(this, container, config);
28512     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
28513     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
28514         var target = this.factory.validRegions[i];
28515         if(config[target]){
28516             this.addRegion(target, config[target]);
28517         }
28518     }
28519 };
28520
28521 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
28522     /**
28523      * Creates and adds a new region if it doesn't already exist.
28524      * @param {String} target The target region key (north, south, east, west or center).
28525      * @param {Object} config The regions config object
28526      * @return {BorderLayoutRegion} The new region
28527      */
28528     addRegion : function(target, config){
28529         if(!this.regions[target]){
28530             var r = this.factory.create(target, this, config);
28531             this.bindRegion(target, r);
28532         }
28533         return this.regions[target];
28534     },
28535
28536     // private (kinda)
28537     bindRegion : function(name, r){
28538         this.regions[name] = r;
28539         r.on("visibilitychange", this.layout, this);
28540         r.on("paneladded", this.layout, this);
28541         r.on("panelremoved", this.layout, this);
28542         r.on("invalidated", this.layout, this);
28543         r.on("resized", this.onRegionResized, this);
28544         r.on("collapsed", this.onRegionCollapsed, this);
28545         r.on("expanded", this.onRegionExpanded, this);
28546     },
28547
28548     /**
28549      * Performs a layout update.
28550      */
28551     layout : function(){
28552         if(this.updating) return;
28553         var size = this.getViewSize();
28554         var w = size.width;
28555         var h = size.height;
28556         var centerW = w;
28557         var centerH = h;
28558         var centerY = 0;
28559         var centerX = 0;
28560         //var x = 0, y = 0;
28561
28562         var rs = this.regions;
28563         var north = rs["north"];
28564         var south = rs["south"]; 
28565         var west = rs["west"];
28566         var east = rs["east"];
28567         var center = rs["center"];
28568         //if(this.hideOnLayout){ // not supported anymore
28569             //c.el.setStyle("display", "none");
28570         //}
28571         if(north && north.isVisible()){
28572             var b = north.getBox();
28573             var m = north.getMargins();
28574             b.width = w - (m.left+m.right);
28575             b.x = m.left;
28576             b.y = m.top;
28577             centerY = b.height + b.y + m.bottom;
28578             centerH -= centerY;
28579             north.updateBox(this.safeBox(b));
28580         }
28581         if(south && south.isVisible()){
28582             var b = south.getBox();
28583             var m = south.getMargins();
28584             b.width = w - (m.left+m.right);
28585             b.x = m.left;
28586             var totalHeight = (b.height + m.top + m.bottom);
28587             b.y = h - totalHeight + m.top;
28588             centerH -= totalHeight;
28589             south.updateBox(this.safeBox(b));
28590         }
28591         if(west && west.isVisible()){
28592             var b = west.getBox();
28593             var m = west.getMargins();
28594             b.height = centerH - (m.top+m.bottom);
28595             b.x = m.left;
28596             b.y = centerY + m.top;
28597             var totalWidth = (b.width + m.left + m.right);
28598             centerX += totalWidth;
28599             centerW -= totalWidth;
28600             west.updateBox(this.safeBox(b));
28601         }
28602         if(east && east.isVisible()){
28603             var b = east.getBox();
28604             var m = east.getMargins();
28605             b.height = centerH - (m.top+m.bottom);
28606             var totalWidth = (b.width + m.left + m.right);
28607             b.x = w - totalWidth + m.left;
28608             b.y = centerY + m.top;
28609             centerW -= totalWidth;
28610             east.updateBox(this.safeBox(b));
28611         }
28612         if(center){
28613             var m = center.getMargins();
28614             var centerBox = {
28615                 x: centerX + m.left,
28616                 y: centerY + m.top,
28617                 width: centerW - (m.left+m.right),
28618                 height: centerH - (m.top+m.bottom)
28619             };
28620             //if(this.hideOnLayout){
28621                 //center.el.setStyle("display", "block");
28622             //}
28623             center.updateBox(this.safeBox(centerBox));
28624         }
28625         this.el.repaint();
28626         this.fireEvent("layout", this);
28627     },
28628
28629     // private
28630     safeBox : function(box){
28631         box.width = Math.max(0, box.width);
28632         box.height = Math.max(0, box.height);
28633         return box;
28634     },
28635
28636     /**
28637      * Adds a ContentPanel (or subclass) to this layout.
28638      * @param {String} target The target region key (north, south, east, west or center).
28639      * @param {Roo.ContentPanel} panel The panel to add
28640      * @return {Roo.ContentPanel} The added panel
28641      */
28642     add : function(target, panel){
28643          
28644         target = target.toLowerCase();
28645         return this.regions[target].add(panel);
28646     },
28647
28648     /**
28649      * Remove a ContentPanel (or subclass) to this layout.
28650      * @param {String} target The target region key (north, south, east, west or center).
28651      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
28652      * @return {Roo.ContentPanel} The removed panel
28653      */
28654     remove : function(target, panel){
28655         target = target.toLowerCase();
28656         return this.regions[target].remove(panel);
28657     },
28658
28659     /**
28660      * Searches all regions for a panel with the specified id
28661      * @param {String} panelId
28662      * @return {Roo.ContentPanel} The panel or null if it wasn't found
28663      */
28664     findPanel : function(panelId){
28665         var rs = this.regions;
28666         for(var target in rs){
28667             if(typeof rs[target] != "function"){
28668                 var p = rs[target].getPanel(panelId);
28669                 if(p){
28670                     return p;
28671                 }
28672             }
28673         }
28674         return null;
28675     },
28676
28677     /**
28678      * Searches all regions for a panel with the specified id and activates (shows) it.
28679      * @param {String/ContentPanel} panelId The panels id or the panel itself
28680      * @return {Roo.ContentPanel} The shown panel or null
28681      */
28682     showPanel : function(panelId) {
28683       var rs = this.regions;
28684       for(var target in rs){
28685          var r = rs[target];
28686          if(typeof r != "function"){
28687             if(r.hasPanel(panelId)){
28688                return r.showPanel(panelId);
28689             }
28690          }
28691       }
28692       return null;
28693    },
28694
28695    /**
28696      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
28697      * @param {Roo.state.Provider} provider (optional) An alternate state provider
28698      */
28699     restoreState : function(provider){
28700         if(!provider){
28701             provider = Roo.state.Manager;
28702         }
28703         var sm = new Roo.LayoutStateManager();
28704         sm.init(this, provider);
28705     },
28706
28707     /**
28708      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
28709      * object should contain properties for each region to add ContentPanels to, and each property's value should be
28710      * a valid ContentPanel config object.  Example:
28711      * <pre><code>
28712 // Create the main layout
28713 var layout = new Roo.BorderLayout('main-ct', {
28714     west: {
28715         split:true,
28716         minSize: 175,
28717         titlebar: true
28718     },
28719     center: {
28720         title:'Components'
28721     }
28722 }, 'main-ct');
28723
28724 // Create and add multiple ContentPanels at once via configs
28725 layout.batchAdd({
28726    west: {
28727        id: 'source-files',
28728        autoCreate:true,
28729        title:'Ext Source Files',
28730        autoScroll:true,
28731        fitToFrame:true
28732    },
28733    center : {
28734        el: cview,
28735        autoScroll:true,
28736        fitToFrame:true,
28737        toolbar: tb,
28738        resizeEl:'cbody'
28739    }
28740 });
28741 </code></pre>
28742      * @param {Object} regions An object containing ContentPanel configs by region name
28743      */
28744     batchAdd : function(regions){
28745         this.beginUpdate();
28746         for(var rname in regions){
28747             var lr = this.regions[rname];
28748             if(lr){
28749                 this.addTypedPanels(lr, regions[rname]);
28750             }
28751         }
28752         this.endUpdate();
28753     },
28754
28755     // private
28756     addTypedPanels : function(lr, ps){
28757         if(typeof ps == 'string'){
28758             lr.add(new Roo.ContentPanel(ps));
28759         }
28760         else if(ps instanceof Array){
28761             for(var i =0, len = ps.length; i < len; i++){
28762                 this.addTypedPanels(lr, ps[i]);
28763             }
28764         }
28765         else if(!ps.events){ // raw config?
28766             var el = ps.el;
28767             delete ps.el; // prevent conflict
28768             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
28769         }
28770         else {  // panel object assumed!
28771             lr.add(ps);
28772         }
28773     },
28774     /**
28775      * Adds a xtype elements to the layout.
28776      * <pre><code>
28777
28778 layout.addxtype({
28779        xtype : 'ContentPanel',
28780        region: 'west',
28781        items: [ .... ]
28782    }
28783 );
28784
28785 layout.addxtype({
28786         xtype : 'NestedLayoutPanel',
28787         region: 'west',
28788         layout: {
28789            center: { },
28790            west: { }   
28791         },
28792         items : [ ... list of content panels or nested layout panels.. ]
28793    }
28794 );
28795 </code></pre>
28796      * @param {Object} cfg Xtype definition of item to add.
28797      */
28798     addxtype : function(cfg)
28799     {
28800         // basically accepts a pannel...
28801         // can accept a layout region..!?!?
28802        // console.log('BorderLayout add ' + cfg.xtype)
28803         
28804         if (!cfg.xtype.match(/Panel$/)) {
28805             return false;
28806         }
28807         var ret = false;
28808         var region = cfg.region;
28809         delete cfg.region;
28810         
28811           
28812         var xitems = [];
28813         if (cfg.items) {
28814             xitems = cfg.items;
28815             delete cfg.items;
28816         }
28817         
28818         
28819         switch(cfg.xtype) 
28820         {
28821             case 'ContentPanel':  // ContentPanel (el, cfg)
28822                 if(cfg.autoCreate) {
28823                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
28824                 } else {
28825                     var el = this.el.createChild();
28826                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
28827                 }
28828                 
28829                 this.add(region, ret);
28830                 break;
28831             
28832             
28833             case 'TreePanel': // our new panel!
28834                 cfg.el = this.el.createChild();
28835                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
28836                 this.add(region, ret);
28837                 break;
28838             
28839             case 'NestedLayoutPanel': 
28840                 // create a new Layout (which is  a Border Layout...
28841                 var el = this.el.createChild();
28842                 var clayout = cfg.layout;
28843                 delete cfg.layout;
28844                 clayout.items   = clayout.items  || [];
28845                 // replace this exitems with the clayout ones..
28846                 xitems = clayout.items;
28847                  
28848                 
28849                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
28850                     cfg.background = false;
28851                 }
28852                 var layout = new Roo.BorderLayout(el, clayout);
28853                 
28854                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
28855                 //console.log('adding nested layout panel '  + cfg.toSource());
28856                 this.add(region, ret);
28857                 
28858                 break;
28859                 
28860             case 'GridPanel': 
28861             
28862                 // needs grid and region
28863                 
28864                 //var el = this.getRegion(region).el.createChild();
28865                 var el = this.el.createChild();
28866                 // create the grid first...
28867                 
28868                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
28869                 delete cfg.grid;
28870                 if (region == 'center' && this.active ) {
28871                     cfg.background = false;
28872                 }
28873                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
28874                 
28875                 this.add(region, ret);
28876                 if (cfg.background) {
28877                     ret.on('activate', function(gp) {
28878                         if (!gp.grid.rendered) {
28879                             gp.grid.render();
28880                         }
28881                     });
28882                 } else {
28883                     grid.render();
28884                 }
28885                 break;
28886            
28887                
28888                 
28889                 
28890             default: 
28891                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
28892                 return;
28893              // GridPanel (grid, cfg)
28894             
28895         }
28896         this.beginUpdate();
28897         // add children..
28898         Roo.each(xitems, function(i)  {
28899             ret.addxtype(i);
28900         });
28901         this.endUpdate();
28902         return ret;
28903         
28904     }
28905 });
28906
28907 /**
28908  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
28909  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
28910  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
28911  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
28912  * <pre><code>
28913 // shorthand
28914 var CP = Roo.ContentPanel;
28915
28916 var layout = Roo.BorderLayout.create({
28917     north: {
28918         initialSize: 25,
28919         titlebar: false,
28920         panels: [new CP("north", "North")]
28921     },
28922     west: {
28923         split:true,
28924         initialSize: 200,
28925         minSize: 175,
28926         maxSize: 400,
28927         titlebar: true,
28928         collapsible: true,
28929         panels: [new CP("west", {title: "West"})]
28930     },
28931     east: {
28932         split:true,
28933         initialSize: 202,
28934         minSize: 175,
28935         maxSize: 400,
28936         titlebar: true,
28937         collapsible: true,
28938         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
28939     },
28940     south: {
28941         split:true,
28942         initialSize: 100,
28943         minSize: 100,
28944         maxSize: 200,
28945         titlebar: true,
28946         collapsible: true,
28947         panels: [new CP("south", {title: "South", closable: true})]
28948     },
28949     center: {
28950         titlebar: true,
28951         autoScroll:true,
28952         resizeTabs: true,
28953         minTabWidth: 50,
28954         preferredTabWidth: 150,
28955         panels: [
28956             new CP("center1", {title: "Close Me", closable: true}),
28957             new CP("center2", {title: "Center Panel", closable: false})
28958         ]
28959     }
28960 }, document.body);
28961
28962 layout.getRegion("center").showPanel("center1");
28963 </code></pre>
28964  * @param config
28965  * @param targetEl
28966  */
28967 Roo.BorderLayout.create = function(config, targetEl){
28968     var layout = new Roo.BorderLayout(targetEl || document.body, config);
28969     layout.beginUpdate();
28970     var regions = Roo.BorderLayout.RegionFactory.validRegions;
28971     for(var j = 0, jlen = regions.length; j < jlen; j++){
28972         var lr = regions[j];
28973         if(layout.regions[lr] && config[lr].panels){
28974             var r = layout.regions[lr];
28975             var ps = config[lr].panels;
28976             layout.addTypedPanels(r, ps);
28977         }
28978     }
28979     layout.endUpdate();
28980     return layout;
28981 };
28982
28983 // private
28984 Roo.BorderLayout.RegionFactory = {
28985     // private
28986     validRegions : ["north","south","east","west","center"],
28987
28988     // private
28989     create : function(target, mgr, config){
28990         target = target.toLowerCase();
28991         if(config.lightweight || config.basic){
28992             return new Roo.BasicLayoutRegion(mgr, config, target);
28993         }
28994         switch(target){
28995             case "north":
28996                 return new Roo.NorthLayoutRegion(mgr, config);
28997             case "south":
28998                 return new Roo.SouthLayoutRegion(mgr, config);
28999             case "east":
29000                 return new Roo.EastLayoutRegion(mgr, config);
29001             case "west":
29002                 return new Roo.WestLayoutRegion(mgr, config);
29003             case "center":
29004                 return new Roo.CenterLayoutRegion(mgr, config);
29005         }
29006         throw 'Layout region "'+target+'" not supported.';
29007     }
29008 };/*
29009  * Based on:
29010  * Ext JS Library 1.1.1
29011  * Copyright(c) 2006-2007, Ext JS, LLC.
29012  *
29013  * Originally Released Under LGPL - original licence link has changed is not relivant.
29014  *
29015  * Fork - LGPL
29016  * <script type="text/javascript">
29017  */
29018  
29019 /**
29020  * @class Roo.BasicLayoutRegion
29021  * @extends Roo.util.Observable
29022  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
29023  * and does not have a titlebar, tabs or any other features. All it does is size and position 
29024  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
29025  */
29026 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
29027     this.mgr = mgr;
29028     this.position  = pos;
29029     this.events = {
29030         /**
29031          * @scope Roo.BasicLayoutRegion
29032          */
29033         
29034         /**
29035          * @event beforeremove
29036          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
29037          * @param {Roo.LayoutRegion} this
29038          * @param {Roo.ContentPanel} panel The panel
29039          * @param {Object} e The cancel event object
29040          */
29041         "beforeremove" : true,
29042         /**
29043          * @event invalidated
29044          * Fires when the layout for this region is changed.
29045          * @param {Roo.LayoutRegion} this
29046          */
29047         "invalidated" : true,
29048         /**
29049          * @event visibilitychange
29050          * Fires when this region is shown or hidden 
29051          * @param {Roo.LayoutRegion} this
29052          * @param {Boolean} visibility true or false
29053          */
29054         "visibilitychange" : true,
29055         /**
29056          * @event paneladded
29057          * Fires when a panel is added. 
29058          * @param {Roo.LayoutRegion} this
29059          * @param {Roo.ContentPanel} panel The panel
29060          */
29061         "paneladded" : true,
29062         /**
29063          * @event panelremoved
29064          * Fires when a panel is removed. 
29065          * @param {Roo.LayoutRegion} this
29066          * @param {Roo.ContentPanel} panel The panel
29067          */
29068         "panelremoved" : true,
29069         /**
29070          * @event collapsed
29071          * Fires when this region is collapsed.
29072          * @param {Roo.LayoutRegion} this
29073          */
29074         "collapsed" : true,
29075         /**
29076          * @event expanded
29077          * Fires when this region is expanded.
29078          * @param {Roo.LayoutRegion} this
29079          */
29080         "expanded" : true,
29081         /**
29082          * @event slideshow
29083          * Fires when this region is slid into view.
29084          * @param {Roo.LayoutRegion} this
29085          */
29086         "slideshow" : true,
29087         /**
29088          * @event slidehide
29089          * Fires when this region slides out of view. 
29090          * @param {Roo.LayoutRegion} this
29091          */
29092         "slidehide" : true,
29093         /**
29094          * @event panelactivated
29095          * Fires when a panel is activated. 
29096          * @param {Roo.LayoutRegion} this
29097          * @param {Roo.ContentPanel} panel The activated panel
29098          */
29099         "panelactivated" : true,
29100         /**
29101          * @event resized
29102          * Fires when the user resizes this region. 
29103          * @param {Roo.LayoutRegion} this
29104          * @param {Number} newSize The new size (width for east/west, height for north/south)
29105          */
29106         "resized" : true
29107     };
29108     /** A collection of panels in this region. @type Roo.util.MixedCollection */
29109     this.panels = new Roo.util.MixedCollection();
29110     this.panels.getKey = this.getPanelId.createDelegate(this);
29111     this.box = null;
29112     this.activePanel = null;
29113     // ensure listeners are added...
29114     
29115     if (config.listeners || config.events) {
29116         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
29117             listeners : config.listeners || {},
29118             events : config.events || {}
29119         });
29120     }
29121     
29122     if(skipConfig !== true){
29123         this.applyConfig(config);
29124     }
29125 };
29126
29127 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
29128     getPanelId : function(p){
29129         return p.getId();
29130     },
29131     
29132     applyConfig : function(config){
29133         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
29134         this.config = config;
29135         
29136     },
29137     
29138     /**
29139      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
29140      * the width, for horizontal (north, south) the height.
29141      * @param {Number} newSize The new width or height
29142      */
29143     resizeTo : function(newSize){
29144         var el = this.el ? this.el :
29145                  (this.activePanel ? this.activePanel.getEl() : null);
29146         if(el){
29147             switch(this.position){
29148                 case "east":
29149                 case "west":
29150                     el.setWidth(newSize);
29151                     this.fireEvent("resized", this, newSize);
29152                 break;
29153                 case "north":
29154                 case "south":
29155                     el.setHeight(newSize);
29156                     this.fireEvent("resized", this, newSize);
29157                 break;                
29158             }
29159         }
29160     },
29161     
29162     getBox : function(){
29163         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
29164     },
29165     
29166     getMargins : function(){
29167         return this.margins;
29168     },
29169     
29170     updateBox : function(box){
29171         this.box = box;
29172         var el = this.activePanel.getEl();
29173         el.dom.style.left = box.x + "px";
29174         el.dom.style.top = box.y + "px";
29175         this.activePanel.setSize(box.width, box.height);
29176     },
29177     
29178     /**
29179      * Returns the container element for this region.
29180      * @return {Roo.Element}
29181      */
29182     getEl : function(){
29183         return this.activePanel;
29184     },
29185     
29186     /**
29187      * Returns true if this region is currently visible.
29188      * @return {Boolean}
29189      */
29190     isVisible : function(){
29191         return this.activePanel ? true : false;
29192     },
29193     
29194     setActivePanel : function(panel){
29195         panel = this.getPanel(panel);
29196         if(this.activePanel && this.activePanel != panel){
29197             this.activePanel.setActiveState(false);
29198             this.activePanel.getEl().setLeftTop(-10000,-10000);
29199         }
29200         this.activePanel = panel;
29201         panel.setActiveState(true);
29202         if(this.box){
29203             panel.setSize(this.box.width, this.box.height);
29204         }
29205         this.fireEvent("panelactivated", this, panel);
29206         this.fireEvent("invalidated");
29207     },
29208     
29209     /**
29210      * Show the specified panel.
29211      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
29212      * @return {Roo.ContentPanel} The shown panel or null
29213      */
29214     showPanel : function(panel){
29215         if(panel = this.getPanel(panel)){
29216             this.setActivePanel(panel);
29217         }
29218         return panel;
29219     },
29220     
29221     /**
29222      * Get the active panel for this region.
29223      * @return {Roo.ContentPanel} The active panel or null
29224      */
29225     getActivePanel : function(){
29226         return this.activePanel;
29227     },
29228     
29229     /**
29230      * Add the passed ContentPanel(s)
29231      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
29232      * @return {Roo.ContentPanel} The panel added (if only one was added)
29233      */
29234     add : function(panel){
29235         if(arguments.length > 1){
29236             for(var i = 0, len = arguments.length; i < len; i++) {
29237                 this.add(arguments[i]);
29238             }
29239             return null;
29240         }
29241         if(this.hasPanel(panel)){
29242             this.showPanel(panel);
29243             return panel;
29244         }
29245         var el = panel.getEl();
29246         if(el.dom.parentNode != this.mgr.el.dom){
29247             this.mgr.el.dom.appendChild(el.dom);
29248         }
29249         if(panel.setRegion){
29250             panel.setRegion(this);
29251         }
29252         this.panels.add(panel);
29253         el.setStyle("position", "absolute");
29254         if(!panel.background){
29255             this.setActivePanel(panel);
29256             if(this.config.initialSize && this.panels.getCount()==1){
29257                 this.resizeTo(this.config.initialSize);
29258             }
29259         }
29260         this.fireEvent("paneladded", this, panel);
29261         return panel;
29262     },
29263     
29264     /**
29265      * Returns true if the panel is in this region.
29266      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29267      * @return {Boolean}
29268      */
29269     hasPanel : function(panel){
29270         if(typeof panel == "object"){ // must be panel obj
29271             panel = panel.getId();
29272         }
29273         return this.getPanel(panel) ? true : false;
29274     },
29275     
29276     /**
29277      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
29278      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29279      * @param {Boolean} preservePanel Overrides the config preservePanel option
29280      * @return {Roo.ContentPanel} The panel that was removed
29281      */
29282     remove : function(panel, preservePanel){
29283         panel = this.getPanel(panel);
29284         if(!panel){
29285             return null;
29286         }
29287         var e = {};
29288         this.fireEvent("beforeremove", this, panel, e);
29289         if(e.cancel === true){
29290             return null;
29291         }
29292         var panelId = panel.getId();
29293         this.panels.removeKey(panelId);
29294         return panel;
29295     },
29296     
29297     /**
29298      * Returns the panel specified or null if it's not in this region.
29299      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29300      * @return {Roo.ContentPanel}
29301      */
29302     getPanel : function(id){
29303         if(typeof id == "object"){ // must be panel obj
29304             return id;
29305         }
29306         return this.panels.get(id);
29307     },
29308     
29309     /**
29310      * Returns this regions position (north/south/east/west/center).
29311      * @return {String} 
29312      */
29313     getPosition: function(){
29314         return this.position;    
29315     }
29316 });/*
29317  * Based on:
29318  * Ext JS Library 1.1.1
29319  * Copyright(c) 2006-2007, Ext JS, LLC.
29320  *
29321  * Originally Released Under LGPL - original licence link has changed is not relivant.
29322  *
29323  * Fork - LGPL
29324  * <script type="text/javascript">
29325  */
29326  
29327 /**
29328  * @class Roo.LayoutRegion
29329  * @extends Roo.BasicLayoutRegion
29330  * This class represents a region in a layout manager.
29331  * @cfg {Boolean} collapsible False to disable collapsing (defaults to true)
29332  * @cfg {Boolean} collapsed True to set the initial display to collapsed (defaults to false)
29333  * @cfg {Boolean} floatable False to disable floating (defaults to true)
29334  * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
29335  * @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})
29336  * @cfg {String} tabPosition "top" or "bottom" (defaults to "bottom")
29337  * @cfg {String} collapsedTitle Optional string message to display in the collapsed block of a north or south region
29338  * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
29339  * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
29340  * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
29341  * @cfg {String} title The title for the region (overrides panel titles)
29342  * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
29343  * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
29344  * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
29345  * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
29346  * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
29347  * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
29348  * the space available, similar to FireFox 1.5 tabs (defaults to false)
29349  * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
29350  * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
29351  * @cfg {Boolean} showPin True to show a pin button
29352 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
29353 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
29354 * @cfg {Boolean} disableTabTips True to disable tab tooltips
29355 * @cfg {Number} width  For East/West panels
29356 * @cfg {Number} height For North/South panels
29357 * @cfg {Boolean} split To show the splitter
29358  */
29359 Roo.LayoutRegion = function(mgr, config, pos){
29360     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
29361     var dh = Roo.DomHelper;
29362     /** This region's container element 
29363     * @type Roo.Element */
29364     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
29365     /** This region's title element 
29366     * @type Roo.Element */
29367
29368     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
29369         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
29370         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
29371     ]}, true);
29372     this.titleEl.enableDisplayMode();
29373     /** This region's title text element 
29374     * @type HTMLElement */
29375     this.titleTextEl = this.titleEl.dom.firstChild;
29376     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
29377     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
29378     this.closeBtn.enableDisplayMode();
29379     this.closeBtn.on("click", this.closeClicked, this);
29380     this.closeBtn.hide();
29381
29382     this.createBody(config);
29383     this.visible = true;
29384     this.collapsed = false;
29385
29386     if(config.hideWhenEmpty){
29387         this.hide();
29388         this.on("paneladded", this.validateVisibility, this);
29389         this.on("panelremoved", this.validateVisibility, this);
29390     }
29391     this.applyConfig(config);
29392 };
29393
29394 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
29395
29396     createBody : function(){
29397         /** This region's body element 
29398         * @type Roo.Element */
29399         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
29400     },
29401
29402     applyConfig : function(c){
29403         if(c.collapsible && this.position != "center" && !this.collapsedEl){
29404             var dh = Roo.DomHelper;
29405             if(c.titlebar !== false){
29406                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
29407                 this.collapseBtn.on("click", this.collapse, this);
29408                 this.collapseBtn.enableDisplayMode();
29409
29410                 if(c.showPin === true || this.showPin){
29411                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
29412                     this.stickBtn.enableDisplayMode();
29413                     this.stickBtn.on("click", this.expand, this);
29414                     this.stickBtn.hide();
29415                 }
29416             }
29417             /** This region's collapsed element
29418             * @type Roo.Element */
29419             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
29420                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
29421             ]}, true);
29422             if(c.floatable !== false){
29423                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
29424                this.collapsedEl.on("click", this.collapseClick, this);
29425             }
29426
29427             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
29428                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
29429                    id: "message", unselectable: "on", style:{"float":"left"}});
29430                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
29431              }
29432             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
29433             this.expandBtn.on("click", this.expand, this);
29434         }
29435         if(this.collapseBtn){
29436             this.collapseBtn.setVisible(c.collapsible == true);
29437         }
29438         this.cmargins = c.cmargins || this.cmargins ||
29439                          (this.position == "west" || this.position == "east" ?
29440                              {top: 0, left: 2, right:2, bottom: 0} :
29441                              {top: 2, left: 0, right:0, bottom: 2});
29442         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
29443         this.bottomTabs = c.tabPosition != "top";
29444         this.autoScroll = c.autoScroll || false;
29445         if(this.autoScroll){
29446             this.bodyEl.setStyle("overflow", "auto");
29447         }else{
29448             this.bodyEl.setStyle("overflow", "hidden");
29449         }
29450         //if(c.titlebar !== false){
29451             if((!c.titlebar && !c.title) || c.titlebar === false){
29452                 this.titleEl.hide();
29453             }else{
29454                 this.titleEl.show();
29455                 if(c.title){
29456                     this.titleTextEl.innerHTML = c.title;
29457                 }
29458             }
29459         //}
29460         this.duration = c.duration || .30;
29461         this.slideDuration = c.slideDuration || .45;
29462         this.config = c;
29463         if(c.collapsed){
29464             this.collapse(true);
29465         }
29466         if(c.hidden){
29467             this.hide();
29468         }
29469     },
29470     /**
29471      * Returns true if this region is currently visible.
29472      * @return {Boolean}
29473      */
29474     isVisible : function(){
29475         return this.visible;
29476     },
29477
29478     /**
29479      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
29480      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
29481      */
29482     setCollapsedTitle : function(title){
29483         title = title || "&#160;";
29484         if(this.collapsedTitleTextEl){
29485             this.collapsedTitleTextEl.innerHTML = title;
29486         }
29487     },
29488
29489     getBox : function(){
29490         var b;
29491         if(!this.collapsed){
29492             b = this.el.getBox(false, true);
29493         }else{
29494             b = this.collapsedEl.getBox(false, true);
29495         }
29496         return b;
29497     },
29498
29499     getMargins : function(){
29500         return this.collapsed ? this.cmargins : this.margins;
29501     },
29502
29503     highlight : function(){
29504         this.el.addClass("x-layout-panel-dragover");
29505     },
29506
29507     unhighlight : function(){
29508         this.el.removeClass("x-layout-panel-dragover");
29509     },
29510
29511     updateBox : function(box){
29512         this.box = box;
29513         if(!this.collapsed){
29514             this.el.dom.style.left = box.x + "px";
29515             this.el.dom.style.top = box.y + "px";
29516             this.updateBody(box.width, box.height);
29517         }else{
29518             this.collapsedEl.dom.style.left = box.x + "px";
29519             this.collapsedEl.dom.style.top = box.y + "px";
29520             this.collapsedEl.setSize(box.width, box.height);
29521         }
29522         if(this.tabs){
29523             this.tabs.autoSizeTabs();
29524         }
29525     },
29526
29527     updateBody : function(w, h){
29528         if(w !== null){
29529             this.el.setWidth(w);
29530             w -= this.el.getBorderWidth("rl");
29531             if(this.config.adjustments){
29532                 w += this.config.adjustments[0];
29533             }
29534         }
29535         if(h !== null){
29536             this.el.setHeight(h);
29537             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
29538             h -= this.el.getBorderWidth("tb");
29539             if(this.config.adjustments){
29540                 h += this.config.adjustments[1];
29541             }
29542             this.bodyEl.setHeight(h);
29543             if(this.tabs){
29544                 h = this.tabs.syncHeight(h);
29545             }
29546         }
29547         if(this.panelSize){
29548             w = w !== null ? w : this.panelSize.width;
29549             h = h !== null ? h : this.panelSize.height;
29550         }
29551         if(this.activePanel){
29552             var el = this.activePanel.getEl();
29553             w = w !== null ? w : el.getWidth();
29554             h = h !== null ? h : el.getHeight();
29555             this.panelSize = {width: w, height: h};
29556             this.activePanel.setSize(w, h);
29557         }
29558         if(Roo.isIE && this.tabs){
29559             this.tabs.el.repaint();
29560         }
29561     },
29562
29563     /**
29564      * Returns the container element for this region.
29565      * @return {Roo.Element}
29566      */
29567     getEl : function(){
29568         return this.el;
29569     },
29570
29571     /**
29572      * Hides this region.
29573      */
29574     hide : function(){
29575         if(!this.collapsed){
29576             this.el.dom.style.left = "-2000px";
29577             this.el.hide();
29578         }else{
29579             this.collapsedEl.dom.style.left = "-2000px";
29580             this.collapsedEl.hide();
29581         }
29582         this.visible = false;
29583         this.fireEvent("visibilitychange", this, false);
29584     },
29585
29586     /**
29587      * Shows this region if it was previously hidden.
29588      */
29589     show : function(){
29590         if(!this.collapsed){
29591             this.el.show();
29592         }else{
29593             this.collapsedEl.show();
29594         }
29595         this.visible = true;
29596         this.fireEvent("visibilitychange", this, true);
29597     },
29598
29599     closeClicked : function(){
29600         if(this.activePanel){
29601             this.remove(this.activePanel);
29602         }
29603     },
29604
29605     collapseClick : function(e){
29606         if(this.isSlid){
29607            e.stopPropagation();
29608            this.slideIn();
29609         }else{
29610            e.stopPropagation();
29611            this.slideOut();
29612         }
29613     },
29614
29615     /**
29616      * Collapses this region.
29617      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
29618      */
29619     collapse : function(skipAnim){
29620         if(this.collapsed) return;
29621         this.collapsed = true;
29622         if(this.split){
29623             this.split.el.hide();
29624         }
29625         if(this.config.animate && skipAnim !== true){
29626             this.fireEvent("invalidated", this);
29627             this.animateCollapse();
29628         }else{
29629             this.el.setLocation(-20000,-20000);
29630             this.el.hide();
29631             this.collapsedEl.show();
29632             this.fireEvent("collapsed", this);
29633             this.fireEvent("invalidated", this);
29634         }
29635     },
29636
29637     animateCollapse : function(){
29638         // overridden
29639     },
29640
29641     /**
29642      * Expands this region if it was previously collapsed.
29643      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
29644      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
29645      */
29646     expand : function(e, skipAnim){
29647         if(e) e.stopPropagation();
29648         if(!this.collapsed || this.el.hasActiveFx()) return;
29649         if(this.isSlid){
29650             this.afterSlideIn();
29651             skipAnim = true;
29652         }
29653         this.collapsed = false;
29654         if(this.config.animate && skipAnim !== true){
29655             this.animateExpand();
29656         }else{
29657             this.el.show();
29658             if(this.split){
29659                 this.split.el.show();
29660             }
29661             this.collapsedEl.setLocation(-2000,-2000);
29662             this.collapsedEl.hide();
29663             this.fireEvent("invalidated", this);
29664             this.fireEvent("expanded", this);
29665         }
29666     },
29667
29668     animateExpand : function(){
29669         // overridden
29670     },
29671
29672     initTabs : function(){
29673         this.bodyEl.setStyle("overflow", "hidden");
29674         var ts = new Roo.TabPanel(this.bodyEl.dom, {
29675             tabPosition: this.bottomTabs ? 'bottom' : 'top',
29676             disableTooltips: this.config.disableTabTips
29677         });
29678         if(this.config.hideTabs){
29679             ts.stripWrap.setDisplayed(false);
29680         }
29681         this.tabs = ts;
29682         ts.resizeTabs = this.config.resizeTabs === true;
29683         ts.minTabWidth = this.config.minTabWidth || 40;
29684         ts.maxTabWidth = this.config.maxTabWidth || 250;
29685         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
29686         ts.monitorResize = false;
29687         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
29688         ts.bodyEl.addClass('x-layout-tabs-body');
29689         this.panels.each(this.initPanelAsTab, this);
29690     },
29691
29692     initPanelAsTab : function(panel){
29693         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
29694                     this.config.closeOnTab && panel.isClosable());
29695         if(panel.tabTip !== undefined){
29696             ti.setTooltip(panel.tabTip);
29697         }
29698         ti.on("activate", function(){
29699               this.setActivePanel(panel);
29700         }, this);
29701         if(this.config.closeOnTab){
29702             ti.on("beforeclose", function(t, e){
29703                 e.cancel = true;
29704                 this.remove(panel);
29705             }, this);
29706         }
29707         return ti;
29708     },
29709
29710     updatePanelTitle : function(panel, title){
29711         if(this.activePanel == panel){
29712             this.updateTitle(title);
29713         }
29714         if(this.tabs){
29715             var ti = this.tabs.getTab(panel.getEl().id);
29716             ti.setText(title);
29717             if(panel.tabTip !== undefined){
29718                 ti.setTooltip(panel.tabTip);
29719             }
29720         }
29721     },
29722
29723     updateTitle : function(title){
29724         if(this.titleTextEl && !this.config.title){
29725             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
29726         }
29727     },
29728
29729     setActivePanel : function(panel){
29730         panel = this.getPanel(panel);
29731         if(this.activePanel && this.activePanel != panel){
29732             this.activePanel.setActiveState(false);
29733         }
29734         this.activePanel = panel;
29735         panel.setActiveState(true);
29736         if(this.panelSize){
29737             panel.setSize(this.panelSize.width, this.panelSize.height);
29738         }
29739         if(this.closeBtn){
29740             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
29741         }
29742         this.updateTitle(panel.getTitle());
29743         if(this.tabs){
29744             this.fireEvent("invalidated", this);
29745         }
29746         this.fireEvent("panelactivated", this, panel);
29747     },
29748
29749     /**
29750      * Shows the specified panel.
29751      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
29752      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
29753      */
29754     showPanel : function(panel){
29755         if(panel = this.getPanel(panel)){
29756             if(this.tabs){
29757                 var tab = this.tabs.getTab(panel.getEl().id);
29758                 if(tab.isHidden()){
29759                     this.tabs.unhideTab(tab.id);
29760                 }
29761                 tab.activate();
29762             }else{
29763                 this.setActivePanel(panel);
29764             }
29765         }
29766         return panel;
29767     },
29768
29769     /**
29770      * Get the active panel for this region.
29771      * @return {Roo.ContentPanel} The active panel or null
29772      */
29773     getActivePanel : function(){
29774         return this.activePanel;
29775     },
29776
29777     validateVisibility : function(){
29778         if(this.panels.getCount() < 1){
29779             this.updateTitle("&#160;");
29780             this.closeBtn.hide();
29781             this.hide();
29782         }else{
29783             if(!this.isVisible()){
29784                 this.show();
29785             }
29786         }
29787     },
29788
29789     /**
29790      * Adds the passed ContentPanel(s) to this region.
29791      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
29792      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
29793      */
29794     add : function(panel){
29795         if(arguments.length > 1){
29796             for(var i = 0, len = arguments.length; i < len; i++) {
29797                 this.add(arguments[i]);
29798             }
29799             return null;
29800         }
29801         if(this.hasPanel(panel)){
29802             this.showPanel(panel);
29803             return panel;
29804         }
29805         panel.setRegion(this);
29806         this.panels.add(panel);
29807         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
29808             this.bodyEl.dom.appendChild(panel.getEl().dom);
29809             if(panel.background !== true){
29810                 this.setActivePanel(panel);
29811             }
29812             this.fireEvent("paneladded", this, panel);
29813             return panel;
29814         }
29815         if(!this.tabs){
29816             this.initTabs();
29817         }else{
29818             this.initPanelAsTab(panel);
29819         }
29820         if(panel.background !== true){
29821             this.tabs.activate(panel.getEl().id);
29822         }
29823         this.fireEvent("paneladded", this, panel);
29824         return panel;
29825     },
29826
29827     /**
29828      * Hides the tab for the specified panel.
29829      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
29830      */
29831     hidePanel : function(panel){
29832         if(this.tabs && (panel = this.getPanel(panel))){
29833             this.tabs.hideTab(panel.getEl().id);
29834         }
29835     },
29836
29837     /**
29838      * Unhides the tab for a previously hidden panel.
29839      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
29840      */
29841     unhidePanel : function(panel){
29842         if(this.tabs && (panel = this.getPanel(panel))){
29843             this.tabs.unhideTab(panel.getEl().id);
29844         }
29845     },
29846
29847     clearPanels : function(){
29848         while(this.panels.getCount() > 0){
29849              this.remove(this.panels.first());
29850         }
29851     },
29852
29853     /**
29854      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
29855      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
29856      * @param {Boolean} preservePanel Overrides the config preservePanel option
29857      * @return {Roo.ContentPanel} The panel that was removed
29858      */
29859     remove : function(panel, preservePanel){
29860         panel = this.getPanel(panel);
29861         if(!panel){
29862             return null;
29863         }
29864         var e = {};
29865         this.fireEvent("beforeremove", this, panel, e);
29866         if(e.cancel === true){
29867             return null;
29868         }
29869         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
29870         var panelId = panel.getId();
29871         this.panels.removeKey(panelId);
29872         if(preservePanel){
29873             document.body.appendChild(panel.getEl().dom);
29874         }
29875         if(this.tabs){
29876             this.tabs.removeTab(panel.getEl().id);
29877         }else if (!preservePanel){
29878             this.bodyEl.dom.removeChild(panel.getEl().dom);
29879         }
29880         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
29881             var p = this.panels.first();
29882             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
29883             tempEl.appendChild(p.getEl().dom);
29884             this.bodyEl.update("");
29885             this.bodyEl.dom.appendChild(p.getEl().dom);
29886             tempEl = null;
29887             this.updateTitle(p.getTitle());
29888             this.tabs = null;
29889             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
29890             this.setActivePanel(p);
29891         }
29892         panel.setRegion(null);
29893         if(this.activePanel == panel){
29894             this.activePanel = null;
29895         }
29896         if(this.config.autoDestroy !== false && preservePanel !== true){
29897             try{panel.destroy();}catch(e){}
29898         }
29899         this.fireEvent("panelremoved", this, panel);
29900         return panel;
29901     },
29902
29903     /**
29904      * Returns the TabPanel component used by this region
29905      * @return {Roo.TabPanel}
29906      */
29907     getTabs : function(){
29908         return this.tabs;
29909     },
29910
29911     createTool : function(parentEl, className){
29912         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
29913             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
29914         btn.addClassOnOver("x-layout-tools-button-over");
29915         return btn;
29916     }
29917 });/*
29918  * Based on:
29919  * Ext JS Library 1.1.1
29920  * Copyright(c) 2006-2007, Ext JS, LLC.
29921  *
29922  * Originally Released Under LGPL - original licence link has changed is not relivant.
29923  *
29924  * Fork - LGPL
29925  * <script type="text/javascript">
29926  */
29927  
29928
29929
29930 /**
29931  * @class Roo.SplitLayoutRegion
29932  * @extends Roo.LayoutRegion
29933  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
29934  */
29935 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
29936     this.cursor = cursor;
29937     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
29938 };
29939
29940 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
29941     splitTip : "Drag to resize.",
29942     collapsibleSplitTip : "Drag to resize. Double click to hide.",
29943     useSplitTips : false,
29944
29945     applyConfig : function(config){
29946         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
29947         if(config.split){
29948             if(!this.split){
29949                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
29950                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
29951                 /** The SplitBar for this region 
29952                 * @type Roo.SplitBar */
29953                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
29954                 this.split.on("moved", this.onSplitMove, this);
29955                 this.split.useShim = config.useShim === true;
29956                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
29957                 if(this.useSplitTips){
29958                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
29959                 }
29960                 if(config.collapsible){
29961                     this.split.el.on("dblclick", this.collapse,  this);
29962                 }
29963             }
29964             if(typeof config.minSize != "undefined"){
29965                 this.split.minSize = config.minSize;
29966             }
29967             if(typeof config.maxSize != "undefined"){
29968                 this.split.maxSize = config.maxSize;
29969             }
29970             if(config.hideWhenEmpty || config.hidden || config.collapsed){
29971                 this.hideSplitter();
29972             }
29973         }
29974     },
29975
29976     getHMaxSize : function(){
29977          var cmax = this.config.maxSize || 10000;
29978          var center = this.mgr.getRegion("center");
29979          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
29980     },
29981
29982     getVMaxSize : function(){
29983          var cmax = this.config.maxSize || 10000;
29984          var center = this.mgr.getRegion("center");
29985          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
29986     },
29987
29988     onSplitMove : function(split, newSize){
29989         this.fireEvent("resized", this, newSize);
29990     },
29991     
29992     /** 
29993      * Returns the {@link Roo.SplitBar} for this region.
29994      * @return {Roo.SplitBar}
29995      */
29996     getSplitBar : function(){
29997         return this.split;
29998     },
29999     
30000     hide : function(){
30001         this.hideSplitter();
30002         Roo.SplitLayoutRegion.superclass.hide.call(this);
30003     },
30004
30005     hideSplitter : function(){
30006         if(this.split){
30007             this.split.el.setLocation(-2000,-2000);
30008             this.split.el.hide();
30009         }
30010     },
30011
30012     show : function(){
30013         if(this.split){
30014             this.split.el.show();
30015         }
30016         Roo.SplitLayoutRegion.superclass.show.call(this);
30017     },
30018     
30019     beforeSlide: function(){
30020         if(Roo.isGecko){// firefox overflow auto bug workaround
30021             this.bodyEl.clip();
30022             if(this.tabs) this.tabs.bodyEl.clip();
30023             if(this.activePanel){
30024                 this.activePanel.getEl().clip();
30025                 
30026                 if(this.activePanel.beforeSlide){
30027                     this.activePanel.beforeSlide();
30028                 }
30029             }
30030         }
30031     },
30032     
30033     afterSlide : function(){
30034         if(Roo.isGecko){// firefox overflow auto bug workaround
30035             this.bodyEl.unclip();
30036             if(this.tabs) this.tabs.bodyEl.unclip();
30037             if(this.activePanel){
30038                 this.activePanel.getEl().unclip();
30039                 if(this.activePanel.afterSlide){
30040                     this.activePanel.afterSlide();
30041                 }
30042             }
30043         }
30044     },
30045
30046     initAutoHide : function(){
30047         if(this.autoHide !== false){
30048             if(!this.autoHideHd){
30049                 var st = new Roo.util.DelayedTask(this.slideIn, this);
30050                 this.autoHideHd = {
30051                     "mouseout": function(e){
30052                         if(!e.within(this.el, true)){
30053                             st.delay(500);
30054                         }
30055                     },
30056                     "mouseover" : function(e){
30057                         st.cancel();
30058                     },
30059                     scope : this
30060                 };
30061             }
30062             this.el.on(this.autoHideHd);
30063         }
30064     },
30065
30066     clearAutoHide : function(){
30067         if(this.autoHide !== false){
30068             this.el.un("mouseout", this.autoHideHd.mouseout);
30069             this.el.un("mouseover", this.autoHideHd.mouseover);
30070         }
30071     },
30072
30073     clearMonitor : function(){
30074         Roo.get(document).un("click", this.slideInIf, this);
30075     },
30076
30077     // these names are backwards but not changed for compat
30078     slideOut : function(){
30079         if(this.isSlid || this.el.hasActiveFx()){
30080             return;
30081         }
30082         this.isSlid = true;
30083         if(this.collapseBtn){
30084             this.collapseBtn.hide();
30085         }
30086         this.closeBtnState = this.closeBtn.getStyle('display');
30087         this.closeBtn.hide();
30088         if(this.stickBtn){
30089             this.stickBtn.show();
30090         }
30091         this.el.show();
30092         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
30093         this.beforeSlide();
30094         this.el.setStyle("z-index", 10001);
30095         this.el.slideIn(this.getSlideAnchor(), {
30096             callback: function(){
30097                 this.afterSlide();
30098                 this.initAutoHide();
30099                 Roo.get(document).on("click", this.slideInIf, this);
30100                 this.fireEvent("slideshow", this);
30101             },
30102             scope: this,
30103             block: true
30104         });
30105     },
30106
30107     afterSlideIn : function(){
30108         this.clearAutoHide();
30109         this.isSlid = false;
30110         this.clearMonitor();
30111         this.el.setStyle("z-index", "");
30112         if(this.collapseBtn){
30113             this.collapseBtn.show();
30114         }
30115         this.closeBtn.setStyle('display', this.closeBtnState);
30116         if(this.stickBtn){
30117             this.stickBtn.hide();
30118         }
30119         this.fireEvent("slidehide", this);
30120     },
30121
30122     slideIn : function(cb){
30123         if(!this.isSlid || this.el.hasActiveFx()){
30124             Roo.callback(cb);
30125             return;
30126         }
30127         this.isSlid = false;
30128         this.beforeSlide();
30129         this.el.slideOut(this.getSlideAnchor(), {
30130             callback: function(){
30131                 this.el.setLeftTop(-10000, -10000);
30132                 this.afterSlide();
30133                 this.afterSlideIn();
30134                 Roo.callback(cb);
30135             },
30136             scope: this,
30137             block: true
30138         });
30139     },
30140     
30141     slideInIf : function(e){
30142         if(!e.within(this.el)){
30143             this.slideIn();
30144         }
30145     },
30146
30147     animateCollapse : function(){
30148         this.beforeSlide();
30149         this.el.setStyle("z-index", 20000);
30150         var anchor = this.getSlideAnchor();
30151         this.el.slideOut(anchor, {
30152             callback : function(){
30153                 this.el.setStyle("z-index", "");
30154                 this.collapsedEl.slideIn(anchor, {duration:.3});
30155                 this.afterSlide();
30156                 this.el.setLocation(-10000,-10000);
30157                 this.el.hide();
30158                 this.fireEvent("collapsed", this);
30159             },
30160             scope: this,
30161             block: true
30162         });
30163     },
30164
30165     animateExpand : function(){
30166         this.beforeSlide();
30167         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
30168         this.el.setStyle("z-index", 20000);
30169         this.collapsedEl.hide({
30170             duration:.1
30171         });
30172         this.el.slideIn(this.getSlideAnchor(), {
30173             callback : function(){
30174                 this.el.setStyle("z-index", "");
30175                 this.afterSlide();
30176                 if(this.split){
30177                     this.split.el.show();
30178                 }
30179                 this.fireEvent("invalidated", this);
30180                 this.fireEvent("expanded", this);
30181             },
30182             scope: this,
30183             block: true
30184         });
30185     },
30186
30187     anchors : {
30188         "west" : "left",
30189         "east" : "right",
30190         "north" : "top",
30191         "south" : "bottom"
30192     },
30193
30194     sanchors : {
30195         "west" : "l",
30196         "east" : "r",
30197         "north" : "t",
30198         "south" : "b"
30199     },
30200
30201     canchors : {
30202         "west" : "tl-tr",
30203         "east" : "tr-tl",
30204         "north" : "tl-bl",
30205         "south" : "bl-tl"
30206     },
30207
30208     getAnchor : function(){
30209         return this.anchors[this.position];
30210     },
30211
30212     getCollapseAnchor : function(){
30213         return this.canchors[this.position];
30214     },
30215
30216     getSlideAnchor : function(){
30217         return this.sanchors[this.position];
30218     },
30219
30220     getAlignAdj : function(){
30221         var cm = this.cmargins;
30222         switch(this.position){
30223             case "west":
30224                 return [0, 0];
30225             break;
30226             case "east":
30227                 return [0, 0];
30228             break;
30229             case "north":
30230                 return [0, 0];
30231             break;
30232             case "south":
30233                 return [0, 0];
30234             break;
30235         }
30236     },
30237
30238     getExpandAdj : function(){
30239         var c = this.collapsedEl, cm = this.cmargins;
30240         switch(this.position){
30241             case "west":
30242                 return [-(cm.right+c.getWidth()+cm.left), 0];
30243             break;
30244             case "east":
30245                 return [cm.right+c.getWidth()+cm.left, 0];
30246             break;
30247             case "north":
30248                 return [0, -(cm.top+cm.bottom+c.getHeight())];
30249             break;
30250             case "south":
30251                 return [0, cm.top+cm.bottom+c.getHeight()];
30252             break;
30253         }
30254     }
30255 });/*
30256  * Based on:
30257  * Ext JS Library 1.1.1
30258  * Copyright(c) 2006-2007, Ext JS, LLC.
30259  *
30260  * Originally Released Under LGPL - original licence link has changed is not relivant.
30261  *
30262  * Fork - LGPL
30263  * <script type="text/javascript">
30264  */
30265 /*
30266  * These classes are private internal classes
30267  */
30268 Roo.CenterLayoutRegion = function(mgr, config){
30269     Roo.LayoutRegion.call(this, mgr, config, "center");
30270     this.visible = true;
30271     this.minWidth = config.minWidth || 20;
30272     this.minHeight = config.minHeight || 20;
30273 };
30274
30275 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
30276     hide : function(){
30277         // center panel can't be hidden
30278     },
30279     
30280     show : function(){
30281         // center panel can't be hidden
30282     },
30283     
30284     getMinWidth: function(){
30285         return this.minWidth;
30286     },
30287     
30288     getMinHeight: function(){
30289         return this.minHeight;
30290     }
30291 });
30292
30293
30294 Roo.NorthLayoutRegion = function(mgr, config){
30295     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
30296     if(this.split){
30297         this.split.placement = Roo.SplitBar.TOP;
30298         this.split.orientation = Roo.SplitBar.VERTICAL;
30299         this.split.el.addClass("x-layout-split-v");
30300     }
30301     var size = config.initialSize || config.height;
30302     if(typeof size != "undefined"){
30303         this.el.setHeight(size);
30304     }
30305 };
30306 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
30307     orientation: Roo.SplitBar.VERTICAL,
30308     getBox : function(){
30309         if(this.collapsed){
30310             return this.collapsedEl.getBox();
30311         }
30312         var box = this.el.getBox();
30313         if(this.split){
30314             box.height += this.split.el.getHeight();
30315         }
30316         return box;
30317     },
30318     
30319     updateBox : function(box){
30320         if(this.split && !this.collapsed){
30321             box.height -= this.split.el.getHeight();
30322             this.split.el.setLeft(box.x);
30323             this.split.el.setTop(box.y+box.height);
30324             this.split.el.setWidth(box.width);
30325         }
30326         if(this.collapsed){
30327             this.updateBody(box.width, null);
30328         }
30329         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30330     }
30331 });
30332
30333 Roo.SouthLayoutRegion = function(mgr, config){
30334     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
30335     if(this.split){
30336         this.split.placement = Roo.SplitBar.BOTTOM;
30337         this.split.orientation = Roo.SplitBar.VERTICAL;
30338         this.split.el.addClass("x-layout-split-v");
30339     }
30340     var size = config.initialSize || config.height;
30341     if(typeof size != "undefined"){
30342         this.el.setHeight(size);
30343     }
30344 };
30345 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
30346     orientation: Roo.SplitBar.VERTICAL,
30347     getBox : function(){
30348         if(this.collapsed){
30349             return this.collapsedEl.getBox();
30350         }
30351         var box = this.el.getBox();
30352         if(this.split){
30353             var sh = this.split.el.getHeight();
30354             box.height += sh;
30355             box.y -= sh;
30356         }
30357         return box;
30358     },
30359     
30360     updateBox : function(box){
30361         if(this.split && !this.collapsed){
30362             var sh = this.split.el.getHeight();
30363             box.height -= sh;
30364             box.y += sh;
30365             this.split.el.setLeft(box.x);
30366             this.split.el.setTop(box.y-sh);
30367             this.split.el.setWidth(box.width);
30368         }
30369         if(this.collapsed){
30370             this.updateBody(box.width, null);
30371         }
30372         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30373     }
30374 });
30375
30376 Roo.EastLayoutRegion = function(mgr, config){
30377     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
30378     if(this.split){
30379         this.split.placement = Roo.SplitBar.RIGHT;
30380         this.split.orientation = Roo.SplitBar.HORIZONTAL;
30381         this.split.el.addClass("x-layout-split-h");
30382     }
30383     var size = config.initialSize || config.width;
30384     if(typeof size != "undefined"){
30385         this.el.setWidth(size);
30386     }
30387 };
30388 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
30389     orientation: Roo.SplitBar.HORIZONTAL,
30390     getBox : function(){
30391         if(this.collapsed){
30392             return this.collapsedEl.getBox();
30393         }
30394         var box = this.el.getBox();
30395         if(this.split){
30396             var sw = this.split.el.getWidth();
30397             box.width += sw;
30398             box.x -= sw;
30399         }
30400         return box;
30401     },
30402
30403     updateBox : function(box){
30404         if(this.split && !this.collapsed){
30405             var sw = this.split.el.getWidth();
30406             box.width -= sw;
30407             this.split.el.setLeft(box.x);
30408             this.split.el.setTop(box.y);
30409             this.split.el.setHeight(box.height);
30410             box.x += sw;
30411         }
30412         if(this.collapsed){
30413             this.updateBody(null, box.height);
30414         }
30415         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30416     }
30417 });
30418
30419 Roo.WestLayoutRegion = function(mgr, config){
30420     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
30421     if(this.split){
30422         this.split.placement = Roo.SplitBar.LEFT;
30423         this.split.orientation = Roo.SplitBar.HORIZONTAL;
30424         this.split.el.addClass("x-layout-split-h");
30425     }
30426     var size = config.initialSize || config.width;
30427     if(typeof size != "undefined"){
30428         this.el.setWidth(size);
30429     }
30430 };
30431 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
30432     orientation: Roo.SplitBar.HORIZONTAL,
30433     getBox : function(){
30434         if(this.collapsed){
30435             return this.collapsedEl.getBox();
30436         }
30437         var box = this.el.getBox();
30438         if(this.split){
30439             box.width += this.split.el.getWidth();
30440         }
30441         return box;
30442     },
30443     
30444     updateBox : function(box){
30445         if(this.split && !this.collapsed){
30446             var sw = this.split.el.getWidth();
30447             box.width -= sw;
30448             this.split.el.setLeft(box.x+box.width);
30449             this.split.el.setTop(box.y);
30450             this.split.el.setHeight(box.height);
30451         }
30452         if(this.collapsed){
30453             this.updateBody(null, box.height);
30454         }
30455         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30456     }
30457 });
30458 /*
30459  * Based on:
30460  * Ext JS Library 1.1.1
30461  * Copyright(c) 2006-2007, Ext JS, LLC.
30462  *
30463  * Originally Released Under LGPL - original licence link has changed is not relivant.
30464  *
30465  * Fork - LGPL
30466  * <script type="text/javascript">
30467  */
30468  
30469  
30470 /*
30471  * Private internal class for reading and applying state
30472  */
30473 Roo.LayoutStateManager = function(layout){
30474      // default empty state
30475      this.state = {
30476         north: {},
30477         south: {},
30478         east: {},
30479         west: {}       
30480     };
30481 };
30482
30483 Roo.LayoutStateManager.prototype = {
30484     init : function(layout, provider){
30485         this.provider = provider;
30486         var state = provider.get(layout.id+"-layout-state");
30487         if(state){
30488             var wasUpdating = layout.isUpdating();
30489             if(!wasUpdating){
30490                 layout.beginUpdate();
30491             }
30492             for(var key in state){
30493                 if(typeof state[key] != "function"){
30494                     var rstate = state[key];
30495                     var r = layout.getRegion(key);
30496                     if(r && rstate){
30497                         if(rstate.size){
30498                             r.resizeTo(rstate.size);
30499                         }
30500                         if(rstate.collapsed == true){
30501                             r.collapse(true);
30502                         }else{
30503                             r.expand(null, true);
30504                         }
30505                     }
30506                 }
30507             }
30508             if(!wasUpdating){
30509                 layout.endUpdate();
30510             }
30511             this.state = state; 
30512         }
30513         this.layout = layout;
30514         layout.on("regionresized", this.onRegionResized, this);
30515         layout.on("regioncollapsed", this.onRegionCollapsed, this);
30516         layout.on("regionexpanded", this.onRegionExpanded, this);
30517     },
30518     
30519     storeState : function(){
30520         this.provider.set(this.layout.id+"-layout-state", this.state);
30521     },
30522     
30523     onRegionResized : function(region, newSize){
30524         this.state[region.getPosition()].size = newSize;
30525         this.storeState();
30526     },
30527     
30528     onRegionCollapsed : function(region){
30529         this.state[region.getPosition()].collapsed = true;
30530         this.storeState();
30531     },
30532     
30533     onRegionExpanded : function(region){
30534         this.state[region.getPosition()].collapsed = false;
30535         this.storeState();
30536     }
30537 };/*
30538  * Based on:
30539  * Ext JS Library 1.1.1
30540  * Copyright(c) 2006-2007, Ext JS, LLC.
30541  *
30542  * Originally Released Under LGPL - original licence link has changed is not relivant.
30543  *
30544  * Fork - LGPL
30545  * <script type="text/javascript">
30546  */
30547 /**
30548  * @class Roo.ContentPanel
30549  * @extends Roo.util.Observable
30550  * A basic ContentPanel element.
30551  * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes  (defaults to false)
30552  * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
30553  * @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
30554  * @cfg {Boolean} closable True if the panel can be closed/removed
30555  * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
30556  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
30557  * @cfg {Toolbar} toolbar A toolbar for this panel
30558  * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
30559  * @cfg {String} title The title for this panel
30560  * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
30561  * @cfg {String} url Calls {@link #setUrl} with this value
30562  * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
30563  * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
30564  * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
30565  * @constructor
30566  * Create a new ContentPanel.
30567  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
30568  * @param {String/Object} config A string to set only the title or a config object
30569  * @param {String} content (optional) Set the HTML content for this panel
30570  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
30571  */
30572 Roo.ContentPanel = function(el, config, content){
30573     
30574      
30575     /*
30576     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
30577         config = el;
30578         el = Roo.id();
30579     }
30580     if (config && config.parentLayout) { 
30581         el = config.parentLayout.el.createChild(); 
30582     }
30583     */
30584     if(el.autoCreate){ // xtype is available if this is called from factory
30585         config = el;
30586         el = Roo.id();
30587     }
30588     this.el = Roo.get(el);
30589     if(!this.el && config && config.autoCreate){
30590         if(typeof config.autoCreate == "object"){
30591             if(!config.autoCreate.id){
30592                 config.autoCreate.id = config.id||el;
30593             }
30594             this.el = Roo.DomHelper.append(document.body,
30595                         config.autoCreate, true);
30596         }else{
30597             this.el = Roo.DomHelper.append(document.body,
30598                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
30599         }
30600     }
30601     this.closable = false;
30602     this.loaded = false;
30603     this.active = false;
30604     if(typeof config == "string"){
30605         this.title = config;
30606     }else{
30607         Roo.apply(this, config);
30608     }
30609     
30610     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
30611         this.wrapEl = this.el.wrap();    
30612         this.toolbar = new Roo.Toolbar(this.el.insertSibling(false, 'before'), [] , this.toolbar);
30613         
30614     }
30615     
30616     
30617     
30618     if(this.resizeEl){
30619         this.resizeEl = Roo.get(this.resizeEl, true);
30620     }else{
30621         this.resizeEl = this.el;
30622     }
30623     this.addEvents({
30624         /**
30625          * @event activate
30626          * Fires when this panel is activated. 
30627          * @param {Roo.ContentPanel} this
30628          */
30629         "activate" : true,
30630         /**
30631          * @event deactivate
30632          * Fires when this panel is activated. 
30633          * @param {Roo.ContentPanel} this
30634          */
30635         "deactivate" : true,
30636
30637         /**
30638          * @event resize
30639          * Fires when this panel is resized if fitToFrame is true.
30640          * @param {Roo.ContentPanel} this
30641          * @param {Number} width The width after any component adjustments
30642          * @param {Number} height The height after any component adjustments
30643          */
30644         "resize" : true
30645     });
30646     if(this.autoScroll){
30647         this.resizeEl.setStyle("overflow", "auto");
30648     }
30649     content = content || this.content;
30650     if(content){
30651         this.setContent(content);
30652     }
30653     if(config && config.url){
30654         this.setUrl(this.url, this.params, this.loadOnce);
30655     }
30656     
30657     
30658     
30659     Roo.ContentPanel.superclass.constructor.call(this);
30660 };
30661
30662 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
30663     tabTip:'',
30664     setRegion : function(region){
30665         this.region = region;
30666         if(region){
30667            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
30668         }else{
30669            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
30670         } 
30671     },
30672     
30673     /**
30674      * Returns the toolbar for this Panel if one was configured. 
30675      * @return {Roo.Toolbar} 
30676      */
30677     getToolbar : function(){
30678         return this.toolbar;
30679     },
30680     
30681     setActiveState : function(active){
30682         this.active = active;
30683         if(!active){
30684             this.fireEvent("deactivate", this);
30685         }else{
30686             this.fireEvent("activate", this);
30687         }
30688     },
30689     /**
30690      * Updates this panel's element
30691      * @param {String} content The new content
30692      * @param {Boolean} loadScripts (optional) true to look for and process scripts
30693     */
30694     setContent : function(content, loadScripts){
30695         this.el.update(content, loadScripts);
30696     },
30697
30698     ignoreResize : function(w, h){
30699         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
30700             return true;
30701         }else{
30702             this.lastSize = {width: w, height: h};
30703             return false;
30704         }
30705     },
30706     /**
30707      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
30708      * @return {Roo.UpdateManager} The UpdateManager
30709      */
30710     getUpdateManager : function(){
30711         return this.el.getUpdateManager();
30712     },
30713      /**
30714      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
30715      * @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:
30716 <pre><code>
30717 panel.load({
30718     url: "your-url.php",
30719     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
30720     callback: yourFunction,
30721     scope: yourObject, //(optional scope)
30722     discardUrl: false,
30723     nocache: false,
30724     text: "Loading...",
30725     timeout: 30,
30726     scripts: false
30727 });
30728 </code></pre>
30729      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
30730      * 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.
30731      * @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}
30732      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
30733      * @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.
30734      * @return {Roo.ContentPanel} this
30735      */
30736     load : function(){
30737         var um = this.el.getUpdateManager();
30738         um.update.apply(um, arguments);
30739         return this;
30740     },
30741
30742
30743     /**
30744      * 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.
30745      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
30746      * @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)
30747      * @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)
30748      * @return {Roo.UpdateManager} The UpdateManager
30749      */
30750     setUrl : function(url, params, loadOnce){
30751         if(this.refreshDelegate){
30752             this.removeListener("activate", this.refreshDelegate);
30753         }
30754         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
30755         this.on("activate", this.refreshDelegate);
30756         return this.el.getUpdateManager();
30757     },
30758     
30759     _handleRefresh : function(url, params, loadOnce){
30760         if(!loadOnce || !this.loaded){
30761             var updater = this.el.getUpdateManager();
30762             updater.update(url, params, this._setLoaded.createDelegate(this));
30763         }
30764     },
30765     
30766     _setLoaded : function(){
30767         this.loaded = true;
30768     }, 
30769     
30770     /**
30771      * Returns this panel's id
30772      * @return {String} 
30773      */
30774     getId : function(){
30775         return this.el.id;
30776     },
30777     
30778     /** 
30779      * Returns this panel's element - used by regiosn to add.
30780      * @return {Roo.Element} 
30781      */
30782     getEl : function(){
30783         return this.wrapEl || this.el;
30784     },
30785     
30786     adjustForComponents : function(width, height){
30787         if(this.resizeEl != this.el){
30788             width -= this.el.getFrameWidth('lr');
30789             height -= this.el.getFrameWidth('tb');
30790         }
30791         if(this.toolbar){
30792             var te = this.toolbar.getEl();
30793             height -= te.getHeight();
30794             te.setWidth(width);
30795         }
30796         if(this.adjustments){
30797             width += this.adjustments[0];
30798             height += this.adjustments[1];
30799         }
30800         return {"width": width, "height": height};
30801     },
30802     
30803     setSize : function(width, height){
30804         if(this.fitToFrame && !this.ignoreResize(width, height)){
30805             if(this.fitContainer && this.resizeEl != this.el){
30806                 this.el.setSize(width, height);
30807             }
30808             var size = this.adjustForComponents(width, height);
30809             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
30810             this.fireEvent('resize', this, size.width, size.height);
30811         }
30812     },
30813     
30814     /**
30815      * Returns this panel's title
30816      * @return {String} 
30817      */
30818     getTitle : function(){
30819         return this.title;
30820     },
30821     
30822     /**
30823      * Set this panel's title
30824      * @param {String} title
30825      */
30826     setTitle : function(title){
30827         this.title = title;
30828         if(this.region){
30829             this.region.updatePanelTitle(this, title);
30830         }
30831     },
30832     
30833     /**
30834      * Returns true is this panel was configured to be closable
30835      * @return {Boolean} 
30836      */
30837     isClosable : function(){
30838         return this.closable;
30839     },
30840     
30841     beforeSlide : function(){
30842         this.el.clip();
30843         this.resizeEl.clip();
30844     },
30845     
30846     afterSlide : function(){
30847         this.el.unclip();
30848         this.resizeEl.unclip();
30849     },
30850     
30851     /**
30852      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
30853      *   Will fail silently if the {@link #setUrl} method has not been called.
30854      *   This does not activate the panel, just updates its content.
30855      */
30856     refresh : function(){
30857         if(this.refreshDelegate){
30858            this.loaded = false;
30859            this.refreshDelegate();
30860         }
30861     },
30862     
30863     /**
30864      * Destroys this panel
30865      */
30866     destroy : function(){
30867         this.el.removeAllListeners();
30868         var tempEl = document.createElement("span");
30869         tempEl.appendChild(this.el.dom);
30870         tempEl.innerHTML = "";
30871         this.el.remove();
30872         this.el = null;
30873     },
30874     
30875       /**
30876      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
30877      * <pre><code>
30878
30879 layout.addxtype({
30880        xtype : 'Form',
30881        items: [ .... ]
30882    }
30883 );
30884
30885 </code></pre>
30886      * @param {Object} cfg Xtype definition of item to add.
30887      */
30888     
30889     addxtype : function(cfg) {
30890         // add form..
30891         if (cfg.xtype.match(/^Form$/)) {
30892             var el = this.el.createChild();
30893
30894             this.form = new  Roo.form.Form(cfg);
30895             
30896             
30897             if ( this.form.allItems.length) this.form.render(el.dom);
30898             return this.form;
30899         }
30900         if (['View', 'JsonView'].indexOf(cfg.xtype) > -1) {
30901             // views..
30902             cfg.el = this.el.appendChild(document.createElement("div"));
30903             // factory?
30904             var ret = new Roo[cfg.xtype](cfg);
30905             ret.render(false, ''); // render blank..
30906             return ret;
30907             
30908         }
30909         return false;
30910         
30911     }
30912 });
30913
30914 /**
30915  * @class Roo.GridPanel
30916  * @extends Roo.ContentPanel
30917  * @constructor
30918  * Create a new GridPanel.
30919  * @param {Roo.grid.Grid} grid The grid for this panel
30920  * @param {String/Object} config A string to set only the panel's title, or a config object
30921  */
30922 Roo.GridPanel = function(grid, config){
30923     
30924   
30925     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
30926         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
30927         
30928     this.wrapper.dom.appendChild(grid.getGridEl().dom);
30929     
30930     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
30931     
30932     if(this.toolbar){
30933         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
30934     }
30935     // xtype created footer. - not sure if will work as we normally have to render first..
30936     if (this.footer && !this.footer.el && this.footer.xtype) {
30937         
30938         this.footer.container = this.grid.getView().getFooterPanel(true);
30939         this.footer.dataSource = this.grid.dataSource;
30940         this.footer = Roo.factory(this.footer, Roo);
30941         
30942     }
30943     
30944     grid.monitorWindowResize = false; // turn off autosizing
30945     grid.autoHeight = false;
30946     grid.autoWidth = false;
30947     this.grid = grid;
30948     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
30949 };
30950
30951 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
30952     getId : function(){
30953         return this.grid.id;
30954     },
30955     
30956     /**
30957      * Returns the grid for this panel
30958      * @return {Roo.grid.Grid} 
30959      */
30960     getGrid : function(){
30961         return this.grid;    
30962     },
30963     
30964     setSize : function(width, height){
30965         if(!this.ignoreResize(width, height)){
30966             var grid = this.grid;
30967             var size = this.adjustForComponents(width, height);
30968             grid.getGridEl().setSize(size.width, size.height);
30969             grid.autoSize();
30970         }
30971     },
30972     
30973     beforeSlide : function(){
30974         this.grid.getView().scroller.clip();
30975     },
30976     
30977     afterSlide : function(){
30978         this.grid.getView().scroller.unclip();
30979     },
30980     
30981     destroy : function(){
30982         this.grid.destroy();
30983         delete this.grid;
30984         Roo.GridPanel.superclass.destroy.call(this); 
30985     }
30986 });
30987
30988
30989 /**
30990  * @class Roo.NestedLayoutPanel
30991  * @extends Roo.ContentPanel
30992  * @constructor
30993  * Create a new NestedLayoutPanel.
30994  * 
30995  * 
30996  * @param {Roo.BorderLayout} layout The layout for this panel
30997  * @param {String/Object} config A string to set only the title or a config object
30998  */
30999 Roo.NestedLayoutPanel = function(layout, config)
31000 {
31001     // construct with only one argument..
31002     /* FIXME - implement nicer consturctors
31003     if (layout.layout) {
31004         config = layout;
31005         layout = config.layout;
31006         delete config.layout;
31007     }
31008     if (layout.xtype && !layout.getEl) {
31009         // then layout needs constructing..
31010         layout = Roo.factory(layout, Roo);
31011     }
31012     */
31013     
31014     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
31015     
31016     layout.monitorWindowResize = false; // turn off autosizing
31017     this.layout = layout;
31018     this.layout.getEl().addClass("x-layout-nested-layout");
31019     
31020     
31021     
31022 };
31023
31024 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
31025
31026     setSize : function(width, height){
31027         if(!this.ignoreResize(width, height)){
31028             var size = this.adjustForComponents(width, height);
31029             var el = this.layout.getEl();
31030             el.setSize(size.width, size.height);
31031             var touch = el.dom.offsetWidth;
31032             this.layout.layout();
31033             // ie requires a double layout on the first pass
31034             if(Roo.isIE && !this.initialized){
31035                 this.initialized = true;
31036                 this.layout.layout();
31037             }
31038         }
31039     },
31040     
31041     // activate all subpanels if not currently active..
31042     
31043     setActiveState : function(active){
31044         this.active = active;
31045         if(!active){
31046             this.fireEvent("deactivate", this);
31047             return;
31048         }
31049         
31050         this.fireEvent("activate", this);
31051         // not sure if this should happen before or after..
31052         if (!this.layout) {
31053             return; // should not happen..
31054         }
31055         var reg = false;
31056         for (var r in this.layout.regions) {
31057             reg = this.layout.getRegion(r);
31058             if (reg.getActivePanel()) {
31059                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
31060                 reg.setActivePanel(reg.getActivePanel());
31061                 continue;
31062             }
31063             if (!reg.panels.length) {
31064                 continue;
31065             }
31066             reg.showPanel(reg.getPanel(0));
31067         }
31068         
31069         
31070         
31071         
31072     },
31073     
31074     /**
31075      * Returns the nested BorderLayout for this panel
31076      * @return {Roo.BorderLayout} 
31077      */
31078     getLayout : function(){
31079         return this.layout;
31080     },
31081     
31082      /**
31083      * Adds a xtype elements to the layout of the nested panel
31084      * <pre><code>
31085
31086 panel.addxtype({
31087        xtype : 'ContentPanel',
31088        region: 'west',
31089        items: [ .... ]
31090    }
31091 );
31092
31093 panel.addxtype({
31094         xtype : 'NestedLayoutPanel',
31095         region: 'west',
31096         layout: {
31097            center: { },
31098            west: { }   
31099         },
31100         items : [ ... list of content panels or nested layout panels.. ]
31101    }
31102 );
31103 </code></pre>
31104      * @param {Object} cfg Xtype definition of item to add.
31105      */
31106     addxtype : function(cfg) {
31107         return this.layout.addxtype(cfg);
31108     
31109     }
31110 });
31111
31112 Roo.ScrollPanel = function(el, config, content){
31113     config = config || {};
31114     config.fitToFrame = true;
31115     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
31116     
31117     this.el.dom.style.overflow = "hidden";
31118     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
31119     this.el.removeClass("x-layout-inactive-content");
31120     this.el.on("mousewheel", this.onWheel, this);
31121
31122     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
31123     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
31124     up.unselectable(); down.unselectable();
31125     up.on("click", this.scrollUp, this);
31126     down.on("click", this.scrollDown, this);
31127     up.addClassOnOver("x-scroller-btn-over");
31128     down.addClassOnOver("x-scroller-btn-over");
31129     up.addClassOnClick("x-scroller-btn-click");
31130     down.addClassOnClick("x-scroller-btn-click");
31131     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
31132
31133     this.resizeEl = this.el;
31134     this.el = wrap; this.up = up; this.down = down;
31135 };
31136
31137 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
31138     increment : 100,
31139     wheelIncrement : 5,
31140     scrollUp : function(){
31141         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
31142     },
31143
31144     scrollDown : function(){
31145         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
31146     },
31147
31148     afterScroll : function(){
31149         var el = this.resizeEl;
31150         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
31151         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31152         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31153     },
31154
31155     setSize : function(){
31156         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
31157         this.afterScroll();
31158     },
31159
31160     onWheel : function(e){
31161         var d = e.getWheelDelta();
31162         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
31163         this.afterScroll();
31164         e.stopEvent();
31165     },
31166
31167     setContent : function(content, loadScripts){
31168         this.resizeEl.update(content, loadScripts);
31169     }
31170
31171 });
31172
31173
31174
31175
31176
31177
31178
31179
31180
31181 /**
31182  * @class Roo.TreePanel
31183  * @extends Roo.ContentPanel
31184  * @constructor
31185  * Create a new TreePanel. - defaults to fit/scoll contents.
31186  * @param {String/Object} config A string to set only the panel's title, or a config object
31187  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
31188  */
31189 Roo.TreePanel = function(config){
31190     var el = config.el;
31191     var tree = config.tree;
31192     delete config.tree; 
31193     delete config.el; // hopefull!
31194     
31195     // wrapper for IE7 strict & safari scroll issue
31196     
31197     var treeEl = el.createChild();
31198     config.resizeEl = treeEl;
31199     
31200     
31201     
31202     Roo.TreePanel.superclass.constructor.call(this, el, config);
31203  
31204  
31205     this.tree = new Roo.tree.TreePanel(treeEl , tree);
31206     //console.log(tree);
31207     this.on('activate', function()
31208     {
31209         if (this.tree.rendered) {
31210             return;
31211         }
31212         //console.log('render tree');
31213         this.tree.render();
31214     });
31215     
31216     this.on('resize',  function (cp, w, h) {
31217             this.tree.innerCt.setWidth(w);
31218             this.tree.innerCt.setHeight(h);
31219             this.tree.innerCt.setStyle('overflow-y', 'auto');
31220     });
31221
31222         
31223     
31224 };
31225
31226 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
31227     fitToFrame : true,
31228     autoScroll : true
31229 });
31230
31231
31232
31233
31234
31235
31236
31237
31238
31239
31240
31241 /*
31242  * Based on:
31243  * Ext JS Library 1.1.1
31244  * Copyright(c) 2006-2007, Ext JS, LLC.
31245  *
31246  * Originally Released Under LGPL - original licence link has changed is not relivant.
31247  *
31248  * Fork - LGPL
31249  * <script type="text/javascript">
31250  */
31251  
31252
31253 /**
31254  * @class Roo.ReaderLayout
31255  * @extends Roo.BorderLayout
31256  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
31257  * center region containing two nested regions (a top one for a list view and one for item preview below),
31258  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
31259  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
31260  * expedites the setup of the overall layout and regions for this common application style.
31261  * Example:
31262  <pre><code>
31263 var reader = new Roo.ReaderLayout();
31264 var CP = Roo.ContentPanel;  // shortcut for adding
31265
31266 reader.beginUpdate();
31267 reader.add("north", new CP("north", "North"));
31268 reader.add("west", new CP("west", {title: "West"}));
31269 reader.add("east", new CP("east", {title: "East"}));
31270
31271 reader.regions.listView.add(new CP("listView", "List"));
31272 reader.regions.preview.add(new CP("preview", "Preview"));
31273 reader.endUpdate();
31274 </code></pre>
31275 * @constructor
31276 * Create a new ReaderLayout
31277 * @param {Object} config Configuration options
31278 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
31279 * document.body if omitted)
31280 */
31281 Roo.ReaderLayout = function(config, renderTo){
31282     var c = config || {size:{}};
31283     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
31284         north: c.north !== false ? Roo.apply({
31285             split:false,
31286             initialSize: 32,
31287             titlebar: false
31288         }, c.north) : false,
31289         west: c.west !== false ? Roo.apply({
31290             split:true,
31291             initialSize: 200,
31292             minSize: 175,
31293             maxSize: 400,
31294             titlebar: true,
31295             collapsible: true,
31296             animate: true,
31297             margins:{left:5,right:0,bottom:5,top:5},
31298             cmargins:{left:5,right:5,bottom:5,top:5}
31299         }, c.west) : false,
31300         east: c.east !== false ? Roo.apply({
31301             split:true,
31302             initialSize: 200,
31303             minSize: 175,
31304             maxSize: 400,
31305             titlebar: true,
31306             collapsible: true,
31307             animate: true,
31308             margins:{left:0,right:5,bottom:5,top:5},
31309             cmargins:{left:5,right:5,bottom:5,top:5}
31310         }, c.east) : false,
31311         center: Roo.apply({
31312             tabPosition: 'top',
31313             autoScroll:false,
31314             closeOnTab: true,
31315             titlebar:false,
31316             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
31317         }, c.center)
31318     });
31319
31320     this.el.addClass('x-reader');
31321
31322     this.beginUpdate();
31323
31324     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
31325         south: c.preview !== false ? Roo.apply({
31326             split:true,
31327             initialSize: 200,
31328             minSize: 100,
31329             autoScroll:true,
31330             collapsible:true,
31331             titlebar: true,
31332             cmargins:{top:5,left:0, right:0, bottom:0}
31333         }, c.preview) : false,
31334         center: Roo.apply({
31335             autoScroll:false,
31336             titlebar:false,
31337             minHeight:200
31338         }, c.listView)
31339     });
31340     this.add('center', new Roo.NestedLayoutPanel(inner,
31341             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
31342
31343     this.endUpdate();
31344
31345     this.regions.preview = inner.getRegion('south');
31346     this.regions.listView = inner.getRegion('center');
31347 };
31348
31349 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
31350  * Based on:
31351  * Ext JS Library 1.1.1
31352  * Copyright(c) 2006-2007, Ext JS, LLC.
31353  *
31354  * Originally Released Under LGPL - original licence link has changed is not relivant.
31355  *
31356  * Fork - LGPL
31357  * <script type="text/javascript">
31358  */
31359  
31360 /**
31361  * @class Roo.grid.Grid
31362  * @extends Roo.util.Observable
31363  * This class represents the primary interface of a component based grid control.
31364  * <br><br>Usage:<pre><code>
31365  var grid = new Roo.grid.Grid("my-container-id", {
31366      ds: myDataStore,
31367      cm: myColModel,
31368      selModel: mySelectionModel,
31369      autoSizeColumns: true,
31370      monitorWindowResize: false,
31371      trackMouseOver: true
31372  });
31373  // set any options
31374  grid.render();
31375  * </code></pre>
31376  * <b>Common Problems:</b><br/>
31377  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
31378  * element will correct this<br/>
31379  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
31380  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
31381  * are unpredictable.<br/>
31382  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
31383  * grid to calculate dimensions/offsets.<br/>
31384   * @constructor
31385  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
31386  * The container MUST have some type of size defined for the grid to fill. The container will be
31387  * automatically set to position relative if it isn't already.
31388  * @param {Object} config A config object that sets properties on this grid.
31389  */
31390 Roo.grid.Grid = function(container, config){
31391         // initialize the container
31392         this.container = Roo.get(container);
31393         this.container.update("");
31394         this.container.setStyle("overflow", "hidden");
31395     this.container.addClass('x-grid-container');
31396
31397     this.id = this.container.id;
31398
31399     Roo.apply(this, config);
31400     // check and correct shorthanded configs
31401     if(this.ds){
31402         this.dataSource = this.ds;
31403         delete this.ds;
31404     }
31405     if(this.cm){
31406         this.colModel = this.cm;
31407         delete this.cm;
31408     }
31409     if(this.sm){
31410         this.selModel = this.sm;
31411         delete this.sm;
31412     }
31413
31414     if (this.selModel) {
31415         this.selModel = Roo.factory(this.selModel, Roo.grid);
31416         this.sm = this.selModel;
31417         this.sm.xmodule = this.xmodule || false;
31418     }
31419     if (typeof(this.colModel.config) == 'undefined') {
31420         this.colModel = new Roo.grid.ColumnModel(this.colModel);
31421         this.cm = this.colModel;
31422         this.cm.xmodule = this.xmodule || false;
31423     }
31424     if (this.dataSource) {
31425         this.dataSource= Roo.factory(this.dataSource, Roo.data);
31426         this.ds = this.dataSource;
31427         this.ds.xmodule = this.xmodule || false;
31428         
31429     }
31430     
31431     
31432     
31433     if(this.width){
31434         this.container.setWidth(this.width);
31435     }
31436
31437     if(this.height){
31438         this.container.setHeight(this.height);
31439     }
31440     /** @private */
31441         this.addEvents({
31442             // raw events
31443             /**
31444              * @event click
31445              * The raw click event for the entire grid.
31446              * @param {Roo.EventObject} e
31447              */
31448             "click" : true,
31449             /**
31450              * @event dblclick
31451              * The raw dblclick event for the entire grid.
31452              * @param {Roo.EventObject} e
31453              */
31454             "dblclick" : true,
31455             /**
31456              * @event contextmenu
31457              * The raw contextmenu event for the entire grid.
31458              * @param {Roo.EventObject} e
31459              */
31460             "contextmenu" : true,
31461             /**
31462              * @event mousedown
31463              * The raw mousedown event for the entire grid.
31464              * @param {Roo.EventObject} e
31465              */
31466             "mousedown" : true,
31467             /**
31468              * @event mouseup
31469              * The raw mouseup event for the entire grid.
31470              * @param {Roo.EventObject} e
31471              */
31472             "mouseup" : true,
31473             /**
31474              * @event mouseover
31475              * The raw mouseover event for the entire grid.
31476              * @param {Roo.EventObject} e
31477              */
31478             "mouseover" : true,
31479             /**
31480              * @event mouseout
31481              * The raw mouseout event for the entire grid.
31482              * @param {Roo.EventObject} e
31483              */
31484             "mouseout" : true,
31485             /**
31486              * @event keypress
31487              * The raw keypress event for the entire grid.
31488              * @param {Roo.EventObject} e
31489              */
31490             "keypress" : true,
31491             /**
31492              * @event keydown
31493              * The raw keydown event for the entire grid.
31494              * @param {Roo.EventObject} e
31495              */
31496             "keydown" : true,
31497
31498             // custom events
31499
31500             /**
31501              * @event cellclick
31502              * Fires when a cell is clicked
31503              * @param {Grid} this
31504              * @param {Number} rowIndex
31505              * @param {Number} columnIndex
31506              * @param {Roo.EventObject} e
31507              */
31508             "cellclick" : true,
31509             /**
31510              * @event celldblclick
31511              * Fires when a cell is double clicked
31512              * @param {Grid} this
31513              * @param {Number} rowIndex
31514              * @param {Number} columnIndex
31515              * @param {Roo.EventObject} e
31516              */
31517             "celldblclick" : true,
31518             /**
31519              * @event rowclick
31520              * Fires when a row is clicked
31521              * @param {Grid} this
31522              * @param {Number} rowIndex
31523              * @param {Roo.EventObject} e
31524              */
31525             "rowclick" : true,
31526             /**
31527              * @event rowdblclick
31528              * Fires when a row is double clicked
31529              * @param {Grid} this
31530              * @param {Number} rowIndex
31531              * @param {Roo.EventObject} e
31532              */
31533             "rowdblclick" : true,
31534             /**
31535              * @event headerclick
31536              * Fires when a header is clicked
31537              * @param {Grid} this
31538              * @param {Number} columnIndex
31539              * @param {Roo.EventObject} e
31540              */
31541             "headerclick" : true,
31542             /**
31543              * @event headerdblclick
31544              * Fires when a header cell is double clicked
31545              * @param {Grid} this
31546              * @param {Number} columnIndex
31547              * @param {Roo.EventObject} e
31548              */
31549             "headerdblclick" : true,
31550             /**
31551              * @event rowcontextmenu
31552              * Fires when a row is right clicked
31553              * @param {Grid} this
31554              * @param {Number} rowIndex
31555              * @param {Roo.EventObject} e
31556              */
31557             "rowcontextmenu" : true,
31558             /**
31559          * @event cellcontextmenu
31560          * Fires when a cell is right clicked
31561          * @param {Grid} this
31562          * @param {Number} rowIndex
31563          * @param {Number} cellIndex
31564          * @param {Roo.EventObject} e
31565          */
31566          "cellcontextmenu" : true,
31567             /**
31568              * @event headercontextmenu
31569              * Fires when a header is right clicked
31570              * @param {Grid} this
31571              * @param {Number} columnIndex
31572              * @param {Roo.EventObject} e
31573              */
31574             "headercontextmenu" : true,
31575             /**
31576              * @event bodyscroll
31577              * Fires when the body element is scrolled
31578              * @param {Number} scrollLeft
31579              * @param {Number} scrollTop
31580              */
31581             "bodyscroll" : true,
31582             /**
31583              * @event columnresize
31584              * Fires when the user resizes a column
31585              * @param {Number} columnIndex
31586              * @param {Number} newSize
31587              */
31588             "columnresize" : true,
31589             /**
31590              * @event columnmove
31591              * Fires when the user moves a column
31592              * @param {Number} oldIndex
31593              * @param {Number} newIndex
31594              */
31595             "columnmove" : true,
31596             /**
31597              * @event startdrag
31598              * Fires when row(s) start being dragged
31599              * @param {Grid} this
31600              * @param {Roo.GridDD} dd The drag drop object
31601              * @param {event} e The raw browser event
31602              */
31603             "startdrag" : true,
31604             /**
31605              * @event enddrag
31606              * Fires when a drag operation is complete
31607              * @param {Grid} this
31608              * @param {Roo.GridDD} dd The drag drop object
31609              * @param {event} e The raw browser event
31610              */
31611             "enddrag" : true,
31612             /**
31613              * @event dragdrop
31614              * Fires when dragged row(s) are dropped on a valid DD target
31615              * @param {Grid} this
31616              * @param {Roo.GridDD} dd The drag drop object
31617              * @param {String} targetId The target drag drop object
31618              * @param {event} e The raw browser event
31619              */
31620             "dragdrop" : true,
31621             /**
31622              * @event dragover
31623              * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
31624              * @param {Grid} this
31625              * @param {Roo.GridDD} dd The drag drop object
31626              * @param {String} targetId The target drag drop object
31627              * @param {event} e The raw browser event
31628              */
31629             "dragover" : true,
31630             /**
31631              * @event dragenter
31632              *  Fires when the dragged row(s) first cross another DD target while being dragged
31633              * @param {Grid} this
31634              * @param {Roo.GridDD} dd The drag drop object
31635              * @param {String} targetId The target drag drop object
31636              * @param {event} e The raw browser event
31637              */
31638             "dragenter" : true,
31639             /**
31640              * @event dragout
31641              * Fires when the dragged row(s) leave another DD target while being dragged
31642              * @param {Grid} this
31643              * @param {Roo.GridDD} dd The drag drop object
31644              * @param {String} targetId The target drag drop object
31645              * @param {event} e The raw browser event
31646              */
31647             "dragout" : true,
31648         /**
31649          * @event render
31650          * Fires when the grid is rendered
31651          * @param {Grid} grid
31652          */
31653         render : true
31654     });
31655
31656     Roo.grid.Grid.superclass.constructor.call(this);
31657 };
31658 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
31659     
31660     /**
31661      * @cfg {String} ddGroup - drag drop group.
31662          */
31663     
31664     /**
31665      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
31666          */
31667         minColumnWidth : 25,
31668
31669     /**
31670          * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
31671          * <b>on initial render.</b> It is more efficient to explicitly size the columns
31672          * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
31673          */
31674         autoSizeColumns : false,
31675
31676         /**
31677          * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
31678          */
31679         autoSizeHeaders : true,
31680
31681         /**
31682          * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
31683          */
31684         monitorWindowResize : true,
31685
31686         /**
31687          * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
31688          * rows measured to get a columns size. Default is 0 (all rows).
31689          */
31690         maxRowsToMeasure : 0,
31691
31692         /**
31693          * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
31694          */
31695         trackMouseOver : true,
31696
31697     /**
31698          * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
31699          */
31700     
31701         /**
31702          * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
31703          */
31704         enableDragDrop : false,
31705
31706         /**
31707          * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
31708          */
31709         enableColumnMove : true,
31710
31711         /**
31712          * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
31713          */
31714         enableColumnHide : true,
31715
31716         /**
31717          * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
31718          */
31719         enableRowHeightSync : false,
31720
31721         /**
31722          * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
31723          */
31724         stripeRows : true,
31725
31726         /**
31727          * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
31728          */
31729         autoHeight : false,
31730
31731     /**
31732      * @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.
31733      */
31734     autoExpandColumn : false,
31735
31736     /**
31737     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
31738     * Default is 50.
31739     */
31740     autoExpandMin : 50,
31741
31742     /**
31743     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
31744     */
31745     autoExpandMax : 1000,
31746
31747     /**
31748          * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
31749          */
31750         view : null,
31751
31752         /**
31753      * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
31754          */
31755         loadMask : false,
31756
31757     // private
31758     rendered : false,
31759
31760     /**
31761     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
31762     * of a fixed width. Default is false.
31763     */
31764     /**
31765     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
31766     */
31767     /**
31768      * Called once after all setup has been completed and the grid is ready to be rendered.
31769      * @return {Roo.grid.Grid} this
31770      */
31771     render : function(){
31772         var c = this.container;
31773         // try to detect autoHeight/width mode
31774         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
31775             this.autoHeight = true;
31776         }
31777         var view = this.getView();
31778         view.init(this);
31779
31780         c.on("click", this.onClick, this);
31781         c.on("dblclick", this.onDblClick, this);
31782         c.on("contextmenu", this.onContextMenu, this);
31783         c.on("keydown", this.onKeyDown, this);
31784
31785         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
31786
31787         this.getSelectionModel().init(this);
31788
31789         view.render();
31790
31791         if(this.loadMask){
31792             this.loadMask = new Roo.LoadMask(this.container,
31793                     Roo.apply({store:this.dataSource}, this.loadMask));
31794         }
31795         
31796         
31797         if (this.toolbar && this.toolbar.xtype) {
31798             this.toolbar.container = this.getView().getHeaderPanel(true);
31799             this.toolbar = new Ext.Toolbar(this.toolbar);
31800         }
31801         if (this.footer && this.footer.xtype) {
31802             this.footer.dataSource = this.getDataSource();
31803             this.footer.container = this.getView().getFooterPanel(true);
31804             this.footer = Roo.factory(this.footer, Roo);
31805         }
31806         this.rendered = true;
31807         this.fireEvent('render', this);
31808         return this;
31809     },
31810
31811         /**
31812          * Reconfigures the grid to use a different Store and Column Model.
31813          * The View will be bound to the new objects and refreshed.
31814          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
31815          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
31816          */
31817     reconfigure : function(dataSource, colModel){
31818         if(this.loadMask){
31819             this.loadMask.destroy();
31820             this.loadMask = new Roo.LoadMask(this.container,
31821                     Roo.apply({store:dataSource}, this.loadMask));
31822         }
31823         this.view.bind(dataSource, colModel);
31824         this.dataSource = dataSource;
31825         this.colModel = colModel;
31826         this.view.refresh(true);
31827     },
31828
31829     // private
31830     onKeyDown : function(e){
31831         this.fireEvent("keydown", e);
31832     },
31833
31834     /**
31835      * Destroy this grid.
31836      * @param {Boolean} removeEl True to remove the element
31837      */
31838     destroy : function(removeEl, keepListeners){
31839         if(this.loadMask){
31840             this.loadMask.destroy();
31841         }
31842         var c = this.container;
31843         c.removeAllListeners();
31844         this.view.destroy();
31845         this.colModel.purgeListeners();
31846         if(!keepListeners){
31847             this.purgeListeners();
31848         }
31849         c.update("");
31850         if(removeEl === true){
31851             c.remove();
31852         }
31853     },
31854
31855     // private
31856     processEvent : function(name, e){
31857         this.fireEvent(name, e);
31858         var t = e.getTarget();
31859         var v = this.view;
31860         var header = v.findHeaderIndex(t);
31861         if(header !== false){
31862             this.fireEvent("header" + name, this, header, e);
31863         }else{
31864             var row = v.findRowIndex(t);
31865             var cell = v.findCellIndex(t);
31866             if(row !== false){
31867                 this.fireEvent("row" + name, this, row, e);
31868                 if(cell !== false){
31869                     this.fireEvent("cell" + name, this, row, cell, e);
31870                 }
31871             }
31872         }
31873     },
31874
31875     // private
31876     onClick : function(e){
31877         this.processEvent("click", e);
31878     },
31879
31880     // private
31881     onContextMenu : function(e, t){
31882         this.processEvent("contextmenu", e);
31883     },
31884
31885     // private
31886     onDblClick : function(e){
31887         this.processEvent("dblclick", e);
31888     },
31889
31890     // private
31891     walkCells : function(row, col, step, fn, scope){
31892         var cm = this.colModel, clen = cm.getColumnCount();
31893         var ds = this.dataSource, rlen = ds.getCount(), first = true;
31894         if(step < 0){
31895             if(col < 0){
31896                 row--;
31897                 first = false;
31898             }
31899             while(row >= 0){
31900                 if(!first){
31901                     col = clen-1;
31902                 }
31903                 first = false;
31904                 while(col >= 0){
31905                     if(fn.call(scope || this, row, col, cm) === true){
31906                         return [row, col];
31907                     }
31908                     col--;
31909                 }
31910                 row--;
31911             }
31912         } else {
31913             if(col >= clen){
31914                 row++;
31915                 first = false;
31916             }
31917             while(row < rlen){
31918                 if(!first){
31919                     col = 0;
31920                 }
31921                 first = false;
31922                 while(col < clen){
31923                     if(fn.call(scope || this, row, col, cm) === true){
31924                         return [row, col];
31925                     }
31926                     col++;
31927                 }
31928                 row++;
31929             }
31930         }
31931         return null;
31932     },
31933
31934     // private
31935     getSelections : function(){
31936         return this.selModel.getSelections();
31937     },
31938
31939     /**
31940      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
31941      * but if manual update is required this method will initiate it.
31942      */
31943     autoSize : function(){
31944         if(this.rendered){
31945             this.view.layout();
31946             if(this.view.adjustForScroll){
31947                 this.view.adjustForScroll();
31948             }
31949         }
31950     },
31951
31952     /**
31953      * Returns the grid's underlying element.
31954      * @return {Element} The element
31955      */
31956     getGridEl : function(){
31957         return this.container;
31958     },
31959
31960     // private for compatibility, overridden by editor grid
31961     stopEditing : function(){},
31962
31963     /**
31964      * Returns the grid's SelectionModel.
31965      * @return {SelectionModel}
31966      */
31967     getSelectionModel : function(){
31968         if(!this.selModel){
31969             this.selModel = new Roo.grid.RowSelectionModel();
31970         }
31971         return this.selModel;
31972     },
31973
31974     /**
31975      * Returns the grid's DataSource.
31976      * @return {DataSource}
31977      */
31978     getDataSource : function(){
31979         return this.dataSource;
31980     },
31981
31982     /**
31983      * Returns the grid's ColumnModel.
31984      * @return {ColumnModel}
31985      */
31986     getColumnModel : function(){
31987         return this.colModel;
31988     },
31989
31990     /**
31991      * Returns the grid's GridView object.
31992      * @return {GridView}
31993      */
31994     getView : function(){
31995         if(!this.view){
31996             this.view = new Roo.grid.GridView(this.viewConfig);
31997         }
31998         return this.view;
31999     },
32000     /**
32001      * Called to get grid's drag proxy text, by default returns this.ddText.
32002      * @return {String}
32003      */
32004     getDragDropText : function(){
32005         var count = this.selModel.getCount();
32006         return String.format(this.ddText, count, count == 1 ? '' : 's');
32007     }
32008 });
32009 /**
32010  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
32011  * %0 is replaced with the number of selected rows.
32012  * @type String
32013  */
32014 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
32015  * Based on:
32016  * Ext JS Library 1.1.1
32017  * Copyright(c) 2006-2007, Ext JS, LLC.
32018  *
32019  * Originally Released Under LGPL - original licence link has changed is not relivant.
32020  *
32021  * Fork - LGPL
32022  * <script type="text/javascript">
32023  */
32024  
32025 Roo.grid.AbstractGridView = function(){
32026         this.grid = null;
32027         
32028         this.events = {
32029             "beforerowremoved" : true,
32030             "beforerowsinserted" : true,
32031             "beforerefresh" : true,
32032             "rowremoved" : true,
32033             "rowsinserted" : true,
32034             "rowupdated" : true,
32035             "refresh" : true
32036         };
32037     Roo.grid.AbstractGridView.superclass.constructor.call(this);
32038 };
32039
32040 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
32041     rowClass : "x-grid-row",
32042     cellClass : "x-grid-cell",
32043     tdClass : "x-grid-td",
32044     hdClass : "x-grid-hd",
32045     splitClass : "x-grid-hd-split",
32046     
32047         init: function(grid){
32048         this.grid = grid;
32049                 var cid = this.grid.getGridEl().id;
32050         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
32051         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
32052         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
32053         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
32054         },
32055         
32056         getColumnRenderers : function(){
32057         var renderers = [];
32058         var cm = this.grid.colModel;
32059         var colCount = cm.getColumnCount();
32060         for(var i = 0; i < colCount; i++){
32061             renderers[i] = cm.getRenderer(i);
32062         }
32063         return renderers;
32064     },
32065     
32066     getColumnIds : function(){
32067         var ids = [];
32068         var cm = this.grid.colModel;
32069         var colCount = cm.getColumnCount();
32070         for(var i = 0; i < colCount; i++){
32071             ids[i] = cm.getColumnId(i);
32072         }
32073         return ids;
32074     },
32075     
32076     getDataIndexes : function(){
32077         if(!this.indexMap){
32078             this.indexMap = this.buildIndexMap();
32079         }
32080         return this.indexMap.colToData;
32081     },
32082     
32083     getColumnIndexByDataIndex : function(dataIndex){
32084         if(!this.indexMap){
32085             this.indexMap = this.buildIndexMap();
32086         }
32087         return this.indexMap.dataToCol[dataIndex];
32088     },
32089     
32090     /**
32091      * Set a css style for a column dynamically. 
32092      * @param {Number} colIndex The index of the column
32093      * @param {String} name The css property name
32094      * @param {String} value The css value
32095      */
32096     setCSSStyle : function(colIndex, name, value){
32097         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
32098         Roo.util.CSS.updateRule(selector, name, value);
32099     },
32100     
32101     generateRules : function(cm){
32102         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
32103         Roo.util.CSS.removeStyleSheet(rulesId);
32104         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
32105             var cid = cm.getColumnId(i);
32106             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
32107                          this.tdSelector, cid, " {\n}\n",
32108                          this.hdSelector, cid, " {\n}\n",
32109                          this.splitSelector, cid, " {\n}\n");
32110         }
32111         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
32112     }
32113 });/*
32114  * Based on:
32115  * Ext JS Library 1.1.1
32116  * Copyright(c) 2006-2007, Ext JS, LLC.
32117  *
32118  * Originally Released Under LGPL - original licence link has changed is not relivant.
32119  *
32120  * Fork - LGPL
32121  * <script type="text/javascript">
32122  */
32123
32124 // private
32125 // This is a support class used internally by the Grid components
32126 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
32127     this.grid = grid;
32128     this.view = grid.getView();
32129     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
32130     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
32131     if(hd2){
32132         this.setHandleElId(Roo.id(hd));
32133         this.setOuterHandleElId(Roo.id(hd2));
32134     }
32135     this.scroll = false;
32136 };
32137 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
32138     maxDragWidth: 120,
32139     getDragData : function(e){
32140         var t = Roo.lib.Event.getTarget(e);
32141         var h = this.view.findHeaderCell(t);
32142         if(h){
32143             return {ddel: h.firstChild, header:h};
32144         }
32145         return false;
32146     },
32147
32148     onInitDrag : function(e){
32149         this.view.headersDisabled = true;
32150         var clone = this.dragData.ddel.cloneNode(true);
32151         clone.id = Roo.id();
32152         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
32153         this.proxy.update(clone);
32154         return true;
32155     },
32156
32157     afterValidDrop : function(){
32158         var v = this.view;
32159         setTimeout(function(){
32160             v.headersDisabled = false;
32161         }, 50);
32162     },
32163
32164     afterInvalidDrop : function(){
32165         var v = this.view;
32166         setTimeout(function(){
32167             v.headersDisabled = false;
32168         }, 50);
32169     }
32170 });
32171 /*
32172  * Based on:
32173  * Ext JS Library 1.1.1
32174  * Copyright(c) 2006-2007, Ext JS, LLC.
32175  *
32176  * Originally Released Under LGPL - original licence link has changed is not relivant.
32177  *
32178  * Fork - LGPL
32179  * <script type="text/javascript">
32180  */
32181 // private
32182 // This is a support class used internally by the Grid components
32183 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
32184     this.grid = grid;
32185     this.view = grid.getView();
32186     // split the proxies so they don't interfere with mouse events
32187     this.proxyTop = Roo.DomHelper.append(document.body, {
32188         cls:"col-move-top", html:"&#160;"
32189     }, true);
32190     this.proxyBottom = Roo.DomHelper.append(document.body, {
32191         cls:"col-move-bottom", html:"&#160;"
32192     }, true);
32193     this.proxyTop.hide = this.proxyBottom.hide = function(){
32194         this.setLeftTop(-100,-100);
32195         this.setStyle("visibility", "hidden");
32196     };
32197     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
32198     // temporarily disabled
32199     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
32200     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
32201 };
32202 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
32203     proxyOffsets : [-4, -9],
32204     fly: Roo.Element.fly,
32205
32206     getTargetFromEvent : function(e){
32207         var t = Roo.lib.Event.getTarget(e);
32208         var cindex = this.view.findCellIndex(t);
32209         if(cindex !== false){
32210             return this.view.getHeaderCell(cindex);
32211         }
32212     },
32213
32214     nextVisible : function(h){
32215         var v = this.view, cm = this.grid.colModel;
32216         h = h.nextSibling;
32217         while(h){
32218             if(!cm.isHidden(v.getCellIndex(h))){
32219                 return h;
32220             }
32221             h = h.nextSibling;
32222         }
32223         return null;
32224     },
32225
32226     prevVisible : function(h){
32227         var v = this.view, cm = this.grid.colModel;
32228         h = h.prevSibling;
32229         while(h){
32230             if(!cm.isHidden(v.getCellIndex(h))){
32231                 return h;
32232             }
32233             h = h.prevSibling;
32234         }
32235         return null;
32236     },
32237
32238     positionIndicator : function(h, n, e){
32239         var x = Roo.lib.Event.getPageX(e);
32240         var r = Roo.lib.Dom.getRegion(n.firstChild);
32241         var px, pt, py = r.top + this.proxyOffsets[1];
32242         if((r.right - x) <= (r.right-r.left)/2){
32243             px = r.right+this.view.borderWidth;
32244             pt = "after";
32245         }else{
32246             px = r.left;
32247             pt = "before";
32248         }
32249         var oldIndex = this.view.getCellIndex(h);
32250         var newIndex = this.view.getCellIndex(n);
32251
32252         if(this.grid.colModel.isFixed(newIndex)){
32253             return false;
32254         }
32255
32256         var locked = this.grid.colModel.isLocked(newIndex);
32257
32258         if(pt == "after"){
32259             newIndex++;
32260         }
32261         if(oldIndex < newIndex){
32262             newIndex--;
32263         }
32264         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
32265             return false;
32266         }
32267         px +=  this.proxyOffsets[0];
32268         this.proxyTop.setLeftTop(px, py);
32269         this.proxyTop.show();
32270         if(!this.bottomOffset){
32271             this.bottomOffset = this.view.mainHd.getHeight();
32272         }
32273         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
32274         this.proxyBottom.show();
32275         return pt;
32276     },
32277
32278     onNodeEnter : function(n, dd, e, data){
32279         if(data.header != n){
32280             this.positionIndicator(data.header, n, e);
32281         }
32282     },
32283
32284     onNodeOver : function(n, dd, e, data){
32285         var result = false;
32286         if(data.header != n){
32287             result = this.positionIndicator(data.header, n, e);
32288         }
32289         if(!result){
32290             this.proxyTop.hide();
32291             this.proxyBottom.hide();
32292         }
32293         return result ? this.dropAllowed : this.dropNotAllowed;
32294     },
32295
32296     onNodeOut : function(n, dd, e, data){
32297         this.proxyTop.hide();
32298         this.proxyBottom.hide();
32299     },
32300
32301     onNodeDrop : function(n, dd, e, data){
32302         var h = data.header;
32303         if(h != n){
32304             var cm = this.grid.colModel;
32305             var x = Roo.lib.Event.getPageX(e);
32306             var r = Roo.lib.Dom.getRegion(n.firstChild);
32307             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
32308             var oldIndex = this.view.getCellIndex(h);
32309             var newIndex = this.view.getCellIndex(n);
32310             var locked = cm.isLocked(newIndex);
32311             if(pt == "after"){
32312                 newIndex++;
32313             }
32314             if(oldIndex < newIndex){
32315                 newIndex--;
32316             }
32317             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
32318                 return false;
32319             }
32320             cm.setLocked(oldIndex, locked, true);
32321             cm.moveColumn(oldIndex, newIndex);
32322             this.grid.fireEvent("columnmove", oldIndex, newIndex);
32323             return true;
32324         }
32325         return false;
32326     }
32327 });
32328 /*
32329  * Based on:
32330  * Ext JS Library 1.1.1
32331  * Copyright(c) 2006-2007, Ext JS, LLC.
32332  *
32333  * Originally Released Under LGPL - original licence link has changed is not relivant.
32334  *
32335  * Fork - LGPL
32336  * <script type="text/javascript">
32337  */
32338   
32339 /**
32340  * @class Roo.grid.GridView
32341  * @extends Roo.util.Observable
32342  *
32343  * @constructor
32344  * @param {Object} config
32345  */
32346 Roo.grid.GridView = function(config){
32347     Roo.grid.GridView.superclass.constructor.call(this);
32348     this.el = null;
32349
32350     Roo.apply(this, config);
32351 };
32352
32353 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
32354
32355     /**
32356      * Override this function to apply custom css classes to rows during rendering
32357      * @param {Record} record The record
32358      * @param {Number} index
32359      * @method getRowClass
32360      */
32361     rowClass : "x-grid-row",
32362
32363     cellClass : "x-grid-col",
32364
32365     tdClass : "x-grid-td",
32366
32367     hdClass : "x-grid-hd",
32368
32369     splitClass : "x-grid-split",
32370
32371     sortClasses : ["sort-asc", "sort-desc"],
32372
32373     enableMoveAnim : false,
32374
32375     hlColor: "C3DAF9",
32376
32377     dh : Roo.DomHelper,
32378
32379     fly : Roo.Element.fly,
32380
32381     css : Roo.util.CSS,
32382
32383     borderWidth: 1,
32384
32385     splitOffset: 3,
32386
32387     scrollIncrement : 22,
32388
32389     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
32390
32391     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
32392
32393     bind : function(ds, cm){
32394         if(this.ds){
32395             this.ds.un("load", this.onLoad, this);
32396             this.ds.un("datachanged", this.onDataChange, this);
32397             this.ds.un("add", this.onAdd, this);
32398             this.ds.un("remove", this.onRemove, this);
32399             this.ds.un("update", this.onUpdate, this);
32400             this.ds.un("clear", this.onClear, this);
32401         }
32402         if(ds){
32403             ds.on("load", this.onLoad, this);
32404             ds.on("datachanged", this.onDataChange, this);
32405             ds.on("add", this.onAdd, this);
32406             ds.on("remove", this.onRemove, this);
32407             ds.on("update", this.onUpdate, this);
32408             ds.on("clear", this.onClear, this);
32409         }
32410         this.ds = ds;
32411
32412         if(this.cm){
32413             this.cm.un("widthchange", this.onColWidthChange, this);
32414             this.cm.un("headerchange", this.onHeaderChange, this);
32415             this.cm.un("hiddenchange", this.onHiddenChange, this);
32416             this.cm.un("columnmoved", this.onColumnMove, this);
32417             this.cm.un("columnlockchange", this.onColumnLock, this);
32418         }
32419         if(cm){
32420             this.generateRules(cm);
32421             cm.on("widthchange", this.onColWidthChange, this);
32422             cm.on("headerchange", this.onHeaderChange, this);
32423             cm.on("hiddenchange", this.onHiddenChange, this);
32424             cm.on("columnmoved", this.onColumnMove, this);
32425             cm.on("columnlockchange", this.onColumnLock, this);
32426         }
32427         this.cm = cm;
32428     },
32429
32430     init: function(grid){
32431                 Roo.grid.GridView.superclass.init.call(this, grid);
32432
32433                 this.bind(grid.dataSource, grid.colModel);
32434
32435             grid.on("headerclick", this.handleHeaderClick, this);
32436
32437         if(grid.trackMouseOver){
32438             grid.on("mouseover", this.onRowOver, this);
32439                 grid.on("mouseout", this.onRowOut, this);
32440             }
32441             grid.cancelTextSelection = function(){};
32442                 this.gridId = grid.id;
32443
32444                 var tpls = this.templates || {};
32445
32446                 if(!tpls.master){
32447                     tpls.master = new Roo.Template(
32448                        '<div class="x-grid" hidefocus="true">',
32449                           '<div class="x-grid-topbar"></div>',
32450                           '<div class="x-grid-scroller"><div></div></div>',
32451                           '<div class="x-grid-locked">',
32452                               '<div class="x-grid-header">{lockedHeader}</div>',
32453                               '<div class="x-grid-body">{lockedBody}</div>',
32454                           "</div>",
32455                           '<div class="x-grid-viewport">',
32456                               '<div class="x-grid-header">{header}</div>',
32457                               '<div class="x-grid-body">{body}</div>',
32458                           "</div>",
32459                           '<div class="x-grid-bottombar"></div>',
32460                           '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
32461                           '<div class="x-grid-resize-proxy">&#160;</div>',
32462                        "</div>"
32463                     );
32464                     tpls.master.disableformats = true;
32465                 }
32466
32467                 if(!tpls.header){
32468                     tpls.header = new Roo.Template(
32469                        '<table border="0" cellspacing="0" cellpadding="0">',
32470                        '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
32471                        "</table>{splits}"
32472                     );
32473                     tpls.header.disableformats = true;
32474                 }
32475                 tpls.header.compile();
32476
32477                 if(!tpls.hcell){
32478                     tpls.hcell = new Roo.Template(
32479                         '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
32480                         '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
32481                         "</div></td>"
32482                      );
32483                      tpls.hcell.disableFormats = true;
32484                 }
32485                 tpls.hcell.compile();
32486
32487                 if(!tpls.hsplit){
32488                     tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
32489                     tpls.hsplit.disableFormats = true;
32490                 }
32491                 tpls.hsplit.compile();
32492
32493                 if(!tpls.body){
32494                     tpls.body = new Roo.Template(
32495                        '<table border="0" cellspacing="0" cellpadding="0">',
32496                        "<tbody>{rows}</tbody>",
32497                        "</table>"
32498                     );
32499                     tpls.body.disableFormats = true;
32500                 }
32501                 tpls.body.compile();
32502
32503                 if(!tpls.row){
32504                     tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
32505                     tpls.row.disableFormats = true;
32506                 }
32507                 tpls.row.compile();
32508
32509                 if(!tpls.cell){
32510                     tpls.cell = new Roo.Template(
32511                         '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
32512                         '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
32513                         "</td>"
32514                     );
32515             tpls.cell.disableFormats = true;
32516         }
32517                 tpls.cell.compile();
32518
32519                 this.templates = tpls;
32520         },
32521
32522         // remap these for backwards compat
32523     onColWidthChange : function(){
32524         this.updateColumns.apply(this, arguments);
32525     },
32526     onHeaderChange : function(){
32527         this.updateHeaders.apply(this, arguments);
32528     }, 
32529     onHiddenChange : function(){
32530         this.handleHiddenChange.apply(this, arguments);
32531     },
32532     onColumnMove : function(){
32533         this.handleColumnMove.apply(this, arguments);
32534     },
32535     onColumnLock : function(){
32536         this.handleLockChange.apply(this, arguments);
32537     },
32538
32539     onDataChange : function(){
32540         this.refresh();
32541         this.updateHeaderSortState();
32542     },
32543
32544         onClear : function(){
32545         this.refresh();
32546     },
32547
32548         onUpdate : function(ds, record){
32549         this.refreshRow(record);
32550     },
32551
32552     refreshRow : function(record){
32553         var ds = this.ds, index;
32554         if(typeof record == 'number'){
32555             index = record;
32556             record = ds.getAt(index);
32557         }else{
32558             index = ds.indexOf(record);
32559         }
32560         this.insertRows(ds, index, index, true);
32561         this.onRemove(ds, record, index+1, true);
32562         this.syncRowHeights(index, index);
32563         this.layout();
32564         this.fireEvent("rowupdated", this, index, record);
32565     },
32566
32567     onAdd : function(ds, records, index){
32568         this.insertRows(ds, index, index + (records.length-1));
32569     },
32570
32571     onRemove : function(ds, record, index, isUpdate){
32572         if(isUpdate !== true){
32573             this.fireEvent("beforerowremoved", this, index, record);
32574         }
32575         var bt = this.getBodyTable(), lt = this.getLockedTable();
32576         if(bt.rows[index]){
32577             bt.firstChild.removeChild(bt.rows[index]);
32578         }
32579         if(lt.rows[index]){
32580             lt.firstChild.removeChild(lt.rows[index]);
32581         }
32582         if(isUpdate !== true){
32583             this.stripeRows(index);
32584             this.syncRowHeights(index, index);
32585             this.layout();
32586             this.fireEvent("rowremoved", this, index, record);
32587         }
32588     },
32589
32590     onLoad : function(){
32591         this.scrollToTop();
32592     },
32593
32594     /**
32595      * Scrolls the grid to the top
32596      */
32597     scrollToTop : function(){
32598         if(this.scroller){
32599             this.scroller.dom.scrollTop = 0;
32600             this.syncScroll();
32601         }
32602     },
32603
32604     /**
32605      * Gets a panel in the header of the grid that can be used for toolbars etc.
32606      * After modifying the contents of this panel a call to grid.autoSize() may be
32607      * required to register any changes in size.
32608      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
32609      * @return Roo.Element
32610      */
32611     getHeaderPanel : function(doShow){
32612         if(doShow){
32613             this.headerPanel.show();
32614         }
32615         return this.headerPanel;
32616         },
32617
32618         /**
32619      * Gets a panel in the footer of the grid that can be used for toolbars etc.
32620      * After modifying the contents of this panel a call to grid.autoSize() may be
32621      * required to register any changes in size.
32622      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
32623      * @return Roo.Element
32624      */
32625     getFooterPanel : function(doShow){
32626         if(doShow){
32627             this.footerPanel.show();
32628         }
32629         return this.footerPanel;
32630         },
32631
32632         initElements : function(){
32633             var E = Roo.Element;
32634             var el = this.grid.getGridEl().dom.firstChild;
32635             var cs = el.childNodes;
32636
32637             this.el = new E(el);
32638             this.headerPanel = new E(el.firstChild);
32639             this.headerPanel.enableDisplayMode("block");
32640
32641         this.scroller = new E(cs[1]);
32642             this.scrollSizer = new E(this.scroller.dom.firstChild);
32643
32644             this.lockedWrap = new E(cs[2]);
32645             this.lockedHd = new E(this.lockedWrap.dom.firstChild);
32646             this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
32647
32648             this.mainWrap = new E(cs[3]);
32649             this.mainHd = new E(this.mainWrap.dom.firstChild);
32650             this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
32651
32652             this.footerPanel = new E(cs[4]);
32653             this.footerPanel.enableDisplayMode("block");
32654
32655         this.focusEl = new E(cs[5]);
32656         this.focusEl.swallowEvent("click", true);
32657         this.resizeProxy = new E(cs[6]);
32658
32659             this.headerSelector = String.format(
32660                '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
32661                this.lockedHd.id, this.mainHd.id
32662             );
32663
32664             this.splitterSelector = String.format(
32665                '#{0} div.x-grid-split, #{1} div.x-grid-split',
32666                this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
32667             );
32668     },
32669     idToCssName : function(s)
32670     {
32671         return s.replace(/[^a-z0-9]+/ig, '-');
32672     },
32673
32674         getHeaderCell : function(index){
32675             return Roo.DomQuery.select(this.headerSelector)[index];
32676         },
32677
32678         getHeaderCellMeasure : function(index){
32679             return this.getHeaderCell(index).firstChild;
32680         },
32681
32682         getHeaderCellText : function(index){
32683             return this.getHeaderCell(index).firstChild.firstChild;
32684         },
32685
32686         getLockedTable : function(){
32687             return this.lockedBody.dom.firstChild;
32688         },
32689
32690         getBodyTable : function(){
32691             return this.mainBody.dom.firstChild;
32692         },
32693
32694         getLockedRow : function(index){
32695             return this.getLockedTable().rows[index];
32696         },
32697
32698         getRow : function(index){
32699             return this.getBodyTable().rows[index];
32700         },
32701
32702         getRowComposite : function(index){
32703             if(!this.rowEl){
32704                 this.rowEl = new Roo.CompositeElementLite();
32705             }
32706         var els = [], lrow, mrow;
32707         if(lrow = this.getLockedRow(index)){
32708             els.push(lrow);
32709         }
32710         if(mrow = this.getRow(index)){
32711             els.push(mrow);
32712         }
32713         this.rowEl.elements = els;
32714             return this.rowEl;
32715         },
32716
32717         getCell : function(rowIndex, colIndex){
32718             var locked = this.cm.getLockedCount();
32719             var source;
32720             if(colIndex < locked){
32721                 source = this.lockedBody.dom.firstChild;
32722             }else{
32723                 source = this.mainBody.dom.firstChild;
32724                 colIndex -= locked;
32725             }
32726         return source.rows[rowIndex].childNodes[colIndex];
32727         },
32728
32729         getCellText : function(rowIndex, colIndex){
32730             return this.getCell(rowIndex, colIndex).firstChild.firstChild;
32731         },
32732
32733         getCellBox : function(cell){
32734             var b = this.fly(cell).getBox();
32735         if(Roo.isOpera){ // opera fails to report the Y
32736             b.y = cell.offsetTop + this.mainBody.getY();
32737         }
32738         return b;
32739     },
32740
32741     getCellIndex : function(cell){
32742         var id = String(cell.className).match(this.cellRE);
32743         if(id){
32744             return parseInt(id[1], 10);
32745         }
32746         return 0;
32747     },
32748
32749     findHeaderIndex : function(n){
32750         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
32751         return r ? this.getCellIndex(r) : false;
32752     },
32753
32754     findHeaderCell : function(n){
32755         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
32756         return r ? r : false;
32757     },
32758
32759     findRowIndex : function(n){
32760         if(!n){
32761             return false;
32762         }
32763         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
32764         return r ? r.rowIndex : false;
32765     },
32766
32767     findCellIndex : function(node){
32768         var stop = this.el.dom;
32769         while(node && node != stop){
32770             if(this.findRE.test(node.className)){
32771                 return this.getCellIndex(node);
32772             }
32773             node = node.parentNode;
32774         }
32775         return false;
32776     },
32777
32778     getColumnId : function(index){
32779             return this.cm.getColumnId(index);
32780         },
32781
32782         getSplitters : function(){
32783             if(this.splitterSelector){
32784                return Roo.DomQuery.select(this.splitterSelector);
32785             }else{
32786                 return null;
32787             }
32788         },
32789
32790         getSplitter : function(index){
32791             return this.getSplitters()[index];
32792         },
32793
32794     onRowOver : function(e, t){
32795         var row;
32796         if((row = this.findRowIndex(t)) !== false){
32797             this.getRowComposite(row).addClass("x-grid-row-over");
32798         }
32799     },
32800
32801     onRowOut : function(e, t){
32802         var row;
32803         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
32804             this.getRowComposite(row).removeClass("x-grid-row-over");
32805         }
32806     },
32807
32808     renderHeaders : function(){
32809             var cm = this.cm;
32810         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
32811         var cb = [], lb = [], sb = [], lsb = [], p = {};
32812         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
32813             p.cellId = "x-grid-hd-0-" + i;
32814             p.splitId = "x-grid-csplit-0-" + i;
32815             p.id = cm.getColumnId(i);
32816             p.title = cm.getColumnTooltip(i) || "";
32817             p.value = cm.getColumnHeader(i) || "";
32818             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
32819             if(!cm.isLocked(i)){
32820                 cb[cb.length] = ct.apply(p);
32821                 sb[sb.length] = st.apply(p);
32822             }else{
32823                 lb[lb.length] = ct.apply(p);
32824                 lsb[lsb.length] = st.apply(p);
32825             }
32826         }
32827         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
32828                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
32829         },
32830
32831         updateHeaders : function(){
32832         var html = this.renderHeaders();
32833         this.lockedHd.update(html[0]);
32834         this.mainHd.update(html[1]);
32835     },
32836
32837     /**
32838      * Focuses the specified row.
32839      * @param {Number} row The row index
32840      */
32841     focusRow : function(row){
32842         var x = this.scroller.dom.scrollLeft;
32843         this.focusCell(row, 0, false);
32844         this.scroller.dom.scrollLeft = x;
32845     },
32846
32847     /**
32848      * Focuses the specified cell.
32849      * @param {Number} row The row index
32850      * @param {Number} col The column index
32851      * @param {Boolean} hscroll false to disable horizontal scrolling
32852      */
32853     focusCell : function(row, col, hscroll){
32854         var el = this.ensureVisible(row, col, hscroll);
32855         this.focusEl.alignTo(el, "tl-tl");
32856         if(Roo.isGecko){
32857             this.focusEl.focus();
32858         }else{
32859             this.focusEl.focus.defer(1, this.focusEl);
32860         }
32861     },
32862
32863     /**
32864      * Scrolls the specified cell into view
32865      * @param {Number} row The row index
32866      * @param {Number} col The column index
32867      * @param {Boolean} hscroll false to disable horizontal scrolling
32868      */
32869     ensureVisible : function(row, col, hscroll){
32870         if(typeof row != "number"){
32871             row = row.rowIndex;
32872         }
32873         if(row < 0 && row >= this.ds.getCount()){
32874             return;
32875         }
32876         col = (col !== undefined ? col : 0);
32877         var cm = this.grid.colModel;
32878         while(cm.isHidden(col)){
32879             col++;
32880         }
32881
32882         var el = this.getCell(row, col);
32883         if(!el){
32884             return;
32885         }
32886         var c = this.scroller.dom;
32887
32888         var ctop = parseInt(el.offsetTop, 10);
32889         var cleft = parseInt(el.offsetLeft, 10);
32890         var cbot = ctop + el.offsetHeight;
32891         var cright = cleft + el.offsetWidth;
32892
32893         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
32894         var stop = parseInt(c.scrollTop, 10);
32895         var sleft = parseInt(c.scrollLeft, 10);
32896         var sbot = stop + ch;
32897         var sright = sleft + c.clientWidth;
32898
32899         if(ctop < stop){
32900                 c.scrollTop = ctop;
32901         }else if(cbot > sbot){
32902             c.scrollTop = cbot-ch;
32903         }
32904
32905         if(hscroll !== false){
32906             if(cleft < sleft){
32907                 c.scrollLeft = cleft;
32908             }else if(cright > sright){
32909                 c.scrollLeft = cright-c.clientWidth;
32910             }
32911         }
32912         return el;
32913     },
32914
32915     updateColumns : function(){
32916         this.grid.stopEditing();
32917         var cm = this.grid.colModel, colIds = this.getColumnIds();
32918         //var totalWidth = cm.getTotalWidth();
32919         var pos = 0;
32920         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
32921             //if(cm.isHidden(i)) continue;
32922             var w = cm.getColumnWidth(i);
32923             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
32924             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
32925         }
32926         this.updateSplitters();
32927     },
32928
32929     generateRules : function(cm){
32930         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
32931         Roo.util.CSS.removeStyleSheet(rulesId);
32932         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
32933             var cid = cm.getColumnId(i);
32934             var align = '';
32935             if(cm.config[i].align){
32936                 align = 'text-align:'+cm.config[i].align+';';
32937             }
32938             var hidden = '';
32939             if(cm.isHidden(i)){
32940                 hidden = 'display:none;';
32941             }
32942             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
32943             ruleBuf.push(
32944                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
32945                     this.hdSelector, cid, " {\n", align, width, "}\n",
32946                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
32947                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
32948         }
32949         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
32950     },
32951
32952     updateSplitters : function(){
32953         var cm = this.cm, s = this.getSplitters();
32954         if(s){ // splitters not created yet
32955             var pos = 0, locked = true;
32956             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
32957                 if(cm.isHidden(i)) continue;
32958                 var w = cm.getColumnWidth(i);
32959                 if(!cm.isLocked(i) && locked){
32960                     pos = 0;
32961                     locked = false;
32962                 }
32963                 pos += w;
32964                 s[i].style.left = (pos-this.splitOffset) + "px";
32965             }
32966         }
32967     },
32968
32969     handleHiddenChange : function(colModel, colIndex, hidden){
32970         if(hidden){
32971             this.hideColumn(colIndex);
32972         }else{
32973             this.unhideColumn(colIndex);
32974         }
32975     },
32976
32977     hideColumn : function(colIndex){
32978         var cid = this.getColumnId(colIndex);
32979         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
32980         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
32981         if(Roo.isSafari){
32982             this.updateHeaders();
32983         }
32984         this.updateSplitters();
32985         this.layout();
32986     },
32987
32988     unhideColumn : function(colIndex){
32989         var cid = this.getColumnId(colIndex);
32990         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
32991         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
32992
32993         if(Roo.isSafari){
32994             this.updateHeaders();
32995         }
32996         this.updateSplitters();
32997         this.layout();
32998     },
32999
33000     insertRows : function(dm, firstRow, lastRow, isUpdate){
33001         if(firstRow == 0 && lastRow == dm.getCount()-1){
33002             this.refresh();
33003         }else{
33004             if(!isUpdate){
33005                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
33006             }
33007             var s = this.getScrollState();
33008             var markup = this.renderRows(firstRow, lastRow);
33009             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
33010             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
33011             this.restoreScroll(s);
33012             if(!isUpdate){
33013                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
33014                 this.syncRowHeights(firstRow, lastRow);
33015                 this.stripeRows(firstRow);
33016                 this.layout();
33017             }
33018         }
33019     },
33020
33021     bufferRows : function(markup, target, index){
33022         var before = null, trows = target.rows, tbody = target.tBodies[0];
33023         if(index < trows.length){
33024             before = trows[index];
33025         }
33026         var b = document.createElement("div");
33027         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
33028         var rows = b.firstChild.rows;
33029         for(var i = 0, len = rows.length; i < len; i++){
33030             if(before){
33031                 tbody.insertBefore(rows[0], before);
33032             }else{
33033                 tbody.appendChild(rows[0]);
33034             }
33035         }
33036         b.innerHTML = "";
33037         b = null;
33038     },
33039
33040     deleteRows : function(dm, firstRow, lastRow){
33041         if(dm.getRowCount()<1){
33042             this.fireEvent("beforerefresh", this);
33043             this.mainBody.update("");
33044             this.lockedBody.update("");
33045             this.fireEvent("refresh", this);
33046         }else{
33047             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
33048             var bt = this.getBodyTable();
33049             var tbody = bt.firstChild;
33050             var rows = bt.rows;
33051             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
33052                 tbody.removeChild(rows[firstRow]);
33053             }
33054             this.stripeRows(firstRow);
33055             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
33056         }
33057     },
33058
33059     updateRows : function(dataSource, firstRow, lastRow){
33060         var s = this.getScrollState();
33061         this.refresh();
33062         this.restoreScroll(s);
33063     },
33064
33065     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
33066         if(!noRefresh){
33067            this.refresh();
33068         }
33069         this.updateHeaderSortState();
33070     },
33071
33072     getScrollState : function(){
33073         var sb = this.scroller.dom;
33074         return {left: sb.scrollLeft, top: sb.scrollTop};
33075     },
33076
33077     stripeRows : function(startRow){
33078         if(!this.grid.stripeRows || this.ds.getCount() < 1){
33079             return;
33080         }
33081         startRow = startRow || 0;
33082         var rows = this.getBodyTable().rows;
33083         var lrows = this.getLockedTable().rows;
33084         var cls = ' x-grid-row-alt ';
33085         for(var i = startRow, len = rows.length; i < len; i++){
33086             var row = rows[i], lrow = lrows[i];
33087             var isAlt = ((i+1) % 2 == 0);
33088             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
33089             if(isAlt == hasAlt){
33090                 continue;
33091             }
33092             if(isAlt){
33093                 row.className += " x-grid-row-alt";
33094             }else{
33095                 row.className = row.className.replace("x-grid-row-alt", "");
33096             }
33097             if(lrow){
33098                 lrow.className = row.className;
33099             }
33100         }
33101     },
33102
33103     restoreScroll : function(state){
33104         var sb = this.scroller.dom;
33105         sb.scrollLeft = state.left;
33106         sb.scrollTop = state.top;
33107         this.syncScroll();
33108     },
33109
33110     syncScroll : function(){
33111         var sb = this.scroller.dom;
33112         var sh = this.mainHd.dom;
33113         var bs = this.mainBody.dom;
33114         var lv = this.lockedBody.dom;
33115         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
33116         lv.scrollTop = bs.scrollTop = sb.scrollTop;
33117     },
33118
33119     handleScroll : function(e){
33120         this.syncScroll();
33121         var sb = this.scroller.dom;
33122         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
33123         e.stopEvent();
33124     },
33125
33126     handleWheel : function(e){
33127         var d = e.getWheelDelta();
33128         this.scroller.dom.scrollTop -= d*22;
33129         // set this here to prevent jumpy scrolling on large tables
33130         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
33131         e.stopEvent();
33132     },
33133
33134     renderRows : function(startRow, endRow){
33135         // pull in all the crap needed to render rows
33136         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
33137         var colCount = cm.getColumnCount();
33138
33139         if(ds.getCount() < 1){
33140             return ["", ""];
33141         }
33142
33143         // build a map for all the columns
33144         var cs = [];
33145         for(var i = 0; i < colCount; i++){
33146             var name = cm.getDataIndex(i);
33147             cs[i] = {
33148                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
33149                 renderer : cm.getRenderer(i),
33150                 id : cm.getColumnId(i),
33151                 locked : cm.isLocked(i)
33152             };
33153         }
33154
33155         startRow = startRow || 0;
33156         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
33157
33158         // records to render
33159         var rs = ds.getRange(startRow, endRow);
33160
33161         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
33162     },
33163
33164     // As much as I hate to duplicate code, this was branched because FireFox really hates
33165     // [].join("") on strings. The performance difference was substantial enough to
33166     // branch this function
33167     doRender : Roo.isGecko ?
33168             function(cs, rs, ds, startRow, colCount, stripe){
33169                 var ts = this.templates, ct = ts.cell, rt = ts.row;
33170                 // buffers
33171                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
33172                 for(var j = 0, len = rs.length; j < len; j++){
33173                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
33174                     for(var i = 0; i < colCount; i++){
33175                         c = cs[i];
33176                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
33177                         p.id = c.id;
33178                         p.css = p.attr = "";
33179                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
33180                         if(p.value == undefined || p.value === "") p.value = "&#160;";
33181                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
33182                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
33183                         }
33184                         var markup = ct.apply(p);
33185                         if(!c.locked){
33186                             cb+= markup;
33187                         }else{
33188                             lcb+= markup;
33189                         }
33190                     }
33191                     var alt = [];
33192                     if(stripe && ((rowIndex+1) % 2 == 0)){
33193                         alt[0] = "x-grid-row-alt";
33194                     }
33195                     if(r.dirty){
33196                         alt[1] = " x-grid-dirty-row";
33197                     }
33198                     rp.cells = lcb;
33199                     if(this.getRowClass){
33200                         alt[2] = this.getRowClass(r, rowIndex);
33201                     }
33202                     rp.alt = alt.join(" ");
33203                     lbuf+= rt.apply(rp);
33204                     rp.cells = cb;
33205                     buf+=  rt.apply(rp);
33206                 }
33207                 return [lbuf, buf];
33208             } :
33209             function(cs, rs, ds, startRow, colCount, stripe){
33210                 var ts = this.templates, ct = ts.cell, rt = ts.row;
33211                 // buffers
33212                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
33213                 for(var j = 0, len = rs.length; j < len; j++){
33214                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
33215                     for(var i = 0; i < colCount; i++){
33216                         c = cs[i];
33217                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
33218                         p.id = c.id;
33219                         p.css = p.attr = "";
33220                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
33221                         if(p.value == undefined || p.value === "") p.value = "&#160;";
33222                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
33223                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
33224                         }
33225                         var markup = ct.apply(p);
33226                         if(!c.locked){
33227                             cb[cb.length] = markup;
33228                         }else{
33229                             lcb[lcb.length] = markup;
33230                         }
33231                     }
33232                     var alt = [];
33233                     if(stripe && ((rowIndex+1) % 2 == 0)){
33234                         alt[0] = "x-grid-row-alt";
33235                     }
33236                     if(r.dirty){
33237                         alt[1] = " x-grid-dirty-row";
33238                     }
33239                     rp.cells = lcb;
33240                     if(this.getRowClass){
33241                         alt[2] = this.getRowClass(r, rowIndex);
33242                     }
33243                     rp.alt = alt.join(" ");
33244                     rp.cells = lcb.join("");
33245                     lbuf[lbuf.length] = rt.apply(rp);
33246                     rp.cells = cb.join("");
33247                     buf[buf.length] =  rt.apply(rp);
33248                 }
33249                 return [lbuf.join(""), buf.join("")];
33250             },
33251
33252     renderBody : function(){
33253         var markup = this.renderRows();
33254         var bt = this.templates.body;
33255         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
33256     },
33257
33258     /**
33259      * Refreshes the grid
33260      * @param {Boolean} headersToo
33261      */
33262     refresh : function(headersToo){
33263         this.fireEvent("beforerefresh", this);
33264         this.grid.stopEditing();
33265         var result = this.renderBody();
33266         this.lockedBody.update(result[0]);
33267         this.mainBody.update(result[1]);
33268         if(headersToo === true){
33269             this.updateHeaders();
33270             this.updateColumns();
33271             this.updateSplitters();
33272             this.updateHeaderSortState();
33273         }
33274         this.syncRowHeights();
33275         this.layout();
33276         this.fireEvent("refresh", this);
33277     },
33278
33279     handleColumnMove : function(cm, oldIndex, newIndex){
33280         this.indexMap = null;
33281         var s = this.getScrollState();
33282         this.refresh(true);
33283         this.restoreScroll(s);
33284         this.afterMove(newIndex);
33285     },
33286
33287     afterMove : function(colIndex){
33288         if(this.enableMoveAnim && Roo.enableFx){
33289             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
33290         }
33291     },
33292
33293     updateCell : function(dm, rowIndex, dataIndex){
33294         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
33295         if(typeof colIndex == "undefined"){ // not present in grid
33296             return;
33297         }
33298         var cm = this.grid.colModel;
33299         var cell = this.getCell(rowIndex, colIndex);
33300         var cellText = this.getCellText(rowIndex, colIndex);
33301
33302         var p = {
33303             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
33304             id : cm.getColumnId(colIndex),
33305             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
33306         };
33307         var renderer = cm.getRenderer(colIndex);
33308         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
33309         if(typeof val == "undefined" || val === "") val = "&#160;";
33310         cellText.innerHTML = val;
33311         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
33312         this.syncRowHeights(rowIndex, rowIndex);
33313     },
33314
33315     calcColumnWidth : function(colIndex, maxRowsToMeasure){
33316         var maxWidth = 0;
33317         if(this.grid.autoSizeHeaders){
33318             var h = this.getHeaderCellMeasure(colIndex);
33319             maxWidth = Math.max(maxWidth, h.scrollWidth);
33320         }
33321         var tb, index;
33322         if(this.cm.isLocked(colIndex)){
33323             tb = this.getLockedTable();
33324             index = colIndex;
33325         }else{
33326             tb = this.getBodyTable();
33327             index = colIndex - this.cm.getLockedCount();
33328         }
33329         if(tb && tb.rows){
33330             var rows = tb.rows;
33331             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
33332             for(var i = 0; i < stopIndex; i++){
33333                 var cell = rows[i].childNodes[index].firstChild;
33334                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
33335             }
33336         }
33337         return maxWidth + /*margin for error in IE*/ 5;
33338     },
33339     /**
33340      * Autofit a column to its content.
33341      * @param {Number} colIndex
33342      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
33343      */
33344      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
33345          if(this.cm.isHidden(colIndex)){
33346              return; // can't calc a hidden column
33347          }
33348         if(forceMinSize){
33349             var cid = this.cm.getColumnId(colIndex);
33350             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
33351            if(this.grid.autoSizeHeaders){
33352                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
33353            }
33354         }
33355         var newWidth = this.calcColumnWidth(colIndex);
33356         this.cm.setColumnWidth(colIndex,
33357             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
33358         if(!suppressEvent){
33359             this.grid.fireEvent("columnresize", colIndex, newWidth);
33360         }
33361     },
33362
33363     /**
33364      * Autofits all columns to their content and then expands to fit any extra space in the grid
33365      */
33366      autoSizeColumns : function(){
33367         var cm = this.grid.colModel;
33368         var colCount = cm.getColumnCount();
33369         for(var i = 0; i < colCount; i++){
33370             this.autoSizeColumn(i, true, true);
33371         }
33372         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
33373             this.fitColumns();
33374         }else{
33375             this.updateColumns();
33376             this.layout();
33377         }
33378     },
33379
33380     /**
33381      * Autofits all columns to the grid's width proportionate with their current size
33382      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
33383      */
33384     fitColumns : function(reserveScrollSpace){
33385         var cm = this.grid.colModel;
33386         var colCount = cm.getColumnCount();
33387         var cols = [];
33388         var width = 0;
33389         var i, w;
33390         for (i = 0; i < colCount; i++){
33391             if(!cm.isHidden(i) && !cm.isFixed(i)){
33392                 w = cm.getColumnWidth(i);
33393                 cols.push(i);
33394                 cols.push(w);
33395                 width += w;
33396             }
33397         }
33398         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
33399         if(reserveScrollSpace){
33400             avail -= 17;
33401         }
33402         var frac = (avail - cm.getTotalWidth())/width;
33403         while (cols.length){
33404             w = cols.pop();
33405             i = cols.pop();
33406             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
33407         }
33408         this.updateColumns();
33409         this.layout();
33410     },
33411
33412     onRowSelect : function(rowIndex){
33413         var row = this.getRowComposite(rowIndex);
33414         row.addClass("x-grid-row-selected");
33415     },
33416
33417     onRowDeselect : function(rowIndex){
33418         var row = this.getRowComposite(rowIndex);
33419         row.removeClass("x-grid-row-selected");
33420     },
33421
33422     onCellSelect : function(row, col){
33423         var cell = this.getCell(row, col);
33424         if(cell){
33425             Roo.fly(cell).addClass("x-grid-cell-selected");
33426         }
33427     },
33428
33429     onCellDeselect : function(row, col){
33430         var cell = this.getCell(row, col);
33431         if(cell){
33432             Roo.fly(cell).removeClass("x-grid-cell-selected");
33433         }
33434     },
33435
33436     updateHeaderSortState : function(){
33437         var state = this.ds.getSortState();
33438         if(!state){
33439             return;
33440         }
33441         this.sortState = state;
33442         var sortColumn = this.cm.findColumnIndex(state.field);
33443         if(sortColumn != -1){
33444             var sortDir = state.direction;
33445             var sc = this.sortClasses;
33446             var hds = this.el.select(this.headerSelector).removeClass(sc);
33447             hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
33448         }
33449     },
33450
33451     handleHeaderClick : function(g, index){
33452         if(this.headersDisabled){
33453             return;
33454         }
33455         var dm = g.dataSource, cm = g.colModel;
33456             if(!cm.isSortable(index)){
33457             return;
33458         }
33459             g.stopEditing();
33460         dm.sort(cm.getDataIndex(index));
33461     },
33462
33463
33464     destroy : function(){
33465         if(this.colMenu){
33466             this.colMenu.removeAll();
33467             Roo.menu.MenuMgr.unregister(this.colMenu);
33468             this.colMenu.getEl().remove();
33469             delete this.colMenu;
33470         }
33471         if(this.hmenu){
33472             this.hmenu.removeAll();
33473             Roo.menu.MenuMgr.unregister(this.hmenu);
33474             this.hmenu.getEl().remove();
33475             delete this.hmenu;
33476         }
33477         if(this.grid.enableColumnMove){
33478             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
33479             if(dds){
33480                 for(var dd in dds){
33481                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
33482                         var elid = dds[dd].dragElId;
33483                         dds[dd].unreg();
33484                         Roo.get(elid).remove();
33485                     } else if(dds[dd].config.isTarget){
33486                         dds[dd].proxyTop.remove();
33487                         dds[dd].proxyBottom.remove();
33488                         dds[dd].unreg();
33489                     }
33490                     if(Roo.dd.DDM.locationCache[dd]){
33491                         delete Roo.dd.DDM.locationCache[dd];
33492                     }
33493                 }
33494                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
33495             }
33496         }
33497         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
33498         this.bind(null, null);
33499         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
33500     },
33501
33502     handleLockChange : function(){
33503         this.refresh(true);
33504     },
33505
33506     onDenyColumnLock : function(){
33507
33508     },
33509
33510     onDenyColumnHide : function(){
33511
33512     },
33513
33514     handleHdMenuClick : function(item){
33515         var index = this.hdCtxIndex;
33516         var cm = this.cm, ds = this.ds;
33517         switch(item.id){
33518             case "asc":
33519                 ds.sort(cm.getDataIndex(index), "ASC");
33520                 break;
33521             case "desc":
33522                 ds.sort(cm.getDataIndex(index), "DESC");
33523                 break;
33524             case "lock":
33525                 var lc = cm.getLockedCount();
33526                 if(cm.getColumnCount(true) <= lc+1){
33527                     this.onDenyColumnLock();
33528                     return;
33529                 }
33530                 if(lc != index){
33531                     cm.setLocked(index, true, true);
33532                     cm.moveColumn(index, lc);
33533                     this.grid.fireEvent("columnmove", index, lc);
33534                 }else{
33535                     cm.setLocked(index, true);
33536                 }
33537             break;
33538             case "unlock":
33539                 var lc = cm.getLockedCount();
33540                 if((lc-1) != index){
33541                     cm.setLocked(index, false, true);
33542                     cm.moveColumn(index, lc-1);
33543                     this.grid.fireEvent("columnmove", index, lc-1);
33544                 }else{
33545                     cm.setLocked(index, false);
33546                 }
33547             break;
33548             default:
33549                 index = cm.getIndexById(item.id.substr(4));
33550                 if(index != -1){
33551                     if(item.checked && cm.getColumnCount(true) <= 1){
33552                         this.onDenyColumnHide();
33553                         return false;
33554                     }
33555                     cm.setHidden(index, item.checked);
33556                 }
33557         }
33558         return true;
33559     },
33560
33561     beforeColMenuShow : function(){
33562         var cm = this.cm,  colCount = cm.getColumnCount();
33563         this.colMenu.removeAll();
33564         for(var i = 0; i < colCount; i++){
33565             this.colMenu.add(new Roo.menu.CheckItem({
33566                 id: "col-"+cm.getColumnId(i),
33567                 text: cm.getColumnHeader(i),
33568                 checked: !cm.isHidden(i),
33569                 hideOnClick:false
33570             }));
33571         }
33572     },
33573
33574     handleHdCtx : function(g, index, e){
33575         e.stopEvent();
33576         var hd = this.getHeaderCell(index);
33577         this.hdCtxIndex = index;
33578         var ms = this.hmenu.items, cm = this.cm;
33579         ms.get("asc").setDisabled(!cm.isSortable(index));
33580         ms.get("desc").setDisabled(!cm.isSortable(index));
33581         if(this.grid.enableColLock !== false){
33582             ms.get("lock").setDisabled(cm.isLocked(index));
33583             ms.get("unlock").setDisabled(!cm.isLocked(index));
33584         }
33585         this.hmenu.show(hd, "tl-bl");
33586     },
33587
33588     handleHdOver : function(e){
33589         var hd = this.findHeaderCell(e.getTarget());
33590         if(hd && !this.headersDisabled){
33591             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
33592                this.fly(hd).addClass("x-grid-hd-over");
33593             }
33594         }
33595     },
33596
33597     handleHdOut : function(e){
33598         var hd = this.findHeaderCell(e.getTarget());
33599         if(hd){
33600             this.fly(hd).removeClass("x-grid-hd-over");
33601         }
33602     },
33603
33604     handleSplitDblClick : function(e, t){
33605         var i = this.getCellIndex(t);
33606         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
33607             this.autoSizeColumn(i, true);
33608             this.layout();
33609         }
33610     },
33611
33612     render : function(){
33613
33614         var cm = this.cm;
33615         var colCount = cm.getColumnCount();
33616
33617         if(this.grid.monitorWindowResize === true){
33618             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
33619         }
33620         var header = this.renderHeaders();
33621         var body = this.templates.body.apply({rows:""});
33622         var html = this.templates.master.apply({
33623             lockedBody: body,
33624             body: body,
33625             lockedHeader: header[0],
33626             header: header[1]
33627         });
33628
33629         //this.updateColumns();
33630
33631         this.grid.getGridEl().dom.innerHTML = html;
33632
33633         this.initElements();
33634
33635         this.scroller.on("scroll", this.handleScroll, this);
33636         this.lockedBody.on("mousewheel", this.handleWheel, this);
33637         this.mainBody.on("mousewheel", this.handleWheel, this);
33638
33639         this.mainHd.on("mouseover", this.handleHdOver, this);
33640         this.mainHd.on("mouseout", this.handleHdOut, this);
33641         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
33642                 {delegate: "."+this.splitClass});
33643
33644         this.lockedHd.on("mouseover", this.handleHdOver, this);
33645         this.lockedHd.on("mouseout", this.handleHdOut, this);
33646         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
33647                 {delegate: "."+this.splitClass});
33648
33649         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
33650             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
33651         }
33652
33653         this.updateSplitters();
33654
33655         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
33656             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
33657             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
33658         }
33659
33660         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
33661             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
33662             this.hmenu.add(
33663                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
33664                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
33665             );
33666             if(this.grid.enableColLock !== false){
33667                 this.hmenu.add('-',
33668                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
33669                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
33670                 );
33671             }
33672             if(this.grid.enableColumnHide !== false){
33673
33674                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
33675                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
33676                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
33677
33678                 this.hmenu.add('-',
33679                     {id:"columns", text: this.columnsText, menu: this.colMenu}
33680                 );
33681             }
33682             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
33683
33684             this.grid.on("headercontextmenu", this.handleHdCtx, this);
33685         }
33686
33687         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
33688             this.dd = new Roo.grid.GridDragZone(this.grid, {
33689                 ddGroup : this.grid.ddGroup || 'GridDD'
33690             });
33691         }
33692
33693         /*
33694         for(var i = 0; i < colCount; i++){
33695             if(cm.isHidden(i)){
33696                 this.hideColumn(i);
33697             }
33698             if(cm.config[i].align){
33699                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
33700                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
33701             }
33702         }*/
33703         
33704         this.updateHeaderSortState();
33705
33706         this.beforeInitialResize();
33707         this.layout(true);
33708
33709         // two part rendering gives faster view to the user
33710         this.renderPhase2.defer(1, this);
33711     },
33712
33713     renderPhase2 : function(){
33714         // render the rows now
33715         this.refresh();
33716         if(this.grid.autoSizeColumns){
33717             this.autoSizeColumns();
33718         }
33719     },
33720
33721     beforeInitialResize : function(){
33722
33723     },
33724
33725     onColumnSplitterMoved : function(i, w){
33726         this.userResized = true;
33727         var cm = this.grid.colModel;
33728         cm.setColumnWidth(i, w, true);
33729         var cid = cm.getColumnId(i);
33730         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
33731         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
33732         this.updateSplitters();
33733         this.layout();
33734         this.grid.fireEvent("columnresize", i, w);
33735     },
33736
33737     syncRowHeights : function(startIndex, endIndex){
33738         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
33739             startIndex = startIndex || 0;
33740             var mrows = this.getBodyTable().rows;
33741             var lrows = this.getLockedTable().rows;
33742             var len = mrows.length-1;
33743             endIndex = Math.min(endIndex || len, len);
33744             for(var i = startIndex; i <= endIndex; i++){
33745                 var m = mrows[i], l = lrows[i];
33746                 var h = Math.max(m.offsetHeight, l.offsetHeight);
33747                 m.style.height = l.style.height = h + "px";
33748             }
33749         }
33750     },
33751
33752     layout : function(initialRender, is2ndPass){
33753         var g = this.grid;
33754         var auto = g.autoHeight;
33755         var scrollOffset = 16;
33756         var c = g.getGridEl(), cm = this.cm,
33757                 expandCol = g.autoExpandColumn,
33758                 gv = this;
33759         //c.beginMeasure();
33760
33761         if(!c.dom.offsetWidth){ // display:none?
33762             if(initialRender){
33763                 this.lockedWrap.show();
33764                 this.mainWrap.show();
33765             }
33766             return;
33767         }
33768
33769         var hasLock = this.cm.isLocked(0);
33770
33771         var tbh = this.headerPanel.getHeight();
33772         var bbh = this.footerPanel.getHeight();
33773
33774         if(auto){
33775             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
33776             var newHeight = ch + c.getBorderWidth("tb");
33777             if(g.maxHeight){
33778                 newHeight = Math.min(g.maxHeight, newHeight);
33779             }
33780             c.setHeight(newHeight);
33781         }
33782
33783         if(g.autoWidth){
33784             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
33785         }
33786
33787         var s = this.scroller;
33788
33789         var csize = c.getSize(true);
33790
33791         this.el.setSize(csize.width, csize.height);
33792
33793         this.headerPanel.setWidth(csize.width);
33794         this.footerPanel.setWidth(csize.width);
33795
33796         var hdHeight = this.mainHd.getHeight();
33797         var vw = csize.width;
33798         var vh = csize.height - (tbh + bbh);
33799
33800         s.setSize(vw, vh);
33801
33802         var bt = this.getBodyTable();
33803         var ltWidth = hasLock ?
33804                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
33805
33806         var scrollHeight = bt.offsetHeight;
33807         var scrollWidth = ltWidth + bt.offsetWidth;
33808         var vscroll = false, hscroll = false;
33809
33810         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
33811
33812         var lw = this.lockedWrap, mw = this.mainWrap;
33813         var lb = this.lockedBody, mb = this.mainBody;
33814
33815         setTimeout(function(){
33816             var t = s.dom.offsetTop;
33817             var w = s.dom.clientWidth,
33818                 h = s.dom.clientHeight;
33819
33820             lw.setTop(t);
33821             lw.setSize(ltWidth, h);
33822
33823             mw.setLeftTop(ltWidth, t);
33824             mw.setSize(w-ltWidth, h);
33825
33826             lb.setHeight(h-hdHeight);
33827             mb.setHeight(h-hdHeight);
33828
33829             if(is2ndPass !== true && !gv.userResized && expandCol){
33830                 // high speed resize without full column calculation
33831                 
33832                 var ci = cm.getIndexById(expandCol);
33833                 if (ci < 0) {
33834                     ci = cm.findColumnIndex(expandCol);
33835                 }
33836                 ci = Math.max(0, ci); // make sure it's got at least the first col.
33837                 var expandId = cm.getColumnId(ci);
33838                 var  tw = cm.getTotalWidth(false);
33839                 var currentWidth = cm.getColumnWidth(ci);
33840                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
33841                 if(currentWidth != cw){
33842                     cm.setColumnWidth(ci, cw, true);
33843                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
33844                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
33845                     gv.updateSplitters();
33846                     gv.layout(false, true);
33847                 }
33848             }
33849
33850             if(initialRender){
33851                 lw.show();
33852                 mw.show();
33853             }
33854             //c.endMeasure();
33855         }, 10);
33856     },
33857
33858     onWindowResize : function(){
33859         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
33860             return;
33861         }
33862         this.layout();
33863     },
33864
33865     appendFooter : function(parentEl){
33866         return null;
33867     },
33868
33869     sortAscText : "Sort Ascending",
33870     sortDescText : "Sort Descending",
33871     lockText : "Lock Column",
33872     unlockText : "Unlock Column",
33873     columnsText : "Columns"
33874 });
33875
33876
33877 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
33878     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
33879     this.proxy.el.addClass('x-grid3-col-dd');
33880 };
33881
33882 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
33883     handleMouseDown : function(e){
33884
33885     },
33886
33887     callHandleMouseDown : function(e){
33888         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
33889     }
33890 });
33891 /*
33892  * Based on:
33893  * Ext JS Library 1.1.1
33894  * Copyright(c) 2006-2007, Ext JS, LLC.
33895  *
33896  * Originally Released Under LGPL - original licence link has changed is not relivant.
33897  *
33898  * Fork - LGPL
33899  * <script type="text/javascript">
33900  */
33901  
33902 // private
33903 // This is a support class used internally by the Grid components
33904 Roo.grid.SplitDragZone = function(grid, hd, hd2){
33905     this.grid = grid;
33906     this.view = grid.getView();
33907     this.proxy = this.view.resizeProxy;
33908     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
33909         "gridSplitters" + this.grid.getGridEl().id, {
33910         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
33911     });
33912     this.setHandleElId(Roo.id(hd));
33913     this.setOuterHandleElId(Roo.id(hd2));
33914     this.scroll = false;
33915 };
33916 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
33917     fly: Roo.Element.fly,
33918
33919     b4StartDrag : function(x, y){
33920         this.view.headersDisabled = true;
33921         this.proxy.setHeight(this.view.mainWrap.getHeight());
33922         var w = this.cm.getColumnWidth(this.cellIndex);
33923         var minw = Math.max(w-this.grid.minColumnWidth, 0);
33924         this.resetConstraints();
33925         this.setXConstraint(minw, 1000);
33926         this.setYConstraint(0, 0);
33927         this.minX = x - minw;
33928         this.maxX = x + 1000;
33929         this.startPos = x;
33930         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
33931     },
33932
33933
33934     handleMouseDown : function(e){
33935         ev = Roo.EventObject.setEvent(e);
33936         var t = this.fly(ev.getTarget());
33937         if(t.hasClass("x-grid-split")){
33938             this.cellIndex = this.view.getCellIndex(t.dom);
33939             this.split = t.dom;
33940             this.cm = this.grid.colModel;
33941             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
33942                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
33943             }
33944         }
33945     },
33946
33947     endDrag : function(e){
33948         this.view.headersDisabled = false;
33949         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
33950         var diff = endX - this.startPos;
33951         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
33952     },
33953
33954     autoOffset : function(){
33955         this.setDelta(0,0);
33956     }
33957 });/*
33958  * Based on:
33959  * Ext JS Library 1.1.1
33960  * Copyright(c) 2006-2007, Ext JS, LLC.
33961  *
33962  * Originally Released Under LGPL - original licence link has changed is not relivant.
33963  *
33964  * Fork - LGPL
33965  * <script type="text/javascript">
33966  */
33967  
33968 // private
33969 // This is a support class used internally by the Grid components
33970 Roo.grid.GridDragZone = function(grid, config){
33971     this.view = grid.getView();
33972     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
33973     if(this.view.lockedBody){
33974         this.setHandleElId(Roo.id(this.view.mainBody.dom));
33975         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
33976     }
33977     this.scroll = false;
33978     this.grid = grid;
33979     this.ddel = document.createElement('div');
33980     this.ddel.className = 'x-grid-dd-wrap';
33981 };
33982
33983 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
33984     ddGroup : "GridDD",
33985
33986     getDragData : function(e){
33987         var t = Roo.lib.Event.getTarget(e);
33988         var rowIndex = this.view.findRowIndex(t);
33989         if(rowIndex !== false){
33990             var sm = this.grid.selModel;
33991             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
33992               //  sm.mouseDown(e, t);
33993             //}
33994             if (e.hasModifier()){
33995                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
33996             }
33997             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
33998         }
33999         return false;
34000     },
34001
34002     onInitDrag : function(e){
34003         var data = this.dragData;
34004         this.ddel.innerHTML = this.grid.getDragDropText();
34005         this.proxy.update(this.ddel);
34006         // fire start drag?
34007     },
34008
34009     afterRepair : function(){
34010         this.dragging = false;
34011     },
34012
34013     getRepairXY : function(e, data){
34014         return false;
34015     },
34016
34017     onEndDrag : function(data, e){
34018         // fire end drag?
34019     },
34020
34021     onValidDrop : function(dd, e, id){
34022         // fire drag drop?
34023         this.hideProxy();
34024     },
34025
34026     beforeInvalidDrop : function(e, id){
34027
34028     }
34029 });/*
34030  * Based on:
34031  * Ext JS Library 1.1.1
34032  * Copyright(c) 2006-2007, Ext JS, LLC.
34033  *
34034  * Originally Released Under LGPL - original licence link has changed is not relivant.
34035  *
34036  * Fork - LGPL
34037  * <script type="text/javascript">
34038  */
34039  
34040
34041 /**
34042  * @class Roo.grid.ColumnModel
34043  * @extends Roo.util.Observable
34044  * This is the default implementation of a ColumnModel used by the Grid. It defines
34045  * the columns in the grid.
34046  * <br>Usage:<br>
34047  <pre><code>
34048  var colModel = new Roo.grid.ColumnModel([
34049         {header: "Ticker", width: 60, sortable: true, locked: true},
34050         {header: "Company Name", width: 150, sortable: true},
34051         {header: "Market Cap.", width: 100, sortable: true},
34052         {header: "$ Sales", width: 100, sortable: true, renderer: money},
34053         {header: "Employees", width: 100, sortable: true, resizable: false}
34054  ]);
34055  </code></pre>
34056  * <p>
34057  
34058  * The config options listed for this class are options which may appear in each
34059  * individual column definition.
34060  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
34061  * @constructor
34062  * @param {Object} config An Array of column config objects. See this class's
34063  * config objects for details.
34064 */
34065 Roo.grid.ColumnModel = function(config){
34066         /**
34067      * The config passed into the constructor
34068      */
34069     this.config = config;
34070     this.lookup = {};
34071
34072     // if no id, create one
34073     // if the column does not have a dataIndex mapping,
34074     // map it to the order it is in the config
34075     for(var i = 0, len = config.length; i < len; i++){
34076         var c = config[i];
34077         if(typeof c.dataIndex == "undefined"){
34078             c.dataIndex = i;
34079         }
34080         if(typeof c.renderer == "string"){
34081             c.renderer = Roo.util.Format[c.renderer];
34082         }
34083         if(typeof c.id == "undefined"){
34084             c.id = Roo.id();
34085         }
34086         if(c.editor && c.editor.xtype){
34087             c.editor  = Roo.factory(c.editor, Roo.grid);
34088         }
34089         if(c.editor && c.editor.isFormField){
34090             c.editor = new Roo.grid.GridEditor(c.editor);
34091         }
34092         this.lookup[c.id] = c;
34093     }
34094
34095     /**
34096      * The width of columns which have no width specified (defaults to 100)
34097      * @type Number
34098      */
34099     this.defaultWidth = 100;
34100
34101     /**
34102      * Default sortable of columns which have no sortable specified (defaults to false)
34103      * @type Boolean
34104      */
34105     this.defaultSortable = false;
34106
34107     this.addEvents({
34108         /**
34109              * @event widthchange
34110              * Fires when the width of a column changes.
34111              * @param {ColumnModel} this
34112              * @param {Number} columnIndex The column index
34113              * @param {Number} newWidth The new width
34114              */
34115             "widthchange": true,
34116         /**
34117              * @event headerchange
34118              * Fires when the text of a header changes.
34119              * @param {ColumnModel} this
34120              * @param {Number} columnIndex The column index
34121              * @param {Number} newText The new header text
34122              */
34123             "headerchange": true,
34124         /**
34125              * @event hiddenchange
34126              * Fires when a column is hidden or "unhidden".
34127              * @param {ColumnModel} this
34128              * @param {Number} columnIndex The column index
34129              * @param {Boolean} hidden true if hidden, false otherwise
34130              */
34131             "hiddenchange": true,
34132             /**
34133          * @event columnmoved
34134          * Fires when a column is moved.
34135          * @param {ColumnModel} this
34136          * @param {Number} oldIndex
34137          * @param {Number} newIndex
34138          */
34139         "columnmoved" : true,
34140         /**
34141          * @event columlockchange
34142          * Fires when a column's locked state is changed
34143          * @param {ColumnModel} this
34144          * @param {Number} colIndex
34145          * @param {Boolean} locked true if locked
34146          */
34147         "columnlockchange" : true
34148     });
34149     Roo.grid.ColumnModel.superclass.constructor.call(this);
34150 };
34151 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
34152     /**
34153      * @cfg {String} header The header text to display in the Grid view.
34154      */
34155     /**
34156      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
34157      * {@link Roo.data.Record} definition from which to draw the column's value. If not
34158      * specified, the column's index is used as an index into the Record's data Array.
34159      */
34160     /**
34161      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
34162      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
34163      */
34164     /**
34165      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
34166      * Defaults to the value of the {@link #defaultSortable} property.
34167      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
34168      */
34169     /**
34170      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
34171      */
34172     /**
34173      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
34174      */
34175     /**
34176      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
34177      */
34178     /**
34179      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
34180      */
34181     /**
34182      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
34183      * given the cell's data value. See {@link #setRenderer}. If not specified, the
34184      * default renderer uses the raw data value.
34185      */
34186        /**
34187      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
34188      */
34189     /**
34190      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
34191      */
34192
34193     /**
34194      * Returns the id of the column at the specified index.
34195      * @param {Number} index The column index
34196      * @return {String} the id
34197      */
34198     getColumnId : function(index){
34199         return this.config[index].id;
34200     },
34201
34202     /**
34203      * Returns the column for a specified id.
34204      * @param {String} id The column id
34205      * @return {Object} the column
34206      */
34207     getColumnById : function(id){
34208         return this.lookup[id];
34209     },
34210
34211     
34212     /**
34213      * Returns the column for a specified dataIndex.
34214      * @param {String} dataIndex The column dataIndex
34215      * @return {Object|Boolean} the column or false if not found
34216      */
34217     getColumnByDataIndex: function(dataIndex){
34218         var index = this.findColumnIndex(dataIndex);
34219         return index > -1 ? this.config[index] : false;
34220     },
34221     
34222     /**
34223      * Returns the index for a specified column id.
34224      * @param {String} id The column id
34225      * @return {Number} the index, or -1 if not found
34226      */
34227     getIndexById : function(id){
34228         for(var i = 0, len = this.config.length; i < len; i++){
34229             if(this.config[i].id == id){
34230                 return i;
34231             }
34232         }
34233         return -1;
34234     },
34235     
34236     /**
34237      * Returns the index for a specified column dataIndex.
34238      * @param {String} dataIndex The column dataIndex
34239      * @return {Number} the index, or -1 if not found
34240      */
34241     
34242     findColumnIndex : function(dataIndex){
34243         for(var i = 0, len = this.config.length; i < len; i++){
34244             if(this.config[i].dataIndex == dataIndex){
34245                 return i;
34246             }
34247         }
34248         return -1;
34249     },
34250     
34251     
34252     moveColumn : function(oldIndex, newIndex){
34253         var c = this.config[oldIndex];
34254         this.config.splice(oldIndex, 1);
34255         this.config.splice(newIndex, 0, c);
34256         this.dataMap = null;
34257         this.fireEvent("columnmoved", this, oldIndex, newIndex);
34258     },
34259
34260     isLocked : function(colIndex){
34261         return this.config[colIndex].locked === true;
34262     },
34263
34264     setLocked : function(colIndex, value, suppressEvent){
34265         if(this.isLocked(colIndex) == value){
34266             return;
34267         }
34268         this.config[colIndex].locked = value;
34269         if(!suppressEvent){
34270             this.fireEvent("columnlockchange", this, colIndex, value);
34271         }
34272     },
34273
34274     getTotalLockedWidth : function(){
34275         var totalWidth = 0;
34276         for(var i = 0; i < this.config.length; i++){
34277             if(this.isLocked(i) && !this.isHidden(i)){
34278                 this.totalWidth += this.getColumnWidth(i);
34279             }
34280         }
34281         return totalWidth;
34282     },
34283
34284     getLockedCount : function(){
34285         for(var i = 0, len = this.config.length; i < len; i++){
34286             if(!this.isLocked(i)){
34287                 return i;
34288             }
34289         }
34290     },
34291
34292     /**
34293      * Returns the number of columns.
34294      * @return {Number}
34295      */
34296     getColumnCount : function(visibleOnly){
34297         if(visibleOnly === true){
34298             var c = 0;
34299             for(var i = 0, len = this.config.length; i < len; i++){
34300                 if(!this.isHidden(i)){
34301                     c++;
34302                 }
34303             }
34304             return c;
34305         }
34306         return this.config.length;
34307     },
34308
34309     /**
34310      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
34311      * @param {Function} fn
34312      * @param {Object} scope (optional)
34313      * @return {Array} result
34314      */
34315     getColumnsBy : function(fn, scope){
34316         var r = [];
34317         for(var i = 0, len = this.config.length; i < len; i++){
34318             var c = this.config[i];
34319             if(fn.call(scope||this, c, i) === true){
34320                 r[r.length] = c;
34321             }
34322         }
34323         return r;
34324     },
34325
34326     /**
34327      * Returns true if the specified column is sortable.
34328      * @param {Number} col The column index
34329      * @return {Boolean}
34330      */
34331     isSortable : function(col){
34332         if(typeof this.config[col].sortable == "undefined"){
34333             return this.defaultSortable;
34334         }
34335         return this.config[col].sortable;
34336     },
34337
34338     /**
34339      * Returns the rendering (formatting) function defined for the column.
34340      * @param {Number} col The column index.
34341      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
34342      */
34343     getRenderer : function(col){
34344         if(!this.config[col].renderer){
34345             return Roo.grid.ColumnModel.defaultRenderer;
34346         }
34347         return this.config[col].renderer;
34348     },
34349
34350     /**
34351      * Sets the rendering (formatting) function for a column.
34352      * @param {Number} col The column index
34353      * @param {Function} fn The function to use to process the cell's raw data
34354      * to return HTML markup for the grid view. The render function is called with
34355      * the following parameters:<ul>
34356      * <li>Data value.</li>
34357      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
34358      * <li>css A CSS style string to apply to the table cell.</li>
34359      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
34360      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
34361      * <li>Row index</li>
34362      * <li>Column index</li>
34363      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
34364      */
34365     setRenderer : function(col, fn){
34366         this.config[col].renderer = fn;
34367     },
34368
34369     /**
34370      * Returns the width for the specified column.
34371      * @param {Number} col The column index
34372      * @return {Number}
34373      */
34374     getColumnWidth : function(col){
34375         return this.config[col].width || this.defaultWidth;
34376     },
34377
34378     /**
34379      * Sets the width for a column.
34380      * @param {Number} col The column index
34381      * @param {Number} width The new width
34382      */
34383     setColumnWidth : function(col, width, suppressEvent){
34384         this.config[col].width = width;
34385         this.totalWidth = null;
34386         if(!suppressEvent){
34387              this.fireEvent("widthchange", this, col, width);
34388         }
34389     },
34390
34391     /**
34392      * Returns the total width of all columns.
34393      * @param {Boolean} includeHidden True to include hidden column widths
34394      * @return {Number}
34395      */
34396     getTotalWidth : function(includeHidden){
34397         if(!this.totalWidth){
34398             this.totalWidth = 0;
34399             for(var i = 0, len = this.config.length; i < len; i++){
34400                 if(includeHidden || !this.isHidden(i)){
34401                     this.totalWidth += this.getColumnWidth(i);
34402                 }
34403             }
34404         }
34405         return this.totalWidth;
34406     },
34407
34408     /**
34409      * Returns the header for the specified column.
34410      * @param {Number} col The column index
34411      * @return {String}
34412      */
34413     getColumnHeader : function(col){
34414         return this.config[col].header;
34415     },
34416
34417     /**
34418      * Sets the header for a column.
34419      * @param {Number} col The column index
34420      * @param {String} header The new header
34421      */
34422     setColumnHeader : function(col, header){
34423         this.config[col].header = header;
34424         this.fireEvent("headerchange", this, col, header);
34425     },
34426
34427     /**
34428      * Returns the tooltip for the specified column.
34429      * @param {Number} col The column index
34430      * @return {String}
34431      */
34432     getColumnTooltip : function(col){
34433             return this.config[col].tooltip;
34434     },
34435     /**
34436      * Sets the tooltip for a column.
34437      * @param {Number} col The column index
34438      * @param {String} tooltip The new tooltip
34439      */
34440     setColumnTooltip : function(col, tooltip){
34441             this.config[col].tooltip = tooltip;
34442     },
34443
34444     /**
34445      * Returns the dataIndex for the specified column.
34446      * @param {Number} col The column index
34447      * @return {Number}
34448      */
34449     getDataIndex : function(col){
34450         return this.config[col].dataIndex;
34451     },
34452
34453     /**
34454      * Sets the dataIndex for a column.
34455      * @param {Number} col The column index
34456      * @param {Number} dataIndex The new dataIndex
34457      */
34458     setDataIndex : function(col, dataIndex){
34459         this.config[col].dataIndex = dataIndex;
34460     },
34461
34462     
34463     
34464     /**
34465      * Returns true if the cell is editable.
34466      * @param {Number} colIndex The column index
34467      * @param {Number} rowIndex The row index
34468      * @return {Boolean}
34469      */
34470     isCellEditable : function(colIndex, rowIndex){
34471         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
34472     },
34473
34474     /**
34475      * Returns the editor defined for the cell/column.
34476      * return false or null to disable editing.
34477      * @param {Number} colIndex The column index
34478      * @param {Number} rowIndex The row index
34479      * @return {Object}
34480      */
34481     getCellEditor : function(colIndex, rowIndex){
34482         return this.config[colIndex].editor;
34483     },
34484
34485     /**
34486      * Sets if a column is editable.
34487      * @param {Number} col The column index
34488      * @param {Boolean} editable True if the column is editable
34489      */
34490     setEditable : function(col, editable){
34491         this.config[col].editable = editable;
34492     },
34493
34494
34495     /**
34496      * Returns true if the column is hidden.
34497      * @param {Number} colIndex The column index
34498      * @return {Boolean}
34499      */
34500     isHidden : function(colIndex){
34501         return this.config[colIndex].hidden;
34502     },
34503
34504
34505     /**
34506      * Returns true if the column width cannot be changed
34507      */
34508     isFixed : function(colIndex){
34509         return this.config[colIndex].fixed;
34510     },
34511
34512     /**
34513      * Returns true if the column can be resized
34514      * @return {Boolean}
34515      */
34516     isResizable : function(colIndex){
34517         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
34518     },
34519     /**
34520      * Sets if a column is hidden.
34521      * @param {Number} colIndex The column index
34522      * @param {Boolean} hidden True if the column is hidden
34523      */
34524     setHidden : function(colIndex, hidden){
34525         this.config[colIndex].hidden = hidden;
34526         this.totalWidth = null;
34527         this.fireEvent("hiddenchange", this, colIndex, hidden);
34528     },
34529
34530     /**
34531      * Sets the editor for a column.
34532      * @param {Number} col The column index
34533      * @param {Object} editor The editor object
34534      */
34535     setEditor : function(col, editor){
34536         this.config[col].editor = editor;
34537     }
34538 });
34539
34540 Roo.grid.ColumnModel.defaultRenderer = function(value){
34541         if(typeof value == "string" && value.length < 1){
34542             return "&#160;";
34543         }
34544         return value;
34545 };
34546
34547 // Alias for backwards compatibility
34548 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
34549 /*
34550  * Based on:
34551  * Ext JS Library 1.1.1
34552  * Copyright(c) 2006-2007, Ext JS, LLC.
34553  *
34554  * Originally Released Under LGPL - original licence link has changed is not relivant.
34555  *
34556  * Fork - LGPL
34557  * <script type="text/javascript">
34558  */
34559
34560 /**
34561  * @class Roo.grid.AbstractSelectionModel
34562  * @extends Roo.util.Observable
34563  * Abstract base class for grid SelectionModels.  It provides the interface that should be
34564  * implemented by descendant classes.  This class should not be directly instantiated.
34565  * @constructor
34566  */
34567 Roo.grid.AbstractSelectionModel = function(){
34568     this.locked = false;
34569     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
34570 };
34571
34572 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
34573     /** @ignore Called by the grid automatically. Do not call directly. */
34574     init : function(grid){
34575         this.grid = grid;
34576         this.initEvents();
34577     },
34578
34579     /**
34580      * Locks the selections.
34581      */
34582     lock : function(){
34583         this.locked = true;
34584     },
34585
34586     /**
34587      * Unlocks the selections.
34588      */
34589     unlock : function(){
34590         this.locked = false;
34591     },
34592
34593     /**
34594      * Returns true if the selections are locked.
34595      * @return {Boolean}
34596      */
34597     isLocked : function(){
34598         return this.locked;
34599     }
34600 });/*
34601  * Based on:
34602  * Ext JS Library 1.1.1
34603  * Copyright(c) 2006-2007, Ext JS, LLC.
34604  *
34605  * Originally Released Under LGPL - original licence link has changed is not relivant.
34606  *
34607  * Fork - LGPL
34608  * <script type="text/javascript">
34609  */
34610 /**
34611  * @extends Roo.grid.AbstractSelectionModel
34612  * @class Roo.grid.RowSelectionModel
34613  * The default SelectionModel used by {@link Roo.grid.Grid}.
34614  * It supports multiple selections and keyboard selection/navigation. 
34615  * @constructor
34616  * @param {Object} config
34617  */
34618 Roo.grid.RowSelectionModel = function(config){
34619     Roo.apply(this, config);
34620     this.selections = new Roo.util.MixedCollection(false, function(o){
34621         return o.id;
34622     });
34623
34624     this.last = false;
34625     this.lastActive = false;
34626
34627     this.addEvents({
34628         /**
34629              * @event selectionchange
34630              * Fires when the selection changes
34631              * @param {SelectionModel} this
34632              */
34633             "selectionchange" : true,
34634         /**
34635              * @event afterselectionchange
34636              * Fires after the selection changes (eg. by key press or clicking)
34637              * @param {SelectionModel} this
34638              */
34639             "afterselectionchange" : true,
34640         /**
34641              * @event beforerowselect
34642              * Fires when a row is selected being selected, return false to cancel.
34643              * @param {SelectionModel} this
34644              * @param {Number} rowIndex The selected index
34645              * @param {Boolean} keepExisting False if other selections will be cleared
34646              */
34647             "beforerowselect" : true,
34648         /**
34649              * @event rowselect
34650              * Fires when a row is selected.
34651              * @param {SelectionModel} this
34652              * @param {Number} rowIndex The selected index
34653              * @param {Roo.data.Record} r The record
34654              */
34655             "rowselect" : true,
34656         /**
34657              * @event rowdeselect
34658              * Fires when a row is deselected.
34659              * @param {SelectionModel} this
34660              * @param {Number} rowIndex The selected index
34661              */
34662         "rowdeselect" : true
34663     });
34664     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
34665     this.locked = false;
34666 };
34667
34668 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
34669     /**
34670      * @cfg {Boolean} singleSelect
34671      * True to allow selection of only one row at a time (defaults to false)
34672      */
34673     singleSelect : false,
34674
34675     // private
34676     initEvents : function(){
34677
34678         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
34679             this.grid.on("mousedown", this.handleMouseDown, this);
34680         }else{ // allow click to work like normal
34681             this.grid.on("rowclick", this.handleDragableRowClick, this);
34682         }
34683
34684         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
34685             "up" : function(e){
34686                 if(!e.shiftKey){
34687                     this.selectPrevious(e.shiftKey);
34688                 }else if(this.last !== false && this.lastActive !== false){
34689                     var last = this.last;
34690                     this.selectRange(this.last,  this.lastActive-1);
34691                     this.grid.getView().focusRow(this.lastActive);
34692                     if(last !== false){
34693                         this.last = last;
34694                     }
34695                 }else{
34696                     this.selectFirstRow();
34697                 }
34698                 this.fireEvent("afterselectionchange", this);
34699             },
34700             "down" : function(e){
34701                 if(!e.shiftKey){
34702                     this.selectNext(e.shiftKey);
34703                 }else if(this.last !== false && this.lastActive !== false){
34704                     var last = this.last;
34705                     this.selectRange(this.last,  this.lastActive+1);
34706                     this.grid.getView().focusRow(this.lastActive);
34707                     if(last !== false){
34708                         this.last = last;
34709                     }
34710                 }else{
34711                     this.selectFirstRow();
34712                 }
34713                 this.fireEvent("afterselectionchange", this);
34714             },
34715             scope: this
34716         });
34717
34718         var view = this.grid.view;
34719         view.on("refresh", this.onRefresh, this);
34720         view.on("rowupdated", this.onRowUpdated, this);
34721         view.on("rowremoved", this.onRemove, this);
34722     },
34723
34724     // private
34725     onRefresh : function(){
34726         var ds = this.grid.dataSource, i, v = this.grid.view;
34727         var s = this.selections;
34728         s.each(function(r){
34729             if((i = ds.indexOfId(r.id)) != -1){
34730                 v.onRowSelect(i);
34731             }else{
34732                 s.remove(r);
34733             }
34734         });
34735     },
34736
34737     // private
34738     onRemove : function(v, index, r){
34739         this.selections.remove(r);
34740     },
34741
34742     // private
34743     onRowUpdated : function(v, index, r){
34744         if(this.isSelected(r)){
34745             v.onRowSelect(index);
34746         }
34747     },
34748
34749     /**
34750      * Select records.
34751      * @param {Array} records The records to select
34752      * @param {Boolean} keepExisting (optional) True to keep existing selections
34753      */
34754     selectRecords : function(records, keepExisting){
34755         if(!keepExisting){
34756             this.clearSelections();
34757         }
34758         var ds = this.grid.dataSource;
34759         for(var i = 0, len = records.length; i < len; i++){
34760             this.selectRow(ds.indexOf(records[i]), true);
34761         }
34762     },
34763
34764     /**
34765      * Gets the number of selected rows.
34766      * @return {Number}
34767      */
34768     getCount : function(){
34769         return this.selections.length;
34770     },
34771
34772     /**
34773      * Selects the first row in the grid.
34774      */
34775     selectFirstRow : function(){
34776         this.selectRow(0);
34777     },
34778
34779     /**
34780      * Select the last row.
34781      * @param {Boolean} keepExisting (optional) True to keep existing selections
34782      */
34783     selectLastRow : function(keepExisting){
34784         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
34785     },
34786
34787     /**
34788      * Selects the row immediately following the last selected row.
34789      * @param {Boolean} keepExisting (optional) True to keep existing selections
34790      */
34791     selectNext : function(keepExisting){
34792         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
34793             this.selectRow(this.last+1, keepExisting);
34794             this.grid.getView().focusRow(this.last);
34795         }
34796     },
34797
34798     /**
34799      * Selects the row that precedes the last selected row.
34800      * @param {Boolean} keepExisting (optional) True to keep existing selections
34801      */
34802     selectPrevious : function(keepExisting){
34803         if(this.last){
34804             this.selectRow(this.last-1, keepExisting);
34805             this.grid.getView().focusRow(this.last);
34806         }
34807     },
34808
34809     /**
34810      * Returns the selected records
34811      * @return {Array} Array of selected records
34812      */
34813     getSelections : function(){
34814         return [].concat(this.selections.items);
34815     },
34816
34817     /**
34818      * Returns the first selected record.
34819      * @return {Record}
34820      */
34821     getSelected : function(){
34822         return this.selections.itemAt(0);
34823     },
34824
34825
34826     /**
34827      * Clears all selections.
34828      */
34829     clearSelections : function(fast){
34830         if(this.locked) return;
34831         if(fast !== true){
34832             var ds = this.grid.dataSource;
34833             var s = this.selections;
34834             s.each(function(r){
34835                 this.deselectRow(ds.indexOfId(r.id));
34836             }, this);
34837             s.clear();
34838         }else{
34839             this.selections.clear();
34840         }
34841         this.last = false;
34842     },
34843
34844
34845     /**
34846      * Selects all rows.
34847      */
34848     selectAll : function(){
34849         if(this.locked) return;
34850         this.selections.clear();
34851         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
34852             this.selectRow(i, true);
34853         }
34854     },
34855
34856     /**
34857      * Returns True if there is a selection.
34858      * @return {Boolean}
34859      */
34860     hasSelection : function(){
34861         return this.selections.length > 0;
34862     },
34863
34864     /**
34865      * Returns True if the specified row is selected.
34866      * @param {Number/Record} record The record or index of the record to check
34867      * @return {Boolean}
34868      */
34869     isSelected : function(index){
34870         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
34871         return (r && this.selections.key(r.id) ? true : false);
34872     },
34873
34874     /**
34875      * Returns True if the specified record id is selected.
34876      * @param {String} id The id of record to check
34877      * @return {Boolean}
34878      */
34879     isIdSelected : function(id){
34880         return (this.selections.key(id) ? true : false);
34881     },
34882
34883     // private
34884     handleMouseDown : function(e, t){
34885         var view = this.grid.getView(), rowIndex;
34886         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
34887             return;
34888         };
34889         if(e.shiftKey && this.last !== false){
34890             var last = this.last;
34891             this.selectRange(last, rowIndex, e.ctrlKey);
34892             this.last = last; // reset the last
34893             view.focusRow(rowIndex);
34894         }else{
34895             var isSelected = this.isSelected(rowIndex);
34896             if(e.button !== 0 && isSelected){
34897                 view.focusRow(rowIndex);
34898             }else if(e.ctrlKey && isSelected){
34899                 this.deselectRow(rowIndex);
34900             }else if(!isSelected){
34901                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
34902                 view.focusRow(rowIndex);
34903             }
34904         }
34905         this.fireEvent("afterselectionchange", this);
34906     },
34907     // private
34908     handleDragableRowClick :  function(grid, rowIndex, e) 
34909     {
34910         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
34911             this.selectRow(rowIndex, false);
34912             grid.view.focusRow(rowIndex);
34913              this.fireEvent("afterselectionchange", this);
34914         }
34915     },
34916     
34917     /**
34918      * Selects multiple rows.
34919      * @param {Array} rows Array of the indexes of the row to select
34920      * @param {Boolean} keepExisting (optional) True to keep existing selections
34921      */
34922     selectRows : function(rows, keepExisting){
34923         if(!keepExisting){
34924             this.clearSelections();
34925         }
34926         for(var i = 0, len = rows.length; i < len; i++){
34927             this.selectRow(rows[i], true);
34928         }
34929     },
34930
34931     /**
34932      * Selects a range of rows. All rows in between startRow and endRow are also selected.
34933      * @param {Number} startRow The index of the first row in the range
34934      * @param {Number} endRow The index of the last row in the range
34935      * @param {Boolean} keepExisting (optional) True to retain existing selections
34936      */
34937     selectRange : function(startRow, endRow, keepExisting){
34938         if(this.locked) return;
34939         if(!keepExisting){
34940             this.clearSelections();
34941         }
34942         if(startRow <= endRow){
34943             for(var i = startRow; i <= endRow; i++){
34944                 this.selectRow(i, true);
34945             }
34946         }else{
34947             for(var i = startRow; i >= endRow; i--){
34948                 this.selectRow(i, true);
34949             }
34950         }
34951     },
34952
34953     /**
34954      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
34955      * @param {Number} startRow The index of the first row in the range
34956      * @param {Number} endRow The index of the last row in the range
34957      */
34958     deselectRange : function(startRow, endRow, preventViewNotify){
34959         if(this.locked) return;
34960         for(var i = startRow; i <= endRow; i++){
34961             this.deselectRow(i, preventViewNotify);
34962         }
34963     },
34964
34965     /**
34966      * Selects a row.
34967      * @param {Number} row The index of the row to select
34968      * @param {Boolean} keepExisting (optional) True to keep existing selections
34969      */
34970     selectRow : function(index, keepExisting, preventViewNotify){
34971         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
34972         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
34973             if(!keepExisting || this.singleSelect){
34974                 this.clearSelections();
34975             }
34976             var r = this.grid.dataSource.getAt(index);
34977             this.selections.add(r);
34978             this.last = this.lastActive = index;
34979             if(!preventViewNotify){
34980                 this.grid.getView().onRowSelect(index);
34981             }
34982             this.fireEvent("rowselect", this, index, r);
34983             this.fireEvent("selectionchange", this);
34984         }
34985     },
34986
34987     /**
34988      * Deselects a row.
34989      * @param {Number} row The index of the row to deselect
34990      */
34991     deselectRow : function(index, preventViewNotify){
34992         if(this.locked) return;
34993         if(this.last == index){
34994             this.last = false;
34995         }
34996         if(this.lastActive == index){
34997             this.lastActive = false;
34998         }
34999         var r = this.grid.dataSource.getAt(index);
35000         this.selections.remove(r);
35001         if(!preventViewNotify){
35002             this.grid.getView().onRowDeselect(index);
35003         }
35004         this.fireEvent("rowdeselect", this, index);
35005         this.fireEvent("selectionchange", this);
35006     },
35007
35008     // private
35009     restoreLast : function(){
35010         if(this._last){
35011             this.last = this._last;
35012         }
35013     },
35014
35015     // private
35016     acceptsNav : function(row, col, cm){
35017         return !cm.isHidden(col) && cm.isCellEditable(col, row);
35018     },
35019
35020     // private
35021     onEditorKey : function(field, e){
35022         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
35023         if(k == e.TAB){
35024             e.stopEvent();
35025             ed.completeEdit();
35026             if(e.shiftKey){
35027                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
35028             }else{
35029                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
35030             }
35031         }else if(k == e.ENTER && !e.ctrlKey){
35032             e.stopEvent();
35033             ed.completeEdit();
35034             if(e.shiftKey){
35035                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
35036             }else{
35037                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
35038             }
35039         }else if(k == e.ESC){
35040             ed.cancelEdit();
35041         }
35042         if(newCell){
35043             g.startEditing(newCell[0], newCell[1]);
35044         }
35045     }
35046 });/*
35047  * Based on:
35048  * Ext JS Library 1.1.1
35049  * Copyright(c) 2006-2007, Ext JS, LLC.
35050  *
35051  * Originally Released Under LGPL - original licence link has changed is not relivant.
35052  *
35053  * Fork - LGPL
35054  * <script type="text/javascript">
35055  */
35056 /**
35057  * @class Roo.grid.CellSelectionModel
35058  * @extends Roo.grid.AbstractSelectionModel
35059  * This class provides the basic implementation for cell selection in a grid.
35060  * @constructor
35061  * @param {Object} config The object containing the configuration of this model.
35062  */
35063 Roo.grid.CellSelectionModel = function(config){
35064     Roo.apply(this, config);
35065
35066     this.selection = null;
35067
35068     this.addEvents({
35069         /**
35070              * @event beforerowselect
35071              * Fires before a cell is selected.
35072              * @param {SelectionModel} this
35073              * @param {Number} rowIndex The selected row index
35074              * @param {Number} colIndex The selected cell index
35075              */
35076             "beforecellselect" : true,
35077         /**
35078              * @event cellselect
35079              * Fires when a cell is selected.
35080              * @param {SelectionModel} this
35081              * @param {Number} rowIndex The selected row index
35082              * @param {Number} colIndex The selected cell index
35083              */
35084             "cellselect" : true,
35085         /**
35086              * @event selectionchange
35087              * Fires when the active selection changes.
35088              * @param {SelectionModel} this
35089              * @param {Object} selection null for no selection or an object (o) with two properties
35090                 <ul>
35091                 <li>o.record: the record object for the row the selection is in</li>
35092                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
35093                 </ul>
35094              */
35095             "selectionchange" : true
35096     });
35097     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
35098 };
35099
35100 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
35101
35102     /** @ignore */
35103     initEvents : function(){
35104         this.grid.on("mousedown", this.handleMouseDown, this);
35105         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
35106         var view = this.grid.view;
35107         view.on("refresh", this.onViewChange, this);
35108         view.on("rowupdated", this.onRowUpdated, this);
35109         view.on("beforerowremoved", this.clearSelections, this);
35110         view.on("beforerowsinserted", this.clearSelections, this);
35111         if(this.grid.isEditor){
35112             this.grid.on("beforeedit", this.beforeEdit,  this);
35113         }
35114     },
35115
35116         //private
35117     beforeEdit : function(e){
35118         this.select(e.row, e.column, false, true, e.record);
35119     },
35120
35121         //private
35122     onRowUpdated : function(v, index, r){
35123         if(this.selection && this.selection.record == r){
35124             v.onCellSelect(index, this.selection.cell[1]);
35125         }
35126     },
35127
35128         //private
35129     onViewChange : function(){
35130         this.clearSelections(true);
35131     },
35132
35133         /**
35134          * Returns the currently selected cell,.
35135          * @return {Array} The selected cell (row, column) or null if none selected.
35136          */
35137     getSelectedCell : function(){
35138         return this.selection ? this.selection.cell : null;
35139     },
35140
35141     /**
35142      * Clears all selections.
35143      * @param {Boolean} true to prevent the gridview from being notified about the change.
35144      */
35145     clearSelections : function(preventNotify){
35146         var s = this.selection;
35147         if(s){
35148             if(preventNotify !== true){
35149                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
35150             }
35151             this.selection = null;
35152             this.fireEvent("selectionchange", this, null);
35153         }
35154     },
35155
35156     /**
35157      * Returns true if there is a selection.
35158      * @return {Boolean}
35159      */
35160     hasSelection : function(){
35161         return this.selection ? true : false;
35162     },
35163
35164     /** @ignore */
35165     handleMouseDown : function(e, t){
35166         var v = this.grid.getView();
35167         if(this.isLocked()){
35168             return;
35169         };
35170         var row = v.findRowIndex(t);
35171         var cell = v.findCellIndex(t);
35172         if(row !== false && cell !== false){
35173             this.select(row, cell);
35174         }
35175     },
35176
35177     /**
35178      * Selects a cell.
35179      * @param {Number} rowIndex
35180      * @param {Number} collIndex
35181      */
35182     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
35183         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
35184             this.clearSelections();
35185             r = r || this.grid.dataSource.getAt(rowIndex);
35186             this.selection = {
35187                 record : r,
35188                 cell : [rowIndex, colIndex]
35189             };
35190             if(!preventViewNotify){
35191                 var v = this.grid.getView();
35192                 v.onCellSelect(rowIndex, colIndex);
35193                 if(preventFocus !== true){
35194                     v.focusCell(rowIndex, colIndex);
35195                 }
35196             }
35197             this.fireEvent("cellselect", this, rowIndex, colIndex);
35198             this.fireEvent("selectionchange", this, this.selection);
35199         }
35200     },
35201
35202         //private
35203     isSelectable : function(rowIndex, colIndex, cm){
35204         return !cm.isHidden(colIndex);
35205     },
35206
35207     /** @ignore */
35208     handleKeyDown : function(e){
35209         Roo.log('Cell Sel Model handleKeyDown');
35210         if(!e.isNavKeyPress()){
35211             return;
35212         }
35213         var g = this.grid, s = this.selection;
35214         if(!s){
35215             e.stopEvent();
35216             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
35217             if(cell){
35218                 this.select(cell[0], cell[1]);
35219             }
35220             return;
35221         }
35222         var sm = this;
35223         var walk = function(row, col, step){
35224             return g.walkCells(row, col, step, sm.isSelectable,  sm);
35225         };
35226         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
35227         var newCell;
35228
35229         switch(k){
35230             case e.TAB:
35231                 // handled by onEditorKey
35232                 if (g.isEditor && g.editing) {
35233                     return;
35234                 }
35235                 if(e.shiftKey){
35236                      newCell = walk(r, c-1, -1);
35237                 }else{
35238                      newCell = walk(r, c+1, 1);
35239                 }
35240              break;
35241              case e.DOWN:
35242                  newCell = walk(r+1, c, 1);
35243              break;
35244              case e.UP:
35245                  newCell = walk(r-1, c, -1);
35246              break;
35247              case e.RIGHT:
35248                  newCell = walk(r, c+1, 1);
35249              break;
35250              case e.LEFT:
35251                  newCell = walk(r, c-1, -1);
35252              break;
35253              case e.ENTER:
35254                  if(g.isEditor && !g.editing){
35255                     g.startEditing(r, c);
35256                     e.stopEvent();
35257                     return;
35258                 }
35259              break;
35260         };
35261         if(newCell){
35262             this.select(newCell[0], newCell[1]);
35263             e.stopEvent();
35264         }
35265     },
35266
35267     acceptsNav : function(row, col, cm){
35268         return !cm.isHidden(col) && cm.isCellEditable(col, row);
35269     },
35270
35271     onEditorKey : function(field, e){
35272         
35273         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
35274         ///Roo.log('onEditorKey' + k);
35275         
35276         if(k == e.TAB){
35277             if(e.shiftKey){
35278                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
35279             }else{
35280                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
35281             }
35282             e.stopEvent();
35283         }else if(k == e.ENTER && !e.ctrlKey){
35284             ed.completeEdit();
35285             e.stopEvent();
35286             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
35287         }else if(k == e.ESC){
35288             ed.cancelEdit();
35289         }
35290         
35291         
35292         if(newCell){
35293             //Roo.log('next cell after edit');
35294             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
35295         }
35296     }
35297 });/*
35298  * Based on:
35299  * Ext JS Library 1.1.1
35300  * Copyright(c) 2006-2007, Ext JS, LLC.
35301  *
35302  * Originally Released Under LGPL - original licence link has changed is not relivant.
35303  *
35304  * Fork - LGPL
35305  * <script type="text/javascript">
35306  */
35307  
35308 /**
35309  * @class Roo.grid.EditorGrid
35310  * @extends Roo.grid.Grid
35311  * Class for creating and editable grid.
35312  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
35313  * The container MUST have some type of size defined for the grid to fill. The container will be 
35314  * automatically set to position relative if it isn't already.
35315  * @param {Object} dataSource The data model to bind to
35316  * @param {Object} colModel The column model with info about this grid's columns
35317  */
35318 Roo.grid.EditorGrid = function(container, config){
35319     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
35320     this.getGridEl().addClass("xedit-grid");
35321
35322     if(!this.selModel){
35323         this.selModel = new Roo.grid.CellSelectionModel();
35324     }
35325
35326     this.activeEditor = null;
35327
35328         this.addEvents({
35329             /**
35330              * @event beforeedit
35331              * Fires before cell editing is triggered. The edit event object has the following properties <br />
35332              * <ul style="padding:5px;padding-left:16px;">
35333              * <li>grid - This grid</li>
35334              * <li>record - The record being edited</li>
35335              * <li>field - The field name being edited</li>
35336              * <li>value - The value for the field being edited.</li>
35337              * <li>row - The grid row index</li>
35338              * <li>column - The grid column index</li>
35339              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
35340              * </ul>
35341              * @param {Object} e An edit event (see above for description)
35342              */
35343             "beforeedit" : true,
35344             /**
35345              * @event afteredit
35346              * Fires after a cell is edited. <br />
35347              * <ul style="padding:5px;padding-left:16px;">
35348              * <li>grid - This grid</li>
35349              * <li>record - The record being edited</li>
35350              * <li>field - The field name being edited</li>
35351              * <li>value - The value being set</li>
35352              * <li>originalValue - The original value for the field, before the edit.</li>
35353              * <li>row - The grid row index</li>
35354              * <li>column - The grid column index</li>
35355              * </ul>
35356              * @param {Object} e An edit event (see above for description)
35357              */
35358             "afteredit" : true,
35359             /**
35360              * @event validateedit
35361              * Fires after a cell is edited, but before the value is set in the record. 
35362          * You can use this to modify the value being set in the field, Return false
35363              * to cancel the change. The edit event object has the following properties <br />
35364              * <ul style="padding:5px;padding-left:16px;">
35365          * <li>editor - This editor</li>
35366              * <li>grid - This grid</li>
35367              * <li>record - The record being edited</li>
35368              * <li>field - The field name being edited</li>
35369              * <li>value - The value being set</li>
35370              * <li>originalValue - The original value for the field, before the edit.</li>
35371              * <li>row - The grid row index</li>
35372              * <li>column - The grid column index</li>
35373              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
35374              * </ul>
35375              * @param {Object} e An edit event (see above for description)
35376              */
35377             "validateedit" : true
35378         });
35379     this.on("bodyscroll", this.stopEditing,  this);
35380     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
35381 };
35382
35383 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
35384     /**
35385      * @cfg {Number} clicksToEdit
35386      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
35387      */
35388     clicksToEdit: 2,
35389
35390     // private
35391     isEditor : true,
35392     // private
35393     trackMouseOver: false, // causes very odd FF errors
35394
35395     onCellDblClick : function(g, row, col){
35396         this.startEditing(row, col);
35397     },
35398
35399     onEditComplete : function(ed, value, startValue){
35400         this.editing = false;
35401         this.activeEditor = null;
35402         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
35403         var r = ed.record;
35404         var field = this.colModel.getDataIndex(ed.col);
35405         var e = {
35406             grid: this,
35407             record: r,
35408             field: field,
35409             originalValue: startValue,
35410             value: value,
35411             row: ed.row,
35412             column: ed.col,
35413             cancel:false,
35414             editor: ed
35415         };
35416         if(String(value) !== String(startValue)){
35417             
35418             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
35419                 r.set(field, e.value);
35420                 delete e.cancel; //?? why!!!
35421                 this.fireEvent("afteredit", e);
35422             }
35423         } else {
35424             this.fireEvent("afteredit", e); // always fir it!
35425         }
35426         this.view.focusCell(ed.row, ed.col);
35427     },
35428
35429     /**
35430      * Starts editing the specified for the specified row/column
35431      * @param {Number} rowIndex
35432      * @param {Number} colIndex
35433      */
35434     startEditing : function(row, col){
35435         this.stopEditing();
35436         if(this.colModel.isCellEditable(col, row)){
35437             this.view.ensureVisible(row, col, true);
35438             var r = this.dataSource.getAt(row);
35439             var field = this.colModel.getDataIndex(col);
35440             var e = {
35441                 grid: this,
35442                 record: r,
35443                 field: field,
35444                 value: r.data[field],
35445                 row: row,
35446                 column: col,
35447                 cancel:false
35448             };
35449             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
35450                 this.editing = true;
35451                 var ed = this.colModel.getCellEditor(col, row);
35452                 
35453                 if (!ed) {
35454                     return;
35455                 }
35456                 if(!ed.rendered){
35457                     ed.render(ed.parentEl || document.body);
35458                 }
35459                 ed.field.reset();
35460                 (function(){ // complex but required for focus issues in safari, ie and opera
35461                     ed.row = row;
35462                     ed.col = col;
35463                     ed.record = r;
35464                     ed.on("complete", this.onEditComplete, this, {single: true});
35465                     ed.on("specialkey", this.selModel.onEditorKey, this.selModel);
35466                     this.activeEditor = ed;
35467                     var v = r.data[field];
35468                     ed.startEdit(this.view.getCell(row, col), v);
35469                 }).defer(50, this);
35470             }
35471         }
35472     },
35473         
35474     /**
35475      * Stops any active editing
35476      */
35477     stopEditing : function(){
35478         if(this.activeEditor){
35479             this.activeEditor.completeEdit();
35480         }
35481         this.activeEditor = null;
35482     }
35483 });/*
35484  * Based on:
35485  * Ext JS Library 1.1.1
35486  * Copyright(c) 2006-2007, Ext JS, LLC.
35487  *
35488  * Originally Released Under LGPL - original licence link has changed is not relivant.
35489  *
35490  * Fork - LGPL
35491  * <script type="text/javascript">
35492  */
35493
35494 // private - not really -- you end up using it !
35495 // This is a support class used internally by the Grid components
35496
35497 /**
35498  * @class Roo.grid.GridEditor
35499  * @extends Roo.Editor
35500  * Class for creating and editable grid elements.
35501  * @param {Object} config any settings (must include field)
35502  */
35503 Roo.grid.GridEditor = function(field, config){
35504     if (!config && field.field) {
35505         config = field;
35506         field = Roo.factory(config.field, Roo.form);
35507     }
35508     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
35509     field.monitorTab = false;
35510 };
35511
35512 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
35513     
35514     /**
35515      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
35516      */
35517     
35518     alignment: "tl-tl",
35519     autoSize: "width",
35520     hideEl : false,
35521     cls: "x-small-editor x-grid-editor",
35522     shim:false,
35523     shadow:"frame"
35524 });/*
35525  * Based on:
35526  * Ext JS Library 1.1.1
35527  * Copyright(c) 2006-2007, Ext JS, LLC.
35528  *
35529  * Originally Released Under LGPL - original licence link has changed is not relivant.
35530  *
35531  * Fork - LGPL
35532  * <script type="text/javascript">
35533  */
35534   
35535
35536   
35537 Roo.grid.PropertyRecord = Roo.data.Record.create([
35538     {name:'name',type:'string'},  'value'
35539 ]);
35540
35541
35542 Roo.grid.PropertyStore = function(grid, source){
35543     this.grid = grid;
35544     this.store = new Roo.data.Store({
35545         recordType : Roo.grid.PropertyRecord
35546     });
35547     this.store.on('update', this.onUpdate,  this);
35548     if(source){
35549         this.setSource(source);
35550     }
35551     Roo.grid.PropertyStore.superclass.constructor.call(this);
35552 };
35553
35554
35555
35556 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
35557     setSource : function(o){
35558         this.source = o;
35559         this.store.removeAll();
35560         var data = [];
35561         for(var k in o){
35562             if(this.isEditableValue(o[k])){
35563                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
35564             }
35565         }
35566         this.store.loadRecords({records: data}, {}, true);
35567     },
35568
35569     onUpdate : function(ds, record, type){
35570         if(type == Roo.data.Record.EDIT){
35571             var v = record.data['value'];
35572             var oldValue = record.modified['value'];
35573             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
35574                 this.source[record.id] = v;
35575                 record.commit();
35576                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
35577             }else{
35578                 record.reject();
35579             }
35580         }
35581     },
35582
35583     getProperty : function(row){
35584        return this.store.getAt(row);
35585     },
35586
35587     isEditableValue: function(val){
35588         if(val && val instanceof Date){
35589             return true;
35590         }else if(typeof val == 'object' || typeof val == 'function'){
35591             return false;
35592         }
35593         return true;
35594     },
35595
35596     setValue : function(prop, value){
35597         this.source[prop] = value;
35598         this.store.getById(prop).set('value', value);
35599     },
35600
35601     getSource : function(){
35602         return this.source;
35603     }
35604 });
35605
35606 Roo.grid.PropertyColumnModel = function(grid, store){
35607     this.grid = grid;
35608     var g = Roo.grid;
35609     g.PropertyColumnModel.superclass.constructor.call(this, [
35610         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
35611         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
35612     ]);
35613     this.store = store;
35614     this.bselect = Roo.DomHelper.append(document.body, {
35615         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
35616             {tag: 'option', value: 'true', html: 'true'},
35617             {tag: 'option', value: 'false', html: 'false'}
35618         ]
35619     });
35620     Roo.id(this.bselect);
35621     var f = Roo.form;
35622     this.editors = {
35623         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
35624         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
35625         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
35626         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
35627         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
35628     };
35629     this.renderCellDelegate = this.renderCell.createDelegate(this);
35630     this.renderPropDelegate = this.renderProp.createDelegate(this);
35631 };
35632
35633 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
35634     
35635     
35636     nameText : 'Name',
35637     valueText : 'Value',
35638     
35639     dateFormat : 'm/j/Y',
35640     
35641     
35642     renderDate : function(dateVal){
35643         return dateVal.dateFormat(this.dateFormat);
35644     },
35645
35646     renderBool : function(bVal){
35647         return bVal ? 'true' : 'false';
35648     },
35649
35650     isCellEditable : function(colIndex, rowIndex){
35651         return colIndex == 1;
35652     },
35653
35654     getRenderer : function(col){
35655         return col == 1 ?
35656             this.renderCellDelegate : this.renderPropDelegate;
35657     },
35658
35659     renderProp : function(v){
35660         return this.getPropertyName(v);
35661     },
35662
35663     renderCell : function(val){
35664         var rv = val;
35665         if(val instanceof Date){
35666             rv = this.renderDate(val);
35667         }else if(typeof val == 'boolean'){
35668             rv = this.renderBool(val);
35669         }
35670         return Roo.util.Format.htmlEncode(rv);
35671     },
35672
35673     getPropertyName : function(name){
35674         var pn = this.grid.propertyNames;
35675         return pn && pn[name] ? pn[name] : name;
35676     },
35677
35678     getCellEditor : function(colIndex, rowIndex){
35679         var p = this.store.getProperty(rowIndex);
35680         var n = p.data['name'], val = p.data['value'];
35681         
35682         if(typeof(this.grid.customEditors[n]) == 'string'){
35683             return this.editors[this.grid.customEditors[n]];
35684         }
35685         if(typeof(this.grid.customEditors[n]) != 'undefined'){
35686             return this.grid.customEditors[n];
35687         }
35688         if(val instanceof Date){
35689             return this.editors['date'];
35690         }else if(typeof val == 'number'){
35691             return this.editors['number'];
35692         }else if(typeof val == 'boolean'){
35693             return this.editors['boolean'];
35694         }else{
35695             return this.editors['string'];
35696         }
35697     }
35698 });
35699
35700 /**
35701  * @class Roo.grid.PropertyGrid
35702  * @extends Roo.grid.EditorGrid
35703  * This class represents the  interface of a component based property grid control.
35704  * <br><br>Usage:<pre><code>
35705  var grid = new Roo.grid.PropertyGrid("my-container-id", {
35706       
35707  });
35708  // set any options
35709  grid.render();
35710  * </code></pre>
35711   
35712  * @constructor
35713  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
35714  * The container MUST have some type of size defined for the grid to fill. The container will be
35715  * automatically set to position relative if it isn't already.
35716  * @param {Object} config A config object that sets properties on this grid.
35717  */
35718 Roo.grid.PropertyGrid = function(container, config){
35719     config = config || {};
35720     var store = new Roo.grid.PropertyStore(this);
35721     this.store = store;
35722     var cm = new Roo.grid.PropertyColumnModel(this, store);
35723     store.store.sort('name', 'ASC');
35724     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
35725         ds: store.store,
35726         cm: cm,
35727         enableColLock:false,
35728         enableColumnMove:false,
35729         stripeRows:false,
35730         trackMouseOver: false,
35731         clicksToEdit:1
35732     }, config));
35733     this.getGridEl().addClass('x-props-grid');
35734     this.lastEditRow = null;
35735     this.on('columnresize', this.onColumnResize, this);
35736     this.addEvents({
35737          /**
35738              * @event beforepropertychange
35739              * Fires before a property changes (return false to stop?)
35740              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
35741              * @param {String} id Record Id
35742              * @param {String} newval New Value
35743          * @param {String} oldval Old Value
35744              */
35745         "beforepropertychange": true,
35746         /**
35747              * @event propertychange
35748              * Fires after a property changes
35749              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
35750              * @param {String} id Record Id
35751              * @param {String} newval New Value
35752          * @param {String} oldval Old Value
35753              */
35754         "propertychange": true
35755     });
35756     this.customEditors = this.customEditors || {};
35757 };
35758 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
35759     
35760      /**
35761      * @cfg {Object} customEditors map of colnames=> custom editors.
35762      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
35763      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
35764      * false disables editing of the field.
35765          */
35766     
35767       /**
35768      * @cfg {Object} propertyNames map of property Names to their displayed value
35769          */
35770     
35771     render : function(){
35772         Roo.grid.PropertyGrid.superclass.render.call(this);
35773         this.autoSize.defer(100, this);
35774     },
35775
35776     autoSize : function(){
35777         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
35778         if(this.view){
35779             this.view.fitColumns();
35780         }
35781     },
35782
35783     onColumnResize : function(){
35784         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
35785         this.autoSize();
35786     },
35787     /**
35788      * Sets the data for the Grid
35789      * accepts a Key => Value object of all the elements avaiable.
35790      * @param {Object} data  to appear in grid.
35791      */
35792     setSource : function(source){
35793         this.store.setSource(source);
35794         //this.autoSize();
35795     },
35796     /**
35797      * Gets all the data from the grid.
35798      * @return {Object} data  data stored in grid
35799      */
35800     getSource : function(){
35801         return this.store.getSource();
35802     }
35803 });/*
35804  * Based on:
35805  * Ext JS Library 1.1.1
35806  * Copyright(c) 2006-2007, Ext JS, LLC.
35807  *
35808  * Originally Released Under LGPL - original licence link has changed is not relivant.
35809  *
35810  * Fork - LGPL
35811  * <script type="text/javascript">
35812  */
35813  
35814 /**
35815  * @class Roo.LoadMask
35816  * A simple utility class for generically masking elements while loading data.  If the element being masked has
35817  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
35818  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
35819  * element's UpdateManager load indicator and will be destroyed after the initial load.
35820  * @constructor
35821  * Create a new LoadMask
35822  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
35823  * @param {Object} config The config object
35824  */
35825 Roo.LoadMask = function(el, config){
35826     this.el = Roo.get(el);
35827     Roo.apply(this, config);
35828     if(this.store){
35829         this.store.on('beforeload', this.onBeforeLoad, this);
35830         this.store.on('load', this.onLoad, this);
35831         this.store.on('loadexception', this.onLoad, this);
35832         this.removeMask = false;
35833     }else{
35834         var um = this.el.getUpdateManager();
35835         um.showLoadIndicator = false; // disable the default indicator
35836         um.on('beforeupdate', this.onBeforeLoad, this);
35837         um.on('update', this.onLoad, this);
35838         um.on('failure', this.onLoad, this);
35839         this.removeMask = true;
35840     }
35841 };
35842
35843 Roo.LoadMask.prototype = {
35844     /**
35845      * @cfg {Boolean} removeMask
35846      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
35847      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
35848      */
35849     /**
35850      * @cfg {String} msg
35851      * The text to display in a centered loading message box (defaults to 'Loading...')
35852      */
35853     msg : 'Loading...',
35854     /**
35855      * @cfg {String} msgCls
35856      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
35857      */
35858     msgCls : 'x-mask-loading',
35859
35860     /**
35861      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
35862      * @type Boolean
35863      */
35864     disabled: false,
35865
35866     /**
35867      * Disables the mask to prevent it from being displayed
35868      */
35869     disable : function(){
35870        this.disabled = true;
35871     },
35872
35873     /**
35874      * Enables the mask so that it can be displayed
35875      */
35876     enable : function(){
35877         this.disabled = false;
35878     },
35879
35880     // private
35881     onLoad : function(){
35882         this.el.unmask(this.removeMask);
35883     },
35884
35885     // private
35886     onBeforeLoad : function(){
35887         if(!this.disabled){
35888             this.el.mask(this.msg, this.msgCls);
35889         }
35890     },
35891
35892     // private
35893     destroy : function(){
35894         if(this.store){
35895             this.store.un('beforeload', this.onBeforeLoad, this);
35896             this.store.un('load', this.onLoad, this);
35897             this.store.un('loadexception', this.onLoad, this);
35898         }else{
35899             var um = this.el.getUpdateManager();
35900             um.un('beforeupdate', this.onBeforeLoad, this);
35901             um.un('update', this.onLoad, this);
35902             um.un('failure', this.onLoad, this);
35903         }
35904     }
35905 };/*
35906  * Based on:
35907  * Ext JS Library 1.1.1
35908  * Copyright(c) 2006-2007, Ext JS, LLC.
35909  *
35910  * Originally Released Under LGPL - original licence link has changed is not relivant.
35911  *
35912  * Fork - LGPL
35913  * <script type="text/javascript">
35914  */
35915 Roo.XTemplate = function(){
35916     Roo.XTemplate.superclass.constructor.apply(this, arguments);
35917     var s = this.html;
35918
35919     s = ['<tpl>', s, '</tpl>'].join('');
35920
35921     var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/;
35922
35923     var nameRe = /^<tpl\b[^>]*?for="(.*?)"/;
35924     var ifRe = /^<tpl\b[^>]*?if="(.*?)"/;
35925     var execRe = /^<tpl\b[^>]*?exec="(.*?)"/;
35926     var m, id = 0;
35927     var tpls = [];
35928
35929     while(m = s.match(re)){
35930        var m2 = m[0].match(nameRe);
35931        var m3 = m[0].match(ifRe);
35932        var m4 = m[0].match(execRe);
35933        var exp = null, fn = null, exec = null;
35934        var name = m2 && m2[1] ? m2[1] : '';
35935        if(m3){
35936            exp = m3 && m3[1] ? m3[1] : null;
35937            if(exp){
35938                fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
35939            }
35940        }
35941        if(m4){
35942            exp = m4 && m4[1] ? m4[1] : null;
35943            if(exp){
35944                exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
35945            }
35946        }
35947        if(name){
35948            switch(name){
35949                case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
35950                case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
35951                default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
35952            }
35953        }
35954        tpls.push({
35955             id: id,
35956             target: name,
35957             exec: exec,
35958             test: fn,
35959             body: m[1]||''
35960         });
35961        s = s.replace(m[0], '{xtpl'+ id + '}');
35962        ++id;
35963     }
35964     for(var i = tpls.length-1; i >= 0; --i){
35965         this.compileTpl(tpls[i]);
35966     }
35967     this.master = tpls[tpls.length-1];
35968     this.tpls = tpls;
35969 };
35970 Roo.extend(Roo.XTemplate, Roo.Template, {
35971
35972     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
35973
35974     applySubTemplate : function(id, values, parent){
35975         var t = this.tpls[id];
35976         if(t.test && !t.test.call(this, values, parent)){
35977             return '';
35978         }
35979         if(t.exec && t.exec.call(this, values, parent)){
35980             return '';
35981         }
35982         var vs = t.target ? t.target.call(this, values, parent) : values;
35983         parent = t.target ? values : parent;
35984         if(t.target && vs instanceof Array){
35985             var buf = [];
35986             for(var i = 0, len = vs.length; i < len; i++){
35987                 buf[buf.length] = t.compiled.call(this, vs[i], parent);
35988             }
35989             return buf.join('');
35990         }
35991         return t.compiled.call(this, vs, parent);
35992     },
35993
35994     compileTpl : function(tpl){
35995         var fm = Roo.util.Format;
35996         var useF = this.disableFormats !== true;
35997         var sep = Roo.isGecko ? "+" : ",";
35998         var fn = function(m, name, format, args){
35999             if(name.substr(0, 4) == 'xtpl'){
36000                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
36001             }
36002             var v;
36003             if(name.indexOf('.') != -1){
36004                 v = name;
36005             }else{
36006                 v = "values['" + name + "']";
36007             }
36008             if(format && useF){
36009                 args = args ? ',' + args : "";
36010                 if(format.substr(0, 5) != "this."){
36011                     format = "fm." + format + '(';
36012                 }else{
36013                     format = 'this.call("'+ format.substr(5) + '", ';
36014                     args = ", values";
36015                 }
36016             }else{
36017                 args= ''; format = "("+v+" === undefined ? '' : ";
36018             }
36019             return "'"+ sep + format + v + args + ")"+sep+"'";
36020         };
36021         var body;
36022         // branched to use + in gecko and [].join() in others
36023         if(Roo.isGecko){
36024             body = "tpl.compiled = function(values, parent){ return '" +
36025                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
36026                     "';};";
36027         }else{
36028             body = ["tpl.compiled = function(values, parent){ return ['"];
36029             body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
36030             body.push("'].join('');};");
36031             body = body.join('');
36032         }
36033         /** eval:var:zzzzzzz */
36034         eval(body);
36035         return this;
36036     },
36037
36038     applyTemplate : function(values){
36039         return this.master.compiled.call(this, values, {});
36040         var s = this.subs;
36041     },
36042
36043     apply : function(){
36044         return this.applyTemplate.apply(this, arguments);
36045     },
36046
36047     compile : function(){return this;}
36048 });
36049
36050 Roo.XTemplate.from = function(el){
36051     el = Roo.getDom(el);
36052     return new Roo.XTemplate(el.value || el.innerHTML);
36053 };/*
36054  * Original code for Roojs - LGPL
36055  * <script type="text/javascript">
36056  */
36057  
36058 /**
36059  * @class Roo.XComponent
36060  * A delayed Element creator...
36061  * 
36062  * Mypart.xyx = new Roo.XComponent({
36063
36064     parent : 'Mypart.xyz', // empty == document.element.!!
36065     order : '001',
36066     name : 'xxxx'
36067     region : 'xxxx'
36068     disabled : function() {} 
36069      
36070     tree : function() { // return an tree of xtype declared components
36071         var MODULE = this;
36072         return 
36073         {
36074             xtype : 'NestedLayoutPanel',
36075             // technicall
36076         }
36077      ]
36078  *})
36079  * @extends Roo.util.Observable
36080  * @constructor
36081  * @param cfg {Object} configuration of component
36082  * 
36083  */
36084 Roo.XComponent = function(cfg) {
36085     Roo.apply(this, cfg);
36086     this.addEvents({ 
36087         /**
36088              * @event built
36089              * Fires when this the componnt is built
36090              * @param {Roo.XComponent} c the component
36091              */
36092         'built' : true,
36093         /**
36094              * @event buildcomplete
36095              * Fires on the top level element when all elements have been built
36096              * @param {Roo.XComponent} c the top level component.
36097          */
36098         'buildcomplete' : true
36099         
36100     });
36101     
36102     Roo.XComponent.register(this);
36103     this.modules = false;
36104     this.el = false; // where the layout goes..
36105     
36106     
36107 }
36108 Roo.extend(Roo.XComponent, Roo.util.Observable, {
36109     /**
36110      * @property el
36111      * The created element (with Roo.factory())
36112      * @type {Roo.Layout}
36113      */
36114     el  : false,
36115     
36116     /**
36117      * @property el
36118      * for BC  - use el in new code
36119      * @type {Roo.Layout}
36120      */
36121     panel : false,
36122     
36123     /**
36124      * @property layout
36125      * for BC  - use el in new code
36126      * @type {Roo.Layout}
36127      */
36128     layout : false,
36129     
36130      /**
36131      * @cfg {Function|boolean} disabled
36132      * If this module is disabled by some rule, return true from the funtion
36133      */
36134     disabled : false,
36135     
36136     /**
36137      * @cfg {String} parent 
36138      * Name of parent element which it get xtype added to..
36139      */
36140     parent: false,
36141     
36142     /**
36143      * @cfg {String} order
36144      * Used to set the order in which elements are created (usefull for multiple tabs)
36145      */
36146     
36147     order : false,
36148     /**
36149      * @cfg {String} name
36150      * String to display while loading.
36151      */
36152     name : false,
36153     /**
36154      * @cfg {Array} items
36155      * A single item array - the first element is the root of the tree..
36156      * It's done this way to stay compatible with the Xtype system...
36157      */
36158     items : false
36159      
36160      
36161     
36162 });
36163
36164 Roo.apply(Roo.XComponent, {
36165     
36166     /**
36167      * @property  buildCompleted
36168      * True when the builder has completed building the interface.
36169      * @type Boolean
36170      */
36171     buildCompleted : false,
36172      
36173     /**
36174      * @property  topModule
36175      * the upper most module - uses document.element as it's constructor.
36176      * @type Object
36177      */
36178      
36179     topModule  : false,
36180       
36181     /**
36182      * @property  modules
36183      * array of modules to be created by registration system.
36184      * @type Roo.XComponent
36185      */
36186     
36187     modules : [],
36188       
36189     
36190     /**
36191      * Register components to be built later.
36192      *
36193      * This solves the following issues
36194      * - Building is not done on page load, but after an authentication process has occured.
36195      * - Interface elements are registered on page load
36196      * - Parent Interface elements may not be loaded before child, so this handles that..
36197      * 
36198      *
36199      * example:
36200      * 
36201      * MyApp.register({
36202           order : '000001',
36203           module : 'Pman.Tab.projectMgr',
36204           region : 'center',
36205           parent : 'Pman.layout',
36206           disabled : false,  // or use a function..
36207         })
36208      
36209      * * @param {Object} details about module
36210      */
36211     register : function(obj) {
36212         this.modules.push(obj);
36213          
36214     },
36215     /**
36216      * convert a string to an object..
36217      * 
36218      */
36219     
36220     toObject : function(str)
36221     {
36222         if (!str || typeof(str) == 'object') {
36223             return str;
36224         }
36225         var ar = str.split('.');
36226         var rt, o;
36227         rt = ar.shift();
36228             /** eval:var:o */
36229         eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
36230         if (o === false) {
36231             throw "Module not found : " + str;
36232         }
36233         Roo.each(ar, function(e) {
36234             if (typeof(o[e]) == 'undefined') {
36235                 throw "Module not found : " + str;
36236             }
36237             o = o[e];
36238         });
36239         return o;
36240         
36241     },
36242     
36243     
36244     /**
36245      * move modules into their correct place in the tree..
36246      * 
36247      */
36248     preBuild : function ()
36249     {
36250         
36251         Roo.each(this.modules , function (obj)
36252         {
36253             obj.parent = this.toObject(obj.parent);
36254             
36255             if (!obj.parent) {
36256                 this.topModule = obj;
36257                 return;
36258             }
36259             
36260             if (!obj.parent.modules) {
36261                 obj.parent.modules = new Roo.util.MixedCollection(false, 
36262                     function(o) { return o.order + '' }
36263                 );
36264             }
36265             
36266             obj.parent.modules.add(obj);
36267         }, this);
36268     },
36269     
36270      /**
36271      * make a list of modules to build.
36272      * @return {Array} list of modules. 
36273      */ 
36274     
36275     buildOrder : function()
36276     {
36277         var _this = this;
36278         var cmp = function(a,b) {   
36279             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
36280         };
36281         
36282         if (!this.topModule || !this.topModule.modules) {
36283             throw "No top level modules to build";
36284         }
36285        
36286         // make a flat list in order of modules to build.
36287         var mods = [ this.topModule ];
36288         
36289         
36290         // add modules to their parents..
36291         var addMod = function(m) {
36292            // Roo.debug && Roo.log(m.modKey);
36293             
36294             mods.push(m);
36295             if (m.modules) {
36296                 m.modules.keySort('ASC',  cmp );
36297                 m.modules.each(addMod);
36298             }
36299             // not sure if this is used any more..
36300             if (m.finalize) {
36301                 m.finalize.name = m.name + " (clean up) ";
36302                 mods.push(m.finalize);
36303             }
36304             
36305         }
36306         this.topModule.modules.keySort('ASC',  cmp );
36307         this.topModule.modules.each(addMod);
36308         return mods;
36309     },
36310     
36311      /**
36312      * Build the registered modules.
36313      * @param {Object} parent element.
36314      * @param {Function} optional method to call after module has been added.
36315      * 
36316      */ 
36317    
36318     build : function() 
36319     {
36320         
36321         this.preBuild();
36322         var mods = this.buildOrder();
36323       
36324         //this.allmods = mods;
36325         //Roo.debug && Roo.log(mods);
36326         //return;
36327         if (!mods.length) { // should not happen
36328             throw "NO modules!!!";
36329         }
36330         
36331         
36332         
36333         // flash it up as modal - so we store the mask!?
36334         Roo.MessageBox.show({ title: 'loading' });
36335         Roo.MessageBox.show({
36336            title: "Please wait...",
36337            msg: "Building Interface...",
36338            width:450,
36339            progress:true,
36340            closable:false,
36341            modal: false
36342           
36343         });
36344         var total = mods.length;
36345         
36346         var _this = this;
36347         var progressRun = function() {
36348             if (!mods.length) {
36349                 Roo.debug && Roo.log('hide?');
36350                 Roo.MessageBox.hide();
36351                 _this.topModule.fireEvent('buildcomplete', _this.topModule);
36352                 return;    
36353             }
36354             
36355             var m = mods.shift();
36356             Roo.debug && Roo.log(m);
36357             if (typeof(m) == 'function') { // not sure if this is supported any more..
36358                 m.call(this);
36359                 return progressRun.defer(10, _this);
36360             } 
36361             
36362             Roo.MessageBox.updateProgress(
36363                 (total  - mods.length)/total,  "Building Interface " + (total  - mods.length) + 
36364                     " of " + total + 
36365                     (m.name ? (' - ' + m.name) : '')
36366                     );
36367             
36368          
36369             
36370             var disabled = (typeof(m.disabled) == 'function') ?
36371                 m.disabled.call(m.module.disabled) : m.disabled;    
36372             
36373             
36374             if (disabled) {
36375                 return progressRun(); // we do not update the display!
36376             }
36377             
36378             if (!m.parent) {
36379                 // it's a top level one..
36380                 var layoutbase = new Ext.BorderLayout(document.body, {
36381                
36382                     center: {
36383                          titlebar: false,
36384                          autoScroll:false,
36385                          closeOnTab: true,
36386                          tabPosition: 'top',
36387                          //resizeTabs: true,
36388                          alwaysShowTabs: true,
36389                          minTabWidth: 140
36390                     }
36391                 });
36392                 var tree = m.tree();
36393                 tree.region = 'center';
36394                 m.el = layoutbase.addxtype(tree);
36395                 m.panel = m.el;
36396                 m.layout = m.panel.layout;    
36397                 return progressRun.defer(10, _this);
36398             }
36399             
36400             var tree = m.tree();
36401             tree.region = tree.region || m.region;
36402             m.el = m.parent.el.addxtype(tree);
36403             m.fireEvent('built', m);
36404             m.panel = m.el;
36405             m.layout = m.panel.layout;    
36406             progressRun.defer(10, _this); 
36407             
36408         }
36409         progressRun.defer(1, _this);
36410      
36411         
36412         
36413     }
36414      
36415    
36416     
36417     
36418 });
36419  //<script type="text/javascript">
36420
36421
36422 /**
36423  * @class Roo.Login
36424  * @extends Roo.LayoutDialog
36425  * A generic Login Dialog..... - only one needed in theory!?!?
36426  *
36427  * Fires XComponent builder on success...
36428  * 
36429  * Sends 
36430  *    username,password, lang = for login actions.
36431  *    check = 1 for periodic checking that sesion is valid.
36432  *    passwordRequest = email request password
36433  *    logout = 1 = to logout
36434  * 
36435  * Affects: (this id="????" elements)
36436  *   loading  (removed) (used to indicate application is loading)
36437  *   loading-mask (hides) (used to hide application when it's building loading)
36438  *   
36439  * 
36440  * Usage: 
36441  *    
36442  * 
36443  * Myapp.login = Roo.Login({
36444      url: xxxx,
36445    
36446      realm : 'Myapp', 
36447      
36448      
36449      method : 'POST',
36450      
36451      
36452      * 
36453  })
36454  * 
36455  * 
36456  * 
36457  **/
36458  
36459 Roo.Login = function(cfg)
36460 {
36461     this.addEvents({
36462         'refreshed' : true
36463     });
36464     
36465     Roo.apply(this,cfg);
36466     
36467     Roo.onReady(function() {
36468         this.onLoad();
36469     }, this);
36470     // call parent..
36471     
36472    
36473     Roo.Login.superclass.constructor.call(this, this);
36474     //this.addxtype(this.items[0]);
36475     
36476     
36477 }
36478
36479
36480 Roo.extend(Roo.Login, Roo.LayoutDialog, {
36481     
36482     /**
36483      * @cfg {String} method
36484      * Method used to query for login details.
36485      */
36486     
36487     method : 'POST',
36488     /**
36489      * @cfg {String} url
36490      * URL to query login data. - eg. baseURL + '/Login.php'
36491      */
36492     url : '',
36493     
36494     /**
36495      * @property user
36496      * The user data - if user.id < 0 then login will be bypassed. (used for inital setup situation.
36497      * @type {Object} 
36498      */
36499     user : false,
36500     /**
36501      * @property checkFails
36502      * Number of times we have attempted to get authentication check, and failed.
36503      * @type {Number} 
36504      */
36505     checkFails : 0,
36506       /**
36507      * @property intervalID
36508      * The window interval that does the constant login checking.
36509      * @type {Number} 
36510      */
36511     intervalID : 0,
36512     
36513     
36514     onLoad : function() // called on page load...
36515     {
36516         // load 
36517          
36518         if (Roo.get('loading')) { // clear any loading indicator..
36519             Roo.get('loading').remove();
36520         }
36521         
36522         //this.switchLang('en'); // set the language to english..
36523        
36524         this.check({
36525             success:  function(response, opts)  {  // check successfull...
36526             
36527                 var res = this.processResponse(response);
36528                 this.checkFails =0;
36529                 if (!res.success) { // error!
36530                     this.checkFails = 5;
36531                     //console.log('call failure');
36532                     return this.failure(response,opts);
36533                 }
36534                 
36535                 if (!res.data.id) { // id=0 == login failure.
36536                     return this.show();
36537                 }
36538                 
36539                               
36540                         //console.log(success);
36541                 this.fillAuth(res.data);   
36542                 this.checkFails =0;
36543                 Roo.XComponent.build();
36544             },
36545             failure : this.show
36546         });
36547         
36548     }, 
36549     
36550     
36551     check: function(cfg) // called every so often to refresh cookie etc..
36552     {
36553         if (cfg.again) { // could be undefined..
36554             this.checkFails++;
36555         } else {
36556             this.checkFails = 0;
36557         }
36558         var _this = this;
36559         if (this.sending) {
36560             if ( this.checkFails > 4) {
36561                 Roo.MessageBox.alert("Error",  
36562                     "Error getting authentication status. - try reloading, or wait a while", function() {
36563                         _this.sending = false;
36564                     }); 
36565                 return;
36566             }
36567             cfg.again = true;
36568             _this.check.defer(10000, _this, [ cfg ]); // check in 10 secs.
36569             return;
36570         }
36571         this.sending = true;
36572         
36573         Roo.Ajax.request({  
36574             url: this.url,
36575             params: {
36576                 getAuthUser: true
36577             },  
36578             method: this.method,
36579             success:  cfg.success || this.success,
36580             failure : cfg.failure || this.failure,
36581             scope : this,
36582             callCfg : cfg
36583               
36584         });  
36585     }, 
36586     
36587     
36588     logout: function()
36589     {
36590         window.onbeforeunload = function() { }; // false does not work for IE..
36591         this.user = false;
36592         var _this = this;
36593         
36594         Roo.Ajax.request({  
36595             url: this.url,
36596             params: {
36597                 logout: 1
36598             },  
36599             method: 'GET',
36600             failure : function() {
36601                 Roo.MessageBox.alert("Error", "Error logging out. - continuing anyway.", function() {
36602                     document.location = document.location.toString() + '?ts=' + Math.random();
36603                 });
36604                 
36605             },
36606             success : function() {
36607                 _this.user = false;
36608                 this.checkFails =0;
36609                 // fixme..
36610                 document.location = document.location.toString() + '?ts=' + Math.random();
36611             }
36612               
36613               
36614         }); 
36615     },
36616     
36617     processResponse : function (response)
36618     {
36619         var res = '';
36620         try {
36621             res = Roo.decode(response.responseText);
36622             // oops...
36623             if (typeof(res) != 'object') {
36624                 res = { success : false, errorMsg : res, errors : true };
36625             }
36626             if (typeof(res.success) == 'undefined') {
36627                 res.success = false;
36628             }
36629             
36630         } catch(e) {
36631             res = { success : false,  errorMsg : response.responseText, errors : true };
36632         }
36633         return res;
36634     },
36635     
36636     success : function(response, opts)  // check successfull...
36637     {  
36638         this.sending = false;
36639         var res = this.processResponse(response);
36640         if (!res.success) {
36641             return this.failure(response, opts);
36642         }
36643         if (!res.data || !res.data.id) {
36644             return this.failure(response,opts);
36645         }
36646         //console.log(res);
36647         this.fillAuth(res.data);
36648         
36649         this.checkFails =0;
36650         
36651     },
36652     
36653     
36654     failure : function (response, opts) // called if login 'check' fails.. (causes re-check)
36655     {
36656         this.authUser = -1;
36657         this.sending = false;
36658         var res = this.processResponse(response);
36659         //console.log(res);
36660         if ( this.checkFails > 2) {
36661         
36662             Roo.MessageBox.alert("Error", res.errorMsg ? res.errorMsg : 
36663                 "Error getting authentication status. - try reloading"); 
36664             return;
36665         }
36666         opts.callCfg.again = true;
36667         this.check.defer(1000, this, [ opts.callCfg ]);
36668         return;  
36669     },
36670     
36671     
36672     
36673     fillAuth: function(au) {
36674         this.startAuthCheck();
36675         this.authUserId = au.id;
36676         this.authUser = au;
36677         this.lastChecked = new Date();
36678         this.fireEvent('refreshed', au);
36679         //Pman.Tab.FaxQueue.newMaxId(au.faxMax);
36680         //Pman.Tab.FaxTab.setTitle(au.faxNumPending);
36681         au.lang = au.lang || 'en';
36682         //this.switchLang(Roo.state.Manager.get('Pman.Login.lang', 'en'));
36683         Roo.state.Manager.set( this.realm + 'lang' , au.lang);
36684         this.switchLang(au.lang );
36685         
36686      
36687         // open system... - -on setyp..
36688         if (this.authUserId  < 0) {
36689             Roo.MessageBox.alert("Warning", 
36690                 "This is an open system - please set up a admin user with a password.");  
36691         }
36692          
36693         //Pman.onload(); // which should do nothing if it's a re-auth result...
36694         
36695              
36696     },
36697     
36698     startAuthCheck : function() // starter for timeout checking..
36699     {
36700         if (this.intervalID) { // timer already in place...
36701             return false;
36702         }
36703         var _this = this;
36704         this.intervalID =  window.setInterval(function() {
36705               _this.check(false);
36706             }, 120000); // every 120 secs = 2mins..
36707         
36708         
36709     },
36710          
36711     
36712     switchLang : function (lang) 
36713     {
36714         _T = typeof(_T) == 'undefined' ? false : _T;
36715           if (!_T || !lang.length) {
36716             return;
36717         }
36718         
36719         if (!_T && lang != 'en') {
36720             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
36721             return;
36722         }
36723         
36724         if (typeof(_T.en) == 'undefined') {
36725             _T.en = {};
36726             Roo.apply(_T.en, _T);
36727         }
36728         
36729         if (typeof(_T[lang]) == 'undefined') {
36730             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
36731             return;
36732         }
36733         
36734         
36735         Roo.apply(_T, _T[lang]);
36736         // just need to set the text values for everything...
36737         var _this = this;
36738         /* this will not work ...
36739         if (this.form) { 
36740             
36741                
36742             function formLabel(name, val) {
36743                 _this.form.findField(name).fieldEl.child('label').dom.innerHTML  = val;
36744             }
36745             
36746             formLabel('password', "Password"+':');
36747             formLabel('username', "Email Address"+':');
36748             formLabel('lang', "Language"+':');
36749             this.dialog.setTitle("Login");
36750             this.dialog.buttons[0].setText("Forgot Password");
36751             this.dialog.buttons[1].setText("Login");
36752         }
36753         */
36754         
36755         
36756     },
36757     
36758     
36759     title: "Login",
36760     modal: true,
36761     width:  350,
36762     //height: 230,
36763     height: 180,
36764     shadow: true,
36765     minWidth:200,
36766     minHeight:180,
36767     //proxyDrag: true,
36768     closable: false,
36769     draggable: false,
36770     collapsible: false,
36771     resizable: false,
36772     center: {  // needed??
36773         autoScroll:false,
36774         titlebar: false,
36775        // tabPosition: 'top',
36776         hideTabs: true,
36777         closeOnTab: true,
36778         alwaysShowTabs: false
36779     } ,
36780     listeners : {
36781         
36782         show  : function(dlg)
36783         {
36784             //console.log(this);
36785             this.form = this.layout.getRegion('center').activePanel.form;
36786             this.form.dialog = dlg;
36787             this.buttons[0].form = this.form;
36788             this.buttons[0].dialog = dlg;
36789             this.buttons[1].form = this.form;
36790             this.buttons[1].dialog = dlg;
36791            
36792            //this.resizeToLogo.defer(1000,this);
36793             // this is all related to resizing for logos..
36794             //var sz = Roo.get(Pman.Login.form.el.query('img')[0]).getSize();
36795            //// if (!sz) {
36796              //   this.resizeToLogo.defer(1000,this);
36797              //   return;
36798            // }
36799             //var w = Ext.lib.Dom.getViewWidth() - 100;
36800             //var h = Ext.lib.Dom.getViewHeight() - 100;
36801             //this.resizeTo(Math.max(350, Math.min(sz.width + 30, w)),Math.min(sz.height+200, h));
36802             //this.center();
36803             if (this.disabled) {
36804                 this.hide();
36805                 return;
36806             }
36807             
36808             if (this.user.id < 0) { // used for inital setup situations.
36809                 return;
36810             }
36811             
36812             if (this.intervalID) {
36813                 // remove the timer
36814                 window.clearInterval(this.intervalID);
36815                 this.intervalID = false;
36816             }
36817             
36818             
36819             if (Roo.get('loading')) {
36820                 Roo.get('loading').remove();
36821             }
36822             if (Roo.get('loading-mask')) {
36823                 Roo.get('loading-mask').hide();
36824             }
36825             
36826             //incomming._node = tnode;
36827             this.form.reset();
36828             //this.dialog.modal = !modal;
36829             //this.dialog.show();
36830             this.el.unmask(); 
36831             
36832             
36833             this.form.setValues({
36834                 'username' : Roo.state.Manager.get(this.realm + '.username', ''),
36835                 'lang' : Roo.state.Manager.get(this.realm + '.lang', 'en')
36836             });
36837             
36838             this.switchLang(Roo.state.Manager.get(this.realm + '.lang', 'en'));
36839             if (this.form.findField('username').getValue().length > 0 ){
36840                 this.form.findField('password').focus();
36841             } else {
36842                this.form.findField('username').focus();
36843             }
36844     
36845         }
36846     },
36847     items : [
36848          {
36849        
36850             xtype : 'ContentPanel',
36851             xns : Roo,
36852             region: 'center',
36853             fitToFrame : true,
36854             
36855             items : [
36856     
36857                 {
36858                
36859                     xtype : 'Form',
36860                     xns : Roo.form,
36861                     labelWidth: 100,
36862                     style : 'margin: 10px;',
36863                     
36864                     listeners : {
36865                         actionfailed : function(f, act) {
36866                             // form can return { errors: .... }
36867                                 
36868                             //act.result.errors // invalid form element list...
36869                             //act.result.errorMsg// invalid form element list...
36870                             
36871                             this.dialog.el.unmask();
36872                             Roo.MessageBox.alert("Error", act.result.errorMsg ? act.result.errorMsg : 
36873                                         "Login failed - communication error - try again.");
36874                                       
36875                         },
36876                         actioncomplete: function(re, act) {
36877                              
36878                             Roo.state.Manager.set(
36879                                 this.dialog.realm + '.username',  
36880                                     this.findField('username').getValue()
36881                             );
36882                             Roo.state.Manager.set(
36883                                 this.dialog.realm + '.lang',  
36884                                 this.findField('lang').getValue() 
36885                             );
36886                             
36887                             this.dialog.fillAuth(act.result.data);
36888                               
36889                             this.dialog.hide();
36890                             
36891                             if (Roo.get('loading-mask')) {
36892                                 Roo.get('loading-mask').show();
36893                             }
36894                             Roo.XComponent.build();
36895                             
36896                              
36897                             
36898                         }
36899                     },
36900                     items : [
36901                         {
36902                             xtype : 'TextField',
36903                             xns : Roo.form,
36904                             fieldLabel: "Email Address",
36905                             name: 'username',
36906                             width:200,
36907                             autoCreate : {tag: "input", type: "text", size: "20"}
36908                         },
36909                         {
36910                             xtype : 'TextField',
36911                             xns : Roo.form,
36912                             fieldLabel: "Password",
36913                             inputType: 'password',
36914                             name: 'password',
36915                             width:200,
36916                             autoCreate : {tag: "input", type: "text", size: "20"},
36917                             listeners : {
36918                                 specialkey : function(e,ev) {
36919                                     if (ev.keyCode == 13) {
36920                                         this.form.dialog.el.mask("Logging in");
36921                                         this.form.doAction('submit', {
36922                                             url: this.form.dialog.url,
36923                                             method: this.form.dialog.method
36924                                         });
36925                                     }
36926                                 }
36927                             }  
36928                         },
36929                         {
36930                             xtype : 'ComboBox',
36931                             xns : Roo.form,
36932                             fieldLabel: "Language",
36933                             name : 'langdisp',
36934                             store: {
36935                                 xtype : 'SimpleStore',
36936                                 fields: ['lang', 'ldisp'],
36937                                 data : [
36938                                     [ 'en', 'English' ],
36939                                     [ 'zh_HK' , '\u7E41\u4E2D' ],
36940                                     [ 'zh_CN', '\u7C21\u4E2D' ]
36941                                 ]
36942                             },
36943                             
36944                             valueField : 'lang',
36945                             hiddenName:  'lang',
36946                             width: 200,
36947                             displayField:'ldisp',
36948                             typeAhead: false,
36949                             editable: false,
36950                             mode: 'local',
36951                             triggerAction: 'all',
36952                             emptyText:'Select a Language...',
36953                             selectOnFocus:true,
36954                             listeners : {
36955                                 select :  function(cb, rec, ix) {
36956                                     this.form.switchLang(rec.data.lang);
36957                                 }
36958                             }
36959                         
36960                         }
36961                     ]
36962                 }
36963                   
36964                 
36965             ]
36966         }
36967     ],
36968     buttons : [
36969         {
36970             xtype : 'Button',
36971             xns : 'Roo',
36972             text : "Forgot Password",
36973             listeners : {
36974                 click : function() {
36975                     //console.log(this);
36976                     var n = this.form.findField('username').getValue();
36977                     if (!n.length) {
36978                         Roo.MessageBox.alert("Error", "Fill in your email address");
36979                         return;
36980                     }
36981                     Roo.Ajax.request({
36982                         url: this.dialog.url,
36983                         params: {
36984                             passwordRequest: n
36985                         },
36986                         method: this.dialog.method,
36987                         success:  function(response, opts)  {  // check successfull...
36988                         
36989                             var res = this.dialog.processResponse(response);
36990                             if (!res.success) { // error!
36991                                Roo.MessageBox.alert("Error" ,
36992                                     res.errorMsg ? res.errorMsg  : "Problem Requesting Password Reset");
36993                                return;
36994                             }
36995                             Roo.MessageBox.alert("Notice" ,
36996                                 "Please check you email for the Password Reset message");
36997                         },
36998                         failure : function() {
36999                             Roo.MessageBox.alert("Error" , "Problem Requesting Password Reset");
37000                         }
37001                         
37002                     });
37003                 }
37004             }
37005         },
37006         {
37007             xtype : 'Button',
37008             xns : 'Roo',
37009             text : "Login",
37010             listeners : {
37011                 
37012                 click : function () {
37013                         
37014                     this.dialog.el.mask("Logging in");
37015                     this.form.doAction('submit', {
37016                             url: this.dialog.url,
37017                             method: this.dialog.method
37018                     });
37019                 }
37020             }
37021         }
37022     ]
37023   
37024   
37025 })
37026  
37027
37028
37029