roojs-ui-debug.js
[roojs1] / roojs-ui-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11
12
13
14 /*
15  * These classes are derivatives of the similarly named classes in the YUI Library.
16  * The original license:
17  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
18  * Code licensed under the BSD License:
19  * http://developer.yahoo.net/yui/license.txt
20  */
21
22 (function() {
23
24 var Event=Roo.EventManager;
25 var Dom=Roo.lib.Dom;
26
27 /**
28  * @class Roo.dd.DragDrop
29  * 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  
8203         if(s.width != sws || s.height != shs){
8204             s.width = sws;
8205             s.height = shs;
8206             if(!Roo.isIE){
8207                 var cn = d.childNodes;
8208                 var sww = Math.max(0, (sw-12))+"px";
8209                 cn[0].childNodes[1].style.width = sww;
8210                 cn[1].childNodes[1].style.width = sww;
8211                 cn[2].childNodes[1].style.width = sww;
8212                 cn[1].style.height = Math.max(0, (sh-12))+"px";
8213             }
8214         }
8215     },
8216
8217     /**
8218      * Hides this shadow
8219      */
8220     hide : function(){
8221         if(this.el){
8222             this.el.dom.style.display = "none";
8223             Roo.Shadow.Pool.push(this.el);
8224             delete this.el;
8225         }
8226     },
8227
8228     /**
8229      * Adjust the z-index of this shadow
8230      * @param {Number} zindex The new z-index
8231      */
8232     setZIndex : function(z){
8233         this.zIndex = z;
8234         if(this.el){
8235             this.el.setStyle("z-index", z);
8236         }
8237     }
8238 };
8239
8240 // Private utility class that manages the internal Shadow cache
8241 Roo.Shadow.Pool = function(){
8242     var p = [];
8243     var markup = Roo.isIE ?
8244                  '<div class="x-ie-shadow"></div>' :
8245                  '<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>';
8246     return {
8247         pull : function(){
8248             var sh = p.shift();
8249             if(!sh){
8250                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
8251                 sh.autoBoxAdjust = false;
8252             }
8253             return sh;
8254         },
8255
8256         push : function(sh){
8257             p.push(sh);
8258         }
8259     };
8260 }();/*
8261  * Based on:
8262  * Ext JS Library 1.1.1
8263  * Copyright(c) 2006-2007, Ext JS, LLC.
8264  *
8265  * Originally Released Under LGPL - original licence link has changed is not relivant.
8266  *
8267  * Fork - LGPL
8268  * <script type="text/javascript">
8269  */
8270
8271 /**
8272  * @class Roo.BoxComponent
8273  * @extends Roo.Component
8274  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
8275  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
8276  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
8277  * layout containers.
8278  * @constructor
8279  * @param {Roo.Element/String/Object} config The configuration options.
8280  */
8281 Roo.BoxComponent = function(config){
8282     Roo.Component.call(this, config);
8283     this.addEvents({
8284         /**
8285          * @event resize
8286          * Fires after the component is resized.
8287              * @param {Roo.Component} this
8288              * @param {Number} adjWidth The box-adjusted width that was set
8289              * @param {Number} adjHeight The box-adjusted height that was set
8290              * @param {Number} rawWidth The width that was originally specified
8291              * @param {Number} rawHeight The height that was originally specified
8292              */
8293         resize : true,
8294         /**
8295          * @event move
8296          * Fires after the component is moved.
8297              * @param {Roo.Component} this
8298              * @param {Number} x The new x position
8299              * @param {Number} y The new y position
8300              */
8301         move : true
8302     });
8303 };
8304
8305 Roo.extend(Roo.BoxComponent, Roo.Component, {
8306     // private, set in afterRender to signify that the component has been rendered
8307     boxReady : false,
8308     // private, used to defer height settings to subclasses
8309     deferHeight: false,
8310     /** @cfg {Number} width
8311      * width (optional) size of component
8312      */
8313      /** @cfg {Number} height
8314      * height (optional) size of component
8315      */
8316      
8317     /**
8318      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
8319      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
8320      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
8321      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
8322      * @return {Roo.BoxComponent} this
8323      */
8324     setSize : function(w, h){
8325         // support for standard size objects
8326         if(typeof w == 'object'){
8327             h = w.height;
8328             w = w.width;
8329         }
8330         // not rendered
8331         if(!this.boxReady){
8332             this.width = w;
8333             this.height = h;
8334             return this;
8335         }
8336
8337         // prevent recalcs when not needed
8338         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
8339             return this;
8340         }
8341         this.lastSize = {width: w, height: h};
8342
8343         var adj = this.adjustSize(w, h);
8344         var aw = adj.width, ah = adj.height;
8345         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
8346             var rz = this.getResizeEl();
8347             if(!this.deferHeight && aw !== undefined && ah !== undefined){
8348                 rz.setSize(aw, ah);
8349             }else if(!this.deferHeight && ah !== undefined){
8350                 rz.setHeight(ah);
8351             }else if(aw !== undefined){
8352                 rz.setWidth(aw);
8353             }
8354             this.onResize(aw, ah, w, h);
8355             this.fireEvent('resize', this, aw, ah, w, h);
8356         }
8357         return this;
8358     },
8359
8360     /**
8361      * Gets the current size of the component's underlying element.
8362      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8363      */
8364     getSize : function(){
8365         return this.el.getSize();
8366     },
8367
8368     /**
8369      * Gets the current XY position of the component's underlying element.
8370      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8371      * @return {Array} The XY position of the element (e.g., [100, 200])
8372      */
8373     getPosition : function(local){
8374         if(local === true){
8375             return [this.el.getLeft(true), this.el.getTop(true)];
8376         }
8377         return this.xy || this.el.getXY();
8378     },
8379
8380     /**
8381      * Gets the current box measurements of the component's underlying element.
8382      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8383      * @returns {Object} box An object in the format {x, y, width, height}
8384      */
8385     getBox : function(local){
8386         var s = this.el.getSize();
8387         if(local){
8388             s.x = this.el.getLeft(true);
8389             s.y = this.el.getTop(true);
8390         }else{
8391             var xy = this.xy || this.el.getXY();
8392             s.x = xy[0];
8393             s.y = xy[1];
8394         }
8395         return s;
8396     },
8397
8398     /**
8399      * Sets the current box measurements of the component's underlying element.
8400      * @param {Object} box An object in the format {x, y, width, height}
8401      * @returns {Roo.BoxComponent} this
8402      */
8403     updateBox : function(box){
8404         this.setSize(box.width, box.height);
8405         this.setPagePosition(box.x, box.y);
8406         return this;
8407     },
8408
8409     // protected
8410     getResizeEl : function(){
8411         return this.resizeEl || this.el;
8412     },
8413
8414     // protected
8415     getPositionEl : function(){
8416         return this.positionEl || this.el;
8417     },
8418
8419     /**
8420      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
8421      * This method fires the move event.
8422      * @param {Number} left The new left
8423      * @param {Number} top The new top
8424      * @returns {Roo.BoxComponent} this
8425      */
8426     setPosition : function(x, y){
8427         this.x = x;
8428         this.y = y;
8429         if(!this.boxReady){
8430             return this;
8431         }
8432         var adj = this.adjustPosition(x, y);
8433         var ax = adj.x, ay = adj.y;
8434
8435         var el = this.getPositionEl();
8436         if(ax !== undefined || ay !== undefined){
8437             if(ax !== undefined && ay !== undefined){
8438                 el.setLeftTop(ax, ay);
8439             }else if(ax !== undefined){
8440                 el.setLeft(ax);
8441             }else if(ay !== undefined){
8442                 el.setTop(ay);
8443             }
8444             this.onPosition(ax, ay);
8445             this.fireEvent('move', this, ax, ay);
8446         }
8447         return this;
8448     },
8449
8450     /**
8451      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
8452      * This method fires the move event.
8453      * @param {Number} x The new x position
8454      * @param {Number} y The new y position
8455      * @returns {Roo.BoxComponent} this
8456      */
8457     setPagePosition : function(x, y){
8458         this.pageX = x;
8459         this.pageY = y;
8460         if(!this.boxReady){
8461             return;
8462         }
8463         if(x === undefined || y === undefined){ // cannot translate undefined points
8464             return;
8465         }
8466         var p = this.el.translatePoints(x, y);
8467         this.setPosition(p.left, p.top);
8468         return this;
8469     },
8470
8471     // private
8472     onRender : function(ct, position){
8473         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
8474         if(this.resizeEl){
8475             this.resizeEl = Roo.get(this.resizeEl);
8476         }
8477         if(this.positionEl){
8478             this.positionEl = Roo.get(this.positionEl);
8479         }
8480     },
8481
8482     // private
8483     afterRender : function(){
8484         Roo.BoxComponent.superclass.afterRender.call(this);
8485         this.boxReady = true;
8486         this.setSize(this.width, this.height);
8487         if(this.x || this.y){
8488             this.setPosition(this.x, this.y);
8489         }
8490         if(this.pageX || this.pageY){
8491             this.setPagePosition(this.pageX, this.pageY);
8492         }
8493     },
8494
8495     /**
8496      * Force the component's size to recalculate based on the underlying element's current height and width.
8497      * @returns {Roo.BoxComponent} this
8498      */
8499     syncSize : function(){
8500         delete this.lastSize;
8501         this.setSize(this.el.getWidth(), this.el.getHeight());
8502         return this;
8503     },
8504
8505     /**
8506      * Called after the component is resized, this method is empty by default but can be implemented by any
8507      * subclass that needs to perform custom logic after a resize occurs.
8508      * @param {Number} adjWidth The box-adjusted width that was set
8509      * @param {Number} adjHeight The box-adjusted height that was set
8510      * @param {Number} rawWidth The width that was originally specified
8511      * @param {Number} rawHeight The height that was originally specified
8512      */
8513     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
8514
8515     },
8516
8517     /**
8518      * Called after the component is moved, this method is empty by default but can be implemented by any
8519      * subclass that needs to perform custom logic after a move occurs.
8520      * @param {Number} x The new x position
8521      * @param {Number} y The new y position
8522      */
8523     onPosition : function(x, y){
8524
8525     },
8526
8527     // private
8528     adjustSize : function(w, h){
8529         if(this.autoWidth){
8530             w = 'auto';
8531         }
8532         if(this.autoHeight){
8533             h = 'auto';
8534         }
8535         return {width : w, height: h};
8536     },
8537
8538     // private
8539     adjustPosition : function(x, y){
8540         return {x : x, y: y};
8541     }
8542 });/*
8543  * Based on:
8544  * Ext JS Library 1.1.1
8545  * Copyright(c) 2006-2007, Ext JS, LLC.
8546  *
8547  * Originally Released Under LGPL - original licence link has changed is not relivant.
8548  *
8549  * Fork - LGPL
8550  * <script type="text/javascript">
8551  */
8552
8553
8554 /**
8555  * @class Roo.SplitBar
8556  * @extends Roo.util.Observable
8557  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
8558  * <br><br>
8559  * Usage:
8560  * <pre><code>
8561 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
8562                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
8563 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
8564 split.minSize = 100;
8565 split.maxSize = 600;
8566 split.animate = true;
8567 split.on('moved', splitterMoved);
8568 </code></pre>
8569  * @constructor
8570  * Create a new SplitBar
8571  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
8572  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
8573  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8574  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
8575                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
8576                         position of the SplitBar).
8577  */
8578 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
8579     
8580     /** @private */
8581     this.el = Roo.get(dragElement, true);
8582     this.el.dom.unselectable = "on";
8583     /** @private */
8584     this.resizingEl = Roo.get(resizingElement, true);
8585
8586     /**
8587      * @private
8588      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8589      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
8590      * @type Number
8591      */
8592     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
8593     
8594     /**
8595      * The minimum size of the resizing element. (Defaults to 0)
8596      * @type Number
8597      */
8598     this.minSize = 0;
8599     
8600     /**
8601      * The maximum size of the resizing element. (Defaults to 2000)
8602      * @type Number
8603      */
8604     this.maxSize = 2000;
8605     
8606     /**
8607      * Whether to animate the transition to the new size
8608      * @type Boolean
8609      */
8610     this.animate = false;
8611     
8612     /**
8613      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
8614      * @type Boolean
8615      */
8616     this.useShim = false;
8617     
8618     /** @private */
8619     this.shim = null;
8620     
8621     if(!existingProxy){
8622         /** @private */
8623         this.proxy = Roo.SplitBar.createProxy(this.orientation);
8624     }else{
8625         this.proxy = Roo.get(existingProxy).dom;
8626     }
8627     /** @private */
8628     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
8629     
8630     /** @private */
8631     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
8632     
8633     /** @private */
8634     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
8635     
8636     /** @private */
8637     this.dragSpecs = {};
8638     
8639     /**
8640      * @private The adapter to use to positon and resize elements
8641      */
8642     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
8643     this.adapter.init(this);
8644     
8645     if(this.orientation == Roo.SplitBar.HORIZONTAL){
8646         /** @private */
8647         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
8648         this.el.addClass("x-splitbar-h");
8649     }else{
8650         /** @private */
8651         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
8652         this.el.addClass("x-splitbar-v");
8653     }
8654     
8655     this.addEvents({
8656         /**
8657          * @event resize
8658          * Fires when the splitter is moved (alias for {@link #event-moved})
8659          * @param {Roo.SplitBar} this
8660          * @param {Number} newSize the new width or height
8661          */
8662         "resize" : true,
8663         /**
8664          * @event moved
8665          * Fires when the splitter is moved
8666          * @param {Roo.SplitBar} this
8667          * @param {Number} newSize the new width or height
8668          */
8669         "moved" : true,
8670         /**
8671          * @event beforeresize
8672          * Fires before the splitter is dragged
8673          * @param {Roo.SplitBar} this
8674          */
8675         "beforeresize" : true,
8676
8677         "beforeapply" : true
8678     });
8679
8680     Roo.util.Observable.call(this);
8681 };
8682
8683 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
8684     onStartProxyDrag : function(x, y){
8685         this.fireEvent("beforeresize", this);
8686         if(!this.overlay){
8687             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
8688             o.unselectable();
8689             o.enableDisplayMode("block");
8690             // all splitbars share the same overlay
8691             Roo.SplitBar.prototype.overlay = o;
8692         }
8693         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8694         this.overlay.show();
8695         Roo.get(this.proxy).setDisplayed("block");
8696         var size = this.adapter.getElementSize(this);
8697         this.activeMinSize = this.getMinimumSize();;
8698         this.activeMaxSize = this.getMaximumSize();;
8699         var c1 = size - this.activeMinSize;
8700         var c2 = Math.max(this.activeMaxSize - size, 0);
8701         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8702             this.dd.resetConstraints();
8703             this.dd.setXConstraint(
8704                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
8705                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
8706             );
8707             this.dd.setYConstraint(0, 0);
8708         }else{
8709             this.dd.resetConstraints();
8710             this.dd.setXConstraint(0, 0);
8711             this.dd.setYConstraint(
8712                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
8713                 this.placement == Roo.SplitBar.TOP ? c2 : c1
8714             );
8715          }
8716         this.dragSpecs.startSize = size;
8717         this.dragSpecs.startPoint = [x, y];
8718         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
8719     },
8720     
8721     /** 
8722      * @private Called after the drag operation by the DDProxy
8723      */
8724     onEndProxyDrag : function(e){
8725         Roo.get(this.proxy).setDisplayed(false);
8726         var endPoint = Roo.lib.Event.getXY(e);
8727         if(this.overlay){
8728             this.overlay.hide();
8729         }
8730         var newSize;
8731         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8732             newSize = this.dragSpecs.startSize + 
8733                 (this.placement == Roo.SplitBar.LEFT ?
8734                     endPoint[0] - this.dragSpecs.startPoint[0] :
8735                     this.dragSpecs.startPoint[0] - endPoint[0]
8736                 );
8737         }else{
8738             newSize = this.dragSpecs.startSize + 
8739                 (this.placement == Roo.SplitBar.TOP ?
8740                     endPoint[1] - this.dragSpecs.startPoint[1] :
8741                     this.dragSpecs.startPoint[1] - endPoint[1]
8742                 );
8743         }
8744         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
8745         if(newSize != this.dragSpecs.startSize){
8746             if(this.fireEvent('beforeapply', this, newSize) !== false){
8747                 this.adapter.setElementSize(this, newSize);
8748                 this.fireEvent("moved", this, newSize);
8749                 this.fireEvent("resize", this, newSize);
8750             }
8751         }
8752     },
8753     
8754     /**
8755      * Get the adapter this SplitBar uses
8756      * @return The adapter object
8757      */
8758     getAdapter : function(){
8759         return this.adapter;
8760     },
8761     
8762     /**
8763      * Set the adapter this SplitBar uses
8764      * @param {Object} adapter A SplitBar adapter object
8765      */
8766     setAdapter : function(adapter){
8767         this.adapter = adapter;
8768         this.adapter.init(this);
8769     },
8770     
8771     /**
8772      * Gets the minimum size for the resizing element
8773      * @return {Number} The minimum size
8774      */
8775     getMinimumSize : function(){
8776         return this.minSize;
8777     },
8778     
8779     /**
8780      * Sets the minimum size for the resizing element
8781      * @param {Number} minSize The minimum size
8782      */
8783     setMinimumSize : function(minSize){
8784         this.minSize = minSize;
8785     },
8786     
8787     /**
8788      * Gets the maximum size for the resizing element
8789      * @return {Number} The maximum size
8790      */
8791     getMaximumSize : function(){
8792         return this.maxSize;
8793     },
8794     
8795     /**
8796      * Sets the maximum size for the resizing element
8797      * @param {Number} maxSize The maximum size
8798      */
8799     setMaximumSize : function(maxSize){
8800         this.maxSize = maxSize;
8801     },
8802     
8803     /**
8804      * Sets the initialize size for the resizing element
8805      * @param {Number} size The initial size
8806      */
8807     setCurrentSize : function(size){
8808         var oldAnimate = this.animate;
8809         this.animate = false;
8810         this.adapter.setElementSize(this, size);
8811         this.animate = oldAnimate;
8812     },
8813     
8814     /**
8815      * Destroy this splitbar. 
8816      * @param {Boolean} removeEl True to remove the element
8817      */
8818     destroy : function(removeEl){
8819         if(this.shim){
8820             this.shim.remove();
8821         }
8822         this.dd.unreg();
8823         this.proxy.parentNode.removeChild(this.proxy);
8824         if(removeEl){
8825             this.el.remove();
8826         }
8827     }
8828 });
8829
8830 /**
8831  * @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.
8832  */
8833 Roo.SplitBar.createProxy = function(dir){
8834     var proxy = new Roo.Element(document.createElement("div"));
8835     proxy.unselectable();
8836     var cls = 'x-splitbar-proxy';
8837     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
8838     document.body.appendChild(proxy.dom);
8839     return proxy.dom;
8840 };
8841
8842 /** 
8843  * @class Roo.SplitBar.BasicLayoutAdapter
8844  * Default Adapter. It assumes the splitter and resizing element are not positioned
8845  * elements and only gets/sets the width of the element. Generally used for table based layouts.
8846  */
8847 Roo.SplitBar.BasicLayoutAdapter = function(){
8848 };
8849
8850 Roo.SplitBar.BasicLayoutAdapter.prototype = {
8851     // do nothing for now
8852     init : function(s){
8853     
8854     },
8855     /**
8856      * Called before drag operations to get the current size of the resizing element. 
8857      * @param {Roo.SplitBar} s The SplitBar using this adapter
8858      */
8859      getElementSize : function(s){
8860         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8861             return s.resizingEl.getWidth();
8862         }else{
8863             return s.resizingEl.getHeight();
8864         }
8865     },
8866     
8867     /**
8868      * Called after drag operations to set the size of the resizing element.
8869      * @param {Roo.SplitBar} s The SplitBar using this adapter
8870      * @param {Number} newSize The new size to set
8871      * @param {Function} onComplete A function to be invoked when resizing is complete
8872      */
8873     setElementSize : function(s, newSize, onComplete){
8874         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8875             if(!s.animate){
8876                 s.resizingEl.setWidth(newSize);
8877                 if(onComplete){
8878                     onComplete(s, newSize);
8879                 }
8880             }else{
8881                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
8882             }
8883         }else{
8884             
8885             if(!s.animate){
8886                 s.resizingEl.setHeight(newSize);
8887                 if(onComplete){
8888                     onComplete(s, newSize);
8889                 }
8890             }else{
8891                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
8892             }
8893         }
8894     }
8895 };
8896
8897 /** 
8898  *@class Roo.SplitBar.AbsoluteLayoutAdapter
8899  * @extends Roo.SplitBar.BasicLayoutAdapter
8900  * Adapter that  moves the splitter element to align with the resized sizing element. 
8901  * Used with an absolute positioned SplitBar.
8902  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
8903  * document.body, make sure you assign an id to the body element.
8904  */
8905 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
8906     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
8907     this.container = Roo.get(container);
8908 };
8909
8910 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
8911     init : function(s){
8912         this.basic.init(s);
8913     },
8914     
8915     getElementSize : function(s){
8916         return this.basic.getElementSize(s);
8917     },
8918     
8919     setElementSize : function(s, newSize, onComplete){
8920         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
8921     },
8922     
8923     moveSplitter : function(s){
8924         var yes = Roo.SplitBar;
8925         switch(s.placement){
8926             case yes.LEFT:
8927                 s.el.setX(s.resizingEl.getRight());
8928                 break;
8929             case yes.RIGHT:
8930                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
8931                 break;
8932             case yes.TOP:
8933                 s.el.setY(s.resizingEl.getBottom());
8934                 break;
8935             case yes.BOTTOM:
8936                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
8937                 break;
8938         }
8939     }
8940 };
8941
8942 /**
8943  * Orientation constant - Create a vertical SplitBar
8944  * @static
8945  * @type Number
8946  */
8947 Roo.SplitBar.VERTICAL = 1;
8948
8949 /**
8950  * Orientation constant - Create a horizontal SplitBar
8951  * @static
8952  * @type Number
8953  */
8954 Roo.SplitBar.HORIZONTAL = 2;
8955
8956 /**
8957  * Placement constant - The resizing element is to the left of the splitter element
8958  * @static
8959  * @type Number
8960  */
8961 Roo.SplitBar.LEFT = 1;
8962
8963 /**
8964  * Placement constant - The resizing element is to the right of the splitter element
8965  * @static
8966  * @type Number
8967  */
8968 Roo.SplitBar.RIGHT = 2;
8969
8970 /**
8971  * Placement constant - The resizing element is positioned above the splitter element
8972  * @static
8973  * @type Number
8974  */
8975 Roo.SplitBar.TOP = 3;
8976
8977 /**
8978  * Placement constant - The resizing element is positioned under splitter element
8979  * @static
8980  * @type Number
8981  */
8982 Roo.SplitBar.BOTTOM = 4;
8983 /*
8984  * Based on:
8985  * Ext JS Library 1.1.1
8986  * Copyright(c) 2006-2007, Ext JS, LLC.
8987  *
8988  * Originally Released Under LGPL - original licence link has changed is not relivant.
8989  *
8990  * Fork - LGPL
8991  * <script type="text/javascript">
8992  */
8993
8994 /**
8995  * @class Roo.View
8996  * @extends Roo.util.Observable
8997  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
8998  * This class also supports single and multi selection modes. <br>
8999  * Create a data model bound view:
9000  <pre><code>
9001  var store = new Roo.data.Store(...);
9002
9003  var view = new Roo.View({
9004     el : "my-element",
9005     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
9006  
9007     singleSelect: true,
9008     selectedClass: "ydataview-selected",
9009     store: store
9010  });
9011
9012  // listen for node click?
9013  view.on("click", function(vw, index, node, e){
9014  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9015  });
9016
9017  // load XML data
9018  dataModel.load("foobar.xml");
9019  </code></pre>
9020  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9021  * <br><br>
9022  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9023  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9024  * 
9025  * Note: old style constructor is still suported (container, template, config)
9026  * 
9027  * @constructor
9028  * Create a new View
9029  * @param {Object} config The config object
9030  * 
9031  */
9032 Roo.View = function(config, depreciated_tpl, depreciated_config){
9033     
9034     if (typeof(depreciated_tpl) == 'undefined') {
9035         // new way.. - universal constructor.
9036         Roo.apply(this, config);
9037         this.el  = Roo.get(this.el);
9038     } else {
9039         // old format..
9040         this.el  = Roo.get(config);
9041         this.tpl = depreciated_tpl;
9042         Roo.apply(this, depreciated_config);
9043     }
9044      
9045     
9046     if(typeof(this.tpl) == "string"){
9047         this.tpl = new Roo.Template(this.tpl);
9048     } else {
9049         // support xtype ctors..
9050         this.tpl = new Roo.factory(this.tpl, Roo);
9051     }
9052     
9053     
9054     this.tpl.compile();
9055    
9056
9057      
9058     /** @private */
9059     this.addEvents({
9060     /**
9061      * @event beforeclick
9062      * Fires before a click is processed. Returns false to cancel the default action.
9063      * @param {Roo.View} this
9064      * @param {Number} index The index of the target node
9065      * @param {HTMLElement} node The target node
9066      * @param {Roo.EventObject} e The raw event object
9067      */
9068         "beforeclick" : true,
9069     /**
9070      * @event click
9071      * Fires when a template node is clicked.
9072      * @param {Roo.View} this
9073      * @param {Number} index The index of the target node
9074      * @param {HTMLElement} node The target node
9075      * @param {Roo.EventObject} e The raw event object
9076      */
9077         "click" : true,
9078     /**
9079      * @event dblclick
9080      * Fires when a template node is double clicked.
9081      * @param {Roo.View} this
9082      * @param {Number} index The index of the target node
9083      * @param {HTMLElement} node The target node
9084      * @param {Roo.EventObject} e The raw event object
9085      */
9086         "dblclick" : true,
9087     /**
9088      * @event contextmenu
9089      * Fires when a template node is right clicked.
9090      * @param {Roo.View} this
9091      * @param {Number} index The index of the target node
9092      * @param {HTMLElement} node The target node
9093      * @param {Roo.EventObject} e The raw event object
9094      */
9095         "contextmenu" : true,
9096     /**
9097      * @event selectionchange
9098      * Fires when the selected nodes change.
9099      * @param {Roo.View} this
9100      * @param {Array} selections Array of the selected nodes
9101      */
9102         "selectionchange" : true,
9103
9104     /**
9105      * @event beforeselect
9106      * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9107      * @param {Roo.View} this
9108      * @param {HTMLElement} node The node to be selected
9109      * @param {Array} selections Array of currently selected nodes
9110      */
9111         "beforeselect" : true
9112     });
9113
9114     this.el.on({
9115         "click": this.onClick,
9116         "dblclick": this.onDblClick,
9117         "contextmenu": this.onContextMenu,
9118         scope:this
9119     });
9120
9121     this.selections = [];
9122     this.nodes = [];
9123     this.cmp = new Roo.CompositeElementLite([]);
9124     if(this.store){
9125         this.store = Roo.factory(this.store, Roo.data);
9126         this.setStore(this.store, true);
9127     }
9128     Roo.View.superclass.constructor.call(this);
9129 };
9130
9131 Roo.extend(Roo.View, Roo.util.Observable, {
9132     
9133      /**
9134      * @cfg {Roo.data.Store} store Data store to load data from.
9135      */
9136     store : false,
9137     
9138     /**
9139      * @cfg {String|Roo.Element} el The container element.
9140      */
9141     el : '',
9142     
9143     /**
9144      * @cfg {String|Roo.Template} tpl The template used by this View 
9145      */
9146     tpl : false,
9147     
9148     /**
9149      * @cfg {String} selectedClass The css class to add to selected nodes
9150      */
9151     selectedClass : "x-view-selected",
9152      /**
9153      * @cfg {String} emptyText The empty text to show when nothing is loaded.
9154      */
9155     emptyText : "",
9156     /**
9157      * @cfg {Boolean} multiSelect Allow multiple selection
9158      */
9159     
9160     multiSelect : false,
9161     /**
9162      * @cfg {Boolean} singleSelect Allow single selection
9163      */
9164     singleSelect:  false,
9165     
9166     /**
9167      * Returns the element this view is bound to.
9168      * @return {Roo.Element}
9169      */
9170     getEl : function(){
9171         return this.el;
9172     },
9173
9174     /**
9175      * Refreshes the view.
9176      */
9177     refresh : function(){
9178         var t = this.tpl;
9179         this.clearSelections();
9180         this.el.update("");
9181         var html = [];
9182         var records = this.store.getRange();
9183         if(records.length < 1){
9184             this.el.update(this.emptyText);
9185             return;
9186         }
9187         for(var i = 0, len = records.length; i < len; i++){
9188             var data = this.prepareData(records[i].data, i, records[i]);
9189             html[html.length] = t.apply(data);
9190         }
9191         this.el.update(html.join(""));
9192         this.nodes = this.el.dom.childNodes;
9193         this.updateIndexes(0);
9194     },
9195
9196     /**
9197      * Function to override to reformat the data that is sent to
9198      * the template for each node.
9199      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9200      * a JSON object for an UpdateManager bound view).
9201      */
9202     prepareData : function(data){
9203         return data;
9204     },
9205
9206     onUpdate : function(ds, record){
9207         this.clearSelections();
9208         var index = this.store.indexOf(record);
9209         var n = this.nodes[index];
9210         this.tpl.insertBefore(n, this.prepareData(record.data));
9211         n.parentNode.removeChild(n);
9212         this.updateIndexes(index, index);
9213     },
9214
9215     onAdd : function(ds, records, index){
9216         this.clearSelections();
9217         if(this.nodes.length == 0){
9218             this.refresh();
9219             return;
9220         }
9221         var n = this.nodes[index];
9222         for(var i = 0, len = records.length; i < len; i++){
9223             var d = this.prepareData(records[i].data);
9224             if(n){
9225                 this.tpl.insertBefore(n, d);
9226             }else{
9227                 this.tpl.append(this.el, d);
9228             }
9229         }
9230         this.updateIndexes(index);
9231     },
9232
9233     onRemove : function(ds, record, index){
9234         this.clearSelections();
9235         this.el.dom.removeChild(this.nodes[index]);
9236         this.updateIndexes(index);
9237     },
9238
9239     /**
9240      * Refresh an individual node.
9241      * @param {Number} index
9242      */
9243     refreshNode : function(index){
9244         this.onUpdate(this.store, this.store.getAt(index));
9245     },
9246
9247     updateIndexes : function(startIndex, endIndex){
9248         var ns = this.nodes;
9249         startIndex = startIndex || 0;
9250         endIndex = endIndex || ns.length - 1;
9251         for(var i = startIndex; i <= endIndex; i++){
9252             ns[i].nodeIndex = i;
9253         }
9254     },
9255
9256     /**
9257      * Changes the data store this view uses and refresh the view.
9258      * @param {Store} store
9259      */
9260     setStore : function(store, initial){
9261         if(!initial && this.store){
9262             this.store.un("datachanged", this.refresh);
9263             this.store.un("add", this.onAdd);
9264             this.store.un("remove", this.onRemove);
9265             this.store.un("update", this.onUpdate);
9266             this.store.un("clear", this.refresh);
9267         }
9268         if(store){
9269           
9270             store.on("datachanged", this.refresh, this);
9271             store.on("add", this.onAdd, this);
9272             store.on("remove", this.onRemove, this);
9273             store.on("update", this.onUpdate, this);
9274             store.on("clear", this.refresh, this);
9275         }
9276         
9277         if(store){
9278             this.refresh();
9279         }
9280     },
9281
9282     /**
9283      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9284      * @param {HTMLElement} node
9285      * @return {HTMLElement} The template node
9286      */
9287     findItemFromChild : function(node){
9288         var el = this.el.dom;
9289         if(!node || node.parentNode == el){
9290                     return node;
9291             }
9292             var p = node.parentNode;
9293             while(p && p != el){
9294             if(p.parentNode == el){
9295                 return p;
9296             }
9297             p = p.parentNode;
9298         }
9299             return null;
9300     },
9301
9302     /** @ignore */
9303     onClick : function(e){
9304         var item = this.findItemFromChild(e.getTarget());
9305         if(item){
9306             var index = this.indexOf(item);
9307             if(this.onItemClick(item, index, e) !== false){
9308                 this.fireEvent("click", this, index, item, e);
9309             }
9310         }else{
9311             this.clearSelections();
9312         }
9313     },
9314
9315     /** @ignore */
9316     onContextMenu : function(e){
9317         var item = this.findItemFromChild(e.getTarget());
9318         if(item){
9319             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9320         }
9321     },
9322
9323     /** @ignore */
9324     onDblClick : function(e){
9325         var item = this.findItemFromChild(e.getTarget());
9326         if(item){
9327             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9328         }
9329     },
9330
9331     onItemClick : function(item, index, e){
9332         if(this.fireEvent("beforeclick", this, index, item, e) === false){
9333             return false;
9334         }
9335         if(this.multiSelect || this.singleSelect){
9336             if(this.multiSelect && e.shiftKey && this.lastSelection){
9337                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9338             }else{
9339                 this.select(item, this.multiSelect && e.ctrlKey);
9340                 this.lastSelection = item;
9341             }
9342             e.preventDefault();
9343         }
9344         return true;
9345     },
9346
9347     /**
9348      * Get the number of selected nodes.
9349      * @return {Number}
9350      */
9351     getSelectionCount : function(){
9352         return this.selections.length;
9353     },
9354
9355     /**
9356      * Get the currently selected nodes.
9357      * @return {Array} An array of HTMLElements
9358      */
9359     getSelectedNodes : function(){
9360         return this.selections;
9361     },
9362
9363     /**
9364      * Get the indexes of the selected nodes.
9365      * @return {Array}
9366      */
9367     getSelectedIndexes : function(){
9368         var indexes = [], s = this.selections;
9369         for(var i = 0, len = s.length; i < len; i++){
9370             indexes.push(s[i].nodeIndex);
9371         }
9372         return indexes;
9373     },
9374
9375     /**
9376      * Clear all selections
9377      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9378      */
9379     clearSelections : function(suppressEvent){
9380         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9381             this.cmp.elements = this.selections;
9382             this.cmp.removeClass(this.selectedClass);
9383             this.selections = [];
9384             if(!suppressEvent){
9385                 this.fireEvent("selectionchange", this, this.selections);
9386             }
9387         }
9388     },
9389
9390     /**
9391      * Returns true if the passed node is selected
9392      * @param {HTMLElement/Number} node The node or node index
9393      * @return {Boolean}
9394      */
9395     isSelected : function(node){
9396         var s = this.selections;
9397         if(s.length < 1){
9398             return false;
9399         }
9400         node = this.getNode(node);
9401         return s.indexOf(node) !== -1;
9402     },
9403
9404     /**
9405      * Selects nodes.
9406      * @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
9407      * @param {Boolean} keepExisting (optional) true to keep existing selections
9408      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9409      */
9410     select : function(nodeInfo, keepExisting, suppressEvent){
9411         if(nodeInfo instanceof Array){
9412             if(!keepExisting){
9413                 this.clearSelections(true);
9414             }
9415             for(var i = 0, len = nodeInfo.length; i < len; i++){
9416                 this.select(nodeInfo[i], true, true);
9417             }
9418         } else{
9419             var node = this.getNode(nodeInfo);
9420             if(node && !this.isSelected(node)){
9421                 if(!keepExisting){
9422                     this.clearSelections(true);
9423                 }
9424                 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9425                     Roo.fly(node).addClass(this.selectedClass);
9426                     this.selections.push(node);
9427                     if(!suppressEvent){
9428                         this.fireEvent("selectionchange", this, this.selections);
9429                     }
9430                 }
9431             }
9432         }
9433     },
9434
9435     /**
9436      * Gets a template node.
9437      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9438      * @return {HTMLElement} The node or null if it wasn't found
9439      */
9440     getNode : function(nodeInfo){
9441         if(typeof nodeInfo == "string"){
9442             return document.getElementById(nodeInfo);
9443         }else if(typeof nodeInfo == "number"){
9444             return this.nodes[nodeInfo];
9445         }
9446         return nodeInfo;
9447     },
9448
9449     /**
9450      * Gets a range template nodes.
9451      * @param {Number} startIndex
9452      * @param {Number} endIndex
9453      * @return {Array} An array of nodes
9454      */
9455     getNodes : function(start, end){
9456         var ns = this.nodes;
9457         start = start || 0;
9458         end = typeof end == "undefined" ? ns.length - 1 : end;
9459         var nodes = [];
9460         if(start <= end){
9461             for(var i = start; i <= end; i++){
9462                 nodes.push(ns[i]);
9463             }
9464         } else{
9465             for(var i = start; i >= end; i--){
9466                 nodes.push(ns[i]);
9467             }
9468         }
9469         return nodes;
9470     },
9471
9472     /**
9473      * Finds the index of the passed node
9474      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9475      * @return {Number} The index of the node or -1
9476      */
9477     indexOf : function(node){
9478         node = this.getNode(node);
9479         if(typeof node.nodeIndex == "number"){
9480             return node.nodeIndex;
9481         }
9482         var ns = this.nodes;
9483         for(var i = 0, len = ns.length; i < len; i++){
9484             if(ns[i] == node){
9485                 return i;
9486             }
9487         }
9488         return -1;
9489     }
9490 });
9491 /*
9492  * Based on:
9493  * Ext JS Library 1.1.1
9494  * Copyright(c) 2006-2007, Ext JS, LLC.
9495  *
9496  * Originally Released Under LGPL - original licence link has changed is not relivant.
9497  *
9498  * Fork - LGPL
9499  * <script type="text/javascript">
9500  */
9501
9502 /**
9503  * @class Roo.JsonView
9504  * @extends Roo.View
9505  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
9506 <pre><code>
9507 var view = new Roo.JsonView({
9508     container: "my-element",
9509     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
9510     multiSelect: true, 
9511     jsonRoot: "data" 
9512 });
9513
9514 // listen for node click?
9515 view.on("click", function(vw, index, node, e){
9516     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9517 });
9518
9519 // direct load of JSON data
9520 view.load("foobar.php");
9521
9522 // Example from my blog list
9523 var tpl = new Roo.Template(
9524     '&lt;div class="entry"&gt;' +
9525     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
9526     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
9527     "&lt;/div&gt;&lt;hr /&gt;"
9528 );
9529
9530 var moreView = new Roo.JsonView({
9531     container :  "entry-list", 
9532     template : tpl,
9533     jsonRoot: "posts"
9534 });
9535 moreView.on("beforerender", this.sortEntries, this);
9536 moreView.load({
9537     url: "/blog/get-posts.php",
9538     params: "allposts=true",
9539     text: "Loading Blog Entries..."
9540 });
9541 </code></pre>
9542
9543 * Note: old code is supported with arguments : (container, template, config)
9544
9545
9546  * @constructor
9547  * Create a new JsonView
9548  * 
9549  * @param {Object} config The config object
9550  * 
9551  */
9552 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
9553     
9554     
9555     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
9556
9557     var um = this.el.getUpdateManager();
9558     um.setRenderer(this);
9559     um.on("update", this.onLoad, this);
9560     um.on("failure", this.onLoadException, this);
9561
9562     /**
9563      * @event beforerender
9564      * Fires before rendering of the downloaded JSON data.
9565      * @param {Roo.JsonView} this
9566      * @param {Object} data The JSON data loaded
9567      */
9568     /**
9569      * @event load
9570      * Fires when data is loaded.
9571      * @param {Roo.JsonView} this
9572      * @param {Object} data The JSON data loaded
9573      * @param {Object} response The raw Connect response object
9574      */
9575     /**
9576      * @event loadexception
9577      * Fires when loading fails.
9578      * @param {Roo.JsonView} this
9579      * @param {Object} response The raw Connect response object
9580      */
9581     this.addEvents({
9582         'beforerender' : true,
9583         'load' : true,
9584         'loadexception' : true
9585     });
9586 };
9587 Roo.extend(Roo.JsonView, Roo.View, {
9588     /**
9589      * @type {String} The root property in the loaded JSON object that contains the data
9590      */
9591     jsonRoot : "",
9592
9593     /**
9594      * Refreshes the view.
9595      */
9596     refresh : function(){
9597         this.clearSelections();
9598         this.el.update("");
9599         var html = [];
9600         var o = this.jsonData;
9601         if(o && o.length > 0){
9602             for(var i = 0, len = o.length; i < len; i++){
9603                 var data = this.prepareData(o[i], i, o);
9604                 html[html.length] = this.tpl.apply(data);
9605             }
9606         }else{
9607             html.push(this.emptyText);
9608         }
9609         this.el.update(html.join(""));
9610         this.nodes = this.el.dom.childNodes;
9611         this.updateIndexes(0);
9612     },
9613
9614     /**
9615      * 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.
9616      * @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:
9617      <pre><code>
9618      view.load({
9619          url: "your-url.php",
9620          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
9621          callback: yourFunction,
9622          scope: yourObject, //(optional scope)
9623          discardUrl: false,
9624          nocache: false,
9625          text: "Loading...",
9626          timeout: 30,
9627          scripts: false
9628      });
9629      </code></pre>
9630      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
9631      * 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.
9632      * @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}
9633      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9634      * @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.
9635      */
9636     load : function(){
9637         var um = this.el.getUpdateManager();
9638         um.update.apply(um, arguments);
9639     },
9640
9641     render : function(el, response){
9642         this.clearSelections();
9643         this.el.update("");
9644         var o;
9645         try{
9646             o = Roo.util.JSON.decode(response.responseText);
9647             if(this.jsonRoot){
9648                 
9649                 o = o[this.jsonRoot];
9650             }
9651         } catch(e){
9652         }
9653         /**
9654          * The current JSON data or null
9655          */
9656         this.jsonData = o;
9657         this.beforeRender();
9658         this.refresh();
9659     },
9660
9661 /**
9662  * Get the number of records in the current JSON dataset
9663  * @return {Number}
9664  */
9665     getCount : function(){
9666         return this.jsonData ? this.jsonData.length : 0;
9667     },
9668
9669 /**
9670  * Returns the JSON object for the specified node(s)
9671  * @param {HTMLElement/Array} node The node or an array of nodes
9672  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
9673  * you get the JSON object for the node
9674  */
9675     getNodeData : function(node){
9676         if(node instanceof Array){
9677             var data = [];
9678             for(var i = 0, len = node.length; i < len; i++){
9679                 data.push(this.getNodeData(node[i]));
9680             }
9681             return data;
9682         }
9683         return this.jsonData[this.indexOf(node)] || null;
9684     },
9685
9686     beforeRender : function(){
9687         this.snapshot = this.jsonData;
9688         if(this.sortInfo){
9689             this.sort.apply(this, this.sortInfo);
9690         }
9691         this.fireEvent("beforerender", this, this.jsonData);
9692     },
9693
9694     onLoad : function(el, o){
9695         this.fireEvent("load", this, this.jsonData, o);
9696     },
9697
9698     onLoadException : function(el, o){
9699         this.fireEvent("loadexception", this, o);
9700     },
9701
9702 /**
9703  * Filter the data by a specific property.
9704  * @param {String} property A property on your JSON objects
9705  * @param {String/RegExp} value Either string that the property values
9706  * should start with, or a RegExp to test against the property
9707  */
9708     filter : function(property, value){
9709         if(this.jsonData){
9710             var data = [];
9711             var ss = this.snapshot;
9712             if(typeof value == "string"){
9713                 var vlen = value.length;
9714                 if(vlen == 0){
9715                     this.clearFilter();
9716                     return;
9717                 }
9718                 value = value.toLowerCase();
9719                 for(var i = 0, len = ss.length; i < len; i++){
9720                     var o = ss[i];
9721                     if(o[property].substr(0, vlen).toLowerCase() == value){
9722                         data.push(o);
9723                     }
9724                 }
9725             } else if(value.exec){ // regex?
9726                 for(var i = 0, len = ss.length; i < len; i++){
9727                     var o = ss[i];
9728                     if(value.test(o[property])){
9729                         data.push(o);
9730                     }
9731                 }
9732             } else{
9733                 return;
9734             }
9735             this.jsonData = data;
9736             this.refresh();
9737         }
9738     },
9739
9740 /**
9741  * Filter by a function. The passed function will be called with each
9742  * object in the current dataset. If the function returns true the value is kept,
9743  * otherwise it is filtered.
9744  * @param {Function} fn
9745  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
9746  */
9747     filterBy : function(fn, scope){
9748         if(this.jsonData){
9749             var data = [];
9750             var ss = this.snapshot;
9751             for(var i = 0, len = ss.length; i < len; i++){
9752                 var o = ss[i];
9753                 if(fn.call(scope || this, o)){
9754                     data.push(o);
9755                 }
9756             }
9757             this.jsonData = data;
9758             this.refresh();
9759         }
9760     },
9761
9762 /**
9763  * Clears the current filter.
9764  */
9765     clearFilter : function(){
9766         if(this.snapshot && this.jsonData != this.snapshot){
9767             this.jsonData = this.snapshot;
9768             this.refresh();
9769         }
9770     },
9771
9772
9773 /**
9774  * Sorts the data for this view and refreshes it.
9775  * @param {String} property A property on your JSON objects to sort on
9776  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
9777  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
9778  */
9779     sort : function(property, dir, sortType){
9780         this.sortInfo = Array.prototype.slice.call(arguments, 0);
9781         if(this.jsonData){
9782             var p = property;
9783             var dsc = dir && dir.toLowerCase() == "desc";
9784             var f = function(o1, o2){
9785                 var v1 = sortType ? sortType(o1[p]) : o1[p];
9786                 var v2 = sortType ? sortType(o2[p]) : o2[p];
9787                 ;
9788                 if(v1 < v2){
9789                     return dsc ? +1 : -1;
9790                 } else if(v1 > v2){
9791                     return dsc ? -1 : +1;
9792                 } else{
9793                     return 0;
9794                 }
9795             };
9796             this.jsonData.sort(f);
9797             this.refresh();
9798             if(this.jsonData != this.snapshot){
9799                 this.snapshot.sort(f);
9800             }
9801         }
9802     }
9803 });/*
9804  * Based on:
9805  * Ext JS Library 1.1.1
9806  * Copyright(c) 2006-2007, Ext JS, LLC.
9807  *
9808  * Originally Released Under LGPL - original licence link has changed is not relivant.
9809  *
9810  * Fork - LGPL
9811  * <script type="text/javascript">
9812  */
9813  
9814
9815 /**
9816  * @class Roo.ColorPalette
9817  * @extends Roo.Component
9818  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
9819  * Here's an example of typical usage:
9820  * <pre><code>
9821 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
9822 cp.render('my-div');
9823
9824 cp.on('select', function(palette, selColor){
9825     // do something with selColor
9826 });
9827 </code></pre>
9828  * @constructor
9829  * Create a new ColorPalette
9830  * @param {Object} config The config object
9831  */
9832 Roo.ColorPalette = function(config){
9833     Roo.ColorPalette.superclass.constructor.call(this, config);
9834     this.addEvents({
9835         /**
9836              * @event select
9837              * Fires when a color is selected
9838              * @param {ColorPalette} this
9839              * @param {String} color The 6-digit color hex code (without the # symbol)
9840              */
9841         select: true
9842     });
9843
9844     if(this.handler){
9845         this.on("select", this.handler, this.scope, true);
9846     }
9847 };
9848 Roo.extend(Roo.ColorPalette, Roo.Component, {
9849     /**
9850      * @cfg {String} itemCls
9851      * The CSS class to apply to the containing element (defaults to "x-color-palette")
9852      */
9853     itemCls : "x-color-palette",
9854     /**
9855      * @cfg {String} value
9856      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
9857      * the hex codes are case-sensitive.
9858      */
9859     value : null,
9860     clickEvent:'click',
9861     // private
9862     ctype: "Roo.ColorPalette",
9863
9864     /**
9865      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
9866      */
9867     allowReselect : false,
9868
9869     /**
9870      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
9871      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
9872      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
9873      * of colors with the width setting until the box is symmetrical.</p>
9874      * <p>You can override individual colors if needed:</p>
9875      * <pre><code>
9876 var cp = new Roo.ColorPalette();
9877 cp.colors[0] = "FF0000";  // change the first box to red
9878 </code></pre>
9879
9880 Or you can provide a custom array of your own for complete control:
9881 <pre><code>
9882 var cp = new Roo.ColorPalette();
9883 cp.colors = ["000000", "993300", "333300"];
9884 </code></pre>
9885      * @type Array
9886      */
9887     colors : [
9888         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
9889         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
9890         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
9891         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
9892         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
9893     ],
9894
9895     // private
9896     onRender : function(container, position){
9897         var t = new Roo.MasterTemplate(
9898             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
9899         );
9900         var c = this.colors;
9901         for(var i = 0, len = c.length; i < len; i++){
9902             t.add([c[i]]);
9903         }
9904         var el = document.createElement("div");
9905         el.className = this.itemCls;
9906         t.overwrite(el);
9907         container.dom.insertBefore(el, position);
9908         this.el = Roo.get(el);
9909         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
9910         if(this.clickEvent != 'click'){
9911             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
9912         }
9913     },
9914
9915     // private
9916     afterRender : function(){
9917         Roo.ColorPalette.superclass.afterRender.call(this);
9918         if(this.value){
9919             var s = this.value;
9920             this.value = null;
9921             this.select(s);
9922         }
9923     },
9924
9925     // private
9926     handleClick : function(e, t){
9927         e.preventDefault();
9928         if(!this.disabled){
9929             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
9930             this.select(c.toUpperCase());
9931         }
9932     },
9933
9934     /**
9935      * Selects the specified color in the palette (fires the select event)
9936      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
9937      */
9938     select : function(color){
9939         color = color.replace("#", "");
9940         if(color != this.value || this.allowReselect){
9941             var el = this.el;
9942             if(this.value){
9943                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
9944             }
9945             el.child("a.color-"+color).addClass("x-color-palette-sel");
9946             this.value = color;
9947             this.fireEvent("select", this, color);
9948         }
9949     }
9950 });/*
9951  * Based on:
9952  * Ext JS Library 1.1.1
9953  * Copyright(c) 2006-2007, Ext JS, LLC.
9954  *
9955  * Originally Released Under LGPL - original licence link has changed is not relivant.
9956  *
9957  * Fork - LGPL
9958  * <script type="text/javascript">
9959  */
9960  
9961 /**
9962  * @class Roo.DatePicker
9963  * @extends Roo.Component
9964  * Simple date picker class.
9965  * @constructor
9966  * Create a new DatePicker
9967  * @param {Object} config The config object
9968  */
9969 Roo.DatePicker = function(config){
9970     Roo.DatePicker.superclass.constructor.call(this, config);
9971
9972     this.value = config && config.value ?
9973                  config.value.clearTime() : new Date().clearTime();
9974
9975     this.addEvents({
9976         /**
9977              * @event select
9978              * Fires when a date is selected
9979              * @param {DatePicker} this
9980              * @param {Date} date The selected date
9981              */
9982         select: true
9983     });
9984
9985     if(this.handler){
9986         this.on("select", this.handler,  this.scope || this);
9987     }
9988     // build the disabledDatesRE
9989     if(!this.disabledDatesRE && this.disabledDates){
9990         var dd = this.disabledDates;
9991         var re = "(?:";
9992         for(var i = 0; i < dd.length; i++){
9993             re += dd[i];
9994             if(i != dd.length-1) re += "|";
9995         }
9996         this.disabledDatesRE = new RegExp(re + ")");
9997     }
9998 };
9999
10000 Roo.extend(Roo.DatePicker, Roo.Component, {
10001     /**
10002      * @cfg {String} todayText
10003      * The text to display on the button that selects the current date (defaults to "Today")
10004      */
10005     todayText : "Today",
10006     /**
10007      * @cfg {String} okText
10008      * The text to display on the ok button
10009      */
10010     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
10011     /**
10012      * @cfg {String} cancelText
10013      * The text to display on the cancel button
10014      */
10015     cancelText : "Cancel",
10016     /**
10017      * @cfg {String} todayTip
10018      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
10019      */
10020     todayTip : "{0} (Spacebar)",
10021     /**
10022      * @cfg {Date} minDate
10023      * Minimum allowable date (JavaScript date object, defaults to null)
10024      */
10025     minDate : null,
10026     /**
10027      * @cfg {Date} maxDate
10028      * Maximum allowable date (JavaScript date object, defaults to null)
10029      */
10030     maxDate : null,
10031     /**
10032      * @cfg {String} minText
10033      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
10034      */
10035     minText : "This date is before the minimum date",
10036     /**
10037      * @cfg {String} maxText
10038      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
10039      */
10040     maxText : "This date is after the maximum date",
10041     /**
10042      * @cfg {String} format
10043      * The default date format string which can be overriden for localization support.  The format must be
10044      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
10045      */
10046     format : "m/d/y",
10047     /**
10048      * @cfg {Array} disabledDays
10049      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
10050      */
10051     disabledDays : null,
10052     /**
10053      * @cfg {String} disabledDaysText
10054      * The tooltip to display when the date falls on a disabled day (defaults to "")
10055      */
10056     disabledDaysText : "",
10057     /**
10058      * @cfg {RegExp} disabledDatesRE
10059      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
10060      */
10061     disabledDatesRE : null,
10062     /**
10063      * @cfg {String} disabledDatesText
10064      * The tooltip text to display when the date falls on a disabled date (defaults to "")
10065      */
10066     disabledDatesText : "",
10067     /**
10068      * @cfg {Boolean} constrainToViewport
10069      * True to constrain the date picker to the viewport (defaults to true)
10070      */
10071     constrainToViewport : true,
10072     /**
10073      * @cfg {Array} monthNames
10074      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
10075      */
10076     monthNames : Date.monthNames,
10077     /**
10078      * @cfg {Array} dayNames
10079      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
10080      */
10081     dayNames : Date.dayNames,
10082     /**
10083      * @cfg {String} nextText
10084      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
10085      */
10086     nextText: 'Next Month (Control+Right)',
10087     /**
10088      * @cfg {String} prevText
10089      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
10090      */
10091     prevText: 'Previous Month (Control+Left)',
10092     /**
10093      * @cfg {String} monthYearText
10094      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
10095      */
10096     monthYearText: 'Choose a month (Control+Up/Down to move years)',
10097     /**
10098      * @cfg {Number} startDay
10099      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
10100      */
10101     startDay : 0,
10102     /**
10103      * @cfg {Bool} showClear
10104      * Show a clear button (usefull for date form elements that can be blank.)
10105      */
10106     
10107     showClear: false,
10108     
10109     /**
10110      * Sets the value of the date field
10111      * @param {Date} value The date to set
10112      */
10113     setValue : function(value){
10114         var old = this.value;
10115         this.value = value.clearTime(true);
10116         if(this.el){
10117             this.update(this.value);
10118         }
10119     },
10120
10121     /**
10122      * Gets the current selected value of the date field
10123      * @return {Date} The selected date
10124      */
10125     getValue : function(){
10126         return this.value;
10127     },
10128
10129     // private
10130     focus : function(){
10131         if(this.el){
10132             this.update(this.activeDate);
10133         }
10134     },
10135
10136     // private
10137     onRender : function(container, position){
10138         var m = [
10139              '<table cellspacing="0">',
10140                 '<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>',
10141                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
10142         var dn = this.dayNames;
10143         for(var i = 0; i < 7; i++){
10144             var d = this.startDay+i;
10145             if(d > 6){
10146                 d = d-7;
10147             }
10148             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
10149         }
10150         m[m.length] = "</tr></thead><tbody><tr>";
10151         for(var i = 0; i < 42; i++) {
10152             if(i % 7 == 0 && i != 0){
10153                 m[m.length] = "</tr><tr>";
10154             }
10155             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
10156         }
10157         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
10158             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
10159
10160         var el = document.createElement("div");
10161         el.className = "x-date-picker";
10162         el.innerHTML = m.join("");
10163
10164         container.dom.insertBefore(el, position);
10165
10166         this.el = Roo.get(el);
10167         this.eventEl = Roo.get(el.firstChild);
10168
10169         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
10170             handler: this.showPrevMonth,
10171             scope: this,
10172             preventDefault:true,
10173             stopDefault:true
10174         });
10175
10176         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
10177             handler: this.showNextMonth,
10178             scope: this,
10179             preventDefault:true,
10180             stopDefault:true
10181         });
10182
10183         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
10184
10185         this.monthPicker = this.el.down('div.x-date-mp');
10186         this.monthPicker.enableDisplayMode('block');
10187         
10188         var kn = new Roo.KeyNav(this.eventEl, {
10189             "left" : function(e){
10190                 e.ctrlKey ?
10191                     this.showPrevMonth() :
10192                     this.update(this.activeDate.add("d", -1));
10193             },
10194
10195             "right" : function(e){
10196                 e.ctrlKey ?
10197                     this.showNextMonth() :
10198                     this.update(this.activeDate.add("d", 1));
10199             },
10200
10201             "up" : function(e){
10202                 e.ctrlKey ?
10203                     this.showNextYear() :
10204                     this.update(this.activeDate.add("d", -7));
10205             },
10206
10207             "down" : function(e){
10208                 e.ctrlKey ?
10209                     this.showPrevYear() :
10210                     this.update(this.activeDate.add("d", 7));
10211             },
10212
10213             "pageUp" : function(e){
10214                 this.showNextMonth();
10215             },
10216
10217             "pageDown" : function(e){
10218                 this.showPrevMonth();
10219             },
10220
10221             "enter" : function(e){
10222                 e.stopPropagation();
10223                 return true;
10224             },
10225
10226             scope : this
10227         });
10228
10229         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
10230
10231         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
10232
10233         this.el.unselectable();
10234         
10235         this.cells = this.el.select("table.x-date-inner tbody td");
10236         this.textNodes = this.el.query("table.x-date-inner tbody span");
10237
10238         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
10239             text: "&#160;",
10240             tooltip: this.monthYearText
10241         });
10242
10243         this.mbtn.on('click', this.showMonthPicker, this);
10244         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
10245
10246
10247         var today = (new Date()).dateFormat(this.format);
10248         
10249         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
10250         if (this.showClear) {
10251             baseTb.add( new Roo.Toolbar.Fill());
10252         }
10253         baseTb.add({
10254             text: String.format(this.todayText, today),
10255             tooltip: String.format(this.todayTip, today),
10256             handler: this.selectToday,
10257             scope: this
10258         });
10259         
10260         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
10261             
10262         //});
10263         if (this.showClear) {
10264             
10265             baseTb.add( new Roo.Toolbar.Fill());
10266             baseTb.add({
10267                 text: '&#160;',
10268                 cls: 'x-btn-icon x-btn-clear',
10269                 handler: function() {
10270                     //this.value = '';
10271                     this.fireEvent("select", this, '');
10272                 },
10273                 scope: this
10274             });
10275         }
10276         
10277         
10278         if(Roo.isIE){
10279             this.el.repaint();
10280         }
10281         this.update(this.value);
10282     },
10283
10284     createMonthPicker : function(){
10285         if(!this.monthPicker.dom.firstChild){
10286             var buf = ['<table border="0" cellspacing="0">'];
10287             for(var i = 0; i < 6; i++){
10288                 buf.push(
10289                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
10290                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
10291                     i == 0 ?
10292                     '<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>' :
10293                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
10294                 );
10295             }
10296             buf.push(
10297                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
10298                     this.okText,
10299                     '</button><button type="button" class="x-date-mp-cancel">',
10300                     this.cancelText,
10301                     '</button></td></tr>',
10302                 '</table>'
10303             );
10304             this.monthPicker.update(buf.join(''));
10305             this.monthPicker.on('click', this.onMonthClick, this);
10306             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
10307
10308             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
10309             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
10310
10311             this.mpMonths.each(function(m, a, i){
10312                 i += 1;
10313                 if((i%2) == 0){
10314                     m.dom.xmonth = 5 + Math.round(i * .5);
10315                 }else{
10316                     m.dom.xmonth = Math.round((i-1) * .5);
10317                 }
10318             });
10319         }
10320     },
10321
10322     showMonthPicker : function(){
10323         this.createMonthPicker();
10324         var size = this.el.getSize();
10325         this.monthPicker.setSize(size);
10326         this.monthPicker.child('table').setSize(size);
10327
10328         this.mpSelMonth = (this.activeDate || this.value).getMonth();
10329         this.updateMPMonth(this.mpSelMonth);
10330         this.mpSelYear = (this.activeDate || this.value).getFullYear();
10331         this.updateMPYear(this.mpSelYear);
10332
10333         this.monthPicker.slideIn('t', {duration:.2});
10334     },
10335
10336     updateMPYear : function(y){
10337         this.mpyear = y;
10338         var ys = this.mpYears.elements;
10339         for(var i = 1; i <= 10; i++){
10340             var td = ys[i-1], y2;
10341             if((i%2) == 0){
10342                 y2 = y + Math.round(i * .5);
10343                 td.firstChild.innerHTML = y2;
10344                 td.xyear = y2;
10345             }else{
10346                 y2 = y - (5-Math.round(i * .5));
10347                 td.firstChild.innerHTML = y2;
10348                 td.xyear = y2;
10349             }
10350             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
10351         }
10352     },
10353
10354     updateMPMonth : function(sm){
10355         this.mpMonths.each(function(m, a, i){
10356             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
10357         });
10358     },
10359
10360     selectMPMonth: function(m){
10361         
10362     },
10363
10364     onMonthClick : function(e, t){
10365         e.stopEvent();
10366         var el = new Roo.Element(t), pn;
10367         if(el.is('button.x-date-mp-cancel')){
10368             this.hideMonthPicker();
10369         }
10370         else if(el.is('button.x-date-mp-ok')){
10371             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10372             this.hideMonthPicker();
10373         }
10374         else if(pn = el.up('td.x-date-mp-month', 2)){
10375             this.mpMonths.removeClass('x-date-mp-sel');
10376             pn.addClass('x-date-mp-sel');
10377             this.mpSelMonth = pn.dom.xmonth;
10378         }
10379         else if(pn = el.up('td.x-date-mp-year', 2)){
10380             this.mpYears.removeClass('x-date-mp-sel');
10381             pn.addClass('x-date-mp-sel');
10382             this.mpSelYear = pn.dom.xyear;
10383         }
10384         else if(el.is('a.x-date-mp-prev')){
10385             this.updateMPYear(this.mpyear-10);
10386         }
10387         else if(el.is('a.x-date-mp-next')){
10388             this.updateMPYear(this.mpyear+10);
10389         }
10390     },
10391
10392     onMonthDblClick : function(e, t){
10393         e.stopEvent();
10394         var el = new Roo.Element(t), pn;
10395         if(pn = el.up('td.x-date-mp-month', 2)){
10396             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
10397             this.hideMonthPicker();
10398         }
10399         else if(pn = el.up('td.x-date-mp-year', 2)){
10400             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10401             this.hideMonthPicker();
10402         }
10403     },
10404
10405     hideMonthPicker : function(disableAnim){
10406         if(this.monthPicker){
10407             if(disableAnim === true){
10408                 this.monthPicker.hide();
10409             }else{
10410                 this.monthPicker.slideOut('t', {duration:.2});
10411             }
10412         }
10413     },
10414
10415     // private
10416     showPrevMonth : function(e){
10417         this.update(this.activeDate.add("mo", -1));
10418     },
10419
10420     // private
10421     showNextMonth : function(e){
10422         this.update(this.activeDate.add("mo", 1));
10423     },
10424
10425     // private
10426     showPrevYear : function(){
10427         this.update(this.activeDate.add("y", -1));
10428     },
10429
10430     // private
10431     showNextYear : function(){
10432         this.update(this.activeDate.add("y", 1));
10433     },
10434
10435     // private
10436     handleMouseWheel : function(e){
10437         var delta = e.getWheelDelta();
10438         if(delta > 0){
10439             this.showPrevMonth();
10440             e.stopEvent();
10441         } else if(delta < 0){
10442             this.showNextMonth();
10443             e.stopEvent();
10444         }
10445     },
10446
10447     // private
10448     handleDateClick : function(e, t){
10449         e.stopEvent();
10450         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
10451             this.setValue(new Date(t.dateValue));
10452             this.fireEvent("select", this, this.value);
10453         }
10454     },
10455
10456     // private
10457     selectToday : function(){
10458         this.setValue(new Date().clearTime());
10459         this.fireEvent("select", this, this.value);
10460     },
10461
10462     // private
10463     update : function(date){
10464         var vd = this.activeDate;
10465         this.activeDate = date;
10466         if(vd && this.el){
10467             var t = date.getTime();
10468             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10469                 this.cells.removeClass("x-date-selected");
10470                 this.cells.each(function(c){
10471                    if(c.dom.firstChild.dateValue == t){
10472                        c.addClass("x-date-selected");
10473                        setTimeout(function(){
10474                             try{c.dom.firstChild.focus();}catch(e){}
10475                        }, 50);
10476                        return false;
10477                    }
10478                 });
10479                 return;
10480             }
10481         }
10482         var days = date.getDaysInMonth();
10483         var firstOfMonth = date.getFirstDateOfMonth();
10484         var startingPos = firstOfMonth.getDay()-this.startDay;
10485
10486         if(startingPos <= this.startDay){
10487             startingPos += 7;
10488         }
10489
10490         var pm = date.add("mo", -1);
10491         var prevStart = pm.getDaysInMonth()-startingPos;
10492
10493         var cells = this.cells.elements;
10494         var textEls = this.textNodes;
10495         days += startingPos;
10496
10497         // convert everything to numbers so it's fast
10498         var day = 86400000;
10499         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10500         var today = new Date().clearTime().getTime();
10501         var sel = date.clearTime().getTime();
10502         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10503         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10504         var ddMatch = this.disabledDatesRE;
10505         var ddText = this.disabledDatesText;
10506         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10507         var ddaysText = this.disabledDaysText;
10508         var format = this.format;
10509
10510         var setCellClass = function(cal, cell){
10511             cell.title = "";
10512             var t = d.getTime();
10513             cell.firstChild.dateValue = t;
10514             if(t == today){
10515                 cell.className += " x-date-today";
10516                 cell.title = cal.todayText;
10517             }
10518             if(t == sel){
10519                 cell.className += " x-date-selected";
10520                 setTimeout(function(){
10521                     try{cell.firstChild.focus();}catch(e){}
10522                 }, 50);
10523             }
10524             // disabling
10525             if(t < min) {
10526                 cell.className = " x-date-disabled";
10527                 cell.title = cal.minText;
10528                 return;
10529             }
10530             if(t > max) {
10531                 cell.className = " x-date-disabled";
10532                 cell.title = cal.maxText;
10533                 return;
10534             }
10535             if(ddays){
10536                 if(ddays.indexOf(d.getDay()) != -1){
10537                     cell.title = ddaysText;
10538                     cell.className = " x-date-disabled";
10539                 }
10540             }
10541             if(ddMatch && format){
10542                 var fvalue = d.dateFormat(format);
10543                 if(ddMatch.test(fvalue)){
10544                     cell.title = ddText.replace("%0", fvalue);
10545                     cell.className = " x-date-disabled";
10546                 }
10547             }
10548         };
10549
10550         var i = 0;
10551         for(; i < startingPos; i++) {
10552             textEls[i].innerHTML = (++prevStart);
10553             d.setDate(d.getDate()+1);
10554             cells[i].className = "x-date-prevday";
10555             setCellClass(this, cells[i]);
10556         }
10557         for(; i < days; i++){
10558             intDay = i - startingPos + 1;
10559             textEls[i].innerHTML = (intDay);
10560             d.setDate(d.getDate()+1);
10561             cells[i].className = "x-date-active";
10562             setCellClass(this, cells[i]);
10563         }
10564         var extraDays = 0;
10565         for(; i < 42; i++) {
10566              textEls[i].innerHTML = (++extraDays);
10567              d.setDate(d.getDate()+1);
10568              cells[i].className = "x-date-nextday";
10569              setCellClass(this, cells[i]);
10570         }
10571
10572         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
10573
10574         if(!this.internalRender){
10575             var main = this.el.dom.firstChild;
10576             var w = main.offsetWidth;
10577             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10578             Roo.fly(main).setWidth(w);
10579             this.internalRender = true;
10580             // opera does not respect the auto grow header center column
10581             // then, after it gets a width opera refuses to recalculate
10582             // without a second pass
10583             if(Roo.isOpera && !this.secondPass){
10584                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10585                 this.secondPass = true;
10586                 this.update.defer(10, this, [date]);
10587             }
10588         }
10589     }
10590 });/*
10591  * Based on:
10592  * Ext JS Library 1.1.1
10593  * Copyright(c) 2006-2007, Ext JS, LLC.
10594  *
10595  * Originally Released Under LGPL - original licence link has changed is not relivant.
10596  *
10597  * Fork - LGPL
10598  * <script type="text/javascript">
10599  */
10600 /**
10601  * @class Roo.TabPanel
10602  * @extends Roo.util.Observable
10603  * A lightweight tab container.
10604  * <br><br>
10605  * Usage:
10606  * <pre><code>
10607 // basic tabs 1, built from existing content
10608 var tabs = new Roo.TabPanel("tabs1");
10609 tabs.addTab("script", "View Script");
10610 tabs.addTab("markup", "View Markup");
10611 tabs.activate("script");
10612
10613 // more advanced tabs, built from javascript
10614 var jtabs = new Roo.TabPanel("jtabs");
10615 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
10616
10617 // set up the UpdateManager
10618 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
10619 var updater = tab2.getUpdateManager();
10620 updater.setDefaultUrl("ajax1.htm");
10621 tab2.on('activate', updater.refresh, updater, true);
10622
10623 // Use setUrl for Ajax loading
10624 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
10625 tab3.setUrl("ajax2.htm", null, true);
10626
10627 // Disabled tab
10628 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
10629 tab4.disable();
10630
10631 jtabs.activate("jtabs-1");
10632  * </code></pre>
10633  * @constructor
10634  * Create a new TabPanel.
10635  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
10636  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
10637  */
10638 Roo.TabPanel = function(container, config){
10639     /**
10640     * The container element for this TabPanel.
10641     * @type Roo.Element
10642     */
10643     this.el = Roo.get(container, true);
10644     if(config){
10645         if(typeof config == "boolean"){
10646             this.tabPosition = config ? "bottom" : "top";
10647         }else{
10648             Roo.apply(this, config);
10649         }
10650     }
10651     if(this.tabPosition == "bottom"){
10652         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10653         this.el.addClass("x-tabs-bottom");
10654     }
10655     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
10656     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
10657     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
10658     if(Roo.isIE){
10659         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
10660     }
10661     if(this.tabPosition != "bottom"){
10662     /** The body element that contains {@link Roo.TabPanelItem} bodies.
10663      * @type Roo.Element
10664      */
10665       this.bodyEl = Roo.get(this.createBody(this.el.dom));
10666       this.el.addClass("x-tabs-top");
10667     }
10668     this.items = [];
10669
10670     this.bodyEl.setStyle("position", "relative");
10671
10672     this.active = null;
10673     this.activateDelegate = this.activate.createDelegate(this);
10674
10675     this.addEvents({
10676         /**
10677          * @event tabchange
10678          * Fires when the active tab changes
10679          * @param {Roo.TabPanel} this
10680          * @param {Roo.TabPanelItem} activePanel The new active tab
10681          */
10682         "tabchange": true,
10683         /**
10684          * @event beforetabchange
10685          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
10686          * @param {Roo.TabPanel} this
10687          * @param {Object} e Set cancel to true on this object to cancel the tab change
10688          * @param {Roo.TabPanelItem} tab The tab being changed to
10689          */
10690         "beforetabchange" : true
10691     });
10692
10693     Roo.EventManager.onWindowResize(this.onResize, this);
10694     this.cpad = this.el.getPadding("lr");
10695     this.hiddenCount = 0;
10696
10697     Roo.TabPanel.superclass.constructor.call(this);
10698 };
10699
10700 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
10701         /*
10702          *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
10703          */
10704     tabPosition : "top",
10705         /*
10706          *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
10707          */
10708     currentTabWidth : 0,
10709         /*
10710          *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
10711          */
10712     minTabWidth : 40,
10713         /*
10714          *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
10715          */
10716     maxTabWidth : 250,
10717         /*
10718          *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
10719          */
10720     preferredTabWidth : 175,
10721         /*
10722          *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
10723          */
10724     resizeTabs : false,
10725         /*
10726          *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
10727          */
10728     monitorResize : true,
10729
10730     /**
10731      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
10732      * @param {String} id The id of the div to use <b>or create</b>
10733      * @param {String} text The text for the tab
10734      * @param {String} content (optional) Content to put in the TabPanelItem body
10735      * @param {Boolean} closable (optional) True to create a close icon on the tab
10736      * @return {Roo.TabPanelItem} The created TabPanelItem
10737      */
10738     addTab : function(id, text, content, closable){
10739         var item = new Roo.TabPanelItem(this, id, text, closable);
10740         this.addTabItem(item);
10741         if(content){
10742             item.setContent(content);
10743         }
10744         return item;
10745     },
10746
10747     /**
10748      * Returns the {@link Roo.TabPanelItem} with the specified id/index
10749      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
10750      * @return {Roo.TabPanelItem}
10751      */
10752     getTab : function(id){
10753         return this.items[id];
10754     },
10755
10756     /**
10757      * Hides the {@link Roo.TabPanelItem} with the specified id/index
10758      * @param {String/Number} id The id or index of the TabPanelItem to hide.
10759      */
10760     hideTab : function(id){
10761         var t = this.items[id];
10762         if(!t.isHidden()){
10763            t.setHidden(true);
10764            this.hiddenCount++;
10765            this.autoSizeTabs();
10766         }
10767     },
10768
10769     /**
10770      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
10771      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
10772      */
10773     unhideTab : function(id){
10774         var t = this.items[id];
10775         if(t.isHidden()){
10776            t.setHidden(false);
10777            this.hiddenCount--;
10778            this.autoSizeTabs();
10779         }
10780     },
10781
10782     /**
10783      * Adds an existing {@link Roo.TabPanelItem}.
10784      * @param {Roo.TabPanelItem} item The TabPanelItem to add
10785      */
10786     addTabItem : function(item){
10787         this.items[item.id] = item;
10788         this.items.push(item);
10789         if(this.resizeTabs){
10790            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
10791            this.autoSizeTabs();
10792         }else{
10793             item.autoSize();
10794         }
10795     },
10796
10797     /**
10798      * Removes a {@link Roo.TabPanelItem}.
10799      * @param {String/Number} id The id or index of the TabPanelItem to remove.
10800      */
10801     removeTab : function(id){
10802         var items = this.items;
10803         var tab = items[id];
10804         if(!tab) { return; }
10805         var index = items.indexOf(tab);
10806         if(this.active == tab && items.length > 1){
10807             var newTab = this.getNextAvailable(index);
10808             if(newTab) {
10809                 newTab.activate();
10810             }
10811         }
10812         this.stripEl.dom.removeChild(tab.pnode.dom);
10813         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
10814             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
10815         }
10816         items.splice(index, 1);
10817         delete this.items[tab.id];
10818         tab.fireEvent("close", tab);
10819         tab.purgeListeners();
10820         this.autoSizeTabs();
10821     },
10822
10823     getNextAvailable : function(start){
10824         var items = this.items;
10825         var index = start;
10826         // look for a next tab that will slide over to
10827         // replace the one being removed
10828         while(index < items.length){
10829             var item = items[++index];
10830             if(item && !item.isHidden()){
10831                 return item;
10832             }
10833         }
10834         // if one isn't found select the previous tab (on the left)
10835         index = start;
10836         while(index >= 0){
10837             var item = items[--index];
10838             if(item && !item.isHidden()){
10839                 return item;
10840             }
10841         }
10842         return null;
10843     },
10844
10845     /**
10846      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
10847      * @param {String/Number} id The id or index of the TabPanelItem to disable.
10848      */
10849     disableTab : function(id){
10850         var tab = this.items[id];
10851         if(tab && this.active != tab){
10852             tab.disable();
10853         }
10854     },
10855
10856     /**
10857      * Enables a {@link Roo.TabPanelItem} that is disabled.
10858      * @param {String/Number} id The id or index of the TabPanelItem to enable.
10859      */
10860     enableTab : function(id){
10861         var tab = this.items[id];
10862         tab.enable();
10863     },
10864
10865     /**
10866      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
10867      * @param {String/Number} id The id or index of the TabPanelItem to activate.
10868      * @return {Roo.TabPanelItem} The TabPanelItem.
10869      */
10870     activate : function(id){
10871         var tab = this.items[id];
10872         if(!tab){
10873             return null;
10874         }
10875         if(tab == this.active || tab.disabled){
10876             return tab;
10877         }
10878         var e = {};
10879         this.fireEvent("beforetabchange", this, e, tab);
10880         if(e.cancel !== true && !tab.disabled){
10881             if(this.active){
10882                 this.active.hide();
10883             }
10884             this.active = this.items[id];
10885             this.active.show();
10886             this.fireEvent("tabchange", this, this.active);
10887         }
10888         return tab;
10889     },
10890
10891     /**
10892      * Gets the active {@link Roo.TabPanelItem}.
10893      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
10894      */
10895     getActiveTab : function(){
10896         return this.active;
10897     },
10898
10899     /**
10900      * Updates the tab body element to fit the height of the container element
10901      * for overflow scrolling
10902      * @param {Number} targetHeight (optional) Override the starting height from the elements height
10903      */
10904     syncHeight : function(targetHeight){
10905         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
10906         var bm = this.bodyEl.getMargins();
10907         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
10908         this.bodyEl.setHeight(newHeight);
10909         return newHeight;
10910     },
10911
10912     onResize : function(){
10913         if(this.monitorResize){
10914             this.autoSizeTabs();
10915         }
10916     },
10917
10918     /**
10919      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
10920      */
10921     beginUpdate : function(){
10922         this.updating = true;
10923     },
10924
10925     /**
10926      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
10927      */
10928     endUpdate : function(){
10929         this.updating = false;
10930         this.autoSizeTabs();
10931     },
10932
10933     /**
10934      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
10935      */
10936     autoSizeTabs : function(){
10937         var count = this.items.length;
10938         var vcount = count - this.hiddenCount;
10939         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
10940         var w = Math.max(this.el.getWidth() - this.cpad, 10);
10941         var availWidth = Math.floor(w / vcount);
10942         var b = this.stripBody;
10943         if(b.getWidth() > w){
10944             var tabs = this.items;
10945             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
10946             if(availWidth < this.minTabWidth){
10947                 /*if(!this.sleft){    // incomplete scrolling code
10948                     this.createScrollButtons();
10949                 }
10950                 this.showScroll();
10951                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
10952             }
10953         }else{
10954             if(this.currentTabWidth < this.preferredTabWidth){
10955                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
10956             }
10957         }
10958     },
10959
10960     /**
10961      * Returns the number of tabs in this TabPanel.
10962      * @return {Number}
10963      */
10964      getCount : function(){
10965          return this.items.length;
10966      },
10967
10968     /**
10969      * Resizes all the tabs to the passed width
10970      * @param {Number} The new width
10971      */
10972     setTabWidth : function(width){
10973         this.currentTabWidth = width;
10974         for(var i = 0, len = this.items.length; i < len; i++) {
10975                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
10976         }
10977     },
10978
10979     /**
10980      * Destroys this TabPanel
10981      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
10982      */
10983     destroy : function(removeEl){
10984         Roo.EventManager.removeResizeListener(this.onResize, this);
10985         for(var i = 0, len = this.items.length; i < len; i++){
10986             this.items[i].purgeListeners();
10987         }
10988         if(removeEl === true){
10989             this.el.update("");
10990             this.el.remove();
10991         }
10992     }
10993 });
10994
10995 /**
10996  * @class Roo.TabPanelItem
10997  * @extends Roo.util.Observable
10998  * Represents an individual item (tab plus body) in a TabPanel.
10999  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
11000  * @param {String} id The id of this TabPanelItem
11001  * @param {String} text The text for the tab of this TabPanelItem
11002  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
11003  */
11004 Roo.TabPanelItem = function(tabPanel, id, text, closable){
11005     /**
11006      * The {@link Roo.TabPanel} this TabPanelItem belongs to
11007      * @type Roo.TabPanel
11008      */
11009     this.tabPanel = tabPanel;
11010     /**
11011      * The id for this TabPanelItem
11012      * @type String
11013      */
11014     this.id = id;
11015     /** @private */
11016     this.disabled = false;
11017     /** @private */
11018     this.text = text;
11019     /** @private */
11020     this.loaded = false;
11021     this.closable = closable;
11022
11023     /**
11024      * The body element for this TabPanelItem.
11025      * @type Roo.Element
11026      */
11027     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
11028     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
11029     this.bodyEl.setStyle("display", "block");
11030     this.bodyEl.setStyle("zoom", "1");
11031     this.hideAction();
11032
11033     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
11034     /** @private */
11035     this.el = Roo.get(els.el, true);
11036     this.inner = Roo.get(els.inner, true);
11037     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
11038     this.pnode = Roo.get(els.el.parentNode, true);
11039     this.el.on("mousedown", this.onTabMouseDown, this);
11040     this.el.on("click", this.onTabClick, this);
11041     /** @private */
11042     if(closable){
11043         var c = Roo.get(els.close, true);
11044         c.dom.title = this.closeText;
11045         c.addClassOnOver("close-over");
11046         c.on("click", this.closeClick, this);
11047      }
11048
11049     this.addEvents({
11050          /**
11051          * @event activate
11052          * Fires when this tab becomes the active tab.
11053          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11054          * @param {Roo.TabPanelItem} this
11055          */
11056         "activate": true,
11057         /**
11058          * @event beforeclose
11059          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
11060          * @param {Roo.TabPanelItem} this
11061          * @param {Object} e Set cancel to true on this object to cancel the close.
11062          */
11063         "beforeclose": true,
11064         /**
11065          * @event close
11066          * Fires when this tab is closed.
11067          * @param {Roo.TabPanelItem} this
11068          */
11069          "close": true,
11070         /**
11071          * @event deactivate
11072          * Fires when this tab is no longer the active tab.
11073          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11074          * @param {Roo.TabPanelItem} this
11075          */
11076          "deactivate" : true
11077     });
11078     this.hidden = false;
11079
11080     Roo.TabPanelItem.superclass.constructor.call(this);
11081 };
11082
11083 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
11084     purgeListeners : function(){
11085        Roo.util.Observable.prototype.purgeListeners.call(this);
11086        this.el.removeAllListeners();
11087     },
11088     /**
11089      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
11090      */
11091     show : function(){
11092         this.pnode.addClass("on");
11093         this.showAction();
11094         if(Roo.isOpera){
11095             this.tabPanel.stripWrap.repaint();
11096         }
11097         this.fireEvent("activate", this.tabPanel, this);
11098     },
11099
11100     /**
11101      * Returns true if this tab is the active tab.
11102      * @return {Boolean}
11103      */
11104     isActive : function(){
11105         return this.tabPanel.getActiveTab() == this;
11106     },
11107
11108     /**
11109      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
11110      */
11111     hide : function(){
11112         this.pnode.removeClass("on");
11113         this.hideAction();
11114         this.fireEvent("deactivate", this.tabPanel, this);
11115     },
11116
11117     hideAction : function(){
11118         this.bodyEl.hide();
11119         this.bodyEl.setStyle("position", "absolute");
11120         this.bodyEl.setLeft("-20000px");
11121         this.bodyEl.setTop("-20000px");
11122     },
11123
11124     showAction : function(){
11125         this.bodyEl.setStyle("position", "relative");
11126         this.bodyEl.setTop("");
11127         this.bodyEl.setLeft("");
11128         this.bodyEl.show();
11129     },
11130
11131     /**
11132      * Set the tooltip for the tab.
11133      * @param {String} tooltip The tab's tooltip
11134      */
11135     setTooltip : function(text){
11136         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
11137             this.textEl.dom.qtip = text;
11138             this.textEl.dom.removeAttribute('title');
11139         }else{
11140             this.textEl.dom.title = text;
11141         }
11142     },
11143
11144     onTabClick : function(e){
11145         e.preventDefault();
11146         this.tabPanel.activate(this.id);
11147     },
11148
11149     onTabMouseDown : function(e){
11150         e.preventDefault();
11151         this.tabPanel.activate(this.id);
11152     },
11153
11154     getWidth : function(){
11155         return this.inner.getWidth();
11156     },
11157
11158     setWidth : function(width){
11159         var iwidth = width - this.pnode.getPadding("lr");
11160         this.inner.setWidth(iwidth);
11161         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
11162         this.pnode.setWidth(width);
11163     },
11164
11165     /**
11166      * Show or hide the tab
11167      * @param {Boolean} hidden True to hide or false to show.
11168      */
11169     setHidden : function(hidden){
11170         this.hidden = hidden;
11171         this.pnode.setStyle("display", hidden ? "none" : "");
11172     },
11173
11174     /**
11175      * Returns true if this tab is "hidden"
11176      * @return {Boolean}
11177      */
11178     isHidden : function(){
11179         return this.hidden;
11180     },
11181
11182     /**
11183      * Returns the text for this tab
11184      * @return {String}
11185      */
11186     getText : function(){
11187         return this.text;
11188     },
11189
11190     autoSize : function(){
11191         //this.el.beginMeasure();
11192         this.textEl.setWidth(1);
11193         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
11194         //this.el.endMeasure();
11195     },
11196
11197     /**
11198      * Sets the text for the tab (Note: this also sets the tooltip text)
11199      * @param {String} text The tab's text and tooltip
11200      */
11201     setText : function(text){
11202         this.text = text;
11203         this.textEl.update(text);
11204         this.setTooltip(text);
11205         if(!this.tabPanel.resizeTabs){
11206             this.autoSize();
11207         }
11208     },
11209     /**
11210      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
11211      */
11212     activate : function(){
11213         this.tabPanel.activate(this.id);
11214     },
11215
11216     /**
11217      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
11218      */
11219     disable : function(){
11220         if(this.tabPanel.active != this){
11221             this.disabled = true;
11222             this.pnode.addClass("disabled");
11223         }
11224     },
11225
11226     /**
11227      * Enables this TabPanelItem if it was previously disabled.
11228      */
11229     enable : function(){
11230         this.disabled = false;
11231         this.pnode.removeClass("disabled");
11232     },
11233
11234     /**
11235      * Sets the content for this TabPanelItem.
11236      * @param {String} content The content
11237      * @param {Boolean} loadScripts true to look for and load scripts
11238      */
11239     setContent : function(content, loadScripts){
11240         this.bodyEl.update(content, loadScripts);
11241     },
11242
11243     /**
11244      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
11245      * @return {Roo.UpdateManager} The UpdateManager
11246      */
11247     getUpdateManager : function(){
11248         return this.bodyEl.getUpdateManager();
11249     },
11250
11251     /**
11252      * Set a URL to be used to load the content for this TabPanelItem.
11253      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
11254      * @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)
11255      * @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)
11256      * @return {Roo.UpdateManager} The UpdateManager
11257      */
11258     setUrl : function(url, params, loadOnce){
11259         if(this.refreshDelegate){
11260             this.un('activate', this.refreshDelegate);
11261         }
11262         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
11263         this.on("activate", this.refreshDelegate);
11264         return this.bodyEl.getUpdateManager();
11265     },
11266
11267     /** @private */
11268     _handleRefresh : function(url, params, loadOnce){
11269         if(!loadOnce || !this.loaded){
11270             var updater = this.bodyEl.getUpdateManager();
11271             updater.update(url, params, this._setLoaded.createDelegate(this));
11272         }
11273     },
11274
11275     /**
11276      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
11277      *   Will fail silently if the setUrl method has not been called.
11278      *   This does not activate the panel, just updates its content.
11279      */
11280     refresh : function(){
11281         if(this.refreshDelegate){
11282            this.loaded = false;
11283            this.refreshDelegate();
11284         }
11285     },
11286
11287     /** @private */
11288     _setLoaded : function(){
11289         this.loaded = true;
11290     },
11291
11292     /** @private */
11293     closeClick : function(e){
11294         var o = {};
11295         e.stopEvent();
11296         this.fireEvent("beforeclose", this, o);
11297         if(o.cancel !== true){
11298             this.tabPanel.removeTab(this.id);
11299         }
11300     },
11301     /**
11302      * The text displayed in the tooltip for the close icon.
11303      * @type String
11304      */
11305     closeText : "Close this tab"
11306 });
11307
11308 /** @private */
11309 Roo.TabPanel.prototype.createStrip = function(container){
11310     var strip = document.createElement("div");
11311     strip.className = "x-tabs-wrap";
11312     container.appendChild(strip);
11313     return strip;
11314 };
11315 /** @private */
11316 Roo.TabPanel.prototype.createStripList = function(strip){
11317     // div wrapper for retard IE
11318     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>';
11319     return strip.firstChild.firstChild.firstChild.firstChild;
11320 };
11321 /** @private */
11322 Roo.TabPanel.prototype.createBody = function(container){
11323     var body = document.createElement("div");
11324     Roo.id(body, "tab-body");
11325     Roo.fly(body).addClass("x-tabs-body");
11326     container.appendChild(body);
11327     return body;
11328 };
11329 /** @private */
11330 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
11331     var body = Roo.getDom(id);
11332     if(!body){
11333         body = document.createElement("div");
11334         body.id = id;
11335     }
11336     Roo.fly(body).addClass("x-tabs-item-body");
11337     bodyEl.insertBefore(body, bodyEl.firstChild);
11338     return body;
11339 };
11340 /** @private */
11341 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
11342     var td = document.createElement("td");
11343     stripEl.appendChild(td);
11344     if(closable){
11345         td.className = "x-tabs-closable";
11346         if(!this.closeTpl){
11347             this.closeTpl = new Roo.Template(
11348                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11349                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
11350                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
11351             );
11352         }
11353         var el = this.closeTpl.overwrite(td, {"text": text});
11354         var close = el.getElementsByTagName("div")[0];
11355         var inner = el.getElementsByTagName("em")[0];
11356         return {"el": el, "close": close, "inner": inner};
11357     } else {
11358         if(!this.tabTpl){
11359             this.tabTpl = new Roo.Template(
11360                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11361                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
11362             );
11363         }
11364         var el = this.tabTpl.overwrite(td, {"text": text});
11365         var inner = el.getElementsByTagName("em")[0];
11366         return {"el": el, "inner": inner};
11367     }
11368 };/*
11369  * Based on:
11370  * Ext JS Library 1.1.1
11371  * Copyright(c) 2006-2007, Ext JS, LLC.
11372  *
11373  * Originally Released Under LGPL - original licence link has changed is not relivant.
11374  *
11375  * Fork - LGPL
11376  * <script type="text/javascript">
11377  */
11378
11379 /**
11380  * @class Roo.Button
11381  * @extends Roo.util.Observable
11382  * Simple Button class
11383  * @cfg {String} text The button text
11384  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
11385  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
11386  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
11387  * @cfg {Object} scope The scope of the handler
11388  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
11389  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
11390  * @cfg {Boolean} hidden True to start hidden (defaults to false)
11391  * @cfg {Boolean} disabled True to start disabled (defaults to false)
11392  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
11393  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
11394    applies if enableToggle = true)
11395  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
11396  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
11397   an {@link Roo.util.ClickRepeater} config object (defaults to false).
11398  * @constructor
11399  * Create a new button
11400  * @param {Object} config The config object
11401  */
11402 Roo.Button = function(renderTo, config)
11403 {
11404     if (!config) {
11405         config = renderTo;
11406         renderTo = config.renderTo || false;
11407     }
11408     
11409     Roo.apply(this, config);
11410     this.addEvents({
11411         /**
11412              * @event click
11413              * Fires when this button is clicked
11414              * @param {Button} this
11415              * @param {EventObject} e The click event
11416              */
11417             "click" : true,
11418         /**
11419              * @event toggle
11420              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
11421              * @param {Button} this
11422              * @param {Boolean} pressed
11423              */
11424             "toggle" : true,
11425         /**
11426              * @event mouseover
11427              * Fires when the mouse hovers over the button
11428              * @param {Button} this
11429              * @param {Event} e The event object
11430              */
11431         'mouseover' : true,
11432         /**
11433              * @event mouseout
11434              * Fires when the mouse exits the button
11435              * @param {Button} this
11436              * @param {Event} e The event object
11437              */
11438         'mouseout': true,
11439          /**
11440              * @event render
11441              * Fires when the button is rendered
11442              * @param {Button} this
11443              */
11444         'render': true
11445     });
11446     if(this.menu){
11447         this.menu = Roo.menu.MenuMgr.get(this.menu);
11448     }
11449     // register listeners first!!  - so render can be captured..
11450     Roo.util.Observable.call(this);
11451     if(renderTo){
11452         this.render(renderTo);
11453     }
11454     
11455   
11456 };
11457
11458 Roo.extend(Roo.Button, Roo.util.Observable, {
11459     /**
11460      * 
11461      */
11462     
11463     /**
11464      * Read-only. True if this button is hidden
11465      * @type Boolean
11466      */
11467     hidden : false,
11468     /**
11469      * Read-only. True if this button is disabled
11470      * @type Boolean
11471      */
11472     disabled : false,
11473     /**
11474      * Read-only. True if this button is pressed (only if enableToggle = true)
11475      * @type Boolean
11476      */
11477     pressed : false,
11478
11479     /**
11480      * @cfg {Number} tabIndex 
11481      * The DOM tabIndex for this button (defaults to undefined)
11482      */
11483     tabIndex : undefined,
11484
11485     /**
11486      * @cfg {Boolean} enableToggle
11487      * True to enable pressed/not pressed toggling (defaults to false)
11488      */
11489     enableToggle: false,
11490     /**
11491      * @cfg {Mixed} menu
11492      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
11493      */
11494     menu : undefined,
11495     /**
11496      * @cfg {String} menuAlign
11497      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
11498      */
11499     menuAlign : "tl-bl?",
11500
11501     /**
11502      * @cfg {String} iconCls
11503      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
11504      */
11505     iconCls : undefined,
11506     /**
11507      * @cfg {String} type
11508      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
11509      */
11510     type : 'button',
11511
11512     // private
11513     menuClassTarget: 'tr',
11514
11515     /**
11516      * @cfg {String} clickEvent
11517      * The type of event to map to the button's event handler (defaults to 'click')
11518      */
11519     clickEvent : 'click',
11520
11521     /**
11522      * @cfg {Boolean} handleMouseEvents
11523      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
11524      */
11525     handleMouseEvents : true,
11526
11527     /**
11528      * @cfg {String} tooltipType
11529      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
11530      */
11531     tooltipType : 'qtip',
11532
11533     /**
11534      * @cfg {String} cls
11535      * A CSS class to apply to the button's main element.
11536      */
11537     
11538     /**
11539      * @cfg {Roo.Template} template (Optional)
11540      * An {@link Roo.Template} with which to create the Button's main element. This Template must
11541      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
11542      * require code modifications if required elements (e.g. a button) aren't present.
11543      */
11544
11545     // private
11546     render : function(renderTo){
11547         var btn;
11548         if(this.hideParent){
11549             this.parentEl = Roo.get(renderTo);
11550         }
11551         if(!this.dhconfig){
11552             if(!this.template){
11553                 if(!Roo.Button.buttonTemplate){
11554                     // hideous table template
11555                     Roo.Button.buttonTemplate = new Roo.Template(
11556                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
11557                         '<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>',
11558                         "</tr></tbody></table>");
11559                 }
11560                 this.template = Roo.Button.buttonTemplate;
11561             }
11562             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
11563             var btnEl = btn.child("button:first");
11564             btnEl.on('focus', this.onFocus, this);
11565             btnEl.on('blur', this.onBlur, this);
11566             if(this.cls){
11567                 btn.addClass(this.cls);
11568             }
11569             if(this.icon){
11570                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
11571             }
11572             if(this.iconCls){
11573                 btnEl.addClass(this.iconCls);
11574                 if(!this.cls){
11575                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11576                 }
11577             }
11578             if(this.tabIndex !== undefined){
11579                 btnEl.dom.tabIndex = this.tabIndex;
11580             }
11581             if(this.tooltip){
11582                 if(typeof this.tooltip == 'object'){
11583                     Roo.QuickTips.tips(Roo.apply({
11584                           target: btnEl.id
11585                     }, this.tooltip));
11586                 } else {
11587                     btnEl.dom[this.tooltipType] = this.tooltip;
11588                 }
11589             }
11590         }else{
11591             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
11592         }
11593         this.el = btn;
11594         if(this.id){
11595             this.el.dom.id = this.el.id = this.id;
11596         }
11597         if(this.menu){
11598             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
11599             this.menu.on("show", this.onMenuShow, this);
11600             this.menu.on("hide", this.onMenuHide, this);
11601         }
11602         btn.addClass("x-btn");
11603         if(Roo.isIE && !Roo.isIE7){
11604             this.autoWidth.defer(1, this);
11605         }else{
11606             this.autoWidth();
11607         }
11608         if(this.handleMouseEvents){
11609             btn.on("mouseover", this.onMouseOver, this);
11610             btn.on("mouseout", this.onMouseOut, this);
11611             btn.on("mousedown", this.onMouseDown, this);
11612         }
11613         btn.on(this.clickEvent, this.onClick, this);
11614         //btn.on("mouseup", this.onMouseUp, this);
11615         if(this.hidden){
11616             this.hide();
11617         }
11618         if(this.disabled){
11619             this.disable();
11620         }
11621         Roo.ButtonToggleMgr.register(this);
11622         if(this.pressed){
11623             this.el.addClass("x-btn-pressed");
11624         }
11625         if(this.repeat){
11626             var repeater = new Roo.util.ClickRepeater(btn,
11627                 typeof this.repeat == "object" ? this.repeat : {}
11628             );
11629             repeater.on("click", this.onClick,  this);
11630         }
11631         
11632         this.fireEvent('render', this);
11633         
11634     },
11635     /**
11636      * Returns the button's underlying element
11637      * @return {Roo.Element} The element
11638      */
11639     getEl : function(){
11640         return this.el;  
11641     },
11642     
11643     /**
11644      * Destroys this Button and removes any listeners.
11645      */
11646     destroy : function(){
11647         Roo.ButtonToggleMgr.unregister(this);
11648         this.el.removeAllListeners();
11649         this.purgeListeners();
11650         this.el.remove();
11651     },
11652
11653     // private
11654     autoWidth : function(){
11655         if(this.el){
11656             this.el.setWidth("auto");
11657             if(Roo.isIE7 && Roo.isStrict){
11658                 var ib = this.el.child('button');
11659                 if(ib && ib.getWidth() > 20){
11660                     ib.clip();
11661                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11662                 }
11663             }
11664             if(this.minWidth){
11665                 if(this.hidden){
11666                     this.el.beginMeasure();
11667                 }
11668                 if(this.el.getWidth() < this.minWidth){
11669                     this.el.setWidth(this.minWidth);
11670                 }
11671                 if(this.hidden){
11672                     this.el.endMeasure();
11673                 }
11674             }
11675         }
11676     },
11677
11678     /**
11679      * Assigns this button's click handler
11680      * @param {Function} handler The function to call when the button is clicked
11681      * @param {Object} scope (optional) Scope for the function passed in
11682      */
11683     setHandler : function(handler, scope){
11684         this.handler = handler;
11685         this.scope = scope;  
11686     },
11687     
11688     /**
11689      * Sets this button's text
11690      * @param {String} text The button text
11691      */
11692     setText : function(text){
11693         this.text = text;
11694         if(this.el){
11695             this.el.child("td.x-btn-center button.x-btn-text").update(text);
11696         }
11697         this.autoWidth();
11698     },
11699     
11700     /**
11701      * Gets the text for this button
11702      * @return {String} The button text
11703      */
11704     getText : function(){
11705         return this.text;  
11706     },
11707     
11708     /**
11709      * Show this button
11710      */
11711     show: function(){
11712         this.hidden = false;
11713         if(this.el){
11714             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
11715         }
11716     },
11717     
11718     /**
11719      * Hide this button
11720      */
11721     hide: function(){
11722         this.hidden = true;
11723         if(this.el){
11724             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
11725         }
11726     },
11727     
11728     /**
11729      * Convenience function for boolean show/hide
11730      * @param {Boolean} visible True to show, false to hide
11731      */
11732     setVisible: function(visible){
11733         if(visible) {
11734             this.show();
11735         }else{
11736             this.hide();
11737         }
11738     },
11739     
11740     /**
11741      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
11742      * @param {Boolean} state (optional) Force a particular state
11743      */
11744     toggle : function(state){
11745         state = state === undefined ? !this.pressed : state;
11746         if(state != this.pressed){
11747             if(state){
11748                 this.el.addClass("x-btn-pressed");
11749                 this.pressed = true;
11750                 this.fireEvent("toggle", this, true);
11751             }else{
11752                 this.el.removeClass("x-btn-pressed");
11753                 this.pressed = false;
11754                 this.fireEvent("toggle", this, false);
11755             }
11756             if(this.toggleHandler){
11757                 this.toggleHandler.call(this.scope || this, this, state);
11758             }
11759         }
11760     },
11761     
11762     /**
11763      * Focus the button
11764      */
11765     focus : function(){
11766         this.el.child('button:first').focus();
11767     },
11768     
11769     /**
11770      * Disable this button
11771      */
11772     disable : function(){
11773         if(this.el){
11774             this.el.addClass("x-btn-disabled");
11775         }
11776         this.disabled = true;
11777     },
11778     
11779     /**
11780      * Enable this button
11781      */
11782     enable : function(){
11783         if(this.el){
11784             this.el.removeClass("x-btn-disabled");
11785         }
11786         this.disabled = false;
11787     },
11788
11789     /**
11790      * Convenience function for boolean enable/disable
11791      * @param {Boolean} enabled True to enable, false to disable
11792      */
11793     setDisabled : function(v){
11794         this[v !== true ? "enable" : "disable"]();
11795     },
11796
11797     // private
11798     onClick : function(e){
11799         if(e){
11800             e.preventDefault();
11801         }
11802         if(e.button != 0){
11803             return;
11804         }
11805         if(!this.disabled){
11806             if(this.enableToggle){
11807                 this.toggle();
11808             }
11809             if(this.menu && !this.menu.isVisible()){
11810                 this.menu.show(this.el, this.menuAlign);
11811             }
11812             this.fireEvent("click", this, e);
11813             if(this.handler){
11814                 this.el.removeClass("x-btn-over");
11815                 this.handler.call(this.scope || this, this, e);
11816             }
11817         }
11818     },
11819     // private
11820     onMouseOver : function(e){
11821         if(!this.disabled){
11822             this.el.addClass("x-btn-over");
11823             this.fireEvent('mouseover', this, e);
11824         }
11825     },
11826     // private
11827     onMouseOut : function(e){
11828         if(!e.within(this.el,  true)){
11829             this.el.removeClass("x-btn-over");
11830             this.fireEvent('mouseout', this, e);
11831         }
11832     },
11833     // private
11834     onFocus : function(e){
11835         if(!this.disabled){
11836             this.el.addClass("x-btn-focus");
11837         }
11838     },
11839     // private
11840     onBlur : function(e){
11841         this.el.removeClass("x-btn-focus");
11842     },
11843     // private
11844     onMouseDown : function(e){
11845         if(!this.disabled && e.button == 0){
11846             this.el.addClass("x-btn-click");
11847             Roo.get(document).on('mouseup', this.onMouseUp, this);
11848         }
11849     },
11850     // private
11851     onMouseUp : function(e){
11852         if(e.button == 0){
11853             this.el.removeClass("x-btn-click");
11854             Roo.get(document).un('mouseup', this.onMouseUp, this);
11855         }
11856     },
11857     // private
11858     onMenuShow : function(e){
11859         this.el.addClass("x-btn-menu-active");
11860     },
11861     // private
11862     onMenuHide : function(e){
11863         this.el.removeClass("x-btn-menu-active");
11864     }   
11865 });
11866
11867 // Private utility class used by Button
11868 Roo.ButtonToggleMgr = function(){
11869    var groups = {};
11870    
11871    function toggleGroup(btn, state){
11872        if(state){
11873            var g = groups[btn.toggleGroup];
11874            for(var i = 0, l = g.length; i < l; i++){
11875                if(g[i] != btn){
11876                    g[i].toggle(false);
11877                }
11878            }
11879        }
11880    }
11881    
11882    return {
11883        register : function(btn){
11884            if(!btn.toggleGroup){
11885                return;
11886            }
11887            var g = groups[btn.toggleGroup];
11888            if(!g){
11889                g = groups[btn.toggleGroup] = [];
11890            }
11891            g.push(btn);
11892            btn.on("toggle", toggleGroup);
11893        },
11894        
11895        unregister : function(btn){
11896            if(!btn.toggleGroup){
11897                return;
11898            }
11899            var g = groups[btn.toggleGroup];
11900            if(g){
11901                g.remove(btn);
11902                btn.un("toggle", toggleGroup);
11903            }
11904        }
11905    };
11906 }();/*
11907  * Based on:
11908  * Ext JS Library 1.1.1
11909  * Copyright(c) 2006-2007, Ext JS, LLC.
11910  *
11911  * Originally Released Under LGPL - original licence link has changed is not relivant.
11912  *
11913  * Fork - LGPL
11914  * <script type="text/javascript">
11915  */
11916  
11917 /**
11918  * @class Roo.SplitButton
11919  * @extends Roo.Button
11920  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
11921  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
11922  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
11923  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
11924  * @cfg {String} arrowTooltip The title attribute of the arrow
11925  * @constructor
11926  * Create a new menu button
11927  * @param {String/HTMLElement/Element} renderTo The element to append the button to
11928  * @param {Object} config The config object
11929  */
11930 Roo.SplitButton = function(renderTo, config){
11931     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
11932     /**
11933      * @event arrowclick
11934      * Fires when this button's arrow is clicked
11935      * @param {SplitButton} this
11936      * @param {EventObject} e The click event
11937      */
11938     this.addEvents({"arrowclick":true});
11939 };
11940
11941 Roo.extend(Roo.SplitButton, Roo.Button, {
11942     render : function(renderTo){
11943         // this is one sweet looking template!
11944         var tpl = new Roo.Template(
11945             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
11946             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
11947             '<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>',
11948             "</tbody></table></td><td>",
11949             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
11950             '<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>',
11951             "</tbody></table></td></tr></table>"
11952         );
11953         var btn = tpl.append(renderTo, [this.text, this.type], true);
11954         var btnEl = btn.child("button");
11955         if(this.cls){
11956             btn.addClass(this.cls);
11957         }
11958         if(this.icon){
11959             btnEl.setStyle('background-image', 'url(' +this.icon +')');
11960         }
11961         if(this.iconCls){
11962             btnEl.addClass(this.iconCls);
11963             if(!this.cls){
11964                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11965             }
11966         }
11967         this.el = btn;
11968         if(this.handleMouseEvents){
11969             btn.on("mouseover", this.onMouseOver, this);
11970             btn.on("mouseout", this.onMouseOut, this);
11971             btn.on("mousedown", this.onMouseDown, this);
11972             btn.on("mouseup", this.onMouseUp, this);
11973         }
11974         btn.on(this.clickEvent, this.onClick, this);
11975         if(this.tooltip){
11976             if(typeof this.tooltip == 'object'){
11977                 Roo.QuickTips.tips(Roo.apply({
11978                       target: btnEl.id
11979                 }, this.tooltip));
11980             } else {
11981                 btnEl.dom[this.tooltipType] = this.tooltip;
11982             }
11983         }
11984         if(this.arrowTooltip){
11985             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
11986         }
11987         if(this.hidden){
11988             this.hide();
11989         }
11990         if(this.disabled){
11991             this.disable();
11992         }
11993         if(this.pressed){
11994             this.el.addClass("x-btn-pressed");
11995         }
11996         if(Roo.isIE && !Roo.isIE7){
11997             this.autoWidth.defer(1, this);
11998         }else{
11999             this.autoWidth();
12000         }
12001         if(this.menu){
12002             this.menu.on("show", this.onMenuShow, this);
12003             this.menu.on("hide", this.onMenuHide, this);
12004         }
12005         this.fireEvent('render', this);
12006     },
12007
12008     // private
12009     autoWidth : function(){
12010         if(this.el){
12011             var tbl = this.el.child("table:first");
12012             var tbl2 = this.el.child("table:last");
12013             this.el.setWidth("auto");
12014             tbl.setWidth("auto");
12015             if(Roo.isIE7 && Roo.isStrict){
12016                 var ib = this.el.child('button:first');
12017                 if(ib && ib.getWidth() > 20){
12018                     ib.clip();
12019                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
12020                 }
12021             }
12022             if(this.minWidth){
12023                 if(this.hidden){
12024                     this.el.beginMeasure();
12025                 }
12026                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
12027                     tbl.setWidth(this.minWidth-tbl2.getWidth());
12028                 }
12029                 if(this.hidden){
12030                     this.el.endMeasure();
12031                 }
12032             }
12033             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
12034         } 
12035     },
12036     /**
12037      * Sets this button's click handler
12038      * @param {Function} handler The function to call when the button is clicked
12039      * @param {Object} scope (optional) Scope for the function passed above
12040      */
12041     setHandler : function(handler, scope){
12042         this.handler = handler;
12043         this.scope = scope;  
12044     },
12045     
12046     /**
12047      * Sets this button's arrow click handler
12048      * @param {Function} handler The function to call when the arrow is clicked
12049      * @param {Object} scope (optional) Scope for the function passed above
12050      */
12051     setArrowHandler : function(handler, scope){
12052         this.arrowHandler = handler;
12053         this.scope = scope;  
12054     },
12055     
12056     /**
12057      * Focus the button
12058      */
12059     focus : function(){
12060         if(this.el){
12061             this.el.child("button:first").focus();
12062         }
12063     },
12064
12065     // private
12066     onClick : function(e){
12067         e.preventDefault();
12068         if(!this.disabled){
12069             if(e.getTarget(".x-btn-menu-arrow-wrap")){
12070                 if(this.menu && !this.menu.isVisible()){
12071                     this.menu.show(this.el, this.menuAlign);
12072                 }
12073                 this.fireEvent("arrowclick", this, e);
12074                 if(this.arrowHandler){
12075                     this.arrowHandler.call(this.scope || this, this, e);
12076                 }
12077             }else{
12078                 this.fireEvent("click", this, e);
12079                 if(this.handler){
12080                     this.handler.call(this.scope || this, this, e);
12081                 }
12082             }
12083         }
12084     },
12085     // private
12086     onMouseDown : function(e){
12087         if(!this.disabled){
12088             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
12089         }
12090     },
12091     // private
12092     onMouseUp : function(e){
12093         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
12094     }   
12095 });
12096
12097
12098 // backwards compat
12099 Roo.MenuButton = Roo.SplitButton;/*
12100  * Based on:
12101  * Ext JS Library 1.1.1
12102  * Copyright(c) 2006-2007, Ext JS, LLC.
12103  *
12104  * Originally Released Under LGPL - original licence link has changed is not relivant.
12105  *
12106  * Fork - LGPL
12107  * <script type="text/javascript">
12108  */
12109
12110 /**
12111  * @class Roo.Toolbar
12112  * Basic Toolbar class.
12113  * @constructor
12114  * Creates a new Toolbar
12115  * @param {Object} config The config object
12116  */ 
12117 Roo.Toolbar = function(container, buttons, config)
12118 {
12119     /// old consturctor format still supported..
12120     if(container instanceof Array){ // omit the container for later rendering
12121         buttons = container;
12122         config = buttons;
12123         container = null;
12124     }
12125     if (typeof(container) == 'object' && container.xtype) {
12126         config = container;
12127         container = config.container;
12128         buttons = config.buttons; // not really - use items!!
12129     }
12130     var xitems = [];
12131     if (config && config.items) {
12132         xitems = config.items;
12133         delete config.items;
12134     }
12135     Roo.apply(this, config);
12136     this.buttons = buttons;
12137     
12138     if(container){
12139         this.render(container);
12140     }
12141     Roo.each(xitems, function(b) {
12142         this.add(b);
12143     }, this);
12144     
12145 };
12146
12147 Roo.Toolbar.prototype = {
12148     /**
12149      * @cfg {Roo.data.Store} items
12150      * array of button configs or elements to add
12151      */
12152     
12153     /**
12154      * @cfg {String/HTMLElement/Element} container
12155      * The id or element that will contain the toolbar
12156      */
12157     // private
12158     render : function(ct){
12159         this.el = Roo.get(ct);
12160         if(this.cls){
12161             this.el.addClass(this.cls);
12162         }
12163         // using a table allows for vertical alignment
12164         // 100% width is needed by Safari...
12165         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
12166         this.tr = this.el.child("tr", true);
12167         var autoId = 0;
12168         this.items = new Roo.util.MixedCollection(false, function(o){
12169             return o.id || ("item" + (++autoId));
12170         });
12171         if(this.buttons){
12172             this.add.apply(this, this.buttons);
12173             delete this.buttons;
12174         }
12175     },
12176
12177     /**
12178      * Adds element(s) to the toolbar -- this function takes a variable number of 
12179      * arguments of mixed type and adds them to the toolbar.
12180      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
12181      * <ul>
12182      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
12183      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
12184      * <li>Field: Any form field (equivalent to {@link #addField})</li>
12185      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
12186      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
12187      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
12188      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
12189      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
12190      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
12191      * </ul>
12192      * @param {Mixed} arg2
12193      * @param {Mixed} etc.
12194      */
12195     add : function(){
12196         var a = arguments, l = a.length;
12197         for(var i = 0; i < l; i++){
12198             this._add(a[i]);
12199         }
12200     },
12201     // private..
12202     _add : function(el) {
12203         
12204         if (el.xtype) {
12205             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
12206         }
12207         
12208         if (el.applyTo){ // some kind of form field
12209             return this.addField(el);
12210         } 
12211         if (el.render){ // some kind of Toolbar.Item
12212             return this.addItem(el);
12213         }
12214         if (typeof el == "string"){ // string
12215             if(el == "separator" || el == "-"){
12216                 return this.addSeparator();
12217             }
12218             if (el == " "){
12219                 return this.addSpacer();
12220             }
12221             if(el == "->"){
12222                 return this.addFill();
12223             }
12224             return this.addText(el);
12225             
12226         }
12227         if(el.tagName){ // element
12228             return this.addElement(el);
12229         }
12230         if(typeof el == "object"){ // must be button config?
12231             return this.addButton(el);
12232         }
12233         // and now what?!?!
12234         return false;
12235         
12236     },
12237     
12238     /**
12239      * Add an Xtype element
12240      * @param {Object} xtype Xtype Object
12241      * @return {Object} created Object
12242      */
12243     addxtype : function(e){
12244         return this.add(e);  
12245     },
12246     
12247     /**
12248      * Returns the Element for this toolbar.
12249      * @return {Roo.Element}
12250      */
12251     getEl : function(){
12252         return this.el;  
12253     },
12254     
12255     /**
12256      * Adds a separator
12257      * @return {Roo.Toolbar.Item} The separator item
12258      */
12259     addSeparator : function(){
12260         return this.addItem(new Roo.Toolbar.Separator());
12261     },
12262
12263     /**
12264      * Adds a spacer element
12265      * @return {Roo.Toolbar.Spacer} The spacer item
12266      */
12267     addSpacer : function(){
12268         return this.addItem(new Roo.Toolbar.Spacer());
12269     },
12270
12271     /**
12272      * Adds a fill element that forces subsequent additions to the right side of the toolbar
12273      * @return {Roo.Toolbar.Fill} The fill item
12274      */
12275     addFill : function(){
12276         return this.addItem(new Roo.Toolbar.Fill());
12277     },
12278
12279     /**
12280      * Adds any standard HTML element to the toolbar
12281      * @param {String/HTMLElement/Element} el The element or id of the element to add
12282      * @return {Roo.Toolbar.Item} The element's item
12283      */
12284     addElement : function(el){
12285         return this.addItem(new Roo.Toolbar.Item(el));
12286     },
12287     /**
12288      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
12289      * @type Roo.util.MixedCollection  
12290      */
12291     items : false,
12292      
12293     /**
12294      * Adds any Toolbar.Item or subclass
12295      * @param {Roo.Toolbar.Item} item
12296      * @return {Roo.Toolbar.Item} The item
12297      */
12298     addItem : function(item){
12299         var td = this.nextBlock();
12300         item.render(td);
12301         this.items.add(item);
12302         return item;
12303     },
12304     
12305     /**
12306      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
12307      * @param {Object/Array} config A button config or array of configs
12308      * @return {Roo.Toolbar.Button/Array}
12309      */
12310     addButton : function(config){
12311         if(config instanceof Array){
12312             var buttons = [];
12313             for(var i = 0, len = config.length; i < len; i++) {
12314                 buttons.push(this.addButton(config[i]));
12315             }
12316             return buttons;
12317         }
12318         var b = config;
12319         if(!(config instanceof Roo.Toolbar.Button)){
12320             b = config.split ?
12321                 new Roo.Toolbar.SplitButton(config) :
12322                 new Roo.Toolbar.Button(config);
12323         }
12324         var td = this.nextBlock();
12325         b.render(td);
12326         this.items.add(b);
12327         return b;
12328     },
12329     
12330     /**
12331      * Adds text to the toolbar
12332      * @param {String} text The text to add
12333      * @return {Roo.Toolbar.Item} The element's item
12334      */
12335     addText : function(text){
12336         return this.addItem(new Roo.Toolbar.TextItem(text));
12337     },
12338     
12339     /**
12340      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
12341      * @param {Number} index The index where the item is to be inserted
12342      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
12343      * @return {Roo.Toolbar.Button/Item}
12344      */
12345     insertButton : function(index, item){
12346         if(item instanceof Array){
12347             var buttons = [];
12348             for(var i = 0, len = item.length; i < len; i++) {
12349                buttons.push(this.insertButton(index + i, item[i]));
12350             }
12351             return buttons;
12352         }
12353         if (!(item instanceof Roo.Toolbar.Button)){
12354            item = new Roo.Toolbar.Button(item);
12355         }
12356         var td = document.createElement("td");
12357         this.tr.insertBefore(td, this.tr.childNodes[index]);
12358         item.render(td);
12359         this.items.insert(index, item);
12360         return item;
12361     },
12362     
12363     /**
12364      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
12365      * @param {Object} config
12366      * @return {Roo.Toolbar.Item} The element's item
12367      */
12368     addDom : function(config, returnEl){
12369         var td = this.nextBlock();
12370         Roo.DomHelper.overwrite(td, config);
12371         var ti = new Roo.Toolbar.Item(td.firstChild);
12372         ti.render(td);
12373         this.items.add(ti);
12374         return ti;
12375     },
12376
12377     /**
12378      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
12379      * @type Roo.util.MixedCollection  
12380      */
12381     fields : false,
12382     
12383     /**
12384      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc). Note: the field should not have
12385      * been rendered yet. For a field that has already been rendered, use {@link #addElement}.
12386      * @param {Roo.form.Field} field
12387      * @return {Roo.ToolbarItem}
12388      */
12389      
12390       
12391     addField : function(field) {
12392         if (!this.fields) {
12393             var autoId = 0;
12394             this.fields = new Roo.util.MixedCollection(false, function(o){
12395                 return o.id || ("item" + (++autoId));
12396             });
12397
12398         }
12399         
12400         var td = this.nextBlock();
12401         field.render(td);
12402         var ti = new Roo.Toolbar.Item(td.firstChild);
12403         ti.render(td);
12404         this.items.add(ti);
12405         this.fields.add(field);
12406         return ti;
12407     },
12408     /**
12409      * Hide the toolbar
12410      * @method hide
12411      */
12412      
12413       
12414     hide : function()
12415     {
12416         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
12417         this.el.child('div').hide();
12418     },
12419     /**
12420      * Show the toolbar
12421      * @method show
12422      */
12423     show : function()
12424     {
12425         this.el.child('div').show();
12426     },
12427       
12428     // private
12429     nextBlock : function(){
12430         var td = document.createElement("td");
12431         this.tr.appendChild(td);
12432         return td;
12433     },
12434
12435     // private
12436     destroy : function(){
12437         if(this.items){ // rendered?
12438             Roo.destroy.apply(Roo, this.items.items);
12439         }
12440         if(this.fields){ // rendered?
12441             Roo.destroy.apply(Roo, this.fields.items);
12442         }
12443         Roo.Element.uncache(this.el, this.tr);
12444     }
12445 };
12446
12447 /**
12448  * @class Roo.Toolbar.Item
12449  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
12450  * @constructor
12451  * Creates a new Item
12452  * @param {HTMLElement} el 
12453  */
12454 Roo.Toolbar.Item = function(el){
12455     this.el = Roo.getDom(el);
12456     this.id = Roo.id(this.el);
12457     this.hidden = false;
12458 };
12459
12460 Roo.Toolbar.Item.prototype = {
12461     
12462     /**
12463      * Get this item's HTML Element
12464      * @return {HTMLElement}
12465      */
12466     getEl : function(){
12467        return this.el;  
12468     },
12469
12470     // private
12471     render : function(td){
12472         this.td = td;
12473         td.appendChild(this.el);
12474     },
12475     
12476     /**
12477      * Removes and destroys this item.
12478      */
12479     destroy : function(){
12480         this.td.parentNode.removeChild(this.td);
12481     },
12482     
12483     /**
12484      * Shows this item.
12485      */
12486     show: function(){
12487         this.hidden = false;
12488         this.td.style.display = "";
12489     },
12490     
12491     /**
12492      * Hides this item.
12493      */
12494     hide: function(){
12495         this.hidden = true;
12496         this.td.style.display = "none";
12497     },
12498     
12499     /**
12500      * Convenience function for boolean show/hide.
12501      * @param {Boolean} visible true to show/false to hide
12502      */
12503     setVisible: function(visible){
12504         if(visible) {
12505             this.show();
12506         }else{
12507             this.hide();
12508         }
12509     },
12510     
12511     /**
12512      * Try to focus this item.
12513      */
12514     focus : function(){
12515         Roo.fly(this.el).focus();
12516     },
12517     
12518     /**
12519      * Disables this item.
12520      */
12521     disable : function(){
12522         Roo.fly(this.td).addClass("x-item-disabled");
12523         this.disabled = true;
12524         this.el.disabled = true;
12525     },
12526     
12527     /**
12528      * Enables this item.
12529      */
12530     enable : function(){
12531         Roo.fly(this.td).removeClass("x-item-disabled");
12532         this.disabled = false;
12533         this.el.disabled = false;
12534     }
12535 };
12536
12537
12538 /**
12539  * @class Roo.Toolbar.Separator
12540  * @extends Roo.Toolbar.Item
12541  * A simple toolbar separator class
12542  * @constructor
12543  * Creates a new Separator
12544  */
12545 Roo.Toolbar.Separator = function(){
12546     var s = document.createElement("span");
12547     s.className = "ytb-sep";
12548     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
12549 };
12550 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
12551     enable:Roo.emptyFn,
12552     disable:Roo.emptyFn,
12553     focus:Roo.emptyFn
12554 });
12555
12556 /**
12557  * @class Roo.Toolbar.Spacer
12558  * @extends Roo.Toolbar.Item
12559  * A simple element that adds extra horizontal space to a toolbar.
12560  * @constructor
12561  * Creates a new Spacer
12562  */
12563 Roo.Toolbar.Spacer = function(){
12564     var s = document.createElement("div");
12565     s.className = "ytb-spacer";
12566     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
12567 };
12568 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
12569     enable:Roo.emptyFn,
12570     disable:Roo.emptyFn,
12571     focus:Roo.emptyFn
12572 });
12573
12574 /**
12575  * @class Roo.Toolbar.Fill
12576  * @extends Roo.Toolbar.Spacer
12577  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
12578  * @constructor
12579  * Creates a new Spacer
12580  */
12581 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
12582     // private
12583     render : function(td){
12584         td.style.width = '100%';
12585         Roo.Toolbar.Fill.superclass.render.call(this, td);
12586     }
12587 });
12588
12589 /**
12590  * @class Roo.Toolbar.TextItem
12591  * @extends Roo.Toolbar.Item
12592  * A simple class that renders text directly into a toolbar.
12593  * @constructor
12594  * Creates a new TextItem
12595  * @param {String} text
12596  */
12597 Roo.Toolbar.TextItem = function(text){
12598     if (typeof(text) == 'object') {
12599         text = text.text;
12600     }
12601     var s = document.createElement("span");
12602     s.className = "ytb-text";
12603     s.innerHTML = text;
12604     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
12605 };
12606 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
12607     enable:Roo.emptyFn,
12608     disable:Roo.emptyFn,
12609     focus:Roo.emptyFn
12610 });
12611
12612 /**
12613  * @class Roo.Toolbar.Button
12614  * @extends Roo.Button
12615  * A button that renders into a toolbar.
12616  * @constructor
12617  * Creates a new Button
12618  * @param {Object} config A standard {@link Roo.Button} config object
12619  */
12620 Roo.Toolbar.Button = function(config){
12621     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
12622 };
12623 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
12624     render : function(td){
12625         this.td = td;
12626         Roo.Toolbar.Button.superclass.render.call(this, td);
12627     },
12628     
12629     /**
12630      * Removes and destroys this button
12631      */
12632     destroy : function(){
12633         Roo.Toolbar.Button.superclass.destroy.call(this);
12634         this.td.parentNode.removeChild(this.td);
12635     },
12636     
12637     /**
12638      * Shows this button
12639      */
12640     show: function(){
12641         this.hidden = false;
12642         this.td.style.display = "";
12643     },
12644     
12645     /**
12646      * Hides this button
12647      */
12648     hide: function(){
12649         this.hidden = true;
12650         this.td.style.display = "none";
12651     },
12652
12653     /**
12654      * Disables this item
12655      */
12656     disable : function(){
12657         Roo.fly(this.td).addClass("x-item-disabled");
12658         this.disabled = true;
12659     },
12660
12661     /**
12662      * Enables this item
12663      */
12664     enable : function(){
12665         Roo.fly(this.td).removeClass("x-item-disabled");
12666         this.disabled = false;
12667     }
12668 });
12669 // backwards compat
12670 Roo.ToolbarButton = Roo.Toolbar.Button;
12671
12672 /**
12673  * @class Roo.Toolbar.SplitButton
12674  * @extends Roo.SplitButton
12675  * A menu button that renders into a toolbar.
12676  * @constructor
12677  * Creates a new SplitButton
12678  * @param {Object} config A standard {@link Roo.SplitButton} config object
12679  */
12680 Roo.Toolbar.SplitButton = function(config){
12681     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
12682 };
12683 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
12684     render : function(td){
12685         this.td = td;
12686         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
12687     },
12688     
12689     /**
12690      * Removes and destroys this button
12691      */
12692     destroy : function(){
12693         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
12694         this.td.parentNode.removeChild(this.td);
12695     },
12696     
12697     /**
12698      * Shows this button
12699      */
12700     show: function(){
12701         this.hidden = false;
12702         this.td.style.display = "";
12703     },
12704     
12705     /**
12706      * Hides this button
12707      */
12708     hide: function(){
12709         this.hidden = true;
12710         this.td.style.display = "none";
12711     }
12712 });
12713
12714 // backwards compat
12715 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
12716  * Based on:
12717  * Ext JS Library 1.1.1
12718  * Copyright(c) 2006-2007, Ext JS, LLC.
12719  *
12720  * Originally Released Under LGPL - original licence link has changed is not relivant.
12721  *
12722  * Fork - LGPL
12723  * <script type="text/javascript">
12724  */
12725  
12726 /**
12727  * @class Roo.PagingToolbar
12728  * @extends Roo.Toolbar
12729  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
12730  * @constructor
12731  * Create a new PagingToolbar
12732  * @param {Object} config The config object
12733  */
12734 Roo.PagingToolbar = function(el, ds, config)
12735 {
12736     // old args format still supported... - xtype is prefered..
12737     if (typeof(el) == 'object' && el.xtype) {
12738         // created from xtype...
12739         config = el;
12740         ds = el.dataSource;
12741         el = config.container;
12742     }
12743     var items = [];
12744     if (this.items) {
12745         items = this.items;
12746         this.items = [];
12747     }
12748     
12749     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
12750     this.ds = ds;
12751     this.cursor = 0;
12752     this.renderButtons(this.el);
12753     this.bind(ds);
12754     var _this = this;
12755     Roo.each(items, function(e) {
12756         _this.add(Roo.factory(e));
12757     });
12758     
12759 };
12760
12761 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
12762     /**
12763      * @cfg {Roo.data.Store} dataSource
12764      * The underlying data store providing the paged data
12765      */
12766     /**
12767      * @cfg {String/HTMLElement/Element} container
12768      * container The id or element that will contain the toolbar
12769      */
12770     /**
12771      * @cfg {Boolean} displayInfo
12772      * True to display the displayMsg (defaults to false)
12773      */
12774     /**
12775      * @cfg {Number} pageSize
12776      * The number of records to display per page (defaults to 20)
12777      */
12778     pageSize: 20,
12779     /**
12780      * @cfg {String} displayMsg
12781      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
12782      */
12783     displayMsg : 'Displaying {0} - {1} of {2}',
12784     /**
12785      * @cfg {String} emptyMsg
12786      * The message to display when no records are found (defaults to "No data to display")
12787      */
12788     emptyMsg : 'No data to display',
12789     /**
12790      * Customizable piece of the default paging text (defaults to "Page")
12791      * @type String
12792      */
12793     beforePageText : "Page",
12794     /**
12795      * Customizable piece of the default paging text (defaults to "of %0")
12796      * @type String
12797      */
12798     afterPageText : "of {0}",
12799     /**
12800      * Customizable piece of the default paging text (defaults to "First Page")
12801      * @type String
12802      */
12803     firstText : "First Page",
12804     /**
12805      * Customizable piece of the default paging text (defaults to "Previous Page")
12806      * @type String
12807      */
12808     prevText : "Previous Page",
12809     /**
12810      * Customizable piece of the default paging text (defaults to "Next Page")
12811      * @type String
12812      */
12813     nextText : "Next Page",
12814     /**
12815      * Customizable piece of the default paging text (defaults to "Last Page")
12816      * @type String
12817      */
12818     lastText : "Last Page",
12819     /**
12820      * Customizable piece of the default paging text (defaults to "Refresh")
12821      * @type String
12822      */
12823     refreshText : "Refresh",
12824
12825     // private
12826     renderButtons : function(el){
12827         Roo.PagingToolbar.superclass.render.call(this, el);
12828         this.first = this.addButton({
12829             tooltip: this.firstText,
12830             cls: "x-btn-icon x-grid-page-first",
12831             disabled: true,
12832             handler: this.onClick.createDelegate(this, ["first"])
12833         });
12834         this.prev = this.addButton({
12835             tooltip: this.prevText,
12836             cls: "x-btn-icon x-grid-page-prev",
12837             disabled: true,
12838             handler: this.onClick.createDelegate(this, ["prev"])
12839         });
12840         //this.addSeparator();
12841         this.add(this.beforePageText);
12842         this.field = Roo.get(this.addDom({
12843            tag: "input",
12844            type: "text",
12845            size: "3",
12846            value: "1",
12847            cls: "x-grid-page-number"
12848         }).el);
12849         this.field.on("keydown", this.onPagingKeydown, this);
12850         this.field.on("focus", function(){this.dom.select();});
12851         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
12852         this.field.setHeight(18);
12853         //this.addSeparator();
12854         this.next = this.addButton({
12855             tooltip: this.nextText,
12856             cls: "x-btn-icon x-grid-page-next",
12857             disabled: true,
12858             handler: this.onClick.createDelegate(this, ["next"])
12859         });
12860         this.last = this.addButton({
12861             tooltip: this.lastText,
12862             cls: "x-btn-icon x-grid-page-last",
12863             disabled: true,
12864             handler: this.onClick.createDelegate(this, ["last"])
12865         });
12866         //this.addSeparator();
12867         this.loading = this.addButton({
12868             tooltip: this.refreshText,
12869             cls: "x-btn-icon x-grid-loading",
12870             handler: this.onClick.createDelegate(this, ["refresh"])
12871         });
12872
12873         if(this.displayInfo){
12874             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
12875         }
12876     },
12877
12878     // private
12879     updateInfo : function(){
12880         if(this.displayEl){
12881             var count = this.ds.getCount();
12882             var msg = count == 0 ?
12883                 this.emptyMsg :
12884                 String.format(
12885                     this.displayMsg,
12886                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
12887                 );
12888             this.displayEl.update(msg);
12889         }
12890     },
12891
12892     // private
12893     onLoad : function(ds, r, o){
12894        this.cursor = o.params ? o.params.start : 0;
12895        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
12896
12897        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
12898        this.field.dom.value = ap;
12899        this.first.setDisabled(ap == 1);
12900        this.prev.setDisabled(ap == 1);
12901        this.next.setDisabled(ap == ps);
12902        this.last.setDisabled(ap == ps);
12903        this.loading.enable();
12904        this.updateInfo();
12905     },
12906
12907     // private
12908     getPageData : function(){
12909         var total = this.ds.getTotalCount();
12910         return {
12911             total : total,
12912             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
12913             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
12914         };
12915     },
12916
12917     // private
12918     onLoadError : function(){
12919         this.loading.enable();
12920     },
12921
12922     // private
12923     onPagingKeydown : function(e){
12924         var k = e.getKey();
12925         var d = this.getPageData();
12926         if(k == e.RETURN){
12927             var v = this.field.dom.value, pageNum;
12928             if(!v || isNaN(pageNum = parseInt(v, 10))){
12929                 this.field.dom.value = d.activePage;
12930                 return;
12931             }
12932             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
12933             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
12934             e.stopEvent();
12935         }
12936         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))
12937         {
12938           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
12939           this.field.dom.value = pageNum;
12940           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
12941           e.stopEvent();
12942         }
12943         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
12944         {
12945           var v = this.field.dom.value, pageNum; 
12946           var increment = (e.shiftKey) ? 10 : 1;
12947           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
12948             increment *= -1;
12949           if(!v || isNaN(pageNum = parseInt(v, 10))) {
12950             this.field.dom.value = d.activePage;
12951             return;
12952           }
12953           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
12954           {
12955             this.field.dom.value = parseInt(v, 10) + increment;
12956             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
12957             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
12958           }
12959           e.stopEvent();
12960         }
12961     },
12962
12963     // private
12964     beforeLoad : function(){
12965         if(this.loading){
12966             this.loading.disable();
12967         }
12968     },
12969
12970     // private
12971     onClick : function(which){
12972         var ds = this.ds;
12973         switch(which){
12974             case "first":
12975                 ds.load({params:{start: 0, limit: this.pageSize}});
12976             break;
12977             case "prev":
12978                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
12979             break;
12980             case "next":
12981                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
12982             break;
12983             case "last":
12984                 var total = ds.getTotalCount();
12985                 var extra = total % this.pageSize;
12986                 var lastStart = extra ? (total - extra) : total-this.pageSize;
12987                 ds.load({params:{start: lastStart, limit: this.pageSize}});
12988             break;
12989             case "refresh":
12990                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
12991             break;
12992         }
12993     },
12994
12995     /**
12996      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
12997      * @param {Roo.data.Store} store The data store to unbind
12998      */
12999     unbind : function(ds){
13000         ds.un("beforeload", this.beforeLoad, this);
13001         ds.un("load", this.onLoad, this);
13002         ds.un("loadexception", this.onLoadError, this);
13003         ds.un("remove", this.updateInfo, this);
13004         ds.un("add", this.updateInfo, this);
13005         this.ds = undefined;
13006     },
13007
13008     /**
13009      * Binds the paging toolbar to the specified {@link Roo.data.Store}
13010      * @param {Roo.data.Store} store The data store to bind
13011      */
13012     bind : function(ds){
13013         ds.on("beforeload", this.beforeLoad, this);
13014         ds.on("load", this.onLoad, this);
13015         ds.on("loadexception", this.onLoadError, this);
13016         ds.on("remove", this.updateInfo, this);
13017         ds.on("add", this.updateInfo, this);
13018         this.ds = ds;
13019     }
13020 });/*
13021  * Based on:
13022  * Ext JS Library 1.1.1
13023  * Copyright(c) 2006-2007, Ext JS, LLC.
13024  *
13025  * Originally Released Under LGPL - original licence link has changed is not relivant.
13026  *
13027  * Fork - LGPL
13028  * <script type="text/javascript">
13029  */
13030
13031 /**
13032  * @class Roo.Resizable
13033  * @extends Roo.util.Observable
13034  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
13035  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
13036  * 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
13037  * the element will be wrapped for you automatically.</p>
13038  * <p>Here is the list of valid resize handles:</p>
13039  * <pre>
13040 Value   Description
13041 ------  -------------------
13042  'n'     north
13043  's'     south
13044  'e'     east
13045  'w'     west
13046  'nw'    northwest
13047  'sw'    southwest
13048  'se'    southeast
13049  'ne'    northeast
13050  'hd'    horizontal drag
13051  'all'   all
13052 </pre>
13053  * <p>Here's an example showing the creation of a typical Resizable:</p>
13054  * <pre><code>
13055 var resizer = new Roo.Resizable("element-id", {
13056     handles: 'all',
13057     minWidth: 200,
13058     minHeight: 100,
13059     maxWidth: 500,
13060     maxHeight: 400,
13061     pinned: true
13062 });
13063 resizer.on("resize", myHandler);
13064 </code></pre>
13065  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
13066  * resizer.east.setDisplayed(false);</p>
13067  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
13068  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
13069  * resize operation's new size (defaults to [0, 0])
13070  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
13071  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
13072  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
13073  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
13074  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
13075  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
13076  * @cfg {Number} width The width of the element in pixels (defaults to null)
13077  * @cfg {Number} height The height of the element in pixels (defaults to null)
13078  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
13079  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
13080  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
13081  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
13082  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
13083  * in favor of the handles config option (defaults to false)
13084  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
13085  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
13086  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
13087  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
13088  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
13089  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
13090  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
13091  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
13092  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
13093  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
13094  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
13095  * @constructor
13096  * Create a new resizable component
13097  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
13098  * @param {Object} config configuration options
13099   */
13100 Roo.Resizable = function(el, config)
13101 {
13102     this.el = Roo.get(el);
13103
13104     if(config && config.wrap){
13105         config.resizeChild = this.el;
13106         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
13107         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
13108         this.el.setStyle("overflow", "hidden");
13109         this.el.setPositioning(config.resizeChild.getPositioning());
13110         config.resizeChild.clearPositioning();
13111         if(!config.width || !config.height){
13112             var csize = config.resizeChild.getSize();
13113             this.el.setSize(csize.width, csize.height);
13114         }
13115         if(config.pinned && !config.adjustments){
13116             config.adjustments = "auto";
13117         }
13118     }
13119
13120     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
13121     this.proxy.unselectable();
13122     this.proxy.enableDisplayMode('block');
13123
13124     Roo.apply(this, config);
13125
13126     if(this.pinned){
13127         this.disableTrackOver = true;
13128         this.el.addClass("x-resizable-pinned");
13129     }
13130     // if the element isn't positioned, make it relative
13131     var position = this.el.getStyle("position");
13132     if(position != "absolute" && position != "fixed"){
13133         this.el.setStyle("position", "relative");
13134     }
13135     if(!this.handles){ // no handles passed, must be legacy style
13136         this.handles = 's,e,se';
13137         if(this.multiDirectional){
13138             this.handles += ',n,w';
13139         }
13140     }
13141     if(this.handles == "all"){
13142         this.handles = "n s e w ne nw se sw";
13143     }
13144     var hs = this.handles.split(/\s*?[,;]\s*?| /);
13145     var ps = Roo.Resizable.positions;
13146     for(var i = 0, len = hs.length; i < len; i++){
13147         if(hs[i] && ps[hs[i]]){
13148             var pos = ps[hs[i]];
13149             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
13150         }
13151     }
13152     // legacy
13153     this.corner = this.southeast;
13154     
13155     // updateBox = the box can move..
13156     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
13157         this.updateBox = true;
13158     }
13159
13160     this.activeHandle = null;
13161
13162     if(this.resizeChild){
13163         if(typeof this.resizeChild == "boolean"){
13164             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
13165         }else{
13166             this.resizeChild = Roo.get(this.resizeChild, true);
13167         }
13168     }
13169     
13170     if(this.adjustments == "auto"){
13171         var rc = this.resizeChild;
13172         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
13173         if(rc && (hw || hn)){
13174             rc.position("relative");
13175             rc.setLeft(hw ? hw.el.getWidth() : 0);
13176             rc.setTop(hn ? hn.el.getHeight() : 0);
13177         }
13178         this.adjustments = [
13179             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
13180             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
13181         ];
13182     }
13183
13184     if(this.draggable){
13185         this.dd = this.dynamic ?
13186             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
13187         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
13188     }
13189
13190     // public events
13191     this.addEvents({
13192         /**
13193          * @event beforeresize
13194          * Fired before resize is allowed. Set enabled to false to cancel resize.
13195          * @param {Roo.Resizable} this
13196          * @param {Roo.EventObject} e The mousedown event
13197          */
13198         "beforeresize" : true,
13199         /**
13200          * @event resize
13201          * Fired after a resize.
13202          * @param {Roo.Resizable} this
13203          * @param {Number} width The new width
13204          * @param {Number} height The new height
13205          * @param {Roo.EventObject} e The mouseup event
13206          */
13207         "resize" : true
13208     });
13209
13210     if(this.width !== null && this.height !== null){
13211         this.resizeTo(this.width, this.height);
13212     }else{
13213         this.updateChildSize();
13214     }
13215     if(Roo.isIE){
13216         this.el.dom.style.zoom = 1;
13217     }
13218     Roo.Resizable.superclass.constructor.call(this);
13219 };
13220
13221 Roo.extend(Roo.Resizable, Roo.util.Observable, {
13222         resizeChild : false,
13223         adjustments : [0, 0],
13224         minWidth : 5,
13225         minHeight : 5,
13226         maxWidth : 10000,
13227         maxHeight : 10000,
13228         enabled : true,
13229         animate : false,
13230         duration : .35,
13231         dynamic : false,
13232         handles : false,
13233         multiDirectional : false,
13234         disableTrackOver : false,
13235         easing : 'easeOutStrong',
13236         widthIncrement : 0,
13237         heightIncrement : 0,
13238         pinned : false,
13239         width : null,
13240         height : null,
13241         preserveRatio : false,
13242         transparent: false,
13243         minX: 0,
13244         minY: 0,
13245         draggable: false,
13246
13247         /**
13248          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
13249          */
13250         constrainTo: undefined,
13251         /**
13252          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
13253          */
13254         resizeRegion: undefined,
13255
13256
13257     /**
13258      * Perform a manual resize
13259      * @param {Number} width
13260      * @param {Number} height
13261      */
13262     resizeTo : function(width, height){
13263         this.el.setSize(width, height);
13264         this.updateChildSize();
13265         this.fireEvent("resize", this, width, height, null);
13266     },
13267
13268     // private
13269     startSizing : function(e, handle){
13270         this.fireEvent("beforeresize", this, e);
13271         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
13272
13273             if(!this.overlay){
13274                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
13275                 this.overlay.unselectable();
13276                 this.overlay.enableDisplayMode("block");
13277                 this.overlay.on("mousemove", this.onMouseMove, this);
13278                 this.overlay.on("mouseup", this.onMouseUp, this);
13279             }
13280             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
13281
13282             this.resizing = true;
13283             this.startBox = this.el.getBox();
13284             this.startPoint = e.getXY();
13285             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
13286                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
13287
13288             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13289             this.overlay.show();
13290
13291             if(this.constrainTo) {
13292                 var ct = Roo.get(this.constrainTo);
13293                 this.resizeRegion = ct.getRegion().adjust(
13294                     ct.getFrameWidth('t'),
13295                     ct.getFrameWidth('l'),
13296                     -ct.getFrameWidth('b'),
13297                     -ct.getFrameWidth('r')
13298                 );
13299             }
13300
13301             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
13302             this.proxy.show();
13303             this.proxy.setBox(this.startBox);
13304             if(!this.dynamic){
13305                 this.proxy.setStyle('visibility', 'visible');
13306             }
13307         }
13308     },
13309
13310     // private
13311     onMouseDown : function(handle, e){
13312         if(this.enabled){
13313             e.stopEvent();
13314             this.activeHandle = handle;
13315             this.startSizing(e, handle);
13316         }
13317     },
13318
13319     // private
13320     onMouseUp : function(e){
13321         var size = this.resizeElement();
13322         this.resizing = false;
13323         this.handleOut();
13324         this.overlay.hide();
13325         this.proxy.hide();
13326         this.fireEvent("resize", this, size.width, size.height, e);
13327     },
13328
13329     // private
13330     updateChildSize : function(){
13331         if(this.resizeChild){
13332             var el = this.el;
13333             var child = this.resizeChild;
13334             var adj = this.adjustments;
13335             if(el.dom.offsetWidth){
13336                 var b = el.getSize(true);
13337                 child.setSize(b.width+adj[0], b.height+adj[1]);
13338             }
13339             // Second call here for IE
13340             // The first call enables instant resizing and
13341             // the second call corrects scroll bars if they
13342             // exist
13343             if(Roo.isIE){
13344                 setTimeout(function(){
13345                     if(el.dom.offsetWidth){
13346                         var b = el.getSize(true);
13347                         child.setSize(b.width+adj[0], b.height+adj[1]);
13348                     }
13349                 }, 10);
13350             }
13351         }
13352     },
13353
13354     // private
13355     snap : function(value, inc, min){
13356         if(!inc || !value) return value;
13357         var newValue = value;
13358         var m = value % inc;
13359         if(m > 0){
13360             if(m > (inc/2)){
13361                 newValue = value + (inc-m);
13362             }else{
13363                 newValue = value - m;
13364             }
13365         }
13366         return Math.max(min, newValue);
13367     },
13368
13369     // private
13370     resizeElement : function(){
13371         var box = this.proxy.getBox();
13372         if(this.updateBox){
13373             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
13374         }else{
13375             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
13376         }
13377         this.updateChildSize();
13378         if(!this.dynamic){
13379             this.proxy.hide();
13380         }
13381         return box;
13382     },
13383
13384     // private
13385     constrain : function(v, diff, m, mx){
13386         if(v - diff < m){
13387             diff = v - m;
13388         }else if(v - diff > mx){
13389             diff = mx - v;
13390         }
13391         return diff;
13392     },
13393
13394     // private
13395     onMouseMove : function(e){
13396         if(this.enabled){
13397             try{// try catch so if something goes wrong the user doesn't get hung
13398
13399             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
13400                 return;
13401             }
13402
13403             //var curXY = this.startPoint;
13404             var curSize = this.curSize || this.startBox;
13405             var x = this.startBox.x, y = this.startBox.y;
13406             var ox = x, oy = y;
13407             var w = curSize.width, h = curSize.height;
13408             var ow = w, oh = h;
13409             var mw = this.minWidth, mh = this.minHeight;
13410             var mxw = this.maxWidth, mxh = this.maxHeight;
13411             var wi = this.widthIncrement;
13412             var hi = this.heightIncrement;
13413
13414             var eventXY = e.getXY();
13415             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
13416             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
13417
13418             var pos = this.activeHandle.position;
13419
13420             switch(pos){
13421                 case "east":
13422                     w += diffX;
13423                     w = Math.min(Math.max(mw, w), mxw);
13424                     break;
13425              
13426                 case "south":
13427                     h += diffY;
13428                     h = Math.min(Math.max(mh, h), mxh);
13429                     break;
13430                 case "southeast":
13431                     w += diffX;
13432                     h += diffY;
13433                     w = Math.min(Math.max(mw, w), mxw);
13434                     h = Math.min(Math.max(mh, h), mxh);
13435                     break;
13436                 case "north":
13437                     diffY = this.constrain(h, diffY, mh, mxh);
13438                     y += diffY;
13439                     h -= diffY;
13440                     break;
13441                 case "hdrag":
13442                     
13443                     if (wi) {
13444                         var adiffX = Math.abs(diffX);
13445                         var sub = (adiffX % wi); // how much 
13446                         if (sub > (wi/2)) { // far enough to snap
13447                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
13448                         } else {
13449                             // remove difference.. 
13450                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
13451                         }
13452                     }
13453                     x += diffX;
13454                     x = Math.max(this.minX, x);
13455                     break;
13456                 case "west":
13457                     diffX = this.constrain(w, diffX, mw, mxw);
13458                     x += diffX;
13459                     w -= diffX;
13460                     break;
13461                 case "northeast":
13462                     w += diffX;
13463                     w = Math.min(Math.max(mw, w), mxw);
13464                     diffY = this.constrain(h, diffY, mh, mxh);
13465                     y += diffY;
13466                     h -= diffY;
13467                     break;
13468                 case "northwest":
13469                     diffX = this.constrain(w, diffX, mw, mxw);
13470                     diffY = this.constrain(h, diffY, mh, mxh);
13471                     y += diffY;
13472                     h -= diffY;
13473                     x += diffX;
13474                     w -= diffX;
13475                     break;
13476                case "southwest":
13477                     diffX = this.constrain(w, diffX, mw, mxw);
13478                     h += diffY;
13479                     h = Math.min(Math.max(mh, h), mxh);
13480                     x += diffX;
13481                     w -= diffX;
13482                     break;
13483             }
13484
13485             var sw = this.snap(w, wi, mw);
13486             var sh = this.snap(h, hi, mh);
13487             if(sw != w || sh != h){
13488                 switch(pos){
13489                     case "northeast":
13490                         y -= sh - h;
13491                     break;
13492                     case "north":
13493                         y -= sh - h;
13494                         break;
13495                     case "southwest":
13496                         x -= sw - w;
13497                     break;
13498                     case "west":
13499                         x -= sw - w;
13500                         break;
13501                     case "northwest":
13502                         x -= sw - w;
13503                         y -= sh - h;
13504                     break;
13505                 }
13506                 w = sw;
13507                 h = sh;
13508             }
13509
13510             if(this.preserveRatio){
13511                 switch(pos){
13512                     case "southeast":
13513                     case "east":
13514                         h = oh * (w/ow);
13515                         h = Math.min(Math.max(mh, h), mxh);
13516                         w = ow * (h/oh);
13517                        break;
13518                     case "south":
13519                         w = ow * (h/oh);
13520                         w = Math.min(Math.max(mw, w), mxw);
13521                         h = oh * (w/ow);
13522                         break;
13523                     case "northeast":
13524                         w = ow * (h/oh);
13525                         w = Math.min(Math.max(mw, w), mxw);
13526                         h = oh * (w/ow);
13527                     break;
13528                     case "north":
13529                         var tw = w;
13530                         w = ow * (h/oh);
13531                         w = Math.min(Math.max(mw, w), mxw);
13532                         h = oh * (w/ow);
13533                         x += (tw - w) / 2;
13534                         break;
13535                     case "southwest":
13536                         h = oh * (w/ow);
13537                         h = Math.min(Math.max(mh, h), mxh);
13538                         var tw = w;
13539                         w = ow * (h/oh);
13540                         x += tw - w;
13541                         break;
13542                     case "west":
13543                         var th = h;
13544                         h = oh * (w/ow);
13545                         h = Math.min(Math.max(mh, h), mxh);
13546                         y += (th - h) / 2;
13547                         var tw = w;
13548                         w = ow * (h/oh);
13549                         x += tw - w;
13550                        break;
13551                     case "northwest":
13552                         var tw = w;
13553                         var th = h;
13554                         h = oh * (w/ow);
13555                         h = Math.min(Math.max(mh, h), mxh);
13556                         w = ow * (h/oh);
13557                         y += th - h;
13558                         x += tw - w;
13559                        break;
13560
13561                 }
13562             }
13563             if (pos == 'hdrag') {
13564                 w = ow;
13565             }
13566             this.proxy.setBounds(x, y, w, h);
13567             if(this.dynamic){
13568                 this.resizeElement();
13569             }
13570             }catch(e){}
13571         }
13572     },
13573
13574     // private
13575     handleOver : function(){
13576         if(this.enabled){
13577             this.el.addClass("x-resizable-over");
13578         }
13579     },
13580
13581     // private
13582     handleOut : function(){
13583         if(!this.resizing){
13584             this.el.removeClass("x-resizable-over");
13585         }
13586     },
13587
13588     /**
13589      * Returns the element this component is bound to.
13590      * @return {Roo.Element}
13591      */
13592     getEl : function(){
13593         return this.el;
13594     },
13595
13596     /**
13597      * Returns the resizeChild element (or null).
13598      * @return {Roo.Element}
13599      */
13600     getResizeChild : function(){
13601         return this.resizeChild;
13602     },
13603
13604     /**
13605      * Destroys this resizable. If the element was wrapped and
13606      * removeEl is not true then the element remains.
13607      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
13608      */
13609     destroy : function(removeEl){
13610         this.proxy.remove();
13611         if(this.overlay){
13612             this.overlay.removeAllListeners();
13613             this.overlay.remove();
13614         }
13615         var ps = Roo.Resizable.positions;
13616         for(var k in ps){
13617             if(typeof ps[k] != "function" && this[ps[k]]){
13618                 var h = this[ps[k]];
13619                 h.el.removeAllListeners();
13620                 h.el.remove();
13621             }
13622         }
13623         if(removeEl){
13624             this.el.update("");
13625             this.el.remove();
13626         }
13627     }
13628 });
13629
13630 // private
13631 // hash to map config positions to true positions
13632 Roo.Resizable.positions = {
13633     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
13634     hd: "hdrag"
13635 };
13636
13637 // private
13638 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
13639     if(!this.tpl){
13640         // only initialize the template if resizable is used
13641         var tpl = Roo.DomHelper.createTemplate(
13642             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
13643         );
13644         tpl.compile();
13645         Roo.Resizable.Handle.prototype.tpl = tpl;
13646     }
13647     this.position = pos;
13648     this.rz = rz;
13649     // show north drag fro topdra
13650     var handlepos = pos == 'hdrag' ? 'north' : pos;
13651     
13652     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
13653     if (pos == 'hdrag') {
13654         this.el.setStyle('cursor', 'pointer');
13655     }
13656     this.el.unselectable();
13657     if(transparent){
13658         this.el.setOpacity(0);
13659     }
13660     this.el.on("mousedown", this.onMouseDown, this);
13661     if(!disableTrackOver){
13662         this.el.on("mouseover", this.onMouseOver, this);
13663         this.el.on("mouseout", this.onMouseOut, this);
13664     }
13665 };
13666
13667 // private
13668 Roo.Resizable.Handle.prototype = {
13669     afterResize : function(rz){
13670         // do nothing
13671     },
13672     // private
13673     onMouseDown : function(e){
13674         this.rz.onMouseDown(this, e);
13675     },
13676     // private
13677     onMouseOver : function(e){
13678         this.rz.handleOver(this, e);
13679     },
13680     // private
13681     onMouseOut : function(e){
13682         this.rz.handleOut(this, e);
13683     }
13684 };/*
13685  * Based on:
13686  * Ext JS Library 1.1.1
13687  * Copyright(c) 2006-2007, Ext JS, LLC.
13688  *
13689  * Originally Released Under LGPL - original licence link has changed is not relivant.
13690  *
13691  * Fork - LGPL
13692  * <script type="text/javascript">
13693  */
13694
13695 /**
13696  * @class Roo.Editor
13697  * @extends Roo.Component
13698  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
13699  * @constructor
13700  * Create a new Editor
13701  * @param {Roo.form.Field} field The Field object (or descendant)
13702  * @param {Object} config The config object
13703  */
13704 Roo.Editor = function(field, config){
13705     Roo.Editor.superclass.constructor.call(this, config);
13706     this.field = field;
13707     this.addEvents({
13708         /**
13709              * @event beforestartedit
13710              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
13711              * false from the handler of this event.
13712              * @param {Editor} this
13713              * @param {Roo.Element} boundEl The underlying element bound to this editor
13714              * @param {Mixed} value The field value being set
13715              */
13716         "beforestartedit" : true,
13717         /**
13718              * @event startedit
13719              * Fires when this editor is displayed
13720              * @param {Roo.Element} boundEl The underlying element bound to this editor
13721              * @param {Mixed} value The starting field value
13722              */
13723         "startedit" : true,
13724         /**
13725              * @event beforecomplete
13726              * Fires after a change has been made to the field, but before the change is reflected in the underlying
13727              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
13728              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
13729              * event will not fire since no edit actually occurred.
13730              * @param {Editor} this
13731              * @param {Mixed} value The current field value
13732              * @param {Mixed} startValue The original field value
13733              */
13734         "beforecomplete" : true,
13735         /**
13736              * @event complete
13737              * Fires after editing is complete and any changed value has been written to the underlying field.
13738              * @param {Editor} this
13739              * @param {Mixed} value The current field value
13740              * @param {Mixed} startValue The original field value
13741              */
13742         "complete" : true,
13743         /**
13744          * @event specialkey
13745          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
13746          * {@link Roo.EventObject#getKey} to determine which key was pressed.
13747          * @param {Roo.form.Field} this
13748          * @param {Roo.EventObject} e The event object
13749          */
13750         "specialkey" : true
13751     });
13752 };
13753
13754 Roo.extend(Roo.Editor, Roo.Component, {
13755     /**
13756      * @cfg {Boolean/String} autosize
13757      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
13758      * or "height" to adopt the height only (defaults to false)
13759      */
13760     /**
13761      * @cfg {Boolean} revertInvalid
13762      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
13763      * validation fails (defaults to true)
13764      */
13765     /**
13766      * @cfg {Boolean} ignoreNoChange
13767      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
13768      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
13769      * will never be ignored.
13770      */
13771     /**
13772      * @cfg {Boolean} hideEl
13773      * False to keep the bound element visible while the editor is displayed (defaults to true)
13774      */
13775     /**
13776      * @cfg {Mixed} value
13777      * The data value of the underlying field (defaults to "")
13778      */
13779     value : "",
13780     /**
13781      * @cfg {String} alignment
13782      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
13783      */
13784     alignment: "c-c?",
13785     /**
13786      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
13787      * for bottom-right shadow (defaults to "frame")
13788      */
13789     shadow : "frame",
13790     /**
13791      * @cfg {Boolean} constrain True to constrain the editor to the viewport
13792      */
13793     constrain : false,
13794     /**
13795      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
13796      */
13797     completeOnEnter : false,
13798     /**
13799      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
13800      */
13801     cancelOnEsc : false,
13802     /**
13803      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
13804      */
13805     updateEl : false,
13806
13807     // private
13808     onRender : function(ct, position){
13809         this.el = new Roo.Layer({
13810             shadow: this.shadow,
13811             cls: "x-editor",
13812             parentEl : ct,
13813             shim : this.shim,
13814             shadowOffset:4,
13815             id: this.id,
13816             constrain: this.constrain
13817         });
13818         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
13819         if(this.field.msgTarget != 'title'){
13820             this.field.msgTarget = 'qtip';
13821         }
13822         this.field.render(this.el);
13823         if(Roo.isGecko){
13824             this.field.el.dom.setAttribute('autocomplete', 'off');
13825         }
13826         this.field.on("specialkey", this.onSpecialKey, this);
13827         if(this.swallowKeys){
13828             this.field.el.swallowEvent(['keydown','keypress']);
13829         }
13830         this.field.show();
13831         this.field.on("blur", this.onBlur, this);
13832         if(this.field.grow){
13833             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
13834         }
13835     },
13836
13837     onSpecialKey : function(field, e){
13838         //Roo.log('editor onSpecialKey');
13839         if(this.completeOnEnter && e.getKey() == e.ENTER){
13840             e.stopEvent();
13841             this.completeEdit();
13842         }else if(this.cancelOnEsc && e.getKey() == e.ESC){
13843             this.cancelEdit();
13844         }else{
13845             this.fireEvent('specialkey', field, e);
13846         }
13847     },
13848
13849     /**
13850      * Starts the editing process and shows the editor.
13851      * @param {String/HTMLElement/Element} el The element to edit
13852      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
13853       * to the innerHTML of el.
13854      */
13855     startEdit : function(el, value){
13856         if(this.editing){
13857             this.completeEdit();
13858         }
13859         this.boundEl = Roo.get(el);
13860         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
13861         if(!this.rendered){
13862             this.render(this.parentEl || document.body);
13863         }
13864         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
13865             return;
13866         }
13867         this.startValue = v;
13868         this.field.setValue(v);
13869         if(this.autoSize){
13870             var sz = this.boundEl.getSize();
13871             switch(this.autoSize){
13872                 case "width":
13873                 this.setSize(sz.width,  "");
13874                 break;
13875                 case "height":
13876                 this.setSize("",  sz.height);
13877                 break;
13878                 default:
13879                 this.setSize(sz.width,  sz.height);
13880             }
13881         }
13882         this.el.alignTo(this.boundEl, this.alignment);
13883         this.editing = true;
13884         if(Roo.QuickTips){
13885             Roo.QuickTips.disable();
13886         }
13887         this.show();
13888     },
13889
13890     /**
13891      * Sets the height and width of this editor.
13892      * @param {Number} width The new width
13893      * @param {Number} height The new height
13894      */
13895     setSize : function(w, h){
13896         this.field.setSize(w, h);
13897         if(this.el){
13898             this.el.sync();
13899         }
13900     },
13901
13902     /**
13903      * Realigns the editor to the bound field based on the current alignment config value.
13904      */
13905     realign : function(){
13906         this.el.alignTo(this.boundEl, this.alignment);
13907     },
13908
13909     /**
13910      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
13911      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
13912      */
13913     completeEdit : function(remainVisible){
13914         if(!this.editing){
13915             return;
13916         }
13917         var v = this.getValue();
13918         if(this.revertInvalid !== false && !this.field.isValid()){
13919             v = this.startValue;
13920             this.cancelEdit(true);
13921         }
13922         if(String(v) === String(this.startValue) && this.ignoreNoChange){
13923             this.editing = false;
13924             this.hide();
13925             return;
13926         }
13927         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
13928             this.editing = false;
13929             if(this.updateEl && this.boundEl){
13930                 this.boundEl.update(v);
13931             }
13932             if(remainVisible !== true){
13933                 this.hide();
13934             }
13935             this.fireEvent("complete", this, v, this.startValue);
13936         }
13937     },
13938
13939     // private
13940     onShow : function(){
13941         this.el.show();
13942         if(this.hideEl !== false){
13943             this.boundEl.hide();
13944         }
13945         this.field.show();
13946         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
13947             this.fixIEFocus = true;
13948             this.deferredFocus.defer(50, this);
13949         }else{
13950             this.field.focus();
13951         }
13952         this.fireEvent("startedit", this.boundEl, this.startValue);
13953     },
13954
13955     deferredFocus : function(){
13956         if(this.editing){
13957             this.field.focus();
13958         }
13959     },
13960
13961     /**
13962      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
13963      * reverted to the original starting value.
13964      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
13965      * cancel (defaults to false)
13966      */
13967     cancelEdit : function(remainVisible){
13968         if(this.editing){
13969             this.setValue(this.startValue);
13970             if(remainVisible !== true){
13971                 this.hide();
13972             }
13973         }
13974     },
13975
13976     // private
13977     onBlur : function(){
13978         if(this.allowBlur !== true && this.editing){
13979             this.completeEdit();
13980         }
13981     },
13982
13983     // private
13984     onHide : function(){
13985         if(this.editing){
13986             this.completeEdit();
13987             return;
13988         }
13989         this.field.blur();
13990         if(this.field.collapse){
13991             this.field.collapse();
13992         }
13993         this.el.hide();
13994         if(this.hideEl !== false){
13995             this.boundEl.show();
13996         }
13997         if(Roo.QuickTips){
13998             Roo.QuickTips.enable();
13999         }
14000     },
14001
14002     /**
14003      * Sets the data value of the editor
14004      * @param {Mixed} value Any valid value supported by the underlying field
14005      */
14006     setValue : function(v){
14007         this.field.setValue(v);
14008     },
14009
14010     /**
14011      * Gets the data value of the editor
14012      * @return {Mixed} The data value
14013      */
14014     getValue : function(){
14015         return this.field.getValue();
14016     }
14017 });/*
14018  * Based on:
14019  * Ext JS Library 1.1.1
14020  * Copyright(c) 2006-2007, Ext JS, LLC.
14021  *
14022  * Originally Released Under LGPL - original licence link has changed is not relivant.
14023  *
14024  * Fork - LGPL
14025  * <script type="text/javascript">
14026  */
14027  
14028 /**
14029  * @class Roo.BasicDialog
14030  * @extends Roo.util.Observable
14031  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
14032  * <pre><code>
14033 var dlg = new Roo.BasicDialog("my-dlg", {
14034     height: 200,
14035     width: 300,
14036     minHeight: 100,
14037     minWidth: 150,
14038     modal: true,
14039     proxyDrag: true,
14040     shadow: true
14041 });
14042 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
14043 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
14044 dlg.addButton('Cancel', dlg.hide, dlg);
14045 dlg.show();
14046 </code></pre>
14047   <b>A Dialog should always be a direct child of the body element.</b>
14048  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
14049  * @cfg {String} title Default text to display in the title bar (defaults to null)
14050  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14051  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14052  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
14053  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
14054  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
14055  * (defaults to null with no animation)
14056  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
14057  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
14058  * property for valid values (defaults to 'all')
14059  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
14060  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
14061  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
14062  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
14063  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
14064  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
14065  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
14066  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
14067  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
14068  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
14069  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
14070  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
14071  * draggable = true (defaults to false)
14072  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
14073  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14074  * shadow (defaults to false)
14075  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
14076  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
14077  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
14078  * @cfg {Array} buttons Array of buttons
14079  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
14080  * @constructor
14081  * Create a new BasicDialog.
14082  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
14083  * @param {Object} config Configuration options
14084  */
14085 Roo.BasicDialog = function(el, config){
14086     this.el = Roo.get(el);
14087     var dh = Roo.DomHelper;
14088     if(!this.el && config && config.autoCreate){
14089         if(typeof config.autoCreate == "object"){
14090             if(!config.autoCreate.id){
14091                 config.autoCreate.id = el;
14092             }
14093             this.el = dh.append(document.body,
14094                         config.autoCreate, true);
14095         }else{
14096             this.el = dh.append(document.body,
14097                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
14098         }
14099     }
14100     el = this.el;
14101     el.setDisplayed(true);
14102     el.hide = this.hideAction;
14103     this.id = el.id;
14104     el.addClass("x-dlg");
14105
14106     Roo.apply(this, config);
14107
14108     this.proxy = el.createProxy("x-dlg-proxy");
14109     this.proxy.hide = this.hideAction;
14110     this.proxy.setOpacity(.5);
14111     this.proxy.hide();
14112
14113     if(config.width){
14114         el.setWidth(config.width);
14115     }
14116     if(config.height){
14117         el.setHeight(config.height);
14118     }
14119     this.size = el.getSize();
14120     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
14121         this.xy = [config.x,config.y];
14122     }else{
14123         this.xy = el.getCenterXY(true);
14124     }
14125     /** The header element @type Roo.Element */
14126     this.header = el.child("> .x-dlg-hd");
14127     /** The body element @type Roo.Element */
14128     this.body = el.child("> .x-dlg-bd");
14129     /** The footer element @type Roo.Element */
14130     this.footer = el.child("> .x-dlg-ft");
14131
14132     if(!this.header){
14133         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
14134     }
14135     if(!this.body){
14136         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
14137     }
14138
14139     this.header.unselectable();
14140     if(this.title){
14141         this.header.update(this.title);
14142     }
14143     // this element allows the dialog to be focused for keyboard event
14144     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
14145     this.focusEl.swallowEvent("click", true);
14146
14147     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
14148
14149     // wrap the body and footer for special rendering
14150     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
14151     if(this.footer){
14152         this.bwrap.dom.appendChild(this.footer.dom);
14153     }
14154
14155     this.bg = this.el.createChild({
14156         tag: "div", cls:"x-dlg-bg",
14157         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
14158     });
14159     this.centerBg = this.bg.child("div.x-dlg-bg-center");
14160
14161
14162     if(this.autoScroll !== false && !this.autoTabs){
14163         this.body.setStyle("overflow", "auto");
14164     }
14165
14166     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
14167
14168     if(this.closable !== false){
14169         this.el.addClass("x-dlg-closable");
14170         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
14171         this.close.on("click", this.closeClick, this);
14172         this.close.addClassOnOver("x-dlg-close-over");
14173     }
14174     if(this.collapsible !== false){
14175         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
14176         this.collapseBtn.on("click", this.collapseClick, this);
14177         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
14178         this.header.on("dblclick", this.collapseClick, this);
14179     }
14180     if(this.resizable !== false){
14181         this.el.addClass("x-dlg-resizable");
14182         this.resizer = new Roo.Resizable(el, {
14183             minWidth: this.minWidth || 80,
14184             minHeight:this.minHeight || 80,
14185             handles: this.resizeHandles || "all",
14186             pinned: true
14187         });
14188         this.resizer.on("beforeresize", this.beforeResize, this);
14189         this.resizer.on("resize", this.onResize, this);
14190     }
14191     if(this.draggable !== false){
14192         el.addClass("x-dlg-draggable");
14193         if (!this.proxyDrag) {
14194             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
14195         }
14196         else {
14197             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
14198         }
14199         dd.setHandleElId(this.header.id);
14200         dd.endDrag = this.endMove.createDelegate(this);
14201         dd.startDrag = this.startMove.createDelegate(this);
14202         dd.onDrag = this.onDrag.createDelegate(this);
14203         dd.scroll = false;
14204         this.dd = dd;
14205     }
14206     if(this.modal){
14207         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
14208         this.mask.enableDisplayMode("block");
14209         this.mask.hide();
14210         this.el.addClass("x-dlg-modal");
14211     }
14212     if(this.shadow){
14213         this.shadow = new Roo.Shadow({
14214             mode : typeof this.shadow == "string" ? this.shadow : "sides",
14215             offset : this.shadowOffset
14216         });
14217     }else{
14218         this.shadowOffset = 0;
14219     }
14220     if(Roo.useShims && this.shim !== false){
14221         this.shim = this.el.createShim();
14222         this.shim.hide = this.hideAction;
14223         this.shim.hide();
14224     }else{
14225         this.shim = false;
14226     }
14227     if(this.autoTabs){
14228         this.initTabs();
14229     }
14230     if (this.buttons) { 
14231         var bts= this.buttons;
14232         this.buttons = [];
14233         Roo.each(bts, function(b) {
14234             this.addButton(b);
14235         }, this);
14236     }
14237     
14238     
14239     this.addEvents({
14240         /**
14241          * @event keydown
14242          * Fires when a key is pressed
14243          * @param {Roo.BasicDialog} this
14244          * @param {Roo.EventObject} e
14245          */
14246         "keydown" : true,
14247         /**
14248          * @event move
14249          * Fires when this dialog is moved by the user.
14250          * @param {Roo.BasicDialog} this
14251          * @param {Number} x The new page X
14252          * @param {Number} y The new page Y
14253          */
14254         "move" : true,
14255         /**
14256          * @event resize
14257          * Fires when this dialog is resized by the user.
14258          * @param {Roo.BasicDialog} this
14259          * @param {Number} width The new width
14260          * @param {Number} height The new height
14261          */
14262         "resize" : true,
14263         /**
14264          * @event beforehide
14265          * Fires before this dialog is hidden.
14266          * @param {Roo.BasicDialog} this
14267          */
14268         "beforehide" : true,
14269         /**
14270          * @event hide
14271          * Fires when this dialog is hidden.
14272          * @param {Roo.BasicDialog} this
14273          */
14274         "hide" : true,
14275         /**
14276          * @event beforeshow
14277          * Fires before this dialog is shown.
14278          * @param {Roo.BasicDialog} this
14279          */
14280         "beforeshow" : true,
14281         /**
14282          * @event show
14283          * Fires when this dialog is shown.
14284          * @param {Roo.BasicDialog} this
14285          */
14286         "show" : true
14287     });
14288     el.on("keydown", this.onKeyDown, this);
14289     el.on("mousedown", this.toFront, this);
14290     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
14291     this.el.hide();
14292     Roo.DialogManager.register(this);
14293     Roo.BasicDialog.superclass.constructor.call(this);
14294 };
14295
14296 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
14297     shadowOffset: Roo.isIE ? 6 : 5,
14298     minHeight: 80,
14299     minWidth: 200,
14300     minButtonWidth: 75,
14301     defaultButton: null,
14302     buttonAlign: "right",
14303     tabTag: 'div',
14304     firstShow: true,
14305
14306     /**
14307      * Sets the dialog title text
14308      * @param {String} text The title text to display
14309      * @return {Roo.BasicDialog} this
14310      */
14311     setTitle : function(text){
14312         this.header.update(text);
14313         return this;
14314     },
14315
14316     // private
14317     closeClick : function(){
14318         this.hide();
14319     },
14320
14321     // private
14322     collapseClick : function(){
14323         this[this.collapsed ? "expand" : "collapse"]();
14324     },
14325
14326     /**
14327      * Collapses the dialog to its minimized state (only the title bar is visible).
14328      * Equivalent to the user clicking the collapse dialog button.
14329      */
14330     collapse : function(){
14331         if(!this.collapsed){
14332             this.collapsed = true;
14333             this.el.addClass("x-dlg-collapsed");
14334             this.restoreHeight = this.el.getHeight();
14335             this.resizeTo(this.el.getWidth(), this.header.getHeight());
14336         }
14337     },
14338
14339     /**
14340      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
14341      * clicking the expand dialog button.
14342      */
14343     expand : function(){
14344         if(this.collapsed){
14345             this.collapsed = false;
14346             this.el.removeClass("x-dlg-collapsed");
14347             this.resizeTo(this.el.getWidth(), this.restoreHeight);
14348         }
14349     },
14350
14351     /**
14352      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
14353      * @return {Roo.TabPanel} The tabs component
14354      */
14355     initTabs : function(){
14356         var tabs = this.getTabs();
14357         while(tabs.getTab(0)){
14358             tabs.removeTab(0);
14359         }
14360         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
14361             var dom = el.dom;
14362             tabs.addTab(Roo.id(dom), dom.title);
14363             dom.title = "";
14364         });
14365         tabs.activate(0);
14366         return tabs;
14367     },
14368
14369     // private
14370     beforeResize : function(){
14371         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
14372     },
14373
14374     // private
14375     onResize : function(){
14376         this.refreshSize();
14377         this.syncBodyHeight();
14378         this.adjustAssets();
14379         this.focus();
14380         this.fireEvent("resize", this, this.size.width, this.size.height);
14381     },
14382
14383     // private
14384     onKeyDown : function(e){
14385         if(this.isVisible()){
14386             this.fireEvent("keydown", this, e);
14387         }
14388     },
14389
14390     /**
14391      * Resizes the dialog.
14392      * @param {Number} width
14393      * @param {Number} height
14394      * @return {Roo.BasicDialog} this
14395      */
14396     resizeTo : function(width, height){
14397         this.el.setSize(width, height);
14398         this.size = {width: width, height: height};
14399         this.syncBodyHeight();
14400         if(this.fixedcenter){
14401             this.center();
14402         }
14403         if(this.isVisible()){
14404             this.constrainXY();
14405             this.adjustAssets();
14406         }
14407         this.fireEvent("resize", this, width, height);
14408         return this;
14409     },
14410
14411
14412     /**
14413      * Resizes the dialog to fit the specified content size.
14414      * @param {Number} width
14415      * @param {Number} height
14416      * @return {Roo.BasicDialog} this
14417      */
14418     setContentSize : function(w, h){
14419         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
14420         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
14421         //if(!this.el.isBorderBox()){
14422             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
14423             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
14424         //}
14425         if(this.tabs){
14426             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
14427             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
14428         }
14429         this.resizeTo(w, h);
14430         return this;
14431     },
14432
14433     /**
14434      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
14435      * executed in response to a particular key being pressed while the dialog is active.
14436      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
14437      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14438      * @param {Function} fn The function to call
14439      * @param {Object} scope (optional) The scope of the function
14440      * @return {Roo.BasicDialog} this
14441      */
14442     addKeyListener : function(key, fn, scope){
14443         var keyCode, shift, ctrl, alt;
14444         if(typeof key == "object" && !(key instanceof Array)){
14445             keyCode = key["key"];
14446             shift = key["shift"];
14447             ctrl = key["ctrl"];
14448             alt = key["alt"];
14449         }else{
14450             keyCode = key;
14451         }
14452         var handler = function(dlg, e){
14453             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14454                 var k = e.getKey();
14455                 if(keyCode instanceof Array){
14456                     for(var i = 0, len = keyCode.length; i < len; i++){
14457                         if(keyCode[i] == k){
14458                           fn.call(scope || window, dlg, k, e);
14459                           return;
14460                         }
14461                     }
14462                 }else{
14463                     if(k == keyCode){
14464                         fn.call(scope || window, dlg, k, e);
14465                     }
14466                 }
14467             }
14468         };
14469         this.on("keydown", handler);
14470         return this;
14471     },
14472
14473     /**
14474      * Returns the TabPanel component (creates it if it doesn't exist).
14475      * Note: If you wish to simply check for the existence of tabs without creating them,
14476      * check for a null 'tabs' property.
14477      * @return {Roo.TabPanel} The tabs component
14478      */
14479     getTabs : function(){
14480         if(!this.tabs){
14481             this.el.addClass("x-dlg-auto-tabs");
14482             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
14483             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
14484         }
14485         return this.tabs;
14486     },
14487
14488     /**
14489      * Adds a button to the footer section of the dialog.
14490      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
14491      * object or a valid Roo.DomHelper element config
14492      * @param {Function} handler The function called when the button is clicked
14493      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
14494      * @return {Roo.Button} The new button
14495      */
14496     addButton : function(config, handler, scope){
14497         var dh = Roo.DomHelper;
14498         if(!this.footer){
14499             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
14500         }
14501         if(!this.btnContainer){
14502             var tb = this.footer.createChild({
14503
14504                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
14505                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
14506             }, null, true);
14507             this.btnContainer = tb.firstChild.firstChild.firstChild;
14508         }
14509         var bconfig = {
14510             handler: handler,
14511             scope: scope,
14512             minWidth: this.minButtonWidth,
14513             hideParent:true
14514         };
14515         if(typeof config == "string"){
14516             bconfig.text = config;
14517         }else{
14518             if(config.tag){
14519                 bconfig.dhconfig = config;
14520             }else{
14521                 Roo.apply(bconfig, config);
14522             }
14523         }
14524         var fc = false;
14525         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
14526             bconfig.position = Math.max(0, bconfig.position);
14527             fc = this.btnContainer.childNodes[bconfig.position];
14528         }
14529          
14530         var btn = new Roo.Button(
14531             fc ? 
14532                 this.btnContainer.insertBefore(document.createElement("td"),fc)
14533                 : this.btnContainer.appendChild(document.createElement("td")),
14534             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
14535             bconfig
14536         );
14537         this.syncBodyHeight();
14538         if(!this.buttons){
14539             /**
14540              * Array of all the buttons that have been added to this dialog via addButton
14541              * @type Array
14542              */
14543             this.buttons = [];
14544         }
14545         this.buttons.push(btn);
14546         return btn;
14547     },
14548
14549     /**
14550      * Sets the default button to be focused when the dialog is displayed.
14551      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
14552      * @return {Roo.BasicDialog} this
14553      */
14554     setDefaultButton : function(btn){
14555         this.defaultButton = btn;
14556         return this;
14557     },
14558
14559     // private
14560     getHeaderFooterHeight : function(safe){
14561         var height = 0;
14562         if(this.header){
14563            height += this.header.getHeight();
14564         }
14565         if(this.footer){
14566            var fm = this.footer.getMargins();
14567             height += (this.footer.getHeight()+fm.top+fm.bottom);
14568         }
14569         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
14570         height += this.centerBg.getPadding("tb");
14571         return height;
14572     },
14573
14574     // private
14575     syncBodyHeight : function(){
14576         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
14577         var height = this.size.height - this.getHeaderFooterHeight(false);
14578         bd.setHeight(height-bd.getMargins("tb"));
14579         var hh = this.header.getHeight();
14580         var h = this.size.height-hh;
14581         cb.setHeight(h);
14582         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
14583         bw.setHeight(h-cb.getPadding("tb"));
14584         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
14585         bd.setWidth(bw.getWidth(true));
14586         if(this.tabs){
14587             this.tabs.syncHeight();
14588             if(Roo.isIE){
14589                 this.tabs.el.repaint();
14590             }
14591         }
14592     },
14593
14594     /**
14595      * Restores the previous state of the dialog if Roo.state is configured.
14596      * @return {Roo.BasicDialog} this
14597      */
14598     restoreState : function(){
14599         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
14600         if(box && box.width){
14601             this.xy = [box.x, box.y];
14602             this.resizeTo(box.width, box.height);
14603         }
14604         return this;
14605     },
14606
14607     // private
14608     beforeShow : function(){
14609         this.expand();
14610         if(this.fixedcenter){
14611             this.xy = this.el.getCenterXY(true);
14612         }
14613         if(this.modal){
14614             Roo.get(document.body).addClass("x-body-masked");
14615             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14616             this.mask.show();
14617         }
14618         this.constrainXY();
14619     },
14620
14621     // private
14622     animShow : function(){
14623         var b = Roo.get(this.animateTarget).getBox();
14624         this.proxy.setSize(b.width, b.height);
14625         this.proxy.setLocation(b.x, b.y);
14626         this.proxy.show();
14627         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
14628                     true, .35, this.showEl.createDelegate(this));
14629     },
14630
14631     /**
14632      * Shows the dialog.
14633      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
14634      * @return {Roo.BasicDialog} this
14635      */
14636     show : function(animateTarget){
14637         if (this.fireEvent("beforeshow", this) === false){
14638             return;
14639         }
14640         if(this.syncHeightBeforeShow){
14641             this.syncBodyHeight();
14642         }else if(this.firstShow){
14643             this.firstShow = false;
14644             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
14645         }
14646         this.animateTarget = animateTarget || this.animateTarget;
14647         if(!this.el.isVisible()){
14648             this.beforeShow();
14649             if(this.animateTarget && Roo.get(this.animateTarget)){
14650                 this.animShow();
14651             }else{
14652                 this.showEl();
14653             }
14654         }
14655         return this;
14656     },
14657
14658     // private
14659     showEl : function(){
14660         this.proxy.hide();
14661         this.el.setXY(this.xy);
14662         this.el.show();
14663         this.adjustAssets(true);
14664         this.toFront();
14665         this.focus();
14666         // IE peekaboo bug - fix found by Dave Fenwick
14667         if(Roo.isIE){
14668             this.el.repaint();
14669         }
14670         this.fireEvent("show", this);
14671     },
14672
14673     /**
14674      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
14675      * dialog itself will receive focus.
14676      */
14677     focus : function(){
14678         if(this.defaultButton){
14679             this.defaultButton.focus();
14680         }else{
14681             this.focusEl.focus();
14682         }
14683     },
14684
14685     // private
14686     constrainXY : function(){
14687         if(this.constraintoviewport !== false){
14688             if(!this.viewSize){
14689                 if(this.container){
14690                     var s = this.container.getSize();
14691                     this.viewSize = [s.width, s.height];
14692                 }else{
14693                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
14694                 }
14695             }
14696             var s = Roo.get(this.container||document).getScroll();
14697
14698             var x = this.xy[0], y = this.xy[1];
14699             var w = this.size.width, h = this.size.height;
14700             var vw = this.viewSize[0], vh = this.viewSize[1];
14701             // only move it if it needs it
14702             var moved = false;
14703             // first validate right/bottom
14704             if(x + w > vw+s.left){
14705                 x = vw - w;
14706                 moved = true;
14707             }
14708             if(y + h > vh+s.top){
14709                 y = vh - h;
14710                 moved = true;
14711             }
14712             // then make sure top/left isn't negative
14713             if(x < s.left){
14714                 x = s.left;
14715                 moved = true;
14716             }
14717             if(y < s.top){
14718                 y = s.top;
14719                 moved = true;
14720             }
14721             if(moved){
14722                 // cache xy
14723                 this.xy = [x, y];
14724                 if(this.isVisible()){
14725                     this.el.setLocation(x, y);
14726                     this.adjustAssets();
14727                 }
14728             }
14729         }
14730     },
14731
14732     // private
14733     onDrag : function(){
14734         if(!this.proxyDrag){
14735             this.xy = this.el.getXY();
14736             this.adjustAssets();
14737         }
14738     },
14739
14740     // private
14741     adjustAssets : function(doShow){
14742         var x = this.xy[0], y = this.xy[1];
14743         var w = this.size.width, h = this.size.height;
14744         if(doShow === true){
14745             if(this.shadow){
14746                 this.shadow.show(this.el);
14747             }
14748             if(this.shim){
14749                 this.shim.show();
14750             }
14751         }
14752         if(this.shadow && this.shadow.isVisible()){
14753             this.shadow.show(this.el);
14754         }
14755         if(this.shim && this.shim.isVisible()){
14756             this.shim.setBounds(x, y, w, h);
14757         }
14758     },
14759
14760     // private
14761     adjustViewport : function(w, h){
14762         if(!w || !h){
14763             w = Roo.lib.Dom.getViewWidth();
14764             h = Roo.lib.Dom.getViewHeight();
14765         }
14766         // cache the size
14767         this.viewSize = [w, h];
14768         if(this.modal && this.mask.isVisible()){
14769             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
14770             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14771         }
14772         if(this.isVisible()){
14773             this.constrainXY();
14774         }
14775     },
14776
14777     /**
14778      * Destroys this dialog and all its supporting elements (including any tabs, shim,
14779      * shadow, proxy, mask, etc.)  Also removes all event listeners.
14780      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
14781      */
14782     destroy : function(removeEl){
14783         if(this.isVisible()){
14784             this.animateTarget = null;
14785             this.hide();
14786         }
14787         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
14788         if(this.tabs){
14789             this.tabs.destroy(removeEl);
14790         }
14791         Roo.destroy(
14792              this.shim,
14793              this.proxy,
14794              this.resizer,
14795              this.close,
14796              this.mask
14797         );
14798         if(this.dd){
14799             this.dd.unreg();
14800         }
14801         if(this.buttons){
14802            for(var i = 0, len = this.buttons.length; i < len; i++){
14803                this.buttons[i].destroy();
14804            }
14805         }
14806         this.el.removeAllListeners();
14807         if(removeEl === true){
14808             this.el.update("");
14809             this.el.remove();
14810         }
14811         Roo.DialogManager.unregister(this);
14812     },
14813
14814     // private
14815     startMove : function(){
14816         if(this.proxyDrag){
14817             this.proxy.show();
14818         }
14819         if(this.constraintoviewport !== false){
14820             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
14821         }
14822     },
14823
14824     // private
14825     endMove : function(){
14826         if(!this.proxyDrag){
14827             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
14828         }else{
14829             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
14830             this.proxy.hide();
14831         }
14832         this.refreshSize();
14833         this.adjustAssets();
14834         this.focus();
14835         this.fireEvent("move", this, this.xy[0], this.xy[1]);
14836     },
14837
14838     /**
14839      * Brings this dialog to the front of any other visible dialogs
14840      * @return {Roo.BasicDialog} this
14841      */
14842     toFront : function(){
14843         Roo.DialogManager.bringToFront(this);
14844         return this;
14845     },
14846
14847     /**
14848      * Sends this dialog to the back (under) of any other visible dialogs
14849      * @return {Roo.BasicDialog} this
14850      */
14851     toBack : function(){
14852         Roo.DialogManager.sendToBack(this);
14853         return this;
14854     },
14855
14856     /**
14857      * Centers this dialog in the viewport
14858      * @return {Roo.BasicDialog} this
14859      */
14860     center : function(){
14861         var xy = this.el.getCenterXY(true);
14862         this.moveTo(xy[0], xy[1]);
14863         return this;
14864     },
14865
14866     /**
14867      * Moves the dialog's top-left corner to the specified point
14868      * @param {Number} x
14869      * @param {Number} y
14870      * @return {Roo.BasicDialog} this
14871      */
14872     moveTo : function(x, y){
14873         this.xy = [x,y];
14874         if(this.isVisible()){
14875             this.el.setXY(this.xy);
14876             this.adjustAssets();
14877         }
14878         return this;
14879     },
14880
14881     /**
14882      * Aligns the dialog to the specified element
14883      * @param {String/HTMLElement/Roo.Element} element The element to align to.
14884      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
14885      * @param {Array} offsets (optional) Offset the positioning by [x, y]
14886      * @return {Roo.BasicDialog} this
14887      */
14888     alignTo : function(element, position, offsets){
14889         this.xy = this.el.getAlignToXY(element, position, offsets);
14890         if(this.isVisible()){
14891             this.el.setXY(this.xy);
14892             this.adjustAssets();
14893         }
14894         return this;
14895     },
14896
14897     /**
14898      * Anchors an element to another element and realigns it when the window is resized.
14899      * @param {String/HTMLElement/Roo.Element} element The element to align to.
14900      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
14901      * @param {Array} offsets (optional) Offset the positioning by [x, y]
14902      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
14903      * is a number, it is used as the buffer delay (defaults to 50ms).
14904      * @return {Roo.BasicDialog} this
14905      */
14906     anchorTo : function(el, alignment, offsets, monitorScroll){
14907         var action = function(){
14908             this.alignTo(el, alignment, offsets);
14909         };
14910         Roo.EventManager.onWindowResize(action, this);
14911         var tm = typeof monitorScroll;
14912         if(tm != 'undefined'){
14913             Roo.EventManager.on(window, 'scroll', action, this,
14914                 {buffer: tm == 'number' ? monitorScroll : 50});
14915         }
14916         action.call(this);
14917         return this;
14918     },
14919
14920     /**
14921      * Returns true if the dialog is visible
14922      * @return {Boolean}
14923      */
14924     isVisible : function(){
14925         return this.el.isVisible();
14926     },
14927
14928     // private
14929     animHide : function(callback){
14930         var b = Roo.get(this.animateTarget).getBox();
14931         this.proxy.show();
14932         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
14933         this.el.hide();
14934         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
14935                     this.hideEl.createDelegate(this, [callback]));
14936     },
14937
14938     /**
14939      * Hides the dialog.
14940      * @param {Function} callback (optional) Function to call when the dialog is hidden
14941      * @return {Roo.BasicDialog} this
14942      */
14943     hide : function(callback){
14944         if (this.fireEvent("beforehide", this) === false){
14945             return;
14946         }
14947         if(this.shadow){
14948             this.shadow.hide();
14949         }
14950         if(this.shim) {
14951           this.shim.hide();
14952         }
14953         // sometimes animateTarget seems to get set.. causing problems...
14954         // this just double checks..
14955         if(this.animateTarget && Roo.get(this.animateTarget)) {
14956            this.animHide(callback);
14957         }else{
14958             this.el.hide();
14959             this.hideEl(callback);
14960         }
14961         return this;
14962     },
14963
14964     // private
14965     hideEl : function(callback){
14966         this.proxy.hide();
14967         if(this.modal){
14968             this.mask.hide();
14969             Roo.get(document.body).removeClass("x-body-masked");
14970         }
14971         this.fireEvent("hide", this);
14972         if(typeof callback == "function"){
14973             callback();
14974         }
14975     },
14976
14977     // private
14978     hideAction : function(){
14979         this.setLeft("-10000px");
14980         this.setTop("-10000px");
14981         this.setStyle("visibility", "hidden");
14982     },
14983
14984     // private
14985     refreshSize : function(){
14986         this.size = this.el.getSize();
14987         this.xy = this.el.getXY();
14988         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
14989     },
14990
14991     // private
14992     // z-index is managed by the DialogManager and may be overwritten at any time
14993     setZIndex : function(index){
14994         if(this.modal){
14995             this.mask.setStyle("z-index", index);
14996         }
14997         if(this.shim){
14998             this.shim.setStyle("z-index", ++index);
14999         }
15000         if(this.shadow){
15001             this.shadow.setZIndex(++index);
15002         }
15003         this.el.setStyle("z-index", ++index);
15004         if(this.proxy){
15005             this.proxy.setStyle("z-index", ++index);
15006         }
15007         if(this.resizer){
15008             this.resizer.proxy.setStyle("z-index", ++index);
15009         }
15010
15011         this.lastZIndex = index;
15012     },
15013
15014     /**
15015      * Returns the element for this dialog
15016      * @return {Roo.Element} The underlying dialog Element
15017      */
15018     getEl : function(){
15019         return this.el;
15020     }
15021 });
15022
15023 /**
15024  * @class Roo.DialogManager
15025  * Provides global access to BasicDialogs that have been created and
15026  * support for z-indexing (layering) multiple open dialogs.
15027  */
15028 Roo.DialogManager = function(){
15029     var list = {};
15030     var accessList = [];
15031     var front = null;
15032
15033     // private
15034     var sortDialogs = function(d1, d2){
15035         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
15036     };
15037
15038     // private
15039     var orderDialogs = function(){
15040         accessList.sort(sortDialogs);
15041         var seed = Roo.DialogManager.zseed;
15042         for(var i = 0, len = accessList.length; i < len; i++){
15043             var dlg = accessList[i];
15044             if(dlg){
15045                 dlg.setZIndex(seed + (i*10));
15046             }
15047         }
15048     };
15049
15050     return {
15051         /**
15052          * The starting z-index for BasicDialogs (defaults to 9000)
15053          * @type Number The z-index value
15054          */
15055         zseed : 9000,
15056
15057         // private
15058         register : function(dlg){
15059             list[dlg.id] = dlg;
15060             accessList.push(dlg);
15061         },
15062
15063         // private
15064         unregister : function(dlg){
15065             delete list[dlg.id];
15066             var i=0;
15067             var len=0;
15068             if(!accessList.indexOf){
15069                 for(  i = 0, len = accessList.length; i < len; i++){
15070                     if(accessList[i] == dlg){
15071                         accessList.splice(i, 1);
15072                         return;
15073                     }
15074                 }
15075             }else{
15076                  i = accessList.indexOf(dlg);
15077                 if(i != -1){
15078                     accessList.splice(i, 1);
15079                 }
15080             }
15081         },
15082
15083         /**
15084          * Gets a registered dialog by id
15085          * @param {String/Object} id The id of the dialog or a dialog
15086          * @return {Roo.BasicDialog} this
15087          */
15088         get : function(id){
15089             return typeof id == "object" ? id : list[id];
15090         },
15091
15092         /**
15093          * Brings the specified dialog to the front
15094          * @param {String/Object} dlg The id of the dialog or a dialog
15095          * @return {Roo.BasicDialog} this
15096          */
15097         bringToFront : function(dlg){
15098             dlg = this.get(dlg);
15099             if(dlg != front){
15100                 front = dlg;
15101                 dlg._lastAccess = new Date().getTime();
15102                 orderDialogs();
15103             }
15104             return dlg;
15105         },
15106
15107         /**
15108          * Sends the specified dialog to the back
15109          * @param {String/Object} dlg The id of the dialog or a dialog
15110          * @return {Roo.BasicDialog} this
15111          */
15112         sendToBack : function(dlg){
15113             dlg = this.get(dlg);
15114             dlg._lastAccess = -(new Date().getTime());
15115             orderDialogs();
15116             return dlg;
15117         },
15118
15119         /**
15120          * Hides all dialogs
15121          */
15122         hideAll : function(){
15123             for(var id in list){
15124                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
15125                     list[id].hide();
15126                 }
15127             }
15128         }
15129     };
15130 }();
15131
15132 /**
15133  * @class Roo.LayoutDialog
15134  * @extends Roo.BasicDialog
15135  * Dialog which provides adjustments for working with a layout in a Dialog.
15136  * Add your necessary layout config options to the dialog's config.<br>
15137  * Example usage (including a nested layout):
15138  * <pre><code>
15139 if(!dialog){
15140     dialog = new Roo.LayoutDialog("download-dlg", {
15141         modal: true,
15142         width:600,
15143         height:450,
15144         shadow:true,
15145         minWidth:500,
15146         minHeight:350,
15147         autoTabs:true,
15148         proxyDrag:true,
15149         // layout config merges with the dialog config
15150         center:{
15151             tabPosition: "top",
15152             alwaysShowTabs: true
15153         }
15154     });
15155     dialog.addKeyListener(27, dialog.hide, dialog);
15156     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
15157     dialog.addButton("Build It!", this.getDownload, this);
15158
15159     // we can even add nested layouts
15160     var innerLayout = new Roo.BorderLayout("dl-inner", {
15161         east: {
15162             initialSize: 200,
15163             autoScroll:true,
15164             split:true
15165         },
15166         center: {
15167             autoScroll:true
15168         }
15169     });
15170     innerLayout.beginUpdate();
15171     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
15172     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
15173     innerLayout.endUpdate(true);
15174
15175     var layout = dialog.getLayout();
15176     layout.beginUpdate();
15177     layout.add("center", new Roo.ContentPanel("standard-panel",
15178                         {title: "Download the Source", fitToFrame:true}));
15179     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
15180                {title: "Build your own roo.js"}));
15181     layout.getRegion("center").showPanel(sp);
15182     layout.endUpdate();
15183 }
15184 </code></pre>
15185     * @constructor
15186     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
15187     * @param {Object} config configuration options
15188   */
15189 Roo.LayoutDialog = function(el, cfg){
15190     
15191     var config=  cfg;
15192     if (typeof(cfg) == 'undefined') {
15193         config = Roo.apply({}, el);
15194         // not sure why we use documentElement here.. - it should always be body.
15195         // IE7 borks horribly if we use documentElement.
15196         el = Roo.get( Roo.isIE ? (document.body || document.documentElement) : (document.documentElement || document.body) ).createChild();
15197         //config.autoCreate = true;
15198     }
15199     
15200     
15201     config.autoTabs = false;
15202     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
15203     this.body.setStyle({overflow:"hidden", position:"relative"});
15204     this.layout = new Roo.BorderLayout(this.body.dom, config);
15205     this.layout.monitorWindowResize = false;
15206     this.el.addClass("x-dlg-auto-layout");
15207     // fix case when center region overwrites center function
15208     this.center = Roo.BasicDialog.prototype.center;
15209     this.on("show", this.layout.layout, this.layout, true);
15210     if (config.items) {
15211         var xitems = config.items;
15212         delete config.items;
15213         Roo.each(xitems, this.addxtype, this);
15214     }
15215     
15216     
15217 };
15218 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
15219     /**
15220      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
15221      * @deprecated
15222      */
15223     endUpdate : function(){
15224         this.layout.endUpdate();
15225     },
15226
15227     /**
15228      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
15229      *  @deprecated
15230      */
15231     beginUpdate : function(){
15232         this.layout.beginUpdate();
15233     },
15234
15235     /**
15236      * Get the BorderLayout for this dialog
15237      * @return {Roo.BorderLayout}
15238      */
15239     getLayout : function(){
15240         return this.layout;
15241     },
15242
15243     showEl : function(){
15244         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
15245         if(Roo.isIE7){
15246             this.layout.layout();
15247         }
15248     },
15249
15250     // private
15251     // Use the syncHeightBeforeShow config option to control this automatically
15252     syncBodyHeight : function(){
15253         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
15254         if(this.layout){this.layout.layout();}
15255     },
15256     
15257       /**
15258      * Add an xtype element (actually adds to the layout.)
15259      * @return {Object} xdata xtype object data.
15260      */
15261     
15262     addxtype : function(c) {
15263         return this.layout.addxtype(c);
15264     }
15265 });/*
15266  * Based on:
15267  * Ext JS Library 1.1.1
15268  * Copyright(c) 2006-2007, Ext JS, LLC.
15269  *
15270  * Originally Released Under LGPL - original licence link has changed is not relivant.
15271  *
15272  * Fork - LGPL
15273  * <script type="text/javascript">
15274  */
15275  
15276 /**
15277  * @class Roo.MessageBox
15278  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
15279  * Example usage:
15280  *<pre><code>
15281 // Basic alert:
15282 Roo.Msg.alert('Status', 'Changes saved successfully.');
15283
15284 // Prompt for user data:
15285 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
15286     if (btn == 'ok'){
15287         // process text value...
15288     }
15289 });
15290
15291 // Show a dialog using config options:
15292 Roo.Msg.show({
15293    title:'Save Changes?',
15294    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
15295    buttons: Roo.Msg.YESNOCANCEL,
15296    fn: processResult,
15297    animEl: 'elId'
15298 });
15299 </code></pre>
15300  * @singleton
15301  */
15302 Roo.MessageBox = function(){
15303     var dlg, opt, mask, waitTimer;
15304     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
15305     var buttons, activeTextEl, bwidth;
15306
15307     // private
15308     var handleButton = function(button){
15309         dlg.hide();
15310         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
15311     };
15312
15313     // private
15314     var handleHide = function(){
15315         if(opt && opt.cls){
15316             dlg.el.removeClass(opt.cls);
15317         }
15318         if(waitTimer){
15319             Roo.TaskMgr.stop(waitTimer);
15320             waitTimer = null;
15321         }
15322     };
15323
15324     // private
15325     var updateButtons = function(b){
15326         var width = 0;
15327         if(!b){
15328             buttons["ok"].hide();
15329             buttons["cancel"].hide();
15330             buttons["yes"].hide();
15331             buttons["no"].hide();
15332             dlg.footer.dom.style.display = 'none';
15333             return width;
15334         }
15335         dlg.footer.dom.style.display = '';
15336         for(var k in buttons){
15337             if(typeof buttons[k] != "function"){
15338                 if(b[k]){
15339                     buttons[k].show();
15340                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
15341                     width += buttons[k].el.getWidth()+15;
15342                 }else{
15343                     buttons[k].hide();
15344                 }
15345             }
15346         }
15347         return width;
15348     };
15349
15350     // private
15351     var handleEsc = function(d, k, e){
15352         if(opt && opt.closable !== false){
15353             dlg.hide();
15354         }
15355         if(e){
15356             e.stopEvent();
15357         }
15358     };
15359
15360     return {
15361         /**
15362          * Returns a reference to the underlying {@link Roo.BasicDialog} element
15363          * @return {Roo.BasicDialog} The BasicDialog element
15364          */
15365         getDialog : function(){
15366            if(!dlg){
15367                 dlg = new Roo.BasicDialog("x-msg-box", {
15368                     autoCreate : true,
15369                     shadow: true,
15370                     draggable: true,
15371                     resizable:false,
15372                     constraintoviewport:false,
15373                     fixedcenter:true,
15374                     collapsible : false,
15375                     shim:true,
15376                     modal: true,
15377                     width:400, height:100,
15378                     buttonAlign:"center",
15379                     closeClick : function(){
15380                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
15381                             handleButton("no");
15382                         }else{
15383                             handleButton("cancel");
15384                         }
15385                     }
15386                 });
15387                 dlg.on("hide", handleHide);
15388                 mask = dlg.mask;
15389                 dlg.addKeyListener(27, handleEsc);
15390                 buttons = {};
15391                 var bt = this.buttonText;
15392                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
15393                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
15394                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
15395                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
15396                 bodyEl = dlg.body.createChild({
15397
15398                     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>'
15399                 });
15400                 msgEl = bodyEl.dom.firstChild;
15401                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
15402                 textboxEl.enableDisplayMode();
15403                 textboxEl.addKeyListener([10,13], function(){
15404                     if(dlg.isVisible() && opt && opt.buttons){
15405                         if(opt.buttons.ok){
15406                             handleButton("ok");
15407                         }else if(opt.buttons.yes){
15408                             handleButton("yes");
15409                         }
15410                     }
15411                 });
15412                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
15413                 textareaEl.enableDisplayMode();
15414                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
15415                 progressEl.enableDisplayMode();
15416                 var pf = progressEl.dom.firstChild;
15417                 if (pf) {
15418                     pp = Roo.get(pf.firstChild);
15419                     pp.setHeight(pf.offsetHeight);
15420                 }
15421                 
15422             }
15423             return dlg;
15424         },
15425
15426         /**
15427          * Updates the message box body text
15428          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
15429          * the XHTML-compliant non-breaking space character '&amp;#160;')
15430          * @return {Roo.MessageBox} This message box
15431          */
15432         updateText : function(text){
15433             if(!dlg.isVisible() && !opt.width){
15434                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
15435             }
15436             msgEl.innerHTML = text || '&#160;';
15437             var w = Math.max(Math.min(opt.width || msgEl.offsetWidth, this.maxWidth), 
15438                         Math.max(opt.minWidth || this.minWidth, bwidth));
15439             if(opt.prompt){
15440                 activeTextEl.setWidth(w);
15441             }
15442             if(dlg.isVisible()){
15443                 dlg.fixedcenter = false;
15444             }
15445             dlg.setContentSize(w, bodyEl.getHeight());
15446             if(dlg.isVisible()){
15447                 dlg.fixedcenter = true;
15448             }
15449             return this;
15450         },
15451
15452         /**
15453          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
15454          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
15455          * @param {Number} value Any number between 0 and 1 (e.g., .5)
15456          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
15457          * @return {Roo.MessageBox} This message box
15458          */
15459         updateProgress : function(value, text){
15460             if(text){
15461                 this.updateText(text);
15462             }
15463             if (pp) { // weird bug on my firefox - for some reason this is not defined
15464                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
15465             }
15466             return this;
15467         },        
15468
15469         /**
15470          * Returns true if the message box is currently displayed
15471          * @return {Boolean} True if the message box is visible, else false
15472          */
15473         isVisible : function(){
15474             return dlg && dlg.isVisible();  
15475         },
15476
15477         /**
15478          * Hides the message box if it is displayed
15479          */
15480         hide : function(){
15481             if(this.isVisible()){
15482                 dlg.hide();
15483             }  
15484         },
15485
15486         /**
15487          * Displays a new message box, or reinitializes an existing message box, based on the config options
15488          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
15489          * The following config object properties are supported:
15490          * <pre>
15491 Property    Type             Description
15492 ----------  ---------------  ------------------------------------------------------------------------------------
15493 animEl            String/Element   An id or Element from which the message box should animate as it opens and
15494                                    closes (defaults to undefined)
15495 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
15496                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
15497 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
15498                                    progress and wait dialogs will ignore this property and always hide the
15499                                    close button as they can only be closed programmatically.
15500 cls               String           A custom CSS class to apply to the message box element
15501 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
15502                                    displayed (defaults to 75)
15503 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
15504                                    function will be btn (the name of the button that was clicked, if applicable,
15505                                    e.g. "ok"), and text (the value of the active text field, if applicable).
15506                                    Progress and wait dialogs will ignore this option since they do not respond to
15507                                    user actions and can only be closed programmatically, so any required function
15508                                    should be called by the same code after it closes the dialog.
15509 icon              String           A CSS class that provides a background image to be used as an icon for
15510                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
15511 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
15512 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
15513 modal             Boolean          False to allow user interaction with the page while the message box is
15514                                    displayed (defaults to true)
15515 msg               String           A string that will replace the existing message box body text (defaults
15516                                    to the XHTML-compliant non-breaking space character '&#160;')
15517 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
15518 progress          Boolean          True to display a progress bar (defaults to false)
15519 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
15520 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
15521 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
15522 title             String           The title text
15523 value             String           The string value to set into the active textbox element if displayed
15524 wait              Boolean          True to display a progress bar (defaults to false)
15525 width             Number           The width of the dialog in pixels
15526 </pre>
15527          *
15528          * Example usage:
15529          * <pre><code>
15530 Roo.Msg.show({
15531    title: 'Address',
15532    msg: 'Please enter your address:',
15533    width: 300,
15534    buttons: Roo.MessageBox.OKCANCEL,
15535    multiline: true,
15536    fn: saveAddress,
15537    animEl: 'addAddressBtn'
15538 });
15539 </code></pre>
15540          * @param {Object} config Configuration options
15541          * @return {Roo.MessageBox} This message box
15542          */
15543         show : function(options){
15544             if(this.isVisible()){
15545                 this.hide();
15546             }
15547             var d = this.getDialog();
15548             opt = options;
15549             d.setTitle(opt.title || "&#160;");
15550             d.close.setDisplayed(opt.closable !== false);
15551             activeTextEl = textboxEl;
15552             opt.prompt = opt.prompt || (opt.multiline ? true : false);
15553             if(opt.prompt){
15554                 if(opt.multiline){
15555                     textboxEl.hide();
15556                     textareaEl.show();
15557                     textareaEl.setHeight(typeof opt.multiline == "number" ?
15558                         opt.multiline : this.defaultTextHeight);
15559                     activeTextEl = textareaEl;
15560                 }else{
15561                     textboxEl.show();
15562                     textareaEl.hide();
15563                 }
15564             }else{
15565                 textboxEl.hide();
15566                 textareaEl.hide();
15567             }
15568             progressEl.setDisplayed(opt.progress === true);
15569             this.updateProgress(0);
15570             activeTextEl.dom.value = opt.value || "";
15571             if(opt.prompt){
15572                 dlg.setDefaultButton(activeTextEl);
15573             }else{
15574                 var bs = opt.buttons;
15575                 var db = null;
15576                 if(bs && bs.ok){
15577                     db = buttons["ok"];
15578                 }else if(bs && bs.yes){
15579                     db = buttons["yes"];
15580                 }
15581                 dlg.setDefaultButton(db);
15582             }
15583             bwidth = updateButtons(opt.buttons);
15584             this.updateText(opt.msg);
15585             if(opt.cls){
15586                 d.el.addClass(opt.cls);
15587             }
15588             d.proxyDrag = opt.proxyDrag === true;
15589             d.modal = opt.modal !== false;
15590             d.mask = opt.modal !== false ? mask : false;
15591             if(!d.isVisible()){
15592                 // force it to the end of the z-index stack so it gets a cursor in FF
15593                 document.body.appendChild(dlg.el.dom);
15594                 d.animateTarget = null;
15595                 d.show(options.animEl);
15596             }
15597             return this;
15598         },
15599
15600         /**
15601          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
15602          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
15603          * and closing the message box when the process is complete.
15604          * @param {String} title The title bar text
15605          * @param {String} msg The message box body text
15606          * @return {Roo.MessageBox} This message box
15607          */
15608         progress : function(title, msg){
15609             this.show({
15610                 title : title,
15611                 msg : msg,
15612                 buttons: false,
15613                 progress:true,
15614                 closable:false,
15615                 minWidth: this.minProgressWidth,
15616                 modal : true
15617             });
15618             return this;
15619         },
15620
15621         /**
15622          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
15623          * If a callback function is passed it will be called after the user clicks the button, and the
15624          * id of the button that was clicked will be passed as the only parameter to the callback
15625          * (could also be the top-right close button).
15626          * @param {String} title The title bar text
15627          * @param {String} msg The message box body text
15628          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15629          * @param {Object} scope (optional) The scope of the callback function
15630          * @return {Roo.MessageBox} This message box
15631          */
15632         alert : function(title, msg, fn, scope){
15633             this.show({
15634                 title : title,
15635                 msg : msg,
15636                 buttons: this.OK,
15637                 fn: fn,
15638                 scope : scope,
15639                 modal : true
15640             });
15641             return this;
15642         },
15643
15644         /**
15645          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
15646          * interaction while waiting for a long-running process to complete that does not have defined intervals.
15647          * You are responsible for closing the message box when the process is complete.
15648          * @param {String} msg The message box body text
15649          * @param {String} title (optional) The title bar text
15650          * @return {Roo.MessageBox} This message box
15651          */
15652         wait : function(msg, title){
15653             this.show({
15654                 title : title,
15655                 msg : msg,
15656                 buttons: false,
15657                 closable:false,
15658                 progress:true,
15659                 modal:true,
15660                 width:300,
15661                 wait:true
15662             });
15663             waitTimer = Roo.TaskMgr.start({
15664                 run: function(i){
15665                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
15666                 },
15667                 interval: 1000
15668             });
15669             return this;
15670         },
15671
15672         /**
15673          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
15674          * If a callback function is passed it will be called after the user clicks either button, and the id of the
15675          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
15676          * @param {String} title The title bar text
15677          * @param {String} msg The message box body text
15678          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15679          * @param {Object} scope (optional) The scope of the callback function
15680          * @return {Roo.MessageBox} This message box
15681          */
15682         confirm : function(title, msg, fn, scope){
15683             this.show({
15684                 title : title,
15685                 msg : msg,
15686                 buttons: this.YESNO,
15687                 fn: fn,
15688                 scope : scope,
15689                 modal : true
15690             });
15691             return this;
15692         },
15693
15694         /**
15695          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
15696          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
15697          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
15698          * (could also be the top-right close button) and the text that was entered will be passed as the two
15699          * parameters to the callback.
15700          * @param {String} title The title bar text
15701          * @param {String} msg The message box body text
15702          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15703          * @param {Object} scope (optional) The scope of the callback function
15704          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
15705          * property, or the height in pixels to create the textbox (defaults to false / single-line)
15706          * @return {Roo.MessageBox} This message box
15707          */
15708         prompt : function(title, msg, fn, scope, multiline){
15709             this.show({
15710                 title : title,
15711                 msg : msg,
15712                 buttons: this.OKCANCEL,
15713                 fn: fn,
15714                 minWidth:250,
15715                 scope : scope,
15716                 prompt:true,
15717                 multiline: multiline,
15718                 modal : true
15719             });
15720             return this;
15721         },
15722
15723         /**
15724          * Button config that displays a single OK button
15725          * @type Object
15726          */
15727         OK : {ok:true},
15728         /**
15729          * Button config that displays Yes and No buttons
15730          * @type Object
15731          */
15732         YESNO : {yes:true, no:true},
15733         /**
15734          * Button config that displays OK and Cancel buttons
15735          * @type Object
15736          */
15737         OKCANCEL : {ok:true, cancel:true},
15738         /**
15739          * Button config that displays Yes, No and Cancel buttons
15740          * @type Object
15741          */
15742         YESNOCANCEL : {yes:true, no:true, cancel:true},
15743
15744         /**
15745          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
15746          * @type Number
15747          */
15748         defaultTextHeight : 75,
15749         /**
15750          * The maximum width in pixels of the message box (defaults to 600)
15751          * @type Number
15752          */
15753         maxWidth : 600,
15754         /**
15755          * The minimum width in pixels of the message box (defaults to 100)
15756          * @type Number
15757          */
15758         minWidth : 100,
15759         /**
15760          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
15761          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
15762          * @type Number
15763          */
15764         minProgressWidth : 250,
15765         /**
15766          * An object containing the default button text strings that can be overriden for localized language support.
15767          * Supported properties are: ok, cancel, yes and no.
15768          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
15769          * @type Object
15770          */
15771         buttonText : {
15772             ok : "OK",
15773             cancel : "Cancel",
15774             yes : "Yes",
15775             no : "No"
15776         }
15777     };
15778 }();
15779
15780 /**
15781  * Shorthand for {@link Roo.MessageBox}
15782  */
15783 Roo.Msg = Roo.MessageBox;/*
15784  * Based on:
15785  * Ext JS Library 1.1.1
15786  * Copyright(c) 2006-2007, Ext JS, LLC.
15787  *
15788  * Originally Released Under LGPL - original licence link has changed is not relivant.
15789  *
15790  * Fork - LGPL
15791  * <script type="text/javascript">
15792  */
15793 /**
15794  * @class Roo.QuickTips
15795  * Provides attractive and customizable tooltips for any element.
15796  * @singleton
15797  */
15798 Roo.QuickTips = function(){
15799     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
15800     var ce, bd, xy, dd;
15801     var visible = false, disabled = true, inited = false;
15802     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
15803     
15804     var onOver = function(e){
15805         if(disabled){
15806             return;
15807         }
15808         var t = e.getTarget();
15809         if(!t || t.nodeType !== 1 || t == document || t == document.body){
15810             return;
15811         }
15812         if(ce && t == ce.el){
15813             clearTimeout(hideProc);
15814             return;
15815         }
15816         if(t && tagEls[t.id]){
15817             tagEls[t.id].el = t;
15818             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
15819             return;
15820         }
15821         var ttp, et = Roo.fly(t);
15822         var ns = cfg.namespace;
15823         if(tm.interceptTitles && t.title){
15824             ttp = t.title;
15825             t.qtip = ttp;
15826             t.removeAttribute("title");
15827             e.preventDefault();
15828         }else{
15829             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
15830         }
15831         if(ttp){
15832             showProc = show.defer(tm.showDelay, tm, [{
15833                 el: t, 
15834                 text: ttp, 
15835                 width: et.getAttributeNS(ns, cfg.width),
15836                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
15837                 title: et.getAttributeNS(ns, cfg.title),
15838                     cls: et.getAttributeNS(ns, cfg.cls)
15839             }]);
15840         }
15841     };
15842     
15843     var onOut = function(e){
15844         clearTimeout(showProc);
15845         var t = e.getTarget();
15846         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
15847             hideProc = setTimeout(hide, tm.hideDelay);
15848         }
15849     };
15850     
15851     var onMove = function(e){
15852         if(disabled){
15853             return;
15854         }
15855         xy = e.getXY();
15856         xy[1] += 18;
15857         if(tm.trackMouse && ce){
15858             el.setXY(xy);
15859         }
15860     };
15861     
15862     var onDown = function(e){
15863         clearTimeout(showProc);
15864         clearTimeout(hideProc);
15865         if(!e.within(el)){
15866             if(tm.hideOnClick){
15867                 hide();
15868                 tm.disable();
15869                 tm.enable.defer(100, tm);
15870             }
15871         }
15872     };
15873     
15874     var getPad = function(){
15875         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
15876     };
15877
15878     var show = function(o){
15879         if(disabled){
15880             return;
15881         }
15882         clearTimeout(dismissProc);
15883         ce = o;
15884         if(removeCls){ // in case manually hidden
15885             el.removeClass(removeCls);
15886             removeCls = null;
15887         }
15888         if(ce.cls){
15889             el.addClass(ce.cls);
15890             removeCls = ce.cls;
15891         }
15892         if(ce.title){
15893             tipTitle.update(ce.title);
15894             tipTitle.show();
15895         }else{
15896             tipTitle.update('');
15897             tipTitle.hide();
15898         }
15899         el.dom.style.width  = tm.maxWidth+'px';
15900         //tipBody.dom.style.width = '';
15901         tipBodyText.update(o.text);
15902         var p = getPad(), w = ce.width;
15903         if(!w){
15904             var td = tipBodyText.dom;
15905             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
15906             if(aw > tm.maxWidth){
15907                 w = tm.maxWidth;
15908             }else if(aw < tm.minWidth){
15909                 w = tm.minWidth;
15910             }else{
15911                 w = aw;
15912             }
15913         }
15914         //tipBody.setWidth(w);
15915         el.setWidth(parseInt(w, 10) + p);
15916         if(ce.autoHide === false){
15917             close.setDisplayed(true);
15918             if(dd){
15919                 dd.unlock();
15920             }
15921         }else{
15922             close.setDisplayed(false);
15923             if(dd){
15924                 dd.lock();
15925             }
15926         }
15927         if(xy){
15928             el.avoidY = xy[1]-18;
15929             el.setXY(xy);
15930         }
15931         if(tm.animate){
15932             el.setOpacity(.1);
15933             el.setStyle("visibility", "visible");
15934             el.fadeIn({callback: afterShow});
15935         }else{
15936             afterShow();
15937         }
15938     };
15939     
15940     var afterShow = function(){
15941         if(ce){
15942             el.show();
15943             esc.enable();
15944             if(tm.autoDismiss && ce.autoHide !== false){
15945                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
15946             }
15947         }
15948     };
15949     
15950     var hide = function(noanim){
15951         clearTimeout(dismissProc);
15952         clearTimeout(hideProc);
15953         ce = null;
15954         if(el.isVisible()){
15955             esc.disable();
15956             if(noanim !== true && tm.animate){
15957                 el.fadeOut({callback: afterHide});
15958             }else{
15959                 afterHide();
15960             } 
15961         }
15962     };
15963     
15964     var afterHide = function(){
15965         el.hide();
15966         if(removeCls){
15967             el.removeClass(removeCls);
15968             removeCls = null;
15969         }
15970     };
15971     
15972     return {
15973         /**
15974         * @cfg {Number} minWidth
15975         * The minimum width of the quick tip (defaults to 40)
15976         */
15977        minWidth : 40,
15978         /**
15979         * @cfg {Number} maxWidth
15980         * The maximum width of the quick tip (defaults to 300)
15981         */
15982        maxWidth : 300,
15983         /**
15984         * @cfg {Boolean} interceptTitles
15985         * True to automatically use the element's DOM title value if available (defaults to false)
15986         */
15987        interceptTitles : false,
15988         /**
15989         * @cfg {Boolean} trackMouse
15990         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
15991         */
15992        trackMouse : false,
15993         /**
15994         * @cfg {Boolean} hideOnClick
15995         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
15996         */
15997        hideOnClick : true,
15998         /**
15999         * @cfg {Number} showDelay
16000         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
16001         */
16002        showDelay : 500,
16003         /**
16004         * @cfg {Number} hideDelay
16005         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
16006         */
16007        hideDelay : 200,
16008         /**
16009         * @cfg {Boolean} autoHide
16010         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
16011         * Used in conjunction with hideDelay.
16012         */
16013        autoHide : true,
16014         /**
16015         * @cfg {Boolean}
16016         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
16017         * (defaults to true).  Used in conjunction with autoDismissDelay.
16018         */
16019        autoDismiss : true,
16020         /**
16021         * @cfg {Number}
16022         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
16023         */
16024        autoDismissDelay : 5000,
16025        /**
16026         * @cfg {Boolean} animate
16027         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
16028         */
16029        animate : false,
16030
16031        /**
16032         * @cfg {String} title
16033         * Title text to display (defaults to '').  This can be any valid HTML markup.
16034         */
16035         title: '',
16036        /**
16037         * @cfg {String} text
16038         * Body text to display (defaults to '').  This can be any valid HTML markup.
16039         */
16040         text : '',
16041        /**
16042         * @cfg {String} cls
16043         * A CSS class to apply to the base quick tip element (defaults to '').
16044         */
16045         cls : '',
16046        /**
16047         * @cfg {Number} width
16048         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
16049         * minWidth or maxWidth.
16050         */
16051         width : null,
16052
16053     /**
16054      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
16055      * or display QuickTips in a page.
16056      */
16057        init : function(){
16058           tm = Roo.QuickTips;
16059           cfg = tm.tagConfig;
16060           if(!inited){
16061               if(!Roo.isReady){ // allow calling of init() before onReady
16062                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
16063                   return;
16064               }
16065               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
16066               el.fxDefaults = {stopFx: true};
16067               // maximum custom styling
16068               //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>');
16069               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>');              
16070               tipTitle = el.child('h3');
16071               tipTitle.enableDisplayMode("block");
16072               tipBody = el.child('div.x-tip-bd');
16073               tipBodyText = el.child('div.x-tip-bd-inner');
16074               //bdLeft = el.child('div.x-tip-bd-left');
16075               //bdRight = el.child('div.x-tip-bd-right');
16076               close = el.child('div.x-tip-close');
16077               close.enableDisplayMode("block");
16078               close.on("click", hide);
16079               var d = Roo.get(document);
16080               d.on("mousedown", onDown);
16081               d.on("mouseover", onOver);
16082               d.on("mouseout", onOut);
16083               d.on("mousemove", onMove);
16084               esc = d.addKeyListener(27, hide);
16085               esc.disable();
16086               if(Roo.dd.DD){
16087                   dd = el.initDD("default", null, {
16088                       onDrag : function(){
16089                           el.sync();  
16090                       }
16091                   });
16092                   dd.setHandleElId(tipTitle.id);
16093                   dd.lock();
16094               }
16095               inited = true;
16096           }
16097           this.enable(); 
16098        },
16099
16100     /**
16101      * Configures a new quick tip instance and assigns it to a target element.  The following config options
16102      * are supported:
16103      * <pre>
16104 Property    Type                   Description
16105 ----------  ---------------------  ------------------------------------------------------------------------
16106 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
16107      * </ul>
16108      * @param {Object} config The config object
16109      */
16110        register : function(config){
16111            var cs = config instanceof Array ? config : arguments;
16112            for(var i = 0, len = cs.length; i < len; i++) {
16113                var c = cs[i];
16114                var target = c.target;
16115                if(target){
16116                    if(target instanceof Array){
16117                        for(var j = 0, jlen = target.length; j < jlen; j++){
16118                            tagEls[target[j]] = c;
16119                        }
16120                    }else{
16121                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
16122                    }
16123                }
16124            }
16125        },
16126
16127     /**
16128      * Removes this quick tip from its element and destroys it.
16129      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
16130      */
16131        unregister : function(el){
16132            delete tagEls[Roo.id(el)];
16133        },
16134
16135     /**
16136      * Enable this quick tip.
16137      */
16138        enable : function(){
16139            if(inited && disabled){
16140                locks.pop();
16141                if(locks.length < 1){
16142                    disabled = false;
16143                }
16144            }
16145        },
16146
16147     /**
16148      * Disable this quick tip.
16149      */
16150        disable : function(){
16151           disabled = true;
16152           clearTimeout(showProc);
16153           clearTimeout(hideProc);
16154           clearTimeout(dismissProc);
16155           if(ce){
16156               hide(true);
16157           }
16158           locks.push(1);
16159        },
16160
16161     /**
16162      * Returns true if the quick tip is enabled, else false.
16163      */
16164        isEnabled : function(){
16165             return !disabled;
16166        },
16167
16168         // private
16169        tagConfig : {
16170            namespace : "ext",
16171            attribute : "qtip",
16172            width : "width",
16173            target : "target",
16174            title : "qtitle",
16175            hide : "hide",
16176            cls : "qclass"
16177        }
16178    };
16179 }();
16180
16181 // backwards compat
16182 Roo.QuickTips.tips = Roo.QuickTips.register;/*
16183  * Based on:
16184  * Ext JS Library 1.1.1
16185  * Copyright(c) 2006-2007, Ext JS, LLC.
16186  *
16187  * Originally Released Under LGPL - original licence link has changed is not relivant.
16188  *
16189  * Fork - LGPL
16190  * <script type="text/javascript">
16191  */
16192  
16193
16194 /**
16195  * @class Roo.tree.TreePanel
16196  * @extends Roo.data.Tree
16197
16198  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
16199  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
16200  * @cfg {Boolean} enableDD true to enable drag and drop
16201  * @cfg {Boolean} enableDrag true to enable just drag
16202  * @cfg {Boolean} enableDrop true to enable just drop
16203  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
16204  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
16205  * @cfg {String} ddGroup The DD group this TreePanel belongs to
16206  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
16207  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
16208  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
16209  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
16210  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
16211  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
16212  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
16213  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
16214  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
16215  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
16216  * @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>
16217  * @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>
16218  * 
16219  * @constructor
16220  * @param {String/HTMLElement/Element} el The container element
16221  * @param {Object} config
16222  */
16223 Roo.tree.TreePanel = function(el, config){
16224     var root = false;
16225     var loader = false;
16226     if (config.root) {
16227         root = config.root;
16228         delete config.root;
16229     }
16230     if (config.loader) {
16231         loader = config.loader;
16232         delete config.loader;
16233     }
16234     
16235     Roo.apply(this, config);
16236     Roo.tree.TreePanel.superclass.constructor.call(this);
16237     this.el = Roo.get(el);
16238     this.el.addClass('x-tree');
16239     //console.log(root);
16240     if (root) {
16241         this.setRootNode( Roo.factory(root, Roo.tree));
16242     }
16243     if (loader) {
16244         this.loader = Roo.factory(loader, Roo.tree);
16245     }
16246    /**
16247     * Read-only. The id of the container element becomes this TreePanel's id.
16248     */
16249    this.id = this.el.id;
16250    this.addEvents({
16251         /**
16252         * @event beforeload
16253         * Fires before a node is loaded, return false to cancel
16254         * @param {Node} node The node being loaded
16255         */
16256         "beforeload" : true,
16257         /**
16258         * @event load
16259         * Fires when a node is loaded
16260         * @param {Node} node The node that was loaded
16261         */
16262         "load" : true,
16263         /**
16264         * @event textchange
16265         * Fires when the text for a node is changed
16266         * @param {Node} node The node
16267         * @param {String} text The new text
16268         * @param {String} oldText The old text
16269         */
16270         "textchange" : true,
16271         /**
16272         * @event beforeexpand
16273         * Fires before a node is expanded, return false to cancel.
16274         * @param {Node} node The node
16275         * @param {Boolean} deep
16276         * @param {Boolean} anim
16277         */
16278         "beforeexpand" : true,
16279         /**
16280         * @event beforecollapse
16281         * Fires before a node is collapsed, return false to cancel.
16282         * @param {Node} node The node
16283         * @param {Boolean} deep
16284         * @param {Boolean} anim
16285         */
16286         "beforecollapse" : true,
16287         /**
16288         * @event expand
16289         * Fires when a node is expanded
16290         * @param {Node} node The node
16291         */
16292         "expand" : true,
16293         /**
16294         * @event disabledchange
16295         * Fires when the disabled status of a node changes
16296         * @param {Node} node The node
16297         * @param {Boolean} disabled
16298         */
16299         "disabledchange" : true,
16300         /**
16301         * @event collapse
16302         * Fires when a node is collapsed
16303         * @param {Node} node The node
16304         */
16305         "collapse" : true,
16306         /**
16307         * @event beforeclick
16308         * Fires before click processing on a node. Return false to cancel the default action.
16309         * @param {Node} node The node
16310         * @param {Roo.EventObject} e The event object
16311         */
16312         "beforeclick":true,
16313         /**
16314         * @event checkchange
16315         * Fires when a node with a checkbox's checked property changes
16316         * @param {Node} this This node
16317         * @param {Boolean} checked
16318         */
16319         "checkchange":true,
16320         /**
16321         * @event click
16322         * Fires when a node is clicked
16323         * @param {Node} node The node
16324         * @param {Roo.EventObject} e The event object
16325         */
16326         "click":true,
16327         /**
16328         * @event dblclick
16329         * Fires when a node is double clicked
16330         * @param {Node} node The node
16331         * @param {Roo.EventObject} e The event object
16332         */
16333         "dblclick":true,
16334         /**
16335         * @event contextmenu
16336         * Fires when a node is right clicked
16337         * @param {Node} node The node
16338         * @param {Roo.EventObject} e The event object
16339         */
16340         "contextmenu":true,
16341         /**
16342         * @event beforechildrenrendered
16343         * Fires right before the child nodes for a node are rendered
16344         * @param {Node} node The node
16345         */
16346         "beforechildrenrendered":true,
16347        /**
16348              * @event startdrag
16349              * Fires when a node starts being dragged
16350              * @param {Roo.tree.TreePanel} this
16351              * @param {Roo.tree.TreeNode} node
16352              * @param {event} e The raw browser event
16353              */ 
16354             "startdrag" : true,
16355             /**
16356              * @event enddrag
16357              * Fires when a drag operation is complete
16358              * @param {Roo.tree.TreePanel} this
16359              * @param {Roo.tree.TreeNode} node
16360              * @param {event} e The raw browser event
16361              */
16362             "enddrag" : true,
16363             /**
16364              * @event dragdrop
16365              * Fires when a dragged node is dropped on a valid DD target
16366              * @param {Roo.tree.TreePanel} this
16367              * @param {Roo.tree.TreeNode} node
16368              * @param {DD} dd The dd it was dropped on
16369              * @param {event} e The raw browser event
16370              */
16371             "dragdrop" : true,
16372             /**
16373              * @event beforenodedrop
16374              * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
16375              * passed to handlers has the following properties:<br />
16376              * <ul style="padding:5px;padding-left:16px;">
16377              * <li>tree - The TreePanel</li>
16378              * <li>target - The node being targeted for the drop</li>
16379              * <li>data - The drag data from the drag source</li>
16380              * <li>point - The point of the drop - append, above or below</li>
16381              * <li>source - The drag source</li>
16382              * <li>rawEvent - Raw mouse event</li>
16383              * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
16384              * to be inserted by setting them on this object.</li>
16385              * <li>cancel - Set this to true to cancel the drop.</li>
16386              * </ul>
16387              * @param {Object} dropEvent
16388              */
16389             "beforenodedrop" : true,
16390             /**
16391              * @event nodedrop
16392              * Fires after a DD object is dropped on a node in this tree. The dropEvent
16393              * passed to handlers has the following properties:<br />
16394              * <ul style="padding:5px;padding-left:16px;">
16395              * <li>tree - The TreePanel</li>
16396              * <li>target - The node being targeted for the drop</li>
16397              * <li>data - The drag data from the drag source</li>
16398              * <li>point - The point of the drop - append, above or below</li>
16399              * <li>source - The drag source</li>
16400              * <li>rawEvent - Raw mouse event</li>
16401              * <li>dropNode - Dropped node(s).</li>
16402              * </ul>
16403              * @param {Object} dropEvent
16404              */
16405             "nodedrop" : true,
16406              /**
16407              * @event nodedragover
16408              * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
16409              * passed to handlers has the following properties:<br />
16410              * <ul style="padding:5px;padding-left:16px;">
16411              * <li>tree - The TreePanel</li>
16412              * <li>target - The node being targeted for the drop</li>
16413              * <li>data - The drag data from the drag source</li>
16414              * <li>point - The point of the drop - append, above or below</li>
16415              * <li>source - The drag source</li>
16416              * <li>rawEvent - Raw mouse event</li>
16417              * <li>dropNode - Drop node(s) provided by the source.</li>
16418              * <li>cancel - Set this to true to signal drop not allowed.</li>
16419              * </ul>
16420              * @param {Object} dragOverEvent
16421              */
16422             "nodedragover" : true
16423         
16424    });
16425    if(this.singleExpand){
16426        this.on("beforeexpand", this.restrictExpand, this);
16427    }
16428 };
16429 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
16430     rootVisible : true,
16431     animate: Roo.enableFx,
16432     lines : true,
16433     enableDD : false,
16434     hlDrop : Roo.enableFx,
16435   
16436     renderer: false,
16437     
16438     rendererTip: false,
16439     // private
16440     restrictExpand : function(node){
16441         var p = node.parentNode;
16442         if(p){
16443             if(p.expandedChild && p.expandedChild.parentNode == p){
16444                 p.expandedChild.collapse();
16445             }
16446             p.expandedChild = node;
16447         }
16448     },
16449
16450     // private override
16451     setRootNode : function(node){
16452         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
16453         if(!this.rootVisible){
16454             node.ui = new Roo.tree.RootTreeNodeUI(node);
16455         }
16456         return node;
16457     },
16458
16459     /**
16460      * Returns the container element for this TreePanel
16461      */
16462     getEl : function(){
16463         return this.el;
16464     },
16465
16466     /**
16467      * Returns the default TreeLoader for this TreePanel
16468      */
16469     getLoader : function(){
16470         return this.loader;
16471     },
16472
16473     /**
16474      * Expand all nodes
16475      */
16476     expandAll : function(){
16477         this.root.expand(true);
16478     },
16479
16480     /**
16481      * Collapse all nodes
16482      */
16483     collapseAll : function(){
16484         this.root.collapse(true);
16485     },
16486
16487     /**
16488      * Returns the selection model used by this TreePanel
16489      */
16490     getSelectionModel : function(){
16491         if(!this.selModel){
16492             this.selModel = new Roo.tree.DefaultSelectionModel();
16493         }
16494         return this.selModel;
16495     },
16496
16497     /**
16498      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
16499      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
16500      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
16501      * @return {Array}
16502      */
16503     getChecked : function(a, startNode){
16504         startNode = startNode || this.root;
16505         var r = [];
16506         var f = function(){
16507             if(this.attributes.checked){
16508                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
16509             }
16510         }
16511         startNode.cascade(f);
16512         return r;
16513     },
16514
16515     /**
16516      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16517      * @param {String} path
16518      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16519      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
16520      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
16521      */
16522     expandPath : function(path, attr, callback){
16523         attr = attr || "id";
16524         var keys = path.split(this.pathSeparator);
16525         var curNode = this.root;
16526         if(curNode.attributes[attr] != keys[1]){ // invalid root
16527             if(callback){
16528                 callback(false, null);
16529             }
16530             return;
16531         }
16532         var index = 1;
16533         var f = function(){
16534             if(++index == keys.length){
16535                 if(callback){
16536                     callback(true, curNode);
16537                 }
16538                 return;
16539             }
16540             var c = curNode.findChild(attr, keys[index]);
16541             if(!c){
16542                 if(callback){
16543                     callback(false, curNode);
16544                 }
16545                 return;
16546             }
16547             curNode = c;
16548             c.expand(false, false, f);
16549         };
16550         curNode.expand(false, false, f);
16551     },
16552
16553     /**
16554      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16555      * @param {String} path
16556      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16557      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
16558      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
16559      */
16560     selectPath : function(path, attr, callback){
16561         attr = attr || "id";
16562         var keys = path.split(this.pathSeparator);
16563         var v = keys.pop();
16564         if(keys.length > 0){
16565             var f = function(success, node){
16566                 if(success && node){
16567                     var n = node.findChild(attr, v);
16568                     if(n){
16569                         n.select();
16570                         if(callback){
16571                             callback(true, n);
16572                         }
16573                     }else if(callback){
16574                         callback(false, n);
16575                     }
16576                 }else{
16577                     if(callback){
16578                         callback(false, n);
16579                     }
16580                 }
16581             };
16582             this.expandPath(keys.join(this.pathSeparator), attr, f);
16583         }else{
16584             this.root.select();
16585             if(callback){
16586                 callback(true, this.root);
16587             }
16588         }
16589     },
16590
16591     getTreeEl : function(){
16592         return this.el;
16593     },
16594
16595     /**
16596      * Trigger rendering of this TreePanel
16597      */
16598     render : function(){
16599         if (this.innerCt) {
16600             return this; // stop it rendering more than once!!
16601         }
16602         
16603         this.innerCt = this.el.createChild({tag:"ul",
16604                cls:"x-tree-root-ct " +
16605                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
16606
16607         if(this.containerScroll){
16608             Roo.dd.ScrollManager.register(this.el);
16609         }
16610         if((this.enableDD || this.enableDrop) && !this.dropZone){
16611            /**
16612             * The dropZone used by this tree if drop is enabled
16613             * @type Roo.tree.TreeDropZone
16614             */
16615              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
16616                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
16617            });
16618         }
16619         if((this.enableDD || this.enableDrag) && !this.dragZone){
16620            /**
16621             * The dragZone used by this tree if drag is enabled
16622             * @type Roo.tree.TreeDragZone
16623             */
16624             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
16625                ddGroup: this.ddGroup || "TreeDD",
16626                scroll: this.ddScroll
16627            });
16628         }
16629         this.getSelectionModel().init(this);
16630         if (!this.root) {
16631             console.log("ROOT not set in tree");
16632             return;
16633         }
16634         this.root.render();
16635         if(!this.rootVisible){
16636             this.root.renderChildren();
16637         }
16638         return this;
16639     }
16640 });/*
16641  * Based on:
16642  * Ext JS Library 1.1.1
16643  * Copyright(c) 2006-2007, Ext JS, LLC.
16644  *
16645  * Originally Released Under LGPL - original licence link has changed is not relivant.
16646  *
16647  * Fork - LGPL
16648  * <script type="text/javascript">
16649  */
16650  
16651
16652 /**
16653  * @class Roo.tree.DefaultSelectionModel
16654  * @extends Roo.util.Observable
16655  * The default single selection for a TreePanel.
16656  */
16657 Roo.tree.DefaultSelectionModel = function(){
16658    this.selNode = null;
16659    
16660    this.addEvents({
16661        /**
16662         * @event selectionchange
16663         * Fires when the selected node changes
16664         * @param {DefaultSelectionModel} this
16665         * @param {TreeNode} node the new selection
16666         */
16667        "selectionchange" : true,
16668
16669        /**
16670         * @event beforeselect
16671         * Fires before the selected node changes, return false to cancel the change
16672         * @param {DefaultSelectionModel} this
16673         * @param {TreeNode} node the new selection
16674         * @param {TreeNode} node the old selection
16675         */
16676        "beforeselect" : true
16677    });
16678 };
16679
16680 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
16681     init : function(tree){
16682         this.tree = tree;
16683         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16684         tree.on("click", this.onNodeClick, this);
16685     },
16686     
16687     onNodeClick : function(node, e){
16688         if (e.ctrlKey && this.selNode == node)  {
16689             this.unselect(node);
16690             return;
16691         }
16692         this.select(node);
16693     },
16694     
16695     /**
16696      * Select a node.
16697      * @param {TreeNode} node The node to select
16698      * @return {TreeNode} The selected node
16699      */
16700     select : function(node){
16701         var last = this.selNode;
16702         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
16703             if(last){
16704                 last.ui.onSelectedChange(false);
16705             }
16706             this.selNode = node;
16707             node.ui.onSelectedChange(true);
16708             this.fireEvent("selectionchange", this, node, last);
16709         }
16710         return node;
16711     },
16712     
16713     /**
16714      * Deselect a node.
16715      * @param {TreeNode} node The node to unselect
16716      */
16717     unselect : function(node){
16718         if(this.selNode == node){
16719             this.clearSelections();
16720         }    
16721     },
16722     
16723     /**
16724      * Clear all selections
16725      */
16726     clearSelections : function(){
16727         var n = this.selNode;
16728         if(n){
16729             n.ui.onSelectedChange(false);
16730             this.selNode = null;
16731             this.fireEvent("selectionchange", this, null);
16732         }
16733         return n;
16734     },
16735     
16736     /**
16737      * Get the selected node
16738      * @return {TreeNode} The selected node
16739      */
16740     getSelectedNode : function(){
16741         return this.selNode;    
16742     },
16743     
16744     /**
16745      * Returns true if the node is selected
16746      * @param {TreeNode} node The node to check
16747      * @return {Boolean}
16748      */
16749     isSelected : function(node){
16750         return this.selNode == node;  
16751     },
16752
16753     /**
16754      * Selects the node above the selected node in the tree, intelligently walking the nodes
16755      * @return TreeNode The new selection
16756      */
16757     selectPrevious : function(){
16758         var s = this.selNode || this.lastSelNode;
16759         if(!s){
16760             return null;
16761         }
16762         var ps = s.previousSibling;
16763         if(ps){
16764             if(!ps.isExpanded() || ps.childNodes.length < 1){
16765                 return this.select(ps);
16766             } else{
16767                 var lc = ps.lastChild;
16768                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
16769                     lc = lc.lastChild;
16770                 }
16771                 return this.select(lc);
16772             }
16773         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
16774             return this.select(s.parentNode);
16775         }
16776         return null;
16777     },
16778
16779     /**
16780      * Selects the node above the selected node in the tree, intelligently walking the nodes
16781      * @return TreeNode The new selection
16782      */
16783     selectNext : function(){
16784         var s = this.selNode || this.lastSelNode;
16785         if(!s){
16786             return null;
16787         }
16788         if(s.firstChild && s.isExpanded()){
16789              return this.select(s.firstChild);
16790          }else if(s.nextSibling){
16791              return this.select(s.nextSibling);
16792          }else if(s.parentNode){
16793             var newS = null;
16794             s.parentNode.bubble(function(){
16795                 if(this.nextSibling){
16796                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
16797                     return false;
16798                 }
16799             });
16800             return newS;
16801          }
16802         return null;
16803     },
16804
16805     onKeyDown : function(e){
16806         var s = this.selNode || this.lastSelNode;
16807         // undesirable, but required
16808         var sm = this;
16809         if(!s){
16810             return;
16811         }
16812         var k = e.getKey();
16813         switch(k){
16814              case e.DOWN:
16815                  e.stopEvent();
16816                  this.selectNext();
16817              break;
16818              case e.UP:
16819                  e.stopEvent();
16820                  this.selectPrevious();
16821              break;
16822              case e.RIGHT:
16823                  e.preventDefault();
16824                  if(s.hasChildNodes()){
16825                      if(!s.isExpanded()){
16826                          s.expand();
16827                      }else if(s.firstChild){
16828                          this.select(s.firstChild, e);
16829                      }
16830                  }
16831              break;
16832              case e.LEFT:
16833                  e.preventDefault();
16834                  if(s.hasChildNodes() && s.isExpanded()){
16835                      s.collapse();
16836                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
16837                      this.select(s.parentNode, e);
16838                  }
16839              break;
16840         };
16841     }
16842 });
16843
16844 /**
16845  * @class Roo.tree.MultiSelectionModel
16846  * @extends Roo.util.Observable
16847  * Multi selection for a TreePanel.
16848  */
16849 Roo.tree.MultiSelectionModel = function(){
16850    this.selNodes = [];
16851    this.selMap = {};
16852    this.addEvents({
16853        /**
16854         * @event selectionchange
16855         * Fires when the selected nodes change
16856         * @param {MultiSelectionModel} this
16857         * @param {Array} nodes Array of the selected nodes
16858         */
16859        "selectionchange" : true
16860    });
16861 };
16862
16863 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
16864     init : function(tree){
16865         this.tree = tree;
16866         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16867         tree.on("click", this.onNodeClick, this);
16868     },
16869     
16870     onNodeClick : function(node, e){
16871         this.select(node, e, e.ctrlKey);
16872     },
16873     
16874     /**
16875      * Select a node.
16876      * @param {TreeNode} node The node to select
16877      * @param {EventObject} e (optional) An event associated with the selection
16878      * @param {Boolean} keepExisting True to retain existing selections
16879      * @return {TreeNode} The selected node
16880      */
16881     select : function(node, e, keepExisting){
16882         if(keepExisting !== true){
16883             this.clearSelections(true);
16884         }
16885         if(this.isSelected(node)){
16886             this.lastSelNode = node;
16887             return node;
16888         }
16889         this.selNodes.push(node);
16890         this.selMap[node.id] = node;
16891         this.lastSelNode = node;
16892         node.ui.onSelectedChange(true);
16893         this.fireEvent("selectionchange", this, this.selNodes);
16894         return node;
16895     },
16896     
16897     /**
16898      * Deselect a node.
16899      * @param {TreeNode} node The node to unselect
16900      */
16901     unselect : function(node){
16902         if(this.selMap[node.id]){
16903             node.ui.onSelectedChange(false);
16904             var sn = this.selNodes;
16905             var index = -1;
16906             if(sn.indexOf){
16907                 index = sn.indexOf(node);
16908             }else{
16909                 for(var i = 0, len = sn.length; i < len; i++){
16910                     if(sn[i] == node){
16911                         index = i;
16912                         break;
16913                     }
16914                 }
16915             }
16916             if(index != -1){
16917                 this.selNodes.splice(index, 1);
16918             }
16919             delete this.selMap[node.id];
16920             this.fireEvent("selectionchange", this, this.selNodes);
16921         }
16922     },
16923     
16924     /**
16925      * Clear all selections
16926      */
16927     clearSelections : function(suppressEvent){
16928         var sn = this.selNodes;
16929         if(sn.length > 0){
16930             for(var i = 0, len = sn.length; i < len; i++){
16931                 sn[i].ui.onSelectedChange(false);
16932             }
16933             this.selNodes = [];
16934             this.selMap = {};
16935             if(suppressEvent !== true){
16936                 this.fireEvent("selectionchange", this, this.selNodes);
16937             }
16938         }
16939     },
16940     
16941     /**
16942      * Returns true if the node is selected
16943      * @param {TreeNode} node The node to check
16944      * @return {Boolean}
16945      */
16946     isSelected : function(node){
16947         return this.selMap[node.id] ? true : false;  
16948     },
16949     
16950     /**
16951      * Returns an array of the selected nodes
16952      * @return {Array}
16953      */
16954     getSelectedNodes : function(){
16955         return this.selNodes;    
16956     },
16957
16958     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
16959
16960     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
16961
16962     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
16963 });/*
16964  * Based on:
16965  * Ext JS Library 1.1.1
16966  * Copyright(c) 2006-2007, Ext JS, LLC.
16967  *
16968  * Originally Released Under LGPL - original licence link has changed is not relivant.
16969  *
16970  * Fork - LGPL
16971  * <script type="text/javascript">
16972  */
16973  
16974 /**
16975  * @class Roo.tree.TreeNode
16976  * @extends Roo.data.Node
16977  * @cfg {String} text The text for this node
16978  * @cfg {Boolean} expanded true to start the node expanded
16979  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
16980  * @cfg {Boolean} allowDrop false if this node cannot be drop on
16981  * @cfg {Boolean} disabled true to start the node disabled
16982  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
16983  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
16984  * @cfg {String} cls A css class to be added to the node
16985  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
16986  * @cfg {String} href URL of the link used for the node (defaults to #)
16987  * @cfg {String} hrefTarget target frame for the link
16988  * @cfg {String} qtip An Ext QuickTip for the node
16989  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
16990  * @cfg {Boolean} singleClickExpand True for single click expand on this node
16991  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
16992  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
16993  * (defaults to undefined with no checkbox rendered)
16994  * @constructor
16995  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
16996  */
16997 Roo.tree.TreeNode = function(attributes){
16998     attributes = attributes || {};
16999     if(typeof attributes == "string"){
17000         attributes = {text: attributes};
17001     }
17002     this.childrenRendered = false;
17003     this.rendered = false;
17004     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
17005     this.expanded = attributes.expanded === true;
17006     this.isTarget = attributes.isTarget !== false;
17007     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
17008     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
17009
17010     /**
17011      * Read-only. The text for this node. To change it use setText().
17012      * @type String
17013      */
17014     this.text = attributes.text;
17015     /**
17016      * True if this node is disabled.
17017      * @type Boolean
17018      */
17019     this.disabled = attributes.disabled === true;
17020
17021     this.addEvents({
17022         /**
17023         * @event textchange
17024         * Fires when the text for this node is changed
17025         * @param {Node} this This node
17026         * @param {String} text The new text
17027         * @param {String} oldText The old text
17028         */
17029         "textchange" : true,
17030         /**
17031         * @event beforeexpand
17032         * Fires before this node is expanded, return false to cancel.
17033         * @param {Node} this This node
17034         * @param {Boolean} deep
17035         * @param {Boolean} anim
17036         */
17037         "beforeexpand" : true,
17038         /**
17039         * @event beforecollapse
17040         * Fires before this node is collapsed, return false to cancel.
17041         * @param {Node} this This node
17042         * @param {Boolean} deep
17043         * @param {Boolean} anim
17044         */
17045         "beforecollapse" : true,
17046         /**
17047         * @event expand
17048         * Fires when this node is expanded
17049         * @param {Node} this This node
17050         */
17051         "expand" : true,
17052         /**
17053         * @event disabledchange
17054         * Fires when the disabled status of this node changes
17055         * @param {Node} this This node
17056         * @param {Boolean} disabled
17057         */
17058         "disabledchange" : true,
17059         /**
17060         * @event collapse
17061         * Fires when this node is collapsed
17062         * @param {Node} this This node
17063         */
17064         "collapse" : true,
17065         /**
17066         * @event beforeclick
17067         * Fires before click processing. Return false to cancel the default action.
17068         * @param {Node} this This node
17069         * @param {Roo.EventObject} e The event object
17070         */
17071         "beforeclick":true,
17072         /**
17073         * @event checkchange
17074         * Fires when a node with a checkbox's checked property changes
17075         * @param {Node} this This node
17076         * @param {Boolean} checked
17077         */
17078         "checkchange":true,
17079         /**
17080         * @event click
17081         * Fires when this node is clicked
17082         * @param {Node} this This node
17083         * @param {Roo.EventObject} e The event object
17084         */
17085         "click":true,
17086         /**
17087         * @event dblclick
17088         * Fires when this node is double clicked
17089         * @param {Node} this This node
17090         * @param {Roo.EventObject} e The event object
17091         */
17092         "dblclick":true,
17093         /**
17094         * @event contextmenu
17095         * Fires when this node is right clicked
17096         * @param {Node} this This node
17097         * @param {Roo.EventObject} e The event object
17098         */
17099         "contextmenu":true,
17100         /**
17101         * @event beforechildrenrendered
17102         * Fires right before the child nodes for this node are rendered
17103         * @param {Node} this This node
17104         */
17105         "beforechildrenrendered":true
17106     });
17107
17108     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
17109
17110     /**
17111      * Read-only. The UI for this node
17112      * @type TreeNodeUI
17113      */
17114     this.ui = new uiClass(this);
17115 };
17116 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
17117     preventHScroll: true,
17118     /**
17119      * Returns true if this node is expanded
17120      * @return {Boolean}
17121      */
17122     isExpanded : function(){
17123         return this.expanded;
17124     },
17125
17126     /**
17127      * Returns the UI object for this node
17128      * @return {TreeNodeUI}
17129      */
17130     getUI : function(){
17131         return this.ui;
17132     },
17133
17134     // private override
17135     setFirstChild : function(node){
17136         var of = this.firstChild;
17137         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
17138         if(this.childrenRendered && of && node != of){
17139             of.renderIndent(true, true);
17140         }
17141         if(this.rendered){
17142             this.renderIndent(true, true);
17143         }
17144     },
17145
17146     // private override
17147     setLastChild : function(node){
17148         var ol = this.lastChild;
17149         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
17150         if(this.childrenRendered && ol && node != ol){
17151             ol.renderIndent(true, true);
17152         }
17153         if(this.rendered){
17154             this.renderIndent(true, true);
17155         }
17156     },
17157
17158     // these methods are overridden to provide lazy rendering support
17159     // private override
17160     appendChild : function(){
17161         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
17162         if(node && this.childrenRendered){
17163             node.render();
17164         }
17165         this.ui.updateExpandIcon();
17166         return node;
17167     },
17168
17169     // private override
17170     removeChild : function(node){
17171         this.ownerTree.getSelectionModel().unselect(node);
17172         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
17173         // if it's been rendered remove dom node
17174         if(this.childrenRendered){
17175             node.ui.remove();
17176         }
17177         if(this.childNodes.length < 1){
17178             this.collapse(false, false);
17179         }else{
17180             this.ui.updateExpandIcon();
17181         }
17182         if(!this.firstChild) {
17183             this.childrenRendered = false;
17184         }
17185         return node;
17186     },
17187
17188     // private override
17189     insertBefore : function(node, refNode){
17190         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
17191         if(newNode && refNode && this.childrenRendered){
17192             node.render();
17193         }
17194         this.ui.updateExpandIcon();
17195         return newNode;
17196     },
17197
17198     /**
17199      * Sets the text for this node
17200      * @param {String} text
17201      */
17202     setText : function(text){
17203         var oldText = this.text;
17204         this.text = text;
17205         this.attributes.text = text;
17206         if(this.rendered){ // event without subscribing
17207             this.ui.onTextChange(this, text, oldText);
17208         }
17209         this.fireEvent("textchange", this, text, oldText);
17210     },
17211
17212     /**
17213      * Triggers selection of this node
17214      */
17215     select : function(){
17216         this.getOwnerTree().getSelectionModel().select(this);
17217     },
17218
17219     /**
17220      * Triggers deselection of this node
17221      */
17222     unselect : function(){
17223         this.getOwnerTree().getSelectionModel().unselect(this);
17224     },
17225
17226     /**
17227      * Returns true if this node is selected
17228      * @return {Boolean}
17229      */
17230     isSelected : function(){
17231         return this.getOwnerTree().getSelectionModel().isSelected(this);
17232     },
17233
17234     /**
17235      * Expand this node.
17236      * @param {Boolean} deep (optional) True to expand all children as well
17237      * @param {Boolean} anim (optional) false to cancel the default animation
17238      * @param {Function} callback (optional) A callback to be called when
17239      * expanding this node completes (does not wait for deep expand to complete).
17240      * Called with 1 parameter, this node.
17241      */
17242     expand : function(deep, anim, callback){
17243         if(!this.expanded){
17244             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
17245                 return;
17246             }
17247             if(!this.childrenRendered){
17248                 this.renderChildren();
17249             }
17250             this.expanded = true;
17251             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
17252                 this.ui.animExpand(function(){
17253                     this.fireEvent("expand", this);
17254                     if(typeof callback == "function"){
17255                         callback(this);
17256                     }
17257                     if(deep === true){
17258                         this.expandChildNodes(true);
17259                     }
17260                 }.createDelegate(this));
17261                 return;
17262             }else{
17263                 this.ui.expand();
17264                 this.fireEvent("expand", this);
17265                 if(typeof callback == "function"){
17266                     callback(this);
17267                 }
17268             }
17269         }else{
17270            if(typeof callback == "function"){
17271                callback(this);
17272            }
17273         }
17274         if(deep === true){
17275             this.expandChildNodes(true);
17276         }
17277     },
17278
17279     isHiddenRoot : function(){
17280         return this.isRoot && !this.getOwnerTree().rootVisible;
17281     },
17282
17283     /**
17284      * Collapse this node.
17285      * @param {Boolean} deep (optional) True to collapse all children as well
17286      * @param {Boolean} anim (optional) false to cancel the default animation
17287      */
17288     collapse : function(deep, anim){
17289         if(this.expanded && !this.isHiddenRoot()){
17290             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
17291                 return;
17292             }
17293             this.expanded = false;
17294             if((this.getOwnerTree().animate && anim !== false) || anim){
17295                 this.ui.animCollapse(function(){
17296                     this.fireEvent("collapse", this);
17297                     if(deep === true){
17298                         this.collapseChildNodes(true);
17299                     }
17300                 }.createDelegate(this));
17301                 return;
17302             }else{
17303                 this.ui.collapse();
17304                 this.fireEvent("collapse", this);
17305             }
17306         }
17307         if(deep === true){
17308             var cs = this.childNodes;
17309             for(var i = 0, len = cs.length; i < len; i++) {
17310                 cs[i].collapse(true, false);
17311             }
17312         }
17313     },
17314
17315     // private
17316     delayedExpand : function(delay){
17317         if(!this.expandProcId){
17318             this.expandProcId = this.expand.defer(delay, this);
17319         }
17320     },
17321
17322     // private
17323     cancelExpand : function(){
17324         if(this.expandProcId){
17325             clearTimeout(this.expandProcId);
17326         }
17327         this.expandProcId = false;
17328     },
17329
17330     /**
17331      * Toggles expanded/collapsed state of the node
17332      */
17333     toggle : function(){
17334         if(this.expanded){
17335             this.collapse();
17336         }else{
17337             this.expand();
17338         }
17339     },
17340
17341     /**
17342      * Ensures all parent nodes are expanded
17343      */
17344     ensureVisible : function(callback){
17345         var tree = this.getOwnerTree();
17346         tree.expandPath(this.parentNode.getPath(), false, function(){
17347             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
17348             Roo.callback(callback);
17349         }.createDelegate(this));
17350     },
17351
17352     /**
17353      * Expand all child nodes
17354      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
17355      */
17356     expandChildNodes : function(deep){
17357         var cs = this.childNodes;
17358         for(var i = 0, len = cs.length; i < len; i++) {
17359                 cs[i].expand(deep);
17360         }
17361     },
17362
17363     /**
17364      * Collapse all child nodes
17365      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
17366      */
17367     collapseChildNodes : function(deep){
17368         var cs = this.childNodes;
17369         for(var i = 0, len = cs.length; i < len; i++) {
17370                 cs[i].collapse(deep);
17371         }
17372     },
17373
17374     /**
17375      * Disables this node
17376      */
17377     disable : function(){
17378         this.disabled = true;
17379         this.unselect();
17380         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17381             this.ui.onDisableChange(this, true);
17382         }
17383         this.fireEvent("disabledchange", this, true);
17384     },
17385
17386     /**
17387      * Enables this node
17388      */
17389     enable : function(){
17390         this.disabled = false;
17391         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17392             this.ui.onDisableChange(this, false);
17393         }
17394         this.fireEvent("disabledchange", this, false);
17395     },
17396
17397     // private
17398     renderChildren : function(suppressEvent){
17399         if(suppressEvent !== false){
17400             this.fireEvent("beforechildrenrendered", this);
17401         }
17402         var cs = this.childNodes;
17403         for(var i = 0, len = cs.length; i < len; i++){
17404             cs[i].render(true);
17405         }
17406         this.childrenRendered = true;
17407     },
17408
17409     // private
17410     sort : function(fn, scope){
17411         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
17412         if(this.childrenRendered){
17413             var cs = this.childNodes;
17414             for(var i = 0, len = cs.length; i < len; i++){
17415                 cs[i].render(true);
17416             }
17417         }
17418     },
17419
17420     // private
17421     render : function(bulkRender){
17422         this.ui.render(bulkRender);
17423         if(!this.rendered){
17424             this.rendered = true;
17425             if(this.expanded){
17426                 this.expanded = false;
17427                 this.expand(false, false);
17428             }
17429         }
17430     },
17431
17432     // private
17433     renderIndent : function(deep, refresh){
17434         if(refresh){
17435             this.ui.childIndent = null;
17436         }
17437         this.ui.renderIndent();
17438         if(deep === true && this.childrenRendered){
17439             var cs = this.childNodes;
17440             for(var i = 0, len = cs.length; i < len; i++){
17441                 cs[i].renderIndent(true, refresh);
17442             }
17443         }
17444     }
17445 });/*
17446  * Based on:
17447  * Ext JS Library 1.1.1
17448  * Copyright(c) 2006-2007, Ext JS, LLC.
17449  *
17450  * Originally Released Under LGPL - original licence link has changed is not relivant.
17451  *
17452  * Fork - LGPL
17453  * <script type="text/javascript">
17454  */
17455  
17456 /**
17457  * @class Roo.tree.AsyncTreeNode
17458  * @extends Roo.tree.TreeNode
17459  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
17460  * @constructor
17461  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
17462  */
17463  Roo.tree.AsyncTreeNode = function(config){
17464     this.loaded = false;
17465     this.loading = false;
17466     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
17467     /**
17468     * @event beforeload
17469     * Fires before this node is loaded, return false to cancel
17470     * @param {Node} this This node
17471     */
17472     this.addEvents({'beforeload':true, 'load': true});
17473     /**
17474     * @event load
17475     * Fires when this node is loaded
17476     * @param {Node} this This node
17477     */
17478     /**
17479      * The loader used by this node (defaults to using the tree's defined loader)
17480      * @type TreeLoader
17481      * @property loader
17482      */
17483 };
17484 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
17485     expand : function(deep, anim, callback){
17486         if(this.loading){ // if an async load is already running, waiting til it's done
17487             var timer;
17488             var f = function(){
17489                 if(!this.loading){ // done loading
17490                     clearInterval(timer);
17491                     this.expand(deep, anim, callback);
17492                 }
17493             }.createDelegate(this);
17494             timer = setInterval(f, 200);
17495             return;
17496         }
17497         if(!this.loaded){
17498             if(this.fireEvent("beforeload", this) === false){
17499                 return;
17500             }
17501             this.loading = true;
17502             this.ui.beforeLoad(this);
17503             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
17504             if(loader){
17505                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
17506                 return;
17507             }
17508         }
17509         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
17510     },
17511     
17512     /**
17513      * Returns true if this node is currently loading
17514      * @return {Boolean}
17515      */
17516     isLoading : function(){
17517         return this.loading;  
17518     },
17519     
17520     loadComplete : function(deep, anim, callback){
17521         this.loading = false;
17522         this.loaded = true;
17523         this.ui.afterLoad(this);
17524         this.fireEvent("load", this);
17525         this.expand(deep, anim, callback);
17526     },
17527     
17528     /**
17529      * Returns true if this node has been loaded
17530      * @return {Boolean}
17531      */
17532     isLoaded : function(){
17533         return this.loaded;
17534     },
17535     
17536     hasChildNodes : function(){
17537         if(!this.isLeaf() && !this.loaded){
17538             return true;
17539         }else{
17540             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
17541         }
17542     },
17543
17544     /**
17545      * Trigger a reload for this node
17546      * @param {Function} callback
17547      */
17548     reload : function(callback){
17549         this.collapse(false, false);
17550         while(this.firstChild){
17551             this.removeChild(this.firstChild);
17552         }
17553         this.childrenRendered = false;
17554         this.loaded = false;
17555         if(this.isHiddenRoot()){
17556             this.expanded = false;
17557         }
17558         this.expand(false, false, callback);
17559     }
17560 });/*
17561  * Based on:
17562  * Ext JS Library 1.1.1
17563  * Copyright(c) 2006-2007, Ext JS, LLC.
17564  *
17565  * Originally Released Under LGPL - original licence link has changed is not relivant.
17566  *
17567  * Fork - LGPL
17568  * <script type="text/javascript">
17569  */
17570  
17571 /**
17572  * @class Roo.tree.TreeNodeUI
17573  * @constructor
17574  * @param {Object} node The node to render
17575  * The TreeNode UI implementation is separate from the
17576  * tree implementation. Unless you are customizing the tree UI,
17577  * you should never have to use this directly.
17578  */
17579 Roo.tree.TreeNodeUI = function(node){
17580     this.node = node;
17581     this.rendered = false;
17582     this.animating = false;
17583     this.emptyIcon = Roo.BLANK_IMAGE_URL;
17584 };
17585
17586 Roo.tree.TreeNodeUI.prototype = {
17587     removeChild : function(node){
17588         if(this.rendered){
17589             this.ctNode.removeChild(node.ui.getEl());
17590         }
17591     },
17592
17593     beforeLoad : function(){
17594          this.addClass("x-tree-node-loading");
17595     },
17596
17597     afterLoad : function(){
17598          this.removeClass("x-tree-node-loading");
17599     },
17600
17601     onTextChange : function(node, text, oldText){
17602         if(this.rendered){
17603             this.textNode.innerHTML = text;
17604         }
17605     },
17606
17607     onDisableChange : function(node, state){
17608         this.disabled = state;
17609         if(state){
17610             this.addClass("x-tree-node-disabled");
17611         }else{
17612             this.removeClass("x-tree-node-disabled");
17613         }
17614     },
17615
17616     onSelectedChange : function(state){
17617         if(state){
17618             this.focus();
17619             this.addClass("x-tree-selected");
17620         }else{
17621             //this.blur();
17622             this.removeClass("x-tree-selected");
17623         }
17624     },
17625
17626     onMove : function(tree, node, oldParent, newParent, index, refNode){
17627         this.childIndent = null;
17628         if(this.rendered){
17629             var targetNode = newParent.ui.getContainer();
17630             if(!targetNode){//target not rendered
17631                 this.holder = document.createElement("div");
17632                 this.holder.appendChild(this.wrap);
17633                 return;
17634             }
17635             var insertBefore = refNode ? refNode.ui.getEl() : null;
17636             if(insertBefore){
17637                 targetNode.insertBefore(this.wrap, insertBefore);
17638             }else{
17639                 targetNode.appendChild(this.wrap);
17640             }
17641             this.node.renderIndent(true);
17642         }
17643     },
17644
17645     addClass : function(cls){
17646         if(this.elNode){
17647             Roo.fly(this.elNode).addClass(cls);
17648         }
17649     },
17650
17651     removeClass : function(cls){
17652         if(this.elNode){
17653             Roo.fly(this.elNode).removeClass(cls);
17654         }
17655     },
17656
17657     remove : function(){
17658         if(this.rendered){
17659             this.holder = document.createElement("div");
17660             this.holder.appendChild(this.wrap);
17661         }
17662     },
17663
17664     fireEvent : function(){
17665         return this.node.fireEvent.apply(this.node, arguments);
17666     },
17667
17668     initEvents : function(){
17669         this.node.on("move", this.onMove, this);
17670         var E = Roo.EventManager;
17671         var a = this.anchor;
17672
17673         var el = Roo.fly(a, '_treeui');
17674
17675         if(Roo.isOpera){ // opera render bug ignores the CSS
17676             el.setStyle("text-decoration", "none");
17677         }
17678
17679         el.on("click", this.onClick, this);
17680         el.on("dblclick", this.onDblClick, this);
17681
17682         if(this.checkbox){
17683             Roo.EventManager.on(this.checkbox,
17684                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
17685         }
17686
17687         el.on("contextmenu", this.onContextMenu, this);
17688
17689         var icon = Roo.fly(this.iconNode);
17690         icon.on("click", this.onClick, this);
17691         icon.on("dblclick", this.onDblClick, this);
17692         icon.on("contextmenu", this.onContextMenu, this);
17693         E.on(this.ecNode, "click", this.ecClick, this, true);
17694
17695         if(this.node.disabled){
17696             this.addClass("x-tree-node-disabled");
17697         }
17698         if(this.node.hidden){
17699             this.addClass("x-tree-node-disabled");
17700         }
17701         var ot = this.node.getOwnerTree();
17702         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
17703         if(dd && (!this.node.isRoot || ot.rootVisible)){
17704             Roo.dd.Registry.register(this.elNode, {
17705                 node: this.node,
17706                 handles: this.getDDHandles(),
17707                 isHandle: false
17708             });
17709         }
17710     },
17711
17712     getDDHandles : function(){
17713         return [this.iconNode, this.textNode];
17714     },
17715
17716     hide : function(){
17717         if(this.rendered){
17718             this.wrap.style.display = "none";
17719         }
17720     },
17721
17722     show : function(){
17723         if(this.rendered){
17724             this.wrap.style.display = "";
17725         }
17726     },
17727
17728     onContextMenu : function(e){
17729         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
17730             e.preventDefault();
17731             this.focus();
17732             this.fireEvent("contextmenu", this.node, e);
17733         }
17734     },
17735
17736     onClick : function(e){
17737         if(this.dropping){
17738             e.stopEvent();
17739             return;
17740         }
17741         if(this.fireEvent("beforeclick", this.node, e) !== false){
17742             if(!this.disabled && this.node.attributes.href){
17743                 this.fireEvent("click", this.node, e);
17744                 return;
17745             }
17746             e.preventDefault();
17747             if(this.disabled){
17748                 return;
17749             }
17750
17751             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
17752                 this.node.toggle();
17753             }
17754
17755             this.fireEvent("click", this.node, e);
17756         }else{
17757             e.stopEvent();
17758         }
17759     },
17760
17761     onDblClick : function(e){
17762         e.preventDefault();
17763         if(this.disabled){
17764             return;
17765         }
17766         if(this.checkbox){
17767             this.toggleCheck();
17768         }
17769         if(!this.animating && this.node.hasChildNodes()){
17770             this.node.toggle();
17771         }
17772         this.fireEvent("dblclick", this.node, e);
17773     },
17774
17775     onCheckChange : function(){
17776         var checked = this.checkbox.checked;
17777         this.node.attributes.checked = checked;
17778         this.fireEvent('checkchange', this.node, checked);
17779     },
17780
17781     ecClick : function(e){
17782         if(!this.animating && this.node.hasChildNodes()){
17783             this.node.toggle();
17784         }
17785     },
17786
17787     startDrop : function(){
17788         this.dropping = true;
17789     },
17790
17791     // delayed drop so the click event doesn't get fired on a drop
17792     endDrop : function(){
17793        setTimeout(function(){
17794            this.dropping = false;
17795        }.createDelegate(this), 50);
17796     },
17797
17798     expand : function(){
17799         this.updateExpandIcon();
17800         this.ctNode.style.display = "";
17801     },
17802
17803     focus : function(){
17804         if(!this.node.preventHScroll){
17805             try{this.anchor.focus();
17806             }catch(e){}
17807         }else if(!Roo.isIE){
17808             try{
17809                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
17810                 var l = noscroll.scrollLeft;
17811                 this.anchor.focus();
17812                 noscroll.scrollLeft = l;
17813             }catch(e){}
17814         }
17815     },
17816
17817     toggleCheck : function(value){
17818         var cb = this.checkbox;
17819         if(cb){
17820             cb.checked = (value === undefined ? !cb.checked : value);
17821         }
17822     },
17823
17824     blur : function(){
17825         try{
17826             this.anchor.blur();
17827         }catch(e){}
17828     },
17829
17830     animExpand : function(callback){
17831         var ct = Roo.get(this.ctNode);
17832         ct.stopFx();
17833         if(!this.node.hasChildNodes()){
17834             this.updateExpandIcon();
17835             this.ctNode.style.display = "";
17836             Roo.callback(callback);
17837             return;
17838         }
17839         this.animating = true;
17840         this.updateExpandIcon();
17841
17842         ct.slideIn('t', {
17843            callback : function(){
17844                this.animating = false;
17845                Roo.callback(callback);
17846             },
17847             scope: this,
17848             duration: this.node.ownerTree.duration || .25
17849         });
17850     },
17851
17852     highlight : function(){
17853         var tree = this.node.getOwnerTree();
17854         Roo.fly(this.wrap).highlight(
17855             tree.hlColor || "C3DAF9",
17856             {endColor: tree.hlBaseColor}
17857         );
17858     },
17859
17860     collapse : function(){
17861         this.updateExpandIcon();
17862         this.ctNode.style.display = "none";
17863     },
17864
17865     animCollapse : function(callback){
17866         var ct = Roo.get(this.ctNode);
17867         ct.enableDisplayMode('block');
17868         ct.stopFx();
17869
17870         this.animating = true;
17871         this.updateExpandIcon();
17872
17873         ct.slideOut('t', {
17874             callback : function(){
17875                this.animating = false;
17876                Roo.callback(callback);
17877             },
17878             scope: this,
17879             duration: this.node.ownerTree.duration || .25
17880         });
17881     },
17882
17883     getContainer : function(){
17884         return this.ctNode;
17885     },
17886
17887     getEl : function(){
17888         return this.wrap;
17889     },
17890
17891     appendDDGhost : function(ghostNode){
17892         ghostNode.appendChild(this.elNode.cloneNode(true));
17893     },
17894
17895     getDDRepairXY : function(){
17896         return Roo.lib.Dom.getXY(this.iconNode);
17897     },
17898
17899     onRender : function(){
17900         this.render();
17901     },
17902
17903     render : function(bulkRender){
17904         var n = this.node, a = n.attributes;
17905         var targetNode = n.parentNode ?
17906               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
17907
17908         if(!this.rendered){
17909             this.rendered = true;
17910
17911             this.renderElements(n, a, targetNode, bulkRender);
17912
17913             if(a.qtip){
17914                if(this.textNode.setAttributeNS){
17915                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
17916                    if(a.qtipTitle){
17917                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
17918                    }
17919                }else{
17920                    this.textNode.setAttribute("ext:qtip", a.qtip);
17921                    if(a.qtipTitle){
17922                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
17923                    }
17924                }
17925             }else if(a.qtipCfg){
17926                 a.qtipCfg.target = Roo.id(this.textNode);
17927                 Roo.QuickTips.register(a.qtipCfg);
17928             }
17929             this.initEvents();
17930             if(!this.node.expanded){
17931                 this.updateExpandIcon();
17932             }
17933         }else{
17934             if(bulkRender === true) {
17935                 targetNode.appendChild(this.wrap);
17936             }
17937         }
17938     },
17939
17940     renderElements : function(n, a, targetNode, bulkRender){
17941         // add some indent caching, this helps performance when rendering a large tree
17942         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
17943         var t = n.getOwnerTree();
17944         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
17945         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
17946         var cb = typeof a.checked == 'boolean';
17947         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
17948         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
17949             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
17950             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
17951             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
17952             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
17953             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
17954              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
17955                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
17956             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
17957             "</li>"];
17958
17959         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
17960             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
17961                                 n.nextSibling.ui.getEl(), buf.join(""));
17962         }else{
17963             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
17964         }
17965
17966         this.elNode = this.wrap.childNodes[0];
17967         this.ctNode = this.wrap.childNodes[1];
17968         var cs = this.elNode.childNodes;
17969         this.indentNode = cs[0];
17970         this.ecNode = cs[1];
17971         this.iconNode = cs[2];
17972         var index = 3;
17973         if(cb){
17974             this.checkbox = cs[3];
17975             index++;
17976         }
17977         this.anchor = cs[index];
17978         this.textNode = cs[index].firstChild;
17979     },
17980
17981     getAnchor : function(){
17982         return this.anchor;
17983     },
17984
17985     getTextEl : function(){
17986         return this.textNode;
17987     },
17988
17989     getIconEl : function(){
17990         return this.iconNode;
17991     },
17992
17993     isChecked : function(){
17994         return this.checkbox ? this.checkbox.checked : false;
17995     },
17996
17997     updateExpandIcon : function(){
17998         if(this.rendered){
17999             var n = this.node, c1, c2;
18000             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
18001             var hasChild = n.hasChildNodes();
18002             if(hasChild){
18003                 if(n.expanded){
18004                     cls += "-minus";
18005                     c1 = "x-tree-node-collapsed";
18006                     c2 = "x-tree-node-expanded";
18007                 }else{
18008                     cls += "-plus";
18009                     c1 = "x-tree-node-expanded";
18010                     c2 = "x-tree-node-collapsed";
18011                 }
18012                 if(this.wasLeaf){
18013                     this.removeClass("x-tree-node-leaf");
18014                     this.wasLeaf = false;
18015                 }
18016                 if(this.c1 != c1 || this.c2 != c2){
18017                     Roo.fly(this.elNode).replaceClass(c1, c2);
18018                     this.c1 = c1; this.c2 = c2;
18019                 }
18020             }else{
18021                 if(!this.wasLeaf){
18022                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
18023                     delete this.c1;
18024                     delete this.c2;
18025                     this.wasLeaf = true;
18026                 }
18027             }
18028             var ecc = "x-tree-ec-icon "+cls;
18029             if(this.ecc != ecc){
18030                 this.ecNode.className = ecc;
18031                 this.ecc = ecc;
18032             }
18033         }
18034     },
18035
18036     getChildIndent : function(){
18037         if(!this.childIndent){
18038             var buf = [];
18039             var p = this.node;
18040             while(p){
18041                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
18042                     if(!p.isLast()) {
18043                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
18044                     } else {
18045                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
18046                     }
18047                 }
18048                 p = p.parentNode;
18049             }
18050             this.childIndent = buf.join("");
18051         }
18052         return this.childIndent;
18053     },
18054
18055     renderIndent : function(){
18056         if(this.rendered){
18057             var indent = "";
18058             var p = this.node.parentNode;
18059             if(p){
18060                 indent = p.ui.getChildIndent();
18061             }
18062             if(this.indentMarkup != indent){ // don't rerender if not required
18063                 this.indentNode.innerHTML = indent;
18064                 this.indentMarkup = indent;
18065             }
18066             this.updateExpandIcon();
18067         }
18068     }
18069 };
18070
18071 Roo.tree.RootTreeNodeUI = function(){
18072     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
18073 };
18074 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
18075     render : function(){
18076         if(!this.rendered){
18077             var targetNode = this.node.ownerTree.innerCt.dom;
18078             this.node.expanded = true;
18079             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
18080             this.wrap = this.ctNode = targetNode.firstChild;
18081         }
18082     },
18083     collapse : function(){
18084     },
18085     expand : function(){
18086     }
18087 });/*
18088  * Based on:
18089  * Ext JS Library 1.1.1
18090  * Copyright(c) 2006-2007, Ext JS, LLC.
18091  *
18092  * Originally Released Under LGPL - original licence link has changed is not relivant.
18093  *
18094  * Fork - LGPL
18095  * <script type="text/javascript">
18096  */
18097 /**
18098  * @class Roo.tree.TreeLoader
18099  * @extends Roo.util.Observable
18100  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
18101  * nodes from a specified URL. The response must be a javascript Array definition
18102  * who's elements are node definition objects. eg:
18103  * <pre><code>
18104    [{ 'id': 1, 'text': 'A folder Node', 'leaf': false },
18105     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }]
18106 </code></pre>
18107  * <br><br>
18108  * A server request is sent, and child nodes are loaded only when a node is expanded.
18109  * The loading node's id is passed to the server under the parameter name "node" to
18110  * enable the server to produce the correct child nodes.
18111  * <br><br>
18112  * To pass extra parameters, an event handler may be attached to the "beforeload"
18113  * event, and the parameters specified in the TreeLoader's baseParams property:
18114  * <pre><code>
18115     myTreeLoader.on("beforeload", function(treeLoader, node) {
18116         this.baseParams.category = node.attributes.category;
18117     }, this);
18118 </code></pre><
18119  * This would pass an HTTP parameter called "category" to the server containing
18120  * the value of the Node's "category" attribute.
18121  * @constructor
18122  * Creates a new Treeloader.
18123  * @param {Object} config A config object containing config properties.
18124  */
18125 Roo.tree.TreeLoader = function(config){
18126     this.baseParams = {};
18127     this.requestMethod = "POST";
18128     Roo.apply(this, config);
18129
18130     this.addEvents({
18131     
18132         /**
18133          * @event beforeload
18134          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
18135          * @param {Object} This TreeLoader object.
18136          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18137          * @param {Object} callback The callback function specified in the {@link #load} call.
18138          */
18139         beforeload : true,
18140         /**
18141          * @event load
18142          * Fires when the node has been successfuly loaded.
18143          * @param {Object} This TreeLoader object.
18144          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18145          * @param {Object} response The response object containing the data from the server.
18146          */
18147         load : true,
18148         /**
18149          * @event loadexception
18150          * Fires if the network request failed.
18151          * @param {Object} This TreeLoader object.
18152          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18153          * @param {Object} response The response object containing the data from the server.
18154          */
18155         loadexception : true,
18156         /**
18157          * @event create
18158          * Fires before a node is created, enabling you to return custom Node types 
18159          * @param {Object} This TreeLoader object.
18160          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
18161          */
18162         create : true
18163     });
18164
18165     Roo.tree.TreeLoader.superclass.constructor.call(this);
18166 };
18167
18168 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
18169     /**
18170     * @cfg {String} dataUrl The URL from which to request a Json string which
18171     * specifies an array of node definition object representing the child nodes
18172     * to be loaded.
18173     */
18174     /**
18175     * @cfg {Object} baseParams (optional) An object containing properties which
18176     * specify HTTP parameters to be passed to each request for child nodes.
18177     */
18178     /**
18179     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
18180     * created by this loader. If the attributes sent by the server have an attribute in this object,
18181     * they take priority.
18182     */
18183     /**
18184     * @cfg {Object} uiProviders (optional) An object containing properties which
18185     * 
18186     * DEPRECIATED - use 'create' event handler to modify attributes - which affect creation.
18187     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
18188     * <i>uiProvider</i> attribute of a returned child node is a string rather
18189     * than a reference to a TreeNodeUI implementation, this that string value
18190     * is used as a property name in the uiProviders object. You can define the provider named
18191     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
18192     */
18193     uiProviders : {},
18194
18195     /**
18196     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
18197     * child nodes before loading.
18198     */
18199     clearOnLoad : true,
18200
18201     /**
18202     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
18203     * property on loading, rather than expecting an array. (eg. more compatible to a standard
18204     * Grid query { data : [ .....] }
18205     */
18206     
18207     root : false,
18208      /**
18209     * @cfg {String} queryParam (optional) 
18210     * Name of the query as it will be passed on the querystring (defaults to 'node')
18211     * eg. the request will be ?node=[id]
18212     */
18213     
18214     
18215     queryParam: false,
18216     
18217     /**
18218      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
18219      * This is called automatically when a node is expanded, but may be used to reload
18220      * a node (or append new children if the {@link #clearOnLoad} option is false.)
18221      * @param {Roo.tree.TreeNode} node
18222      * @param {Function} callback
18223      */
18224     load : function(node, callback){
18225         if(this.clearOnLoad){
18226             while(node.firstChild){
18227                 node.removeChild(node.firstChild);
18228             }
18229         }
18230         if(node.attributes.children){ // preloaded json children
18231             var cs = node.attributes.children;
18232             for(var i = 0, len = cs.length; i < len; i++){
18233                 node.appendChild(this.createNode(cs[i]));
18234             }
18235             if(typeof callback == "function"){
18236                 callback();
18237             }
18238         }else if(this.dataUrl){
18239             this.requestData(node, callback);
18240         }
18241     },
18242
18243     getParams: function(node){
18244         var buf = [], bp = this.baseParams;
18245         for(var key in bp){
18246             if(typeof bp[key] != "function"){
18247                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
18248             }
18249         }
18250         var n = this.queryParam === false ? 'node' : this.queryParam;
18251         buf.push(n + "=", encodeURIComponent(node.id));
18252         return buf.join("");
18253     },
18254
18255     requestData : function(node, callback){
18256         if(this.fireEvent("beforeload", this, node, callback) !== false){
18257             this.transId = Roo.Ajax.request({
18258                 method:this.requestMethod,
18259                 url: this.dataUrl||this.url,
18260                 success: this.handleResponse,
18261                 failure: this.handleFailure,
18262                 scope: this,
18263                 argument: {callback: callback, node: node},
18264                 params: this.getParams(node)
18265             });
18266         }else{
18267             // if the load is cancelled, make sure we notify
18268             // the node that we are done
18269             if(typeof callback == "function"){
18270                 callback();
18271             }
18272         }
18273     },
18274
18275     isLoading : function(){
18276         return this.transId ? true : false;
18277     },
18278
18279     abort : function(){
18280         if(this.isLoading()){
18281             Roo.Ajax.abort(this.transId);
18282         }
18283     },
18284
18285     // private
18286     createNode : function(attr){
18287         // apply baseAttrs, nice idea Corey!
18288         if(this.baseAttrs){
18289             Roo.applyIf(attr, this.baseAttrs);
18290         }
18291         if(this.applyLoader !== false){
18292             attr.loader = this;
18293         }
18294         // uiProvider = depreciated..
18295         
18296         if(typeof(attr.uiProvider) == 'string'){
18297            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
18298                 /**  eval:var:attr */ eval(attr.uiProvider);
18299         }
18300         if(typeof(this.uiProviders['default']) != 'undefined') {
18301             attr.uiProvider = this.uiProviders['default'];
18302         }
18303         
18304         this.fireEvent('create', this, attr);
18305         
18306         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
18307         return(attr.leaf ?
18308                         new Roo.tree.TreeNode(attr) :
18309                         new Roo.tree.AsyncTreeNode(attr));
18310     },
18311
18312     processResponse : function(response, node, callback){
18313         var json = response.responseText;
18314         try {
18315             
18316             var o = /**  eval:var:zzzzzzzzzz */ eval("("+json+")");
18317             if (this.root !== false) {
18318                 o = o[this.root];
18319             }
18320             
18321             for(var i = 0, len = o.length; i < len; i++){
18322                 var n = this.createNode(o[i]);
18323                 if(n){
18324                     node.appendChild(n);
18325                 }
18326             }
18327             if(typeof callback == "function"){
18328                 callback(this, node);
18329             }
18330         }catch(e){
18331             this.handleFailure(response);
18332         }
18333     },
18334
18335     handleResponse : function(response){
18336         this.transId = false;
18337         var a = response.argument;
18338         this.processResponse(response, a.node, a.callback);
18339         this.fireEvent("load", this, a.node, response);
18340     },
18341
18342     handleFailure : function(response){
18343         this.transId = false;
18344         var a = response.argument;
18345         this.fireEvent("loadexception", this, a.node, response);
18346         if(typeof a.callback == "function"){
18347             a.callback(this, a.node);
18348         }
18349     }
18350 });/*
18351  * Based on:
18352  * Ext JS Library 1.1.1
18353  * Copyright(c) 2006-2007, Ext JS, LLC.
18354  *
18355  * Originally Released Under LGPL - original licence link has changed is not relivant.
18356  *
18357  * Fork - LGPL
18358  * <script type="text/javascript">
18359  */
18360
18361 /**
18362 * @class Roo.tree.TreeFilter
18363 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
18364 * @param {TreePanel} tree
18365 * @param {Object} config (optional)
18366  */
18367 Roo.tree.TreeFilter = function(tree, config){
18368     this.tree = tree;
18369     this.filtered = {};
18370     Roo.apply(this, config);
18371 };
18372
18373 Roo.tree.TreeFilter.prototype = {
18374     clearBlank:false,
18375     reverse:false,
18376     autoClear:false,
18377     remove:false,
18378
18379      /**
18380      * Filter the data by a specific attribute.
18381      * @param {String/RegExp} value Either string that the attribute value
18382      * should start with or a RegExp to test against the attribute
18383      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
18384      * @param {TreeNode} startNode (optional) The node to start the filter at.
18385      */
18386     filter : function(value, attr, startNode){
18387         attr = attr || "text";
18388         var f;
18389         if(typeof value == "string"){
18390             var vlen = value.length;
18391             // auto clear empty filter
18392             if(vlen == 0 && this.clearBlank){
18393                 this.clear();
18394                 return;
18395             }
18396             value = value.toLowerCase();
18397             f = function(n){
18398                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
18399             };
18400         }else if(value.exec){ // regex?
18401             f = function(n){
18402                 return value.test(n.attributes[attr]);
18403             };
18404         }else{
18405             throw 'Illegal filter type, must be string or regex';
18406         }
18407         this.filterBy(f, null, startNode);
18408         },
18409
18410     /**
18411      * Filter by a function. The passed function will be called with each
18412      * node in the tree (or from the startNode). If the function returns true, the node is kept
18413      * otherwise it is filtered. If a node is filtered, its children are also filtered.
18414      * @param {Function} fn The filter function
18415      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
18416      */
18417     filterBy : function(fn, scope, startNode){
18418         startNode = startNode || this.tree.root;
18419         if(this.autoClear){
18420             this.clear();
18421         }
18422         var af = this.filtered, rv = this.reverse;
18423         var f = function(n){
18424             if(n == startNode){
18425                 return true;
18426             }
18427             if(af[n.id]){
18428                 return false;
18429             }
18430             var m = fn.call(scope || n, n);
18431             if(!m || rv){
18432                 af[n.id] = n;
18433                 n.ui.hide();
18434                 return false;
18435             }
18436             return true;
18437         };
18438         startNode.cascade(f);
18439         if(this.remove){
18440            for(var id in af){
18441                if(typeof id != "function"){
18442                    var n = af[id];
18443                    if(n && n.parentNode){
18444                        n.parentNode.removeChild(n);
18445                    }
18446                }
18447            }
18448         }
18449     },
18450
18451     /**
18452      * Clears the current filter. Note: with the "remove" option
18453      * set a filter cannot be cleared.
18454      */
18455     clear : function(){
18456         var t = this.tree;
18457         var af = this.filtered;
18458         for(var id in af){
18459             if(typeof id != "function"){
18460                 var n = af[id];
18461                 if(n){
18462                     n.ui.show();
18463                 }
18464             }
18465         }
18466         this.filtered = {};
18467     }
18468 };
18469 /*
18470  * Based on:
18471  * Ext JS Library 1.1.1
18472  * Copyright(c) 2006-2007, Ext JS, LLC.
18473  *
18474  * Originally Released Under LGPL - original licence link has changed is not relivant.
18475  *
18476  * Fork - LGPL
18477  * <script type="text/javascript">
18478  */
18479  
18480
18481 /**
18482  * @class Roo.tree.TreeSorter
18483  * Provides sorting of nodes in a TreePanel
18484  * 
18485  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
18486  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
18487  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
18488  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
18489  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
18490  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
18491  * @constructor
18492  * @param {TreePanel} tree
18493  * @param {Object} config
18494  */
18495 Roo.tree.TreeSorter = function(tree, config){
18496     Roo.apply(this, config);
18497     tree.on("beforechildrenrendered", this.doSort, this);
18498     tree.on("append", this.updateSort, this);
18499     tree.on("insert", this.updateSort, this);
18500     
18501     var dsc = this.dir && this.dir.toLowerCase() == "desc";
18502     var p = this.property || "text";
18503     var sortType = this.sortType;
18504     var fs = this.folderSort;
18505     var cs = this.caseSensitive === true;
18506     var leafAttr = this.leafAttr || 'leaf';
18507
18508     this.sortFn = function(n1, n2){
18509         if(fs){
18510             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
18511                 return 1;
18512             }
18513             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
18514                 return -1;
18515             }
18516         }
18517         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
18518         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
18519         if(v1 < v2){
18520                         return dsc ? +1 : -1;
18521                 }else if(v1 > v2){
18522                         return dsc ? -1 : +1;
18523         }else{
18524                 return 0;
18525         }
18526     };
18527 };
18528
18529 Roo.tree.TreeSorter.prototype = {
18530     doSort : function(node){
18531         node.sort(this.sortFn);
18532     },
18533     
18534     compareNodes : function(n1, n2){
18535         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
18536     },
18537     
18538     updateSort : function(tree, node){
18539         if(node.childrenRendered){
18540             this.doSort.defer(1, this, [node]);
18541         }
18542     }
18543 };/*
18544  * Based on:
18545  * Ext JS Library 1.1.1
18546  * Copyright(c) 2006-2007, Ext JS, LLC.
18547  *
18548  * Originally Released Under LGPL - original licence link has changed is not relivant.
18549  *
18550  * Fork - LGPL
18551  * <script type="text/javascript">
18552  */
18553
18554 if(Roo.dd.DropZone){
18555     
18556 Roo.tree.TreeDropZone = function(tree, config){
18557     this.allowParentInsert = false;
18558     this.allowContainerDrop = false;
18559     this.appendOnly = false;
18560     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
18561     this.tree = tree;
18562     this.lastInsertClass = "x-tree-no-status";
18563     this.dragOverData = {};
18564 };
18565
18566 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
18567     ddGroup : "TreeDD",
18568     
18569     expandDelay : 1000,
18570     
18571     expandNode : function(node){
18572         if(node.hasChildNodes() && !node.isExpanded()){
18573             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
18574         }
18575     },
18576     
18577     queueExpand : function(node){
18578         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
18579     },
18580     
18581     cancelExpand : function(){
18582         if(this.expandProcId){
18583             clearTimeout(this.expandProcId);
18584             this.expandProcId = false;
18585         }
18586     },
18587     
18588     isValidDropPoint : function(n, pt, dd, e, data){
18589         if(!n || !data){ return false; }
18590         var targetNode = n.node;
18591         var dropNode = data.node;
18592         // default drop rules
18593         if(!(targetNode && targetNode.isTarget && pt)){
18594             return false;
18595         }
18596         if(pt == "append" && targetNode.allowChildren === false){
18597             return false;
18598         }
18599         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
18600             return false;
18601         }
18602         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
18603             return false;
18604         }
18605         // reuse the object
18606         var overEvent = this.dragOverData;
18607         overEvent.tree = this.tree;
18608         overEvent.target = targetNode;
18609         overEvent.data = data;
18610         overEvent.point = pt;
18611         overEvent.source = dd;
18612         overEvent.rawEvent = e;
18613         overEvent.dropNode = dropNode;
18614         overEvent.cancel = false;  
18615         var result = this.tree.fireEvent("nodedragover", overEvent);
18616         return overEvent.cancel === false && result !== false;
18617     },
18618     
18619     getDropPoint : function(e, n, dd){
18620         var tn = n.node;
18621         if(tn.isRoot){
18622             return tn.allowChildren !== false ? "append" : false; // always append for root
18623         }
18624         var dragEl = n.ddel;
18625         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
18626         var y = Roo.lib.Event.getPageY(e);
18627         //var noAppend = tn.allowChildren === false || tn.isLeaf();
18628         
18629         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
18630         var noAppend = tn.allowChildren === false;
18631         if(this.appendOnly || tn.parentNode.allowChildren === false){
18632             return noAppend ? false : "append";
18633         }
18634         var noBelow = false;
18635         if(!this.allowParentInsert){
18636             noBelow = tn.hasChildNodes() && tn.isExpanded();
18637         }
18638         var q = (b - t) / (noAppend ? 2 : 3);
18639         if(y >= t && y < (t + q)){
18640             return "above";
18641         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
18642             return "below";
18643         }else{
18644             return "append";
18645         }
18646     },
18647     
18648     onNodeEnter : function(n, dd, e, data){
18649         this.cancelExpand();
18650     },
18651     
18652     onNodeOver : function(n, dd, e, data){
18653         var pt = this.getDropPoint(e, n, dd);
18654         var node = n.node;
18655         
18656         // auto node expand check
18657         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
18658             this.queueExpand(node);
18659         }else if(pt != "append"){
18660             this.cancelExpand();
18661         }
18662         
18663         // set the insert point style on the target node
18664         var returnCls = this.dropNotAllowed;
18665         if(this.isValidDropPoint(n, pt, dd, e, data)){
18666            if(pt){
18667                var el = n.ddel;
18668                var cls;
18669                if(pt == "above"){
18670                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
18671                    cls = "x-tree-drag-insert-above";
18672                }else if(pt == "below"){
18673                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
18674                    cls = "x-tree-drag-insert-below";
18675                }else{
18676                    returnCls = "x-tree-drop-ok-append";
18677                    cls = "x-tree-drag-append";
18678                }
18679                if(this.lastInsertClass != cls){
18680                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
18681                    this.lastInsertClass = cls;
18682                }
18683            }
18684        }
18685        return returnCls;
18686     },
18687     
18688     onNodeOut : function(n, dd, e, data){
18689         this.cancelExpand();
18690         this.removeDropIndicators(n);
18691     },
18692     
18693     onNodeDrop : function(n, dd, e, data){
18694         var point = this.getDropPoint(e, n, dd);
18695         var targetNode = n.node;
18696         targetNode.ui.startDrop();
18697         if(!this.isValidDropPoint(n, point, dd, e, data)){
18698             targetNode.ui.endDrop();
18699             return false;
18700         }
18701         // first try to find the drop node
18702         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
18703         var dropEvent = {
18704             tree : this.tree,
18705             target: targetNode,
18706             data: data,
18707             point: point,
18708             source: dd,
18709             rawEvent: e,
18710             dropNode: dropNode,
18711             cancel: !dropNode   
18712         };
18713         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
18714         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
18715             targetNode.ui.endDrop();
18716             return false;
18717         }
18718         // allow target changing
18719         targetNode = dropEvent.target;
18720         if(point == "append" && !targetNode.isExpanded()){
18721             targetNode.expand(false, null, function(){
18722                 this.completeDrop(dropEvent);
18723             }.createDelegate(this));
18724         }else{
18725             this.completeDrop(dropEvent);
18726         }
18727         return true;
18728     },
18729     
18730     completeDrop : function(de){
18731         var ns = de.dropNode, p = de.point, t = de.target;
18732         if(!(ns instanceof Array)){
18733             ns = [ns];
18734         }
18735         var n;
18736         for(var i = 0, len = ns.length; i < len; i++){
18737             n = ns[i];
18738             if(p == "above"){
18739                 t.parentNode.insertBefore(n, t);
18740             }else if(p == "below"){
18741                 t.parentNode.insertBefore(n, t.nextSibling);
18742             }else{
18743                 t.appendChild(n);
18744             }
18745         }
18746         n.ui.focus();
18747         if(this.tree.hlDrop){
18748             n.ui.highlight();
18749         }
18750         t.ui.endDrop();
18751         this.tree.fireEvent("nodedrop", de);
18752     },
18753     
18754     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
18755         if(this.tree.hlDrop){
18756             dropNode.ui.focus();
18757             dropNode.ui.highlight();
18758         }
18759         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
18760     },
18761     
18762     getTree : function(){
18763         return this.tree;
18764     },
18765     
18766     removeDropIndicators : function(n){
18767         if(n && n.ddel){
18768             var el = n.ddel;
18769             Roo.fly(el).removeClass([
18770                     "x-tree-drag-insert-above",
18771                     "x-tree-drag-insert-below",
18772                     "x-tree-drag-append"]);
18773             this.lastInsertClass = "_noclass";
18774         }
18775     },
18776     
18777     beforeDragDrop : function(target, e, id){
18778         this.cancelExpand();
18779         return true;
18780     },
18781     
18782     afterRepair : function(data){
18783         if(data && Roo.enableFx){
18784             data.node.ui.highlight();
18785         }
18786         this.hideProxy();
18787     }    
18788 });
18789
18790 }
18791 /*
18792  * Based on:
18793  * Ext JS Library 1.1.1
18794  * Copyright(c) 2006-2007, Ext JS, LLC.
18795  *
18796  * Originally Released Under LGPL - original licence link has changed is not relivant.
18797  *
18798  * Fork - LGPL
18799  * <script type="text/javascript">
18800  */
18801  
18802
18803 if(Roo.dd.DragZone){
18804 Roo.tree.TreeDragZone = function(tree, config){
18805     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
18806     this.tree = tree;
18807 };
18808
18809 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
18810     ddGroup : "TreeDD",
18811     
18812     onBeforeDrag : function(data, e){
18813         var n = data.node;
18814         return n && n.draggable && !n.disabled;
18815     },
18816     
18817     onInitDrag : function(e){
18818         var data = this.dragData;
18819         this.tree.getSelectionModel().select(data.node);
18820         this.proxy.update("");
18821         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
18822         this.tree.fireEvent("startdrag", this.tree, data.node, e);
18823     },
18824     
18825     getRepairXY : function(e, data){
18826         return data.node.ui.getDDRepairXY();
18827     },
18828     
18829     onEndDrag : function(data, e){
18830         this.tree.fireEvent("enddrag", this.tree, data.node, e);
18831     },
18832     
18833     onValidDrop : function(dd, e, id){
18834         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
18835         this.hideProxy();
18836     },
18837     
18838     beforeInvalidDrop : function(e, id){
18839         // this scrolls the original position back into view
18840         var sm = this.tree.getSelectionModel();
18841         sm.clearSelections();
18842         sm.select(this.dragData.node);
18843     }
18844 });
18845 }/*
18846  * Based on:
18847  * Ext JS Library 1.1.1
18848  * Copyright(c) 2006-2007, Ext JS, LLC.
18849  *
18850  * Originally Released Under LGPL - original licence link has changed is not relivant.
18851  *
18852  * Fork - LGPL
18853  * <script type="text/javascript">
18854  */
18855 /**
18856  * @class Roo.tree.TreeEditor
18857  * @extends Roo.Editor
18858  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
18859  * as the editor field.
18860  * @constructor
18861  * @param {TreePanel} tree
18862  * @param {Object} config Either a prebuilt {@link Roo.form.Field} instance or a Field config object
18863  */
18864 Roo.tree.TreeEditor = function(tree, config){
18865     config = config || {};
18866     var field = config.events ? config : new Roo.form.TextField(config);
18867     Roo.tree.TreeEditor.superclass.constructor.call(this, field);
18868
18869     this.tree = tree;
18870
18871     tree.on('beforeclick', this.beforeNodeClick, this);
18872     tree.getTreeEl().on('mousedown', this.hide, this);
18873     this.on('complete', this.updateNode, this);
18874     this.on('beforestartedit', this.fitToTree, this);
18875     this.on('startedit', this.bindScroll, this, {delay:10});
18876     this.on('specialkey', this.onSpecialKey, this);
18877 };
18878
18879 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
18880     /**
18881      * @cfg {String} alignment
18882      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
18883      */
18884     alignment: "l-l",
18885     // inherit
18886     autoSize: false,
18887     /**
18888      * @cfg {Boolean} hideEl
18889      * True to hide the bound element while the editor is displayed (defaults to false)
18890      */
18891     hideEl : false,
18892     /**
18893      * @cfg {String} cls
18894      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
18895      */
18896     cls: "x-small-editor x-tree-editor",
18897     /**
18898      * @cfg {Boolean} shim
18899      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
18900      */
18901     shim:false,
18902     // inherit
18903     shadow:"frame",
18904     /**
18905      * @cfg {Number} maxWidth
18906      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
18907      * the containing tree element's size, it will be automatically limited for you to the container width, taking
18908      * scroll and client offsets into account prior to each edit.
18909      */
18910     maxWidth: 250,
18911
18912     editDelay : 350,
18913
18914     // private
18915     fitToTree : function(ed, el){
18916         var td = this.tree.getTreeEl().dom, nd = el.dom;
18917         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
18918             td.scrollLeft = nd.offsetLeft;
18919         }
18920         var w = Math.min(
18921                 this.maxWidth,
18922                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
18923         this.setSize(w, '');
18924     },
18925
18926     // private
18927     triggerEdit : function(node){
18928         this.completeEdit();
18929         this.editNode = node;
18930         this.startEdit(node.ui.textNode, node.text);
18931     },
18932
18933     // private
18934     bindScroll : function(){
18935         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
18936     },
18937
18938     // private
18939     beforeNodeClick : function(node, e){
18940         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
18941         this.lastClick = new Date();
18942         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
18943             e.stopEvent();
18944             this.triggerEdit(node);
18945             return false;
18946         }
18947     },
18948
18949     // private
18950     updateNode : function(ed, value){
18951         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
18952         this.editNode.setText(value);
18953     },
18954
18955     // private
18956     onHide : function(){
18957         Roo.tree.TreeEditor.superclass.onHide.call(this);
18958         if(this.editNode){
18959             this.editNode.ui.focus();
18960         }
18961     },
18962
18963     // private
18964     onSpecialKey : function(field, e){
18965         var k = e.getKey();
18966         if(k == e.ESC){
18967             e.stopEvent();
18968             this.cancelEdit();
18969         }else if(k == e.ENTER && !e.hasModifier()){
18970             e.stopEvent();
18971             this.completeEdit();
18972         }
18973     }
18974 });//<Script type="text/javascript">
18975 /*
18976  * Based on:
18977  * Ext JS Library 1.1.1
18978  * Copyright(c) 2006-2007, Ext JS, LLC.
18979  *
18980  * Originally Released Under LGPL - original licence link has changed is not relivant.
18981  *
18982  * Fork - LGPL
18983  * <script type="text/javascript">
18984  */
18985  
18986 /**
18987  * Not documented??? - probably should be...
18988  */
18989
18990 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
18991     //focus: Roo.emptyFn, // prevent odd scrolling behavior
18992     
18993     renderElements : function(n, a, targetNode, bulkRender){
18994         //consel.log("renderElements?");
18995         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
18996
18997         var t = n.getOwnerTree();
18998         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
18999         
19000         var cols = t.columns;
19001         var bw = t.borderWidth;
19002         var c = cols[0];
19003         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
19004          var cb = typeof a.checked == "boolean";
19005         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19006         var colcls = 'x-t-' + tid + '-c0';
19007         var buf = [
19008             '<li class="x-tree-node">',
19009             
19010                 
19011                 '<div class="x-tree-node-el ', a.cls,'">',
19012                     // extran...
19013                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
19014                 
19015                 
19016                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
19017                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
19018                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
19019                            (a.icon ? ' x-tree-node-inline-icon' : ''),
19020                            (a.iconCls ? ' '+a.iconCls : ''),
19021                            '" unselectable="on" />',
19022                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
19023                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
19024                              
19025                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19026                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
19027                             '<span unselectable="on" qtip="' + tx + '">',
19028                              tx,
19029                              '</span></a>' ,
19030                     '</div>',
19031                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19032                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
19033                  ];
19034         for(var i = 1, len = cols.length; i < len; i++){
19035             c = cols[i];
19036             colcls = 'x-t-' + tid + '-c' +i;
19037             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19038             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
19039                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
19040                       "</div>");
19041          }
19042          
19043          buf.push(
19044             '</a>',
19045             '<div class="x-clear"></div></div>',
19046             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
19047             "</li>");
19048         
19049         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
19050             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
19051                                 n.nextSibling.ui.getEl(), buf.join(""));
19052         }else{
19053             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
19054         }
19055         var el = this.wrap.firstChild;
19056         this.elRow = el;
19057         this.elNode = el.firstChild;
19058         this.ranchor = el.childNodes[1];
19059         this.ctNode = this.wrap.childNodes[1];
19060         var cs = el.firstChild.childNodes;
19061         this.indentNode = cs[0];
19062         this.ecNode = cs[1];
19063         this.iconNode = cs[2];
19064         var index = 3;
19065         if(cb){
19066             this.checkbox = cs[3];
19067             index++;
19068         }
19069         this.anchor = cs[index];
19070         
19071         this.textNode = cs[index].firstChild;
19072         
19073         //el.on("click", this.onClick, this);
19074         //el.on("dblclick", this.onDblClick, this);
19075         
19076         
19077        // console.log(this);
19078     },
19079     initEvents : function(){
19080         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
19081         
19082             
19083         var a = this.ranchor;
19084
19085         var el = Roo.get(a);
19086
19087         if(Roo.isOpera){ // opera render bug ignores the CSS
19088             el.setStyle("text-decoration", "none");
19089         }
19090
19091         el.on("click", this.onClick, this);
19092         el.on("dblclick", this.onDblClick, this);
19093         el.on("contextmenu", this.onContextMenu, this);
19094         
19095     },
19096     
19097     /*onSelectedChange : function(state){
19098         if(state){
19099             this.focus();
19100             this.addClass("x-tree-selected");
19101         }else{
19102             //this.blur();
19103             this.removeClass("x-tree-selected");
19104         }
19105     },*/
19106     addClass : function(cls){
19107         if(this.elRow){
19108             Roo.fly(this.elRow).addClass(cls);
19109         }
19110         
19111     },
19112     
19113     
19114     removeClass : function(cls){
19115         if(this.elRow){
19116             Roo.fly(this.elRow).removeClass(cls);
19117         }
19118     }
19119
19120     
19121     
19122 });//<Script type="text/javascript">
19123
19124 /*
19125  * Based on:
19126  * Ext JS Library 1.1.1
19127  * Copyright(c) 2006-2007, Ext JS, LLC.
19128  *
19129  * Originally Released Under LGPL - original licence link has changed is not relivant.
19130  *
19131  * Fork - LGPL
19132  * <script type="text/javascript">
19133  */
19134  
19135
19136 /**
19137  * @class Roo.tree.ColumnTree
19138  * @extends Roo.data.TreePanel
19139  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
19140  * @cfg {int} borderWidth  compined right/left border allowance
19141  * @constructor
19142  * @param {String/HTMLElement/Element} el The container element
19143  * @param {Object} config
19144  */
19145 Roo.tree.ColumnTree =  function(el, config)
19146 {
19147    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
19148    this.addEvents({
19149         /**
19150         * @event resize
19151         * Fire this event on a container when it resizes
19152         * @param {int} w Width
19153         * @param {int} h Height
19154         */
19155        "resize" : true
19156     });
19157     this.on('resize', this.onResize, this);
19158 };
19159
19160 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
19161     //lines:false,
19162     
19163     
19164     borderWidth: Roo.isBorderBox ? 0 : 2, 
19165     headEls : false,
19166     
19167     render : function(){
19168         // add the header.....
19169        
19170         Roo.tree.ColumnTree.superclass.render.apply(this);
19171         
19172         this.el.addClass('x-column-tree');
19173         
19174         this.headers = this.el.createChild(
19175             {cls:'x-tree-headers'},this.innerCt.dom);
19176    
19177         var cols = this.columns, c;
19178         var totalWidth = 0;
19179         this.headEls = [];
19180         var  len = cols.length;
19181         for(var i = 0; i < len; i++){
19182              c = cols[i];
19183              totalWidth += c.width;
19184             this.headEls.push(this.headers.createChild({
19185                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
19186                  cn: {
19187                      cls:'x-tree-hd-text',
19188                      html: c.header
19189                  },
19190                  style:'width:'+(c.width-this.borderWidth)+'px;'
19191              }));
19192         }
19193         this.headers.createChild({cls:'x-clear'});
19194         // prevent floats from wrapping when clipped
19195         this.headers.setWidth(totalWidth);
19196         //this.innerCt.setWidth(totalWidth);
19197         this.innerCt.setStyle({ overflow: 'auto' });
19198         this.onResize(this.width, this.height);
19199              
19200         
19201     },
19202     onResize : function(w,h)
19203     {
19204         this.height = h;
19205         this.width = w;
19206         // resize cols..
19207         this.innerCt.setWidth(this.width);
19208         this.innerCt.setHeight(this.height-20);
19209         
19210         // headers...
19211         var cols = this.columns, c;
19212         var totalWidth = 0;
19213         var expEl = false;
19214         var len = cols.length;
19215         for(var i = 0; i < len; i++){
19216             c = cols[i];
19217             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
19218                 // it's the expander..
19219                 expEl  = this.headEls[i];
19220                 continue;
19221             }
19222             totalWidth += c.width;
19223             
19224         }
19225         if (expEl) {
19226             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
19227         }
19228         this.headers.setWidth(w-20);
19229
19230         
19231         
19232         
19233     }
19234 });
19235 /*
19236  * Based on:
19237  * Ext JS Library 1.1.1
19238  * Copyright(c) 2006-2007, Ext JS, LLC.
19239  *
19240  * Originally Released Under LGPL - original licence link has changed is not relivant.
19241  *
19242  * Fork - LGPL
19243  * <script type="text/javascript">
19244  */
19245  
19246 /**
19247  * @class Roo.menu.Menu
19248  * @extends Roo.util.Observable
19249  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
19250  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
19251  * @constructor
19252  * Creates a new Menu
19253  * @param {Object} config Configuration options
19254  */
19255 Roo.menu.Menu = function(config){
19256     Roo.apply(this, config);
19257     this.id = this.id || Roo.id();
19258     this.addEvents({
19259         /**
19260          * @event beforeshow
19261          * Fires before this menu is displayed
19262          * @param {Roo.menu.Menu} this
19263          */
19264         beforeshow : true,
19265         /**
19266          * @event beforehide
19267          * Fires before this menu is hidden
19268          * @param {Roo.menu.Menu} this
19269          */
19270         beforehide : true,
19271         /**
19272          * @event show
19273          * Fires after this menu is displayed
19274          * @param {Roo.menu.Menu} this
19275          */
19276         show : true,
19277         /**
19278          * @event hide
19279          * Fires after this menu is hidden
19280          * @param {Roo.menu.Menu} this
19281          */
19282         hide : true,
19283         /**
19284          * @event click
19285          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19286          * @param {Roo.menu.Menu} this
19287          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19288          * @param {Roo.EventObject} e
19289          */
19290         click : true,
19291         /**
19292          * @event mouseover
19293          * Fires when the mouse is hovering over this menu
19294          * @param {Roo.menu.Menu} this
19295          * @param {Roo.EventObject} e
19296          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19297          */
19298         mouseover : true,
19299         /**
19300          * @event mouseout
19301          * Fires when the mouse exits this menu
19302          * @param {Roo.menu.Menu} this
19303          * @param {Roo.EventObject} e
19304          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19305          */
19306         mouseout : true,
19307         /**
19308          * @event itemclick
19309          * Fires when a menu item contained in this menu is clicked
19310          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
19311          * @param {Roo.EventObject} e
19312          */
19313         itemclick: true
19314     });
19315     if (this.registerMenu) {
19316         Roo.menu.MenuMgr.register(this);
19317     }
19318     
19319     var mis = this.items;
19320     this.items = new Roo.util.MixedCollection();
19321     if(mis){
19322         this.add.apply(this, mis);
19323     }
19324 };
19325
19326 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
19327     /**
19328      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
19329      */
19330     minWidth : 120,
19331     /**
19332      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
19333      * for bottom-right shadow (defaults to "sides")
19334      */
19335     shadow : "sides",
19336     /**
19337      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
19338      * this menu (defaults to "tl-tr?")
19339      */
19340     subMenuAlign : "tl-tr?",
19341     /**
19342      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
19343      * relative to its element of origin (defaults to "tl-bl?")
19344      */
19345     defaultAlign : "tl-bl?",
19346     /**
19347      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
19348      */
19349     allowOtherMenus : false,
19350     /**
19351      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
19352      */
19353     registerMenu : true,
19354
19355     hidden:true,
19356
19357     // private
19358     render : function(){
19359         if(this.el){
19360             return;
19361         }
19362         var el = this.el = new Roo.Layer({
19363             cls: "x-menu",
19364             shadow:this.shadow,
19365             constrain: false,
19366             parentEl: this.parentEl || document.body,
19367             zindex:15000
19368         });
19369
19370         this.keyNav = new Roo.menu.MenuNav(this);
19371
19372         if(this.plain){
19373             el.addClass("x-menu-plain");
19374         }
19375         if(this.cls){
19376             el.addClass(this.cls);
19377         }
19378         // generic focus element
19379         this.focusEl = el.createChild({
19380             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
19381         });
19382         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
19383         ul.on("click", this.onClick, this);
19384         ul.on("mouseover", this.onMouseOver, this);
19385         ul.on("mouseout", this.onMouseOut, this);
19386         this.items.each(function(item){
19387             var li = document.createElement("li");
19388             li.className = "x-menu-list-item";
19389             ul.dom.appendChild(li);
19390             item.render(li, this);
19391         }, this);
19392         this.ul = ul;
19393         this.autoWidth();
19394     },
19395
19396     // private
19397     autoWidth : function(){
19398         var el = this.el, ul = this.ul;
19399         if(!el){
19400             return;
19401         }
19402         var w = this.width;
19403         if(w){
19404             el.setWidth(w);
19405         }else if(Roo.isIE){
19406             el.setWidth(this.minWidth);
19407             var t = el.dom.offsetWidth; // force recalc
19408             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
19409         }
19410     },
19411
19412     // private
19413     delayAutoWidth : function(){
19414         if(this.rendered){
19415             if(!this.awTask){
19416                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
19417             }
19418             this.awTask.delay(20);
19419         }
19420     },
19421
19422     // private
19423     findTargetItem : function(e){
19424         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
19425         if(t && t.menuItemId){
19426             return this.items.get(t.menuItemId);
19427         }
19428     },
19429
19430     // private
19431     onClick : function(e){
19432         var t;
19433         if(t = this.findTargetItem(e)){
19434             t.onClick(e);
19435             this.fireEvent("click", this, t, e);
19436         }
19437     },
19438
19439     // private
19440     setActiveItem : function(item, autoExpand){
19441         if(item != this.activeItem){
19442             if(this.activeItem){
19443                 this.activeItem.deactivate();
19444             }
19445             this.activeItem = item;
19446             item.activate(autoExpand);
19447         }else if(autoExpand){
19448             item.expandMenu();
19449         }
19450     },
19451
19452     // private
19453     tryActivate : function(start, step){
19454         var items = this.items;
19455         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
19456             var item = items.get(i);
19457             if(!item.disabled && item.canActivate){
19458                 this.setActiveItem(item, false);
19459                 return item;
19460             }
19461         }
19462         return false;
19463     },
19464
19465     // private
19466     onMouseOver : function(e){
19467         var t;
19468         if(t = this.findTargetItem(e)){
19469             if(t.canActivate && !t.disabled){
19470                 this.setActiveItem(t, true);
19471             }
19472         }
19473         this.fireEvent("mouseover", this, e, t);
19474     },
19475
19476     // private
19477     onMouseOut : function(e){
19478         var t;
19479         if(t = this.findTargetItem(e)){
19480             if(t == this.activeItem && t.shouldDeactivate(e)){
19481                 this.activeItem.deactivate();
19482                 delete this.activeItem;
19483             }
19484         }
19485         this.fireEvent("mouseout", this, e, t);
19486     },
19487
19488     /**
19489      * Read-only.  Returns true if the menu is currently displayed, else false.
19490      * @type Boolean
19491      */
19492     isVisible : function(){
19493         return this.el && !this.hidden;
19494     },
19495
19496     /**
19497      * Displays this menu relative to another element
19498      * @param {String/HTMLElement/Roo.Element} element The element to align to
19499      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
19500      * the element (defaults to this.defaultAlign)
19501      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19502      */
19503     show : function(el, pos, parentMenu){
19504         this.parentMenu = parentMenu;
19505         if(!this.el){
19506             this.render();
19507         }
19508         this.fireEvent("beforeshow", this);
19509         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
19510     },
19511
19512     /**
19513      * Displays this menu at a specific xy position
19514      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
19515      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19516      */
19517     showAt : function(xy, parentMenu, /* private: */_e){
19518         this.parentMenu = parentMenu;
19519         if(!this.el){
19520             this.render();
19521         }
19522         if(_e !== false){
19523             this.fireEvent("beforeshow", this);
19524             xy = this.el.adjustForConstraints(xy);
19525         }
19526         this.el.setXY(xy);
19527         this.el.show();
19528         this.hidden = false;
19529         this.focus();
19530         this.fireEvent("show", this);
19531     },
19532
19533     focus : function(){
19534         if(!this.hidden){
19535             this.doFocus.defer(50, this);
19536         }
19537     },
19538
19539     doFocus : function(){
19540         if(!this.hidden){
19541             this.focusEl.focus();
19542         }
19543     },
19544
19545     /**
19546      * Hides this menu and optionally all parent menus
19547      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
19548      */
19549     hide : function(deep){
19550         if(this.el && this.isVisible()){
19551             this.fireEvent("beforehide", this);
19552             if(this.activeItem){
19553                 this.activeItem.deactivate();
19554                 this.activeItem = null;
19555             }
19556             this.el.hide();
19557             this.hidden = true;
19558             this.fireEvent("hide", this);
19559         }
19560         if(deep === true && this.parentMenu){
19561             this.parentMenu.hide(true);
19562         }
19563     },
19564
19565     /**
19566      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
19567      * Any of the following are valid:
19568      * <ul>
19569      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
19570      * <li>An HTMLElement object which will be converted to a menu item</li>
19571      * <li>A menu item config object that will be created as a new menu item</li>
19572      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
19573      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
19574      * </ul>
19575      * Usage:
19576      * <pre><code>
19577 // Create the menu
19578 var menu = new Roo.menu.Menu();
19579
19580 // Create a menu item to add by reference
19581 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
19582
19583 // Add a bunch of items at once using different methods.
19584 // Only the last item added will be returned.
19585 var item = menu.add(
19586     menuItem,                // add existing item by ref
19587     'Dynamic Item',          // new TextItem
19588     '-',                     // new separator
19589     { text: 'Config Item' }  // new item by config
19590 );
19591 </code></pre>
19592      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
19593      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
19594      */
19595     add : function(){
19596         var a = arguments, l = a.length, item;
19597         for(var i = 0; i < l; i++){
19598             var el = a[i];
19599             if ((typeof(el) == "object") && el.xtype && el.xns) {
19600                 el = Roo.factory(el, Roo.menu);
19601             }
19602             
19603             if(el.render){ // some kind of Item
19604                 item = this.addItem(el);
19605             }else if(typeof el == "string"){ // string
19606                 if(el == "separator" || el == "-"){
19607                     item = this.addSeparator();
19608                 }else{
19609                     item = this.addText(el);
19610                 }
19611             }else if(el.tagName || el.el){ // element
19612                 item = this.addElement(el);
19613             }else if(typeof el == "object"){ // must be menu item config?
19614                 item = this.addMenuItem(el);
19615             }
19616         }
19617         return item;
19618     },
19619
19620     /**
19621      * Returns this menu's underlying {@link Roo.Element} object
19622      * @return {Roo.Element} The element
19623      */
19624     getEl : function(){
19625         if(!this.el){
19626             this.render();
19627         }
19628         return this.el;
19629     },
19630
19631     /**
19632      * Adds a separator bar to the menu
19633      * @return {Roo.menu.Item} The menu item that was added
19634      */
19635     addSeparator : function(){
19636         return this.addItem(new Roo.menu.Separator());
19637     },
19638
19639     /**
19640      * Adds an {@link Roo.Element} object to the menu
19641      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
19642      * @return {Roo.menu.Item} The menu item that was added
19643      */
19644     addElement : function(el){
19645         return this.addItem(new Roo.menu.BaseItem(el));
19646     },
19647
19648     /**
19649      * Adds an existing object based on {@link Roo.menu.Item} to the menu
19650      * @param {Roo.menu.Item} item The menu item to add
19651      * @return {Roo.menu.Item} The menu item that was added
19652      */
19653     addItem : function(item){
19654         this.items.add(item);
19655         if(this.ul){
19656             var li = document.createElement("li");
19657             li.className = "x-menu-list-item";
19658             this.ul.dom.appendChild(li);
19659             item.render(li, this);
19660             this.delayAutoWidth();
19661         }
19662         return item;
19663     },
19664
19665     /**
19666      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
19667      * @param {Object} config A MenuItem config object
19668      * @return {Roo.menu.Item} The menu item that was added
19669      */
19670     addMenuItem : function(config){
19671         if(!(config instanceof Roo.menu.Item)){
19672             if(typeof config.checked == "boolean"){ // must be check menu item config?
19673                 config = new Roo.menu.CheckItem(config);
19674             }else{
19675                 config = new Roo.menu.Item(config);
19676             }
19677         }
19678         return this.addItem(config);
19679     },
19680
19681     /**
19682      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
19683      * @param {String} text The text to display in the menu item
19684      * @return {Roo.menu.Item} The menu item that was added
19685      */
19686     addText : function(text){
19687         return this.addItem(new Roo.menu.TextItem({ text : text }));
19688     },
19689
19690     /**
19691      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
19692      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
19693      * @param {Roo.menu.Item} item The menu item to add
19694      * @return {Roo.menu.Item} The menu item that was added
19695      */
19696     insert : function(index, item){
19697         this.items.insert(index, item);
19698         if(this.ul){
19699             var li = document.createElement("li");
19700             li.className = "x-menu-list-item";
19701             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
19702             item.render(li, this);
19703             this.delayAutoWidth();
19704         }
19705         return item;
19706     },
19707
19708     /**
19709      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
19710      * @param {Roo.menu.Item} item The menu item to remove
19711      */
19712     remove : function(item){
19713         this.items.removeKey(item.id);
19714         item.destroy();
19715     },
19716
19717     /**
19718      * Removes and destroys all items in the menu
19719      */
19720     removeAll : function(){
19721         var f;
19722         while(f = this.items.first()){
19723             this.remove(f);
19724         }
19725     }
19726 });
19727
19728 // MenuNav is a private utility class used internally by the Menu
19729 Roo.menu.MenuNav = function(menu){
19730     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
19731     this.scope = this.menu = menu;
19732 };
19733
19734 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
19735     doRelay : function(e, h){
19736         var k = e.getKey();
19737         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
19738             this.menu.tryActivate(0, 1);
19739             return false;
19740         }
19741         return h.call(this.scope || this, e, this.menu);
19742     },
19743
19744     up : function(e, m){
19745         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
19746             m.tryActivate(m.items.length-1, -1);
19747         }
19748     },
19749
19750     down : function(e, m){
19751         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
19752             m.tryActivate(0, 1);
19753         }
19754     },
19755
19756     right : function(e, m){
19757         if(m.activeItem){
19758             m.activeItem.expandMenu(true);
19759         }
19760     },
19761
19762     left : function(e, m){
19763         m.hide();
19764         if(m.parentMenu && m.parentMenu.activeItem){
19765             m.parentMenu.activeItem.activate();
19766         }
19767     },
19768
19769     enter : function(e, m){
19770         if(m.activeItem){
19771             e.stopPropagation();
19772             m.activeItem.onClick(e);
19773             m.fireEvent("click", this, m.activeItem);
19774             return true;
19775         }
19776     }
19777 });/*
19778  * Based on:
19779  * Ext JS Library 1.1.1
19780  * Copyright(c) 2006-2007, Ext JS, LLC.
19781  *
19782  * Originally Released Under LGPL - original licence link has changed is not relivant.
19783  *
19784  * Fork - LGPL
19785  * <script type="text/javascript">
19786  */
19787  
19788 /**
19789  * @class Roo.menu.MenuMgr
19790  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
19791  * @singleton
19792  */
19793 Roo.menu.MenuMgr = function(){
19794    var menus, active, groups = {}, attached = false, lastShow = new Date();
19795
19796    // private - called when first menu is created
19797    function init(){
19798        menus = {};
19799        active = new Roo.util.MixedCollection();
19800        Roo.get(document).addKeyListener(27, function(){
19801            if(active.length > 0){
19802                hideAll();
19803            }
19804        });
19805    }
19806
19807    // private
19808    function hideAll(){
19809        if(active && active.length > 0){
19810            var c = active.clone();
19811            c.each(function(m){
19812                m.hide();
19813            });
19814        }
19815    }
19816
19817    // private
19818    function onHide(m){
19819        active.remove(m);
19820        if(active.length < 1){
19821            Roo.get(document).un("mousedown", onMouseDown);
19822            attached = false;
19823        }
19824    }
19825
19826    // private
19827    function onShow(m){
19828        var last = active.last();
19829        lastShow = new Date();
19830        active.add(m);
19831        if(!attached){
19832            Roo.get(document).on("mousedown", onMouseDown);
19833            attached = true;
19834        }
19835        if(m.parentMenu){
19836           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
19837           m.parentMenu.activeChild = m;
19838        }else if(last && last.isVisible()){
19839           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
19840        }
19841    }
19842
19843    // private
19844    function onBeforeHide(m){
19845        if(m.activeChild){
19846            m.activeChild.hide();
19847        }
19848        if(m.autoHideTimer){
19849            clearTimeout(m.autoHideTimer);
19850            delete m.autoHideTimer;
19851        }
19852    }
19853
19854    // private
19855    function onBeforeShow(m){
19856        var pm = m.parentMenu;
19857        if(!pm && !m.allowOtherMenus){
19858            hideAll();
19859        }else if(pm && pm.activeChild && active != m){
19860            pm.activeChild.hide();
19861        }
19862    }
19863
19864    // private
19865    function onMouseDown(e){
19866        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
19867            hideAll();
19868        }
19869    }
19870
19871    // private
19872    function onBeforeCheck(mi, state){
19873        if(state){
19874            var g = groups[mi.group];
19875            for(var i = 0, l = g.length; i < l; i++){
19876                if(g[i] != mi){
19877                    g[i].setChecked(false);
19878                }
19879            }
19880        }
19881    }
19882
19883    return {
19884
19885        /**
19886         * Hides all menus that are currently visible
19887         */
19888        hideAll : function(){
19889             hideAll();  
19890        },
19891
19892        // private
19893        register : function(menu){
19894            if(!menus){
19895                init();
19896            }
19897            menus[menu.id] = menu;
19898            menu.on("beforehide", onBeforeHide);
19899            menu.on("hide", onHide);
19900            menu.on("beforeshow", onBeforeShow);
19901            menu.on("show", onShow);
19902            var g = menu.group;
19903            if(g && menu.events["checkchange"]){
19904                if(!groups[g]){
19905                    groups[g] = [];
19906                }
19907                groups[g].push(menu);
19908                menu.on("checkchange", onCheck);
19909            }
19910        },
19911
19912         /**
19913          * Returns a {@link Roo.menu.Menu} object
19914          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
19915          * be used to generate and return a new Menu instance.
19916          */
19917        get : function(menu){
19918            if(typeof menu == "string"){ // menu id
19919                return menus[menu];
19920            }else if(menu.events){  // menu instance
19921                return menu;
19922            }else if(typeof menu.length == 'number'){ // array of menu items?
19923                return new Roo.menu.Menu({items:menu});
19924            }else{ // otherwise, must be a config
19925                return new Roo.menu.Menu(menu);
19926            }
19927        },
19928
19929        // private
19930        unregister : function(menu){
19931            delete menus[menu.id];
19932            menu.un("beforehide", onBeforeHide);
19933            menu.un("hide", onHide);
19934            menu.un("beforeshow", onBeforeShow);
19935            menu.un("show", onShow);
19936            var g = menu.group;
19937            if(g && menu.events["checkchange"]){
19938                groups[g].remove(menu);
19939                menu.un("checkchange", onCheck);
19940            }
19941        },
19942
19943        // private
19944        registerCheckable : function(menuItem){
19945            var g = menuItem.group;
19946            if(g){
19947                if(!groups[g]){
19948                    groups[g] = [];
19949                }
19950                groups[g].push(menuItem);
19951                menuItem.on("beforecheckchange", onBeforeCheck);
19952            }
19953        },
19954
19955        // private
19956        unregisterCheckable : function(menuItem){
19957            var g = menuItem.group;
19958            if(g){
19959                groups[g].remove(menuItem);
19960                menuItem.un("beforecheckchange", onBeforeCheck);
19961            }
19962        }
19963    };
19964 }();/*
19965  * Based on:
19966  * Ext JS Library 1.1.1
19967  * Copyright(c) 2006-2007, Ext JS, LLC.
19968  *
19969  * Originally Released Under LGPL - original licence link has changed is not relivant.
19970  *
19971  * Fork - LGPL
19972  * <script type="text/javascript">
19973  */
19974  
19975
19976 /**
19977  * @class Roo.menu.BaseItem
19978  * @extends Roo.Component
19979  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
19980  * management and base configuration options shared by all menu components.
19981  * @constructor
19982  * Creates a new BaseItem
19983  * @param {Object} config Configuration options
19984  */
19985 Roo.menu.BaseItem = function(config){
19986     Roo.menu.BaseItem.superclass.constructor.call(this, config);
19987
19988     this.addEvents({
19989         /**
19990          * @event click
19991          * Fires when this item is clicked
19992          * @param {Roo.menu.BaseItem} this
19993          * @param {Roo.EventObject} e
19994          */
19995         click: true,
19996         /**
19997          * @event activate
19998          * Fires when this item is activated
19999          * @param {Roo.menu.BaseItem} this
20000          */
20001         activate : true,
20002         /**
20003          * @event deactivate
20004          * Fires when this item is deactivated
20005          * @param {Roo.menu.BaseItem} this
20006          */
20007         deactivate : true
20008     });
20009
20010     if(this.handler){
20011         this.on("click", this.handler, this.scope, true);
20012     }
20013 };
20014
20015 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
20016     /**
20017      * @cfg {Function} handler
20018      * A function that will handle the click event of this menu item (defaults to undefined)
20019      */
20020     /**
20021      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
20022      */
20023     canActivate : false,
20024     /**
20025      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
20026      */
20027     activeClass : "x-menu-item-active",
20028     /**
20029      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
20030      */
20031     hideOnClick : true,
20032     /**
20033      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
20034      */
20035     hideDelay : 100,
20036
20037     // private
20038     ctype: "Roo.menu.BaseItem",
20039
20040     // private
20041     actionMode : "container",
20042
20043     // private
20044     render : function(container, parentMenu){
20045         this.parentMenu = parentMenu;
20046         Roo.menu.BaseItem.superclass.render.call(this, container);
20047         this.container.menuItemId = this.id;
20048     },
20049
20050     // private
20051     onRender : function(container, position){
20052         this.el = Roo.get(this.el);
20053         container.dom.appendChild(this.el.dom);
20054     },
20055
20056     // private
20057     onClick : function(e){
20058         if(!this.disabled && this.fireEvent("click", this, e) !== false
20059                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
20060             this.handleClick(e);
20061         }else{
20062             e.stopEvent();
20063         }
20064     },
20065
20066     // private
20067     activate : function(){
20068         if(this.disabled){
20069             return false;
20070         }
20071         var li = this.container;
20072         li.addClass(this.activeClass);
20073         this.region = li.getRegion().adjust(2, 2, -2, -2);
20074         this.fireEvent("activate", this);
20075         return true;
20076     },
20077
20078     // private
20079     deactivate : function(){
20080         this.container.removeClass(this.activeClass);
20081         this.fireEvent("deactivate", this);
20082     },
20083
20084     // private
20085     shouldDeactivate : function(e){
20086         return !this.region || !this.region.contains(e.getPoint());
20087     },
20088
20089     // private
20090     handleClick : function(e){
20091         if(this.hideOnClick){
20092             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
20093         }
20094     },
20095
20096     // private
20097     expandMenu : function(autoActivate){
20098         // do nothing
20099     },
20100
20101     // private
20102     hideMenu : function(){
20103         // do nothing
20104     }
20105 });/*
20106  * Based on:
20107  * Ext JS Library 1.1.1
20108  * Copyright(c) 2006-2007, Ext JS, LLC.
20109  *
20110  * Originally Released Under LGPL - original licence link has changed is not relivant.
20111  *
20112  * Fork - LGPL
20113  * <script type="text/javascript">
20114  */
20115  
20116 /**
20117  * @class Roo.menu.Adapter
20118  * @extends Roo.menu.BaseItem
20119  * 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.
20120  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
20121  * @constructor
20122  * Creates a new Adapter
20123  * @param {Object} config Configuration options
20124  */
20125 Roo.menu.Adapter = function(component, config){
20126     Roo.menu.Adapter.superclass.constructor.call(this, config);
20127     this.component = component;
20128 };
20129 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
20130     // private
20131     canActivate : true,
20132
20133     // private
20134     onRender : function(container, position){
20135         this.component.render(container);
20136         this.el = this.component.getEl();
20137     },
20138
20139     // private
20140     activate : function(){
20141         if(this.disabled){
20142             return false;
20143         }
20144         this.component.focus();
20145         this.fireEvent("activate", this);
20146         return true;
20147     },
20148
20149     // private
20150     deactivate : function(){
20151         this.fireEvent("deactivate", this);
20152     },
20153
20154     // private
20155     disable : function(){
20156         this.component.disable();
20157         Roo.menu.Adapter.superclass.disable.call(this);
20158     },
20159
20160     // private
20161     enable : function(){
20162         this.component.enable();
20163         Roo.menu.Adapter.superclass.enable.call(this);
20164     }
20165 });/*
20166  * Based on:
20167  * Ext JS Library 1.1.1
20168  * Copyright(c) 2006-2007, Ext JS, LLC.
20169  *
20170  * Originally Released Under LGPL - original licence link has changed is not relivant.
20171  *
20172  * Fork - LGPL
20173  * <script type="text/javascript">
20174  */
20175
20176 /**
20177  * @class Roo.menu.TextItem
20178  * @extends Roo.menu.BaseItem
20179  * Adds a static text string to a menu, usually used as either a heading or group separator.
20180  * Note: old style constructor with text is still supported.
20181  * 
20182  * @constructor
20183  * Creates a new TextItem
20184  * @param {Object} cfg Configuration
20185  */
20186 Roo.menu.TextItem = function(cfg){
20187     if (typeof(cfg) == 'string') {
20188         this.text = cfg;
20189     } else {
20190         Roo.apply(this,cfg);
20191     }
20192     
20193     Roo.menu.TextItem.superclass.constructor.call(this);
20194 };
20195
20196 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
20197     /**
20198      * @cfg {Boolean} text Text to show on item.
20199      */
20200     text : '',
20201     
20202     /**
20203      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20204      */
20205     hideOnClick : false,
20206     /**
20207      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
20208      */
20209     itemCls : "x-menu-text",
20210
20211     // private
20212     onRender : function(){
20213         var s = document.createElement("span");
20214         s.className = this.itemCls;
20215         s.innerHTML = this.text;
20216         this.el = s;
20217         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
20218     }
20219 });/*
20220  * Based on:
20221  * Ext JS Library 1.1.1
20222  * Copyright(c) 2006-2007, Ext JS, LLC.
20223  *
20224  * Originally Released Under LGPL - original licence link has changed is not relivant.
20225  *
20226  * Fork - LGPL
20227  * <script type="text/javascript">
20228  */
20229
20230 /**
20231  * @class Roo.menu.Separator
20232  * @extends Roo.menu.BaseItem
20233  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
20234  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
20235  * @constructor
20236  * @param {Object} config Configuration options
20237  */
20238 Roo.menu.Separator = function(config){
20239     Roo.menu.Separator.superclass.constructor.call(this, config);
20240 };
20241
20242 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
20243     /**
20244      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
20245      */
20246     itemCls : "x-menu-sep",
20247     /**
20248      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20249      */
20250     hideOnClick : false,
20251
20252     // private
20253     onRender : function(li){
20254         var s = document.createElement("span");
20255         s.className = this.itemCls;
20256         s.innerHTML = "&#160;";
20257         this.el = s;
20258         li.addClass("x-menu-sep-li");
20259         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
20260     }
20261 });/*
20262  * Based on:
20263  * Ext JS Library 1.1.1
20264  * Copyright(c) 2006-2007, Ext JS, LLC.
20265  *
20266  * Originally Released Under LGPL - original licence link has changed is not relivant.
20267  *
20268  * Fork - LGPL
20269  * <script type="text/javascript">
20270  */
20271 /**
20272  * @class Roo.menu.Item
20273  * @extends Roo.menu.BaseItem
20274  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
20275  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
20276  * activation and click handling.
20277  * @constructor
20278  * Creates a new Item
20279  * @param {Object} config Configuration options
20280  */
20281 Roo.menu.Item = function(config){
20282     Roo.menu.Item.superclass.constructor.call(this, config);
20283     if(this.menu){
20284         this.menu = Roo.menu.MenuMgr.get(this.menu);
20285     }
20286 };
20287 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
20288     
20289     /**
20290      * @cfg {String} text
20291      * The text to show on the menu item.
20292      */
20293     text: '',
20294      /**
20295      * @cfg {String} HTML to render in menu
20296      * The text to show on the menu item (HTML version).
20297      */
20298     html: '',
20299     /**
20300      * @cfg {String} icon
20301      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
20302      */
20303     icon: undefined,
20304     /**
20305      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
20306      */
20307     itemCls : "x-menu-item",
20308     /**
20309      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
20310      */
20311     canActivate : true,
20312     /**
20313      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
20314      */
20315     showDelay: 200,
20316     // doc'd in BaseItem
20317     hideDelay: 200,
20318
20319     // private
20320     ctype: "Roo.menu.Item",
20321     
20322     // private
20323     onRender : function(container, position){
20324         var el = document.createElement("a");
20325         el.hideFocus = true;
20326         el.unselectable = "on";
20327         el.href = this.href || "#";
20328         if(this.hrefTarget){
20329             el.target = this.hrefTarget;
20330         }
20331         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
20332         
20333         var html = this.html.length ? this.html  : String.format('{0}',this.text);
20334         
20335         el.innerHTML = String.format(
20336                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
20337                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
20338         this.el = el;
20339         Roo.menu.Item.superclass.onRender.call(this, container, position);
20340     },
20341
20342     /**
20343      * Sets the text to display in this menu item
20344      * @param {String} text The text to display
20345      * @param {Boolean} isHTML true to indicate text is pure html.
20346      */
20347     setText : function(text, isHTML){
20348         if (isHTML) {
20349             this.html = text;
20350         } else {
20351             this.text = text;
20352             this.html = '';
20353         }
20354         if(this.rendered){
20355             var html = this.html.length ? this.html  : String.format('{0}',this.text);
20356      
20357             this.el.update(String.format(
20358                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
20359                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
20360             this.parentMenu.autoWidth();
20361         }
20362     },
20363
20364     // private
20365     handleClick : function(e){
20366         if(!this.href){ // if no link defined, stop the event automatically
20367             e.stopEvent();
20368         }
20369         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
20370     },
20371
20372     // private
20373     activate : function(autoExpand){
20374         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
20375             this.focus();
20376             if(autoExpand){
20377                 this.expandMenu();
20378             }
20379         }
20380         return true;
20381     },
20382
20383     // private
20384     shouldDeactivate : function(e){
20385         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
20386             if(this.menu && this.menu.isVisible()){
20387                 return !this.menu.getEl().getRegion().contains(e.getPoint());
20388             }
20389             return true;
20390         }
20391         return false;
20392     },
20393
20394     // private
20395     deactivate : function(){
20396         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
20397         this.hideMenu();
20398     },
20399
20400     // private
20401     expandMenu : function(autoActivate){
20402         if(!this.disabled && this.menu){
20403             clearTimeout(this.hideTimer);
20404             delete this.hideTimer;
20405             if(!this.menu.isVisible() && !this.showTimer){
20406                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
20407             }else if (this.menu.isVisible() && autoActivate){
20408                 this.menu.tryActivate(0, 1);
20409             }
20410         }
20411     },
20412
20413     // private
20414     deferExpand : function(autoActivate){
20415         delete this.showTimer;
20416         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
20417         if(autoActivate){
20418             this.menu.tryActivate(0, 1);
20419         }
20420     },
20421
20422     // private
20423     hideMenu : function(){
20424         clearTimeout(this.showTimer);
20425         delete this.showTimer;
20426         if(!this.hideTimer && this.menu && this.menu.isVisible()){
20427             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
20428         }
20429     },
20430
20431     // private
20432     deferHide : function(){
20433         delete this.hideTimer;
20434         this.menu.hide();
20435     }
20436 });/*
20437  * Based on:
20438  * Ext JS Library 1.1.1
20439  * Copyright(c) 2006-2007, Ext JS, LLC.
20440  *
20441  * Originally Released Under LGPL - original licence link has changed is not relivant.
20442  *
20443  * Fork - LGPL
20444  * <script type="text/javascript">
20445  */
20446  
20447 /**
20448  * @class Roo.menu.CheckItem
20449  * @extends Roo.menu.Item
20450  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
20451  * @constructor
20452  * Creates a new CheckItem
20453  * @param {Object} config Configuration options
20454  */
20455 Roo.menu.CheckItem = function(config){
20456     Roo.menu.CheckItem.superclass.constructor.call(this, config);
20457     this.addEvents({
20458         /**
20459          * @event beforecheckchange
20460          * Fires before the checked value is set, providing an opportunity to cancel if needed
20461          * @param {Roo.menu.CheckItem} this
20462          * @param {Boolean} checked The new checked value that will be set
20463          */
20464         "beforecheckchange" : true,
20465         /**
20466          * @event checkchange
20467          * Fires after the checked value has been set
20468          * @param {Roo.menu.CheckItem} this
20469          * @param {Boolean} checked The checked value that was set
20470          */
20471         "checkchange" : true
20472     });
20473     if(this.checkHandler){
20474         this.on('checkchange', this.checkHandler, this.scope);
20475     }
20476 };
20477 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
20478     /**
20479      * @cfg {String} group
20480      * All check items with the same group name will automatically be grouped into a single-select
20481      * radio button group (defaults to '')
20482      */
20483     /**
20484      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
20485      */
20486     itemCls : "x-menu-item x-menu-check-item",
20487     /**
20488      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
20489      */
20490     groupClass : "x-menu-group-item",
20491
20492     /**
20493      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
20494      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
20495      * initialized with checked = true will be rendered as checked.
20496      */
20497     checked: false,
20498
20499     // private
20500     ctype: "Roo.menu.CheckItem",
20501
20502     // private
20503     onRender : function(c){
20504         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
20505         if(this.group){
20506             this.el.addClass(this.groupClass);
20507         }
20508         Roo.menu.MenuMgr.registerCheckable(this);
20509         if(this.checked){
20510             this.checked = false;
20511             this.setChecked(true, true);
20512         }
20513     },
20514
20515     // private
20516     destroy : function(){
20517         if(this.rendered){
20518             Roo.menu.MenuMgr.unregisterCheckable(this);
20519         }
20520         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
20521     },
20522
20523     /**
20524      * Set the checked state of this item
20525      * @param {Boolean} checked The new checked value
20526      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
20527      */
20528     setChecked : function(state, suppressEvent){
20529         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
20530             if(this.container){
20531                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
20532             }
20533             this.checked = state;
20534             if(suppressEvent !== true){
20535                 this.fireEvent("checkchange", this, state);
20536             }
20537         }
20538     },
20539
20540     // private
20541     handleClick : function(e){
20542        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
20543            this.setChecked(!this.checked);
20544        }
20545        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
20546     }
20547 });/*
20548  * Based on:
20549  * Ext JS Library 1.1.1
20550  * Copyright(c) 2006-2007, Ext JS, LLC.
20551  *
20552  * Originally Released Under LGPL - original licence link has changed is not relivant.
20553  *
20554  * Fork - LGPL
20555  * <script type="text/javascript">
20556  */
20557  
20558 /**
20559  * @class Roo.menu.DateItem
20560  * @extends Roo.menu.Adapter
20561  * A menu item that wraps the {@link Roo.DatPicker} component.
20562  * @constructor
20563  * Creates a new DateItem
20564  * @param {Object} config Configuration options
20565  */
20566 Roo.menu.DateItem = function(config){
20567     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
20568     /** The Roo.DatePicker object @type Roo.DatePicker */
20569     this.picker = this.component;
20570     this.addEvents({select: true});
20571     
20572     this.picker.on("render", function(picker){
20573         picker.getEl().swallowEvent("click");
20574         picker.container.addClass("x-menu-date-item");
20575     });
20576
20577     this.picker.on("select", this.onSelect, this);
20578 };
20579
20580 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
20581     // private
20582     onSelect : function(picker, date){
20583         this.fireEvent("select", this, date, picker);
20584         Roo.menu.DateItem.superclass.handleClick.call(this);
20585     }
20586 });/*
20587  * Based on:
20588  * Ext JS Library 1.1.1
20589  * Copyright(c) 2006-2007, Ext JS, LLC.
20590  *
20591  * Originally Released Under LGPL - original licence link has changed is not relivant.
20592  *
20593  * Fork - LGPL
20594  * <script type="text/javascript">
20595  */
20596  
20597 /**
20598  * @class Roo.menu.ColorItem
20599  * @extends Roo.menu.Adapter
20600  * A menu item that wraps the {@link Roo.ColorPalette} component.
20601  * @constructor
20602  * Creates a new ColorItem
20603  * @param {Object} config Configuration options
20604  */
20605 Roo.menu.ColorItem = function(config){
20606     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
20607     /** The Roo.ColorPalette object @type Roo.ColorPalette */
20608     this.palette = this.component;
20609     this.relayEvents(this.palette, ["select"]);
20610     if(this.selectHandler){
20611         this.on('select', this.selectHandler, this.scope);
20612     }
20613 };
20614 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
20615  * Based on:
20616  * Ext JS Library 1.1.1
20617  * Copyright(c) 2006-2007, Ext JS, LLC.
20618  *
20619  * Originally Released Under LGPL - original licence link has changed is not relivant.
20620  *
20621  * Fork - LGPL
20622  * <script type="text/javascript">
20623  */
20624  
20625
20626 /**
20627  * @class Roo.menu.DateMenu
20628  * @extends Roo.menu.Menu
20629  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
20630  * @constructor
20631  * Creates a new DateMenu
20632  * @param {Object} config Configuration options
20633  */
20634 Roo.menu.DateMenu = function(config){
20635     Roo.menu.DateMenu.superclass.constructor.call(this, config);
20636     this.plain = true;
20637     var di = new Roo.menu.DateItem(config);
20638     this.add(di);
20639     /**
20640      * The {@link Roo.DatePicker} instance for this DateMenu
20641      * @type DatePicker
20642      */
20643     this.picker = di.picker;
20644     /**
20645      * @event select
20646      * @param {DatePicker} picker
20647      * @param {Date} date
20648      */
20649     this.relayEvents(di, ["select"]);
20650
20651     this.on('beforeshow', function(){
20652         if(this.picker){
20653             this.picker.hideMonthPicker(true);
20654         }
20655     }, this);
20656 };
20657 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
20658     cls:'x-date-menu'
20659 });/*
20660  * Based on:
20661  * Ext JS Library 1.1.1
20662  * Copyright(c) 2006-2007, Ext JS, LLC.
20663  *
20664  * Originally Released Under LGPL - original licence link has changed is not relivant.
20665  *
20666  * Fork - LGPL
20667  * <script type="text/javascript">
20668  */
20669  
20670
20671 /**
20672  * @class Roo.menu.ColorMenu
20673  * @extends Roo.menu.Menu
20674  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
20675  * @constructor
20676  * Creates a new ColorMenu
20677  * @param {Object} config Configuration options
20678  */
20679 Roo.menu.ColorMenu = function(config){
20680     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
20681     this.plain = true;
20682     var ci = new Roo.menu.ColorItem(config);
20683     this.add(ci);
20684     /**
20685      * The {@link Roo.ColorPalette} instance for this ColorMenu
20686      * @type ColorPalette
20687      */
20688     this.palette = ci.palette;
20689     /**
20690      * @event select
20691      * @param {ColorPalette} palette
20692      * @param {String} color
20693      */
20694     this.relayEvents(ci, ["select"]);
20695 };
20696 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
20697  * Based on:
20698  * Ext JS Library 1.1.1
20699  * Copyright(c) 2006-2007, Ext JS, LLC.
20700  *
20701  * Originally Released Under LGPL - original licence link has changed is not relivant.
20702  *
20703  * Fork - LGPL
20704  * <script type="text/javascript">
20705  */
20706  
20707 /**
20708  * @class Roo.form.Field
20709  * @extends Roo.BoxComponent
20710  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
20711  * @constructor
20712  * Creates a new Field
20713  * @param {Object} config Configuration options
20714  */
20715 Roo.form.Field = function(config){
20716     Roo.form.Field.superclass.constructor.call(this, config);
20717 };
20718
20719 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
20720     /**
20721      * @cfg {String} fieldLabel Label to use when rendering a form.
20722      */
20723        /**
20724      * @cfg {String} qtip Mouse over tip
20725      */
20726      
20727     /**
20728      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
20729      */
20730     invalidClass : "x-form-invalid",
20731     /**
20732      * @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")
20733      */
20734     invalidText : "The value in this field is invalid",
20735     /**
20736      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
20737      */
20738     focusClass : "x-form-focus",
20739     /**
20740      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
20741       automatic validation (defaults to "keyup").
20742      */
20743     validationEvent : "keyup",
20744     /**
20745      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
20746      */
20747     validateOnBlur : true,
20748     /**
20749      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
20750      */
20751     validationDelay : 250,
20752     /**
20753      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20754      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
20755      */
20756     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
20757     /**
20758      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
20759      */
20760     fieldClass : "x-form-field",
20761     /**
20762      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
20763      *<pre>
20764 Value         Description
20765 -----------   ----------------------------------------------------------------------
20766 qtip          Display a quick tip when the user hovers over the field
20767 title         Display a default browser title attribute popup
20768 under         Add a block div beneath the field containing the error text
20769 side          Add an error icon to the right of the field with a popup on hover
20770 [element id]  Add the error text directly to the innerHTML of the specified element
20771 </pre>
20772      */
20773     msgTarget : 'qtip',
20774     /**
20775      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
20776      */
20777     msgFx : 'normal',
20778
20779     /**
20780      * @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.
20781      */
20782     readOnly : false,
20783
20784     /**
20785      * @cfg {Boolean} disabled True to disable the field (defaults to false).
20786      */
20787     disabled : false,
20788
20789     /**
20790      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
20791      */
20792     inputType : undefined,
20793     
20794     /**
20795      * @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).
20796          */
20797         tabIndex : undefined,
20798         
20799     // private
20800     isFormField : true,
20801
20802     // private
20803     hasFocus : false,
20804     /**
20805      * @property {Roo.Element} fieldEl
20806      * Element Containing the rendered Field (with label etc.)
20807      */
20808     /**
20809      * @cfg {Mixed} value A value to initialize this field with.
20810      */
20811     value : undefined,
20812
20813     /**
20814      * @cfg {String} name The field's HTML name attribute.
20815      */
20816     /**
20817      * @cfg {String} cls A CSS class to apply to the field's underlying element.
20818      */
20819
20820         // private ??
20821         initComponent : function(){
20822         Roo.form.Field.superclass.initComponent.call(this);
20823         this.addEvents({
20824             /**
20825              * @event focus
20826              * Fires when this field receives input focus.
20827              * @param {Roo.form.Field} this
20828              */
20829             focus : true,
20830             /**
20831              * @event blur
20832              * Fires when this field loses input focus.
20833              * @param {Roo.form.Field} this
20834              */
20835             blur : true,
20836             /**
20837              * @event specialkey
20838              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
20839              * {@link Roo.EventObject#getKey} to determine which key was pressed.
20840              * @param {Roo.form.Field} this
20841              * @param {Roo.EventObject} e The event object
20842              */
20843             specialkey : true,
20844             /**
20845              * @event change
20846              * Fires just before the field blurs if the field value has changed.
20847              * @param {Roo.form.Field} this
20848              * @param {Mixed} newValue The new value
20849              * @param {Mixed} oldValue The original value
20850              */
20851             change : true,
20852             /**
20853              * @event invalid
20854              * Fires after the field has been marked as invalid.
20855              * @param {Roo.form.Field} this
20856              * @param {String} msg The validation message
20857              */
20858             invalid : true,
20859             /**
20860              * @event valid
20861              * Fires after the field has been validated with no errors.
20862              * @param {Roo.form.Field} this
20863              */
20864             valid : true,
20865              /**
20866              * @event keyup
20867              * Fires after the key up
20868              * @param {Roo.form.Field} this
20869              * @param {Roo.EventObject}  e The event Object
20870              */
20871             keyup : true
20872         });
20873     },
20874
20875     /**
20876      * Returns the name attribute of the field if available
20877      * @return {String} name The field name
20878      */
20879     getName: function(){
20880          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
20881     },
20882
20883     // private
20884     onRender : function(ct, position){
20885         Roo.form.Field.superclass.onRender.call(this, ct, position);
20886         if(!this.el){
20887             var cfg = this.getAutoCreate();
20888             if(!cfg.name){
20889                 cfg.name = this.name || this.id;
20890             }
20891             if(this.inputType){
20892                 cfg.type = this.inputType;
20893             }
20894             this.el = ct.createChild(cfg, position);
20895         }
20896         var type = this.el.dom.type;
20897         if(type){
20898             if(type == 'password'){
20899                 type = 'text';
20900             }
20901             this.el.addClass('x-form-'+type);
20902         }
20903         if(this.readOnly){
20904             this.el.dom.readOnly = true;
20905         }
20906         if(this.tabIndex !== undefined){
20907             this.el.dom.setAttribute('tabIndex', this.tabIndex);
20908         }
20909
20910         this.el.addClass([this.fieldClass, this.cls]);
20911         this.initValue();
20912     },
20913
20914     /**
20915      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
20916      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
20917      * @return {Roo.form.Field} this
20918      */
20919     applyTo : function(target){
20920         this.allowDomMove = false;
20921         this.el = Roo.get(target);
20922         this.render(this.el.dom.parentNode);
20923         return this;
20924     },
20925
20926     // private
20927     initValue : function(){
20928         if(this.value !== undefined){
20929             this.setValue(this.value);
20930         }else if(this.el.dom.value.length > 0){
20931             this.setValue(this.el.dom.value);
20932         }
20933     },
20934
20935     /**
20936      * Returns true if this field has been changed since it was originally loaded and is not disabled.
20937      */
20938     isDirty : function() {
20939         if(this.disabled) {
20940             return false;
20941         }
20942         return String(this.getValue()) !== String(this.originalValue);
20943     },
20944
20945     // private
20946     afterRender : function(){
20947         Roo.form.Field.superclass.afterRender.call(this);
20948         this.initEvents();
20949     },
20950
20951     // private
20952     fireKey : function(e){
20953         //Roo.log('field ' + e.getKey());
20954         if(e.isNavKeyPress()){
20955             this.fireEvent("specialkey", this, e);
20956         }
20957     },
20958
20959     /**
20960      * Resets the current field value to the originally loaded value and clears any validation messages
20961      */
20962     reset : function(){
20963         this.setValue(this.originalValue);
20964         this.clearInvalid();
20965     },
20966
20967     // private
20968     initEvents : function(){
20969         // safari killled keypress - so keydown is now used..
20970         this.el.on("keydown" , this.fireKey,  this);
20971         this.el.on("focus", this.onFocus,  this);
20972         this.el.on("blur", this.onBlur,  this);
20973         this.el.relayEvent('keyup', this);
20974
20975         // reference to original value for reset
20976         this.originalValue = this.getValue();
20977     },
20978
20979     // private
20980     onFocus : function(){
20981         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
20982             this.el.addClass(this.focusClass);
20983         }
20984         if(!this.hasFocus){
20985             this.hasFocus = true;
20986             this.startValue = this.getValue();
20987             this.fireEvent("focus", this);
20988         }
20989     },
20990
20991     beforeBlur : Roo.emptyFn,
20992
20993     // private
20994     onBlur : function(){
20995         this.beforeBlur();
20996         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
20997             this.el.removeClass(this.focusClass);
20998         }
20999         this.hasFocus = false;
21000         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
21001             this.validate();
21002         }
21003         var v = this.getValue();
21004         if(String(v) !== String(this.startValue)){
21005             this.fireEvent('change', this, v, this.startValue);
21006         }
21007         this.fireEvent("blur", this);
21008     },
21009
21010     /**
21011      * Returns whether or not the field value is currently valid
21012      * @param {Boolean} preventMark True to disable marking the field invalid
21013      * @return {Boolean} True if the value is valid, else false
21014      */
21015     isValid : function(preventMark){
21016         if(this.disabled){
21017             return true;
21018         }
21019         var restore = this.preventMark;
21020         this.preventMark = preventMark === true;
21021         var v = this.validateValue(this.processValue(this.getRawValue()));
21022         this.preventMark = restore;
21023         return v;
21024     },
21025
21026     /**
21027      * Validates the field value
21028      * @return {Boolean} True if the value is valid, else false
21029      */
21030     validate : function(){
21031         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
21032             this.clearInvalid();
21033             return true;
21034         }
21035         return false;
21036     },
21037
21038     processValue : function(value){
21039         return value;
21040     },
21041
21042     // private
21043     // Subclasses should provide the validation implementation by overriding this
21044     validateValue : function(value){
21045         return true;
21046     },
21047
21048     /**
21049      * Mark this field as invalid
21050      * @param {String} msg The validation message
21051      */
21052     markInvalid : function(msg){
21053         if(!this.rendered || this.preventMark){ // not rendered
21054             return;
21055         }
21056         this.el.addClass(this.invalidClass);
21057         msg = msg || this.invalidText;
21058         switch(this.msgTarget){
21059             case 'qtip':
21060                 this.el.dom.qtip = msg;
21061                 this.el.dom.qclass = 'x-form-invalid-tip';
21062                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
21063                     Roo.QuickTips.enable();
21064                 }
21065                 break;
21066             case 'title':
21067                 this.el.dom.title = msg;
21068                 break;
21069             case 'under':
21070                 if(!this.errorEl){
21071                     var elp = this.el.findParent('.x-form-element', 5, true);
21072                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
21073                     this.errorEl.setWidth(elp.getWidth(true)-20);
21074                 }
21075                 this.errorEl.update(msg);
21076                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
21077                 break;
21078             case 'side':
21079                 if(!this.errorIcon){
21080                     var elp = this.el.findParent('.x-form-element', 5, true);
21081                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
21082                 }
21083                 this.alignErrorIcon();
21084                 this.errorIcon.dom.qtip = msg;
21085                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
21086                 this.errorIcon.show();
21087                 this.on('resize', this.alignErrorIcon, this);
21088                 break;
21089             default:
21090                 var t = Roo.getDom(this.msgTarget);
21091                 t.innerHTML = msg;
21092                 t.style.display = this.msgDisplay;
21093                 break;
21094         }
21095         this.fireEvent('invalid', this, msg);
21096     },
21097
21098     // private
21099     alignErrorIcon : function(){
21100         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
21101     },
21102
21103     /**
21104      * Clear any invalid styles/messages for this field
21105      */
21106     clearInvalid : function(){
21107         if(!this.rendered || this.preventMark){ // not rendered
21108             return;
21109         }
21110         this.el.removeClass(this.invalidClass);
21111         switch(this.msgTarget){
21112             case 'qtip':
21113                 this.el.dom.qtip = '';
21114                 break;
21115             case 'title':
21116                 this.el.dom.title = '';
21117                 break;
21118             case 'under':
21119                 if(this.errorEl){
21120                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
21121                 }
21122                 break;
21123             case 'side':
21124                 if(this.errorIcon){
21125                     this.errorIcon.dom.qtip = '';
21126                     this.errorIcon.hide();
21127                     this.un('resize', this.alignErrorIcon, this);
21128                 }
21129                 break;
21130             default:
21131                 var t = Roo.getDom(this.msgTarget);
21132                 t.innerHTML = '';
21133                 t.style.display = 'none';
21134                 break;
21135         }
21136         this.fireEvent('valid', this);
21137     },
21138
21139     /**
21140      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
21141      * @return {Mixed} value The field value
21142      */
21143     getRawValue : function(){
21144         var v = this.el.getValue();
21145         if(v === this.emptyText){
21146             v = '';
21147         }
21148         return v;
21149     },
21150
21151     /**
21152      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
21153      * @return {Mixed} value The field value
21154      */
21155     getValue : function(){
21156         var v = this.el.getValue();
21157         if(v === this.emptyText || v === undefined){
21158             v = '';
21159         }
21160         return v;
21161     },
21162
21163     /**
21164      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
21165      * @param {Mixed} value The value to set
21166      */
21167     setRawValue : function(v){
21168         return this.el.dom.value = (v === null || v === undefined ? '' : v);
21169     },
21170
21171     /**
21172      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
21173      * @param {Mixed} value The value to set
21174      */
21175     setValue : function(v){
21176         this.value = v;
21177         if(this.rendered){
21178             this.el.dom.value = (v === null || v === undefined ? '' : v);
21179             this.validate();
21180         }
21181     },
21182
21183     adjustSize : function(w, h){
21184         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
21185         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
21186         return s;
21187     },
21188
21189     adjustWidth : function(tag, w){
21190         tag = tag.toLowerCase();
21191         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
21192             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
21193                 if(tag == 'input'){
21194                     return w + 2;
21195                 }
21196                 if(tag = 'textarea'){
21197                     return w-2;
21198                 }
21199             }else if(Roo.isOpera){
21200                 if(tag == 'input'){
21201                     return w + 2;
21202                 }
21203                 if(tag = 'textarea'){
21204                     return w-2;
21205                 }
21206             }
21207         }
21208         return w;
21209     }
21210 });
21211
21212
21213 // anything other than normal should be considered experimental
21214 Roo.form.Field.msgFx = {
21215     normal : {
21216         show: function(msgEl, f){
21217             msgEl.setDisplayed('block');
21218         },
21219
21220         hide : function(msgEl, f){
21221             msgEl.setDisplayed(false).update('');
21222         }
21223     },
21224
21225     slide : {
21226         show: function(msgEl, f){
21227             msgEl.slideIn('t', {stopFx:true});
21228         },
21229
21230         hide : function(msgEl, f){
21231             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
21232         }
21233     },
21234
21235     slideRight : {
21236         show: function(msgEl, f){
21237             msgEl.fixDisplay();
21238             msgEl.alignTo(f.el, 'tl-tr');
21239             msgEl.slideIn('l', {stopFx:true});
21240         },
21241
21242         hide : function(msgEl, f){
21243             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
21244         }
21245     }
21246 };/*
21247  * Based on:
21248  * Ext JS Library 1.1.1
21249  * Copyright(c) 2006-2007, Ext JS, LLC.
21250  *
21251  * Originally Released Under LGPL - original licence link has changed is not relivant.
21252  *
21253  * Fork - LGPL
21254  * <script type="text/javascript">
21255  */
21256  
21257
21258 /**
21259  * @class Roo.form.TextField
21260  * @extends Roo.form.Field
21261  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
21262  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
21263  * @constructor
21264  * Creates a new TextField
21265  * @param {Object} config Configuration options
21266  */
21267 Roo.form.TextField = function(config){
21268     Roo.form.TextField.superclass.constructor.call(this, config);
21269     this.addEvents({
21270         /**
21271          * @event autosize
21272          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
21273          * according to the default logic, but this event provides a hook for the developer to apply additional
21274          * logic at runtime to resize the field if needed.
21275              * @param {Roo.form.Field} this This text field
21276              * @param {Number} width The new field width
21277              */
21278         autosize : true
21279     });
21280 };
21281
21282 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
21283     /**
21284      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
21285      */
21286     grow : false,
21287     /**
21288      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
21289      */
21290     growMin : 30,
21291     /**
21292      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
21293      */
21294     growMax : 800,
21295     /**
21296      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
21297      */
21298     vtype : null,
21299     /**
21300      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
21301      */
21302     maskRe : null,
21303     /**
21304      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
21305      */
21306     disableKeyFilter : false,
21307     /**
21308      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
21309      */
21310     allowBlank : true,
21311     /**
21312      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
21313      */
21314     minLength : 0,
21315     /**
21316      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
21317      */
21318     maxLength : Number.MAX_VALUE,
21319     /**
21320      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
21321      */
21322     minLengthText : "The minimum length for this field is {0}",
21323     /**
21324      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
21325      */
21326     maxLengthText : "The maximum length for this field is {0}",
21327     /**
21328      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
21329      */
21330     selectOnFocus : false,
21331     /**
21332      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
21333      */
21334     blankText : "This field is required",
21335     /**
21336      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
21337      * If available, this function will be called only after the basic validators all return true, and will be passed the
21338      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
21339      */
21340     validator : null,
21341     /**
21342      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
21343      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
21344      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
21345      */
21346     regex : null,
21347     /**
21348      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
21349      */
21350     regexText : "",
21351     /**
21352      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
21353      */
21354     emptyText : null,
21355     /**
21356      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
21357      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
21358      */
21359     emptyClass : 'x-form-empty-field',
21360
21361     // private
21362     initEvents : function(){
21363         Roo.form.TextField.superclass.initEvents.call(this);
21364         if(this.validationEvent == 'keyup'){
21365             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
21366             this.el.on('keyup', this.filterValidation, this);
21367         }
21368         else if(this.validationEvent !== false){
21369             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
21370         }
21371         if(this.selectOnFocus || this.emptyText){
21372             this.on("focus", this.preFocus, this);
21373             if(this.emptyText){
21374                 this.on('blur', this.postBlur, this);
21375                 this.applyEmptyText();
21376             }
21377         }
21378         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
21379             this.el.on("keypress", this.filterKeys, this);
21380         }
21381         if(this.grow){
21382             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
21383             this.el.on("click", this.autoSize,  this);
21384         }
21385     },
21386
21387     processValue : function(value){
21388         if(this.stripCharsRe){
21389             var newValue = value.replace(this.stripCharsRe, '');
21390             if(newValue !== value){
21391                 this.setRawValue(newValue);
21392                 return newValue;
21393             }
21394         }
21395         return value;
21396     },
21397
21398     filterValidation : function(e){
21399         if(!e.isNavKeyPress()){
21400             this.validationTask.delay(this.validationDelay);
21401         }
21402     },
21403
21404     // private
21405     onKeyUp : function(e){
21406         if(!e.isNavKeyPress()){
21407             this.autoSize();
21408         }
21409     },
21410
21411     /**
21412      * Resets the current field value to the originally-loaded value and clears any validation messages.
21413      * Also adds emptyText and emptyClass if the original value was blank.
21414      */
21415     reset : function(){
21416         Roo.form.TextField.superclass.reset.call(this);
21417         this.applyEmptyText();
21418     },
21419
21420     applyEmptyText : function(){
21421         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
21422             this.setRawValue(this.emptyText);
21423             this.el.addClass(this.emptyClass);
21424         }
21425     },
21426
21427     // private
21428     preFocus : function(){
21429         if(this.emptyText){
21430             if(this.el.dom.value == this.emptyText){
21431                 this.setRawValue('');
21432             }
21433             this.el.removeClass(this.emptyClass);
21434         }
21435         if(this.selectOnFocus){
21436             this.el.dom.select();
21437         }
21438     },
21439
21440     // private
21441     postBlur : function(){
21442         this.applyEmptyText();
21443     },
21444
21445     // private
21446     filterKeys : function(e){
21447         var k = e.getKey();
21448         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
21449             return;
21450         }
21451         var c = e.getCharCode(), cc = String.fromCharCode(c);
21452         if(Roo.isIE && (e.isSpecialKey() || !cc)){
21453             return;
21454         }
21455         if(!this.maskRe.test(cc)){
21456             e.stopEvent();
21457         }
21458     },
21459
21460     setValue : function(v){
21461         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
21462             this.el.removeClass(this.emptyClass);
21463         }
21464         Roo.form.TextField.superclass.setValue.apply(this, arguments);
21465         this.applyEmptyText();
21466         this.autoSize();
21467     },
21468
21469     /**
21470      * Validates a value according to the field's validation rules and marks the field as invalid
21471      * if the validation fails
21472      * @param {Mixed} value The value to validate
21473      * @return {Boolean} True if the value is valid, else false
21474      */
21475     validateValue : function(value){
21476         if(value.length < 1 || value === this.emptyText){ // if it's blank
21477              if(this.allowBlank){
21478                 this.clearInvalid();
21479                 return true;
21480              }else{
21481                 this.markInvalid(this.blankText);
21482                 return false;
21483              }
21484         }
21485         if(value.length < this.minLength){
21486             this.markInvalid(String.format(this.minLengthText, this.minLength));
21487             return false;
21488         }
21489         if(value.length > this.maxLength){
21490             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
21491             return false;
21492         }
21493         if(this.vtype){
21494             var vt = Roo.form.VTypes;
21495             if(!vt[this.vtype](value, this)){
21496                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
21497                 return false;
21498             }
21499         }
21500         if(typeof this.validator == "function"){
21501             var msg = this.validator(value);
21502             if(msg !== true){
21503                 this.markInvalid(msg);
21504                 return false;
21505             }
21506         }
21507         if(this.regex && !this.regex.test(value)){
21508             this.markInvalid(this.regexText);
21509             return false;
21510         }
21511         return true;
21512     },
21513
21514     /**
21515      * Selects text in this field
21516      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
21517      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
21518      */
21519     selectText : function(start, end){
21520         var v = this.getRawValue();
21521         if(v.length > 0){
21522             start = start === undefined ? 0 : start;
21523             end = end === undefined ? v.length : end;
21524             var d = this.el.dom;
21525             if(d.setSelectionRange){
21526                 d.setSelectionRange(start, end);
21527             }else if(d.createTextRange){
21528                 var range = d.createTextRange();
21529                 range.moveStart("character", start);
21530                 range.moveEnd("character", v.length-end);
21531                 range.select();
21532             }
21533         }
21534     },
21535
21536     /**
21537      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
21538      * This only takes effect if grow = true, and fires the autosize event.
21539      */
21540     autoSize : function(){
21541         if(!this.grow || !this.rendered){
21542             return;
21543         }
21544         if(!this.metrics){
21545             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
21546         }
21547         var el = this.el;
21548         var v = el.dom.value;
21549         var d = document.createElement('div');
21550         d.appendChild(document.createTextNode(v));
21551         v = d.innerHTML;
21552         d = null;
21553         v += "&#160;";
21554         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
21555         this.el.setWidth(w);
21556         this.fireEvent("autosize", this, w);
21557     }
21558 });/*
21559  * Based on:
21560  * Ext JS Library 1.1.1
21561  * Copyright(c) 2006-2007, Ext JS, LLC.
21562  *
21563  * Originally Released Under LGPL - original licence link has changed is not relivant.
21564  *
21565  * Fork - LGPL
21566  * <script type="text/javascript">
21567  */
21568  
21569 /**
21570  * @class Roo.form.Hidden
21571  * @extends Roo.form.TextField
21572  * Simple Hidden element used on forms 
21573  * 
21574  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
21575  * 
21576  * @constructor
21577  * Creates a new Hidden form element.
21578  * @param {Object} config Configuration options
21579  */
21580
21581
21582
21583 // easy hidden field...
21584 Roo.form.Hidden = function(config){
21585     Roo.form.Hidden.superclass.constructor.call(this, config);
21586 };
21587   
21588 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
21589     fieldLabel:      '',
21590     inputType:      'hidden',
21591     width:          50,
21592     allowBlank:     true,
21593     labelSeparator: '',
21594     hidden:         true,
21595     itemCls :       'x-form-item-display-none'
21596
21597
21598 });
21599
21600
21601 /*
21602  * Based on:
21603  * Ext JS Library 1.1.1
21604  * Copyright(c) 2006-2007, Ext JS, LLC.
21605  *
21606  * Originally Released Under LGPL - original licence link has changed is not relivant.
21607  *
21608  * Fork - LGPL
21609  * <script type="text/javascript">
21610  */
21611  
21612 /**
21613  * @class Roo.form.TriggerField
21614  * @extends Roo.form.TextField
21615  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
21616  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
21617  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
21618  * for which you can provide a custom implementation.  For example:
21619  * <pre><code>
21620 var trigger = new Roo.form.TriggerField();
21621 trigger.onTriggerClick = myTriggerFn;
21622 trigger.applyTo('my-field');
21623 </code></pre>
21624  *
21625  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
21626  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
21627  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
21628  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
21629  * @constructor
21630  * Create a new TriggerField.
21631  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
21632  * to the base TextField)
21633  */
21634 Roo.form.TriggerField = function(config){
21635     this.mimicing = false;
21636     Roo.form.TriggerField.superclass.constructor.call(this, config);
21637 };
21638
21639 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
21640     /**
21641      * @cfg {String} triggerClass A CSS class to apply to the trigger
21642      */
21643     /**
21644      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21645      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
21646      */
21647     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
21648     /**
21649      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
21650      */
21651     hideTrigger:false,
21652
21653     /** @cfg {Boolean} grow @hide */
21654     /** @cfg {Number} growMin @hide */
21655     /** @cfg {Number} growMax @hide */
21656
21657     /**
21658      * @hide 
21659      * @method
21660      */
21661     autoSize: Roo.emptyFn,
21662     // private
21663     monitorTab : true,
21664     // private
21665     deferHeight : true,
21666
21667     
21668     actionMode : 'wrap',
21669     // private
21670     onResize : function(w, h){
21671         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
21672         if(typeof w == 'number'){
21673             var x = w - this.trigger.getWidth();
21674             this.el.setWidth(this.adjustWidth('input', x));
21675             this.trigger.setStyle('left', x+'px');
21676         }
21677     },
21678
21679     // private
21680     adjustSize : Roo.BoxComponent.prototype.adjustSize,
21681
21682     // private
21683     getResizeEl : function(){
21684         return this.wrap;
21685     },
21686
21687     // private
21688     getPositionEl : function(){
21689         return this.wrap;
21690     },
21691
21692     // private
21693     alignErrorIcon : function(){
21694         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
21695     },
21696
21697     // private
21698     onRender : function(ct, position){
21699         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
21700         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
21701         this.trigger = this.wrap.createChild(this.triggerConfig ||
21702                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
21703         if(this.hideTrigger){
21704             this.trigger.setDisplayed(false);
21705         }
21706         this.initTrigger();
21707         if(!this.width){
21708             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
21709         }
21710     },
21711
21712     // private
21713     initTrigger : function(){
21714         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
21715         this.trigger.addClassOnOver('x-form-trigger-over');
21716         this.trigger.addClassOnClick('x-form-trigger-click');
21717     },
21718
21719     // private
21720     onDestroy : function(){
21721         if(this.trigger){
21722             this.trigger.removeAllListeners();
21723             this.trigger.remove();
21724         }
21725         if(this.wrap){
21726             this.wrap.remove();
21727         }
21728         Roo.form.TriggerField.superclass.onDestroy.call(this);
21729     },
21730
21731     // private
21732     onFocus : function(){
21733         Roo.form.TriggerField.superclass.onFocus.call(this);
21734         if(!this.mimicing){
21735             this.wrap.addClass('x-trigger-wrap-focus');
21736             this.mimicing = true;
21737             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
21738             if(this.monitorTab){
21739                 this.el.on("keydown", this.checkTab, this);
21740             }
21741         }
21742     },
21743
21744     // private
21745     checkTab : function(e){
21746         if(e.getKey() == e.TAB){
21747             this.triggerBlur();
21748         }
21749     },
21750
21751     // private
21752     onBlur : function(){
21753         // do nothing
21754     },
21755
21756     // private
21757     mimicBlur : function(e, t){
21758         if(!this.wrap.contains(t) && this.validateBlur()){
21759             this.triggerBlur();
21760         }
21761     },
21762
21763     // private
21764     triggerBlur : function(){
21765         this.mimicing = false;
21766         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
21767         if(this.monitorTab){
21768             this.el.un("keydown", this.checkTab, this);
21769         }
21770         this.wrap.removeClass('x-trigger-wrap-focus');
21771         Roo.form.TriggerField.superclass.onBlur.call(this);
21772     },
21773
21774     // private
21775     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
21776     validateBlur : function(e, t){
21777         return true;
21778     },
21779
21780     // private
21781     onDisable : function(){
21782         Roo.form.TriggerField.superclass.onDisable.call(this);
21783         if(this.wrap){
21784             this.wrap.addClass('x-item-disabled');
21785         }
21786     },
21787
21788     // private
21789     onEnable : function(){
21790         Roo.form.TriggerField.superclass.onEnable.call(this);
21791         if(this.wrap){
21792             this.wrap.removeClass('x-item-disabled');
21793         }
21794     },
21795
21796     // private
21797     onShow : function(){
21798         var ae = this.getActionEl();
21799         
21800         if(ae){
21801             ae.dom.style.display = '';
21802             ae.dom.style.visibility = 'visible';
21803         }
21804     },
21805
21806     // private
21807     
21808     onHide : function(){
21809         var ae = this.getActionEl();
21810         ae.dom.style.display = 'none';
21811     },
21812
21813     /**
21814      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
21815      * by an implementing function.
21816      * @method
21817      * @param {EventObject} e
21818      */
21819     onTriggerClick : Roo.emptyFn
21820 });
21821
21822 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
21823 // to be extended by an implementing class.  For an example of implementing this class, see the custom
21824 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
21825 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
21826     initComponent : function(){
21827         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
21828
21829         this.triggerConfig = {
21830             tag:'span', cls:'x-form-twin-triggers', cn:[
21831             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
21832             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
21833         ]};
21834     },
21835
21836     getTrigger : function(index){
21837         return this.triggers[index];
21838     },
21839
21840     initTrigger : function(){
21841         var ts = this.trigger.select('.x-form-trigger', true);
21842         this.wrap.setStyle('overflow', 'hidden');
21843         var triggerField = this;
21844         ts.each(function(t, all, index){
21845             t.hide = function(){
21846                 var w = triggerField.wrap.getWidth();
21847                 this.dom.style.display = 'none';
21848                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21849             };
21850             t.show = function(){
21851                 var w = triggerField.wrap.getWidth();
21852                 this.dom.style.display = '';
21853                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21854             };
21855             var triggerIndex = 'Trigger'+(index+1);
21856
21857             if(this['hide'+triggerIndex]){
21858                 t.dom.style.display = 'none';
21859             }
21860             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
21861             t.addClassOnOver('x-form-trigger-over');
21862             t.addClassOnClick('x-form-trigger-click');
21863         }, this);
21864         this.triggers = ts.elements;
21865     },
21866
21867     onTrigger1Click : Roo.emptyFn,
21868     onTrigger2Click : Roo.emptyFn
21869 });/*
21870  * Based on:
21871  * Ext JS Library 1.1.1
21872  * Copyright(c) 2006-2007, Ext JS, LLC.
21873  *
21874  * Originally Released Under LGPL - original licence link has changed is not relivant.
21875  *
21876  * Fork - LGPL
21877  * <script type="text/javascript">
21878  */
21879  
21880 /**
21881  * @class Roo.form.TextArea
21882  * @extends Roo.form.TextField
21883  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
21884  * support for auto-sizing.
21885  * @constructor
21886  * Creates a new TextArea
21887  * @param {Object} config Configuration options
21888  */
21889 Roo.form.TextArea = function(config){
21890     Roo.form.TextArea.superclass.constructor.call(this, config);
21891     // these are provided exchanges for backwards compat
21892     // minHeight/maxHeight were replaced by growMin/growMax to be
21893     // compatible with TextField growing config values
21894     if(this.minHeight !== undefined){
21895         this.growMin = this.minHeight;
21896     }
21897     if(this.maxHeight !== undefined){
21898         this.growMax = this.maxHeight;
21899     }
21900 };
21901
21902 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
21903     /**
21904      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
21905      */
21906     growMin : 60,
21907     /**
21908      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
21909      */
21910     growMax: 1000,
21911     /**
21912      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
21913      * in the field (equivalent to setting overflow: hidden, defaults to false)
21914      */
21915     preventScrollbars: false,
21916     /**
21917      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21918      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
21919      */
21920
21921     // private
21922     onRender : function(ct, position){
21923         if(!this.el){
21924             this.defaultAutoCreate = {
21925                 tag: "textarea",
21926                 style:"width:300px;height:60px;",
21927                 autocomplete: "off"
21928             };
21929         }
21930         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
21931         if(this.grow){
21932             this.textSizeEl = Roo.DomHelper.append(document.body, {
21933                 tag: "pre", cls: "x-form-grow-sizer"
21934             });
21935             if(this.preventScrollbars){
21936                 this.el.setStyle("overflow", "hidden");
21937             }
21938             this.el.setHeight(this.growMin);
21939         }
21940     },
21941
21942     onDestroy : function(){
21943         if(this.textSizeEl){
21944             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
21945         }
21946         Roo.form.TextArea.superclass.onDestroy.call(this);
21947     },
21948
21949     // private
21950     onKeyUp : function(e){
21951         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
21952             this.autoSize();
21953         }
21954     },
21955
21956     /**
21957      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
21958      * This only takes effect if grow = true, and fires the autosize event if the height changes.
21959      */
21960     autoSize : function(){
21961         if(!this.grow || !this.textSizeEl){
21962             return;
21963         }
21964         var el = this.el;
21965         var v = el.dom.value;
21966         var ts = this.textSizeEl;
21967
21968         ts.innerHTML = '';
21969         ts.appendChild(document.createTextNode(v));
21970         v = ts.innerHTML;
21971
21972         Roo.fly(ts).setWidth(this.el.getWidth());
21973         if(v.length < 1){
21974             v = "&#160;&#160;";
21975         }else{
21976             if(Roo.isIE){
21977                 v = v.replace(/\n/g, '<p>&#160;</p>');
21978             }
21979             v += "&#160;\n&#160;";
21980         }
21981         ts.innerHTML = v;
21982         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
21983         if(h != this.lastHeight){
21984             this.lastHeight = h;
21985             this.el.setHeight(h);
21986             this.fireEvent("autosize", this, h);
21987         }
21988     }
21989 });/*
21990  * Based on:
21991  * Ext JS Library 1.1.1
21992  * Copyright(c) 2006-2007, Ext JS, LLC.
21993  *
21994  * Originally Released Under LGPL - original licence link has changed is not relivant.
21995  *
21996  * Fork - LGPL
21997  * <script type="text/javascript">
21998  */
21999  
22000
22001 /**
22002  * @class Roo.form.NumberField
22003  * @extends Roo.form.TextField
22004  * Numeric text field that provides automatic keystroke filtering and numeric validation.
22005  * @constructor
22006  * Creates a new NumberField
22007  * @param {Object} config Configuration options
22008  */
22009 Roo.form.NumberField = function(config){
22010     Roo.form.NumberField.superclass.constructor.call(this, config);
22011 };
22012
22013 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
22014     /**
22015      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
22016      */
22017     fieldClass: "x-form-field x-form-num-field",
22018     /**
22019      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
22020      */
22021     allowDecimals : true,
22022     /**
22023      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
22024      */
22025     decimalSeparator : ".",
22026     /**
22027      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
22028      */
22029     decimalPrecision : 2,
22030     /**
22031      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
22032      */
22033     allowNegative : true,
22034     /**
22035      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
22036      */
22037     minValue : Number.NEGATIVE_INFINITY,
22038     /**
22039      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
22040      */
22041     maxValue : Number.MAX_VALUE,
22042     /**
22043      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
22044      */
22045     minText : "The minimum value for this field is {0}",
22046     /**
22047      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
22048      */
22049     maxText : "The maximum value for this field is {0}",
22050     /**
22051      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
22052      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
22053      */
22054     nanText : "{0} is not a valid number",
22055
22056     // private
22057     initEvents : function(){
22058         Roo.form.NumberField.superclass.initEvents.call(this);
22059         var allowed = "0123456789";
22060         if(this.allowDecimals){
22061             allowed += this.decimalSeparator;
22062         }
22063         if(this.allowNegative){
22064             allowed += "-";
22065         }
22066         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
22067         var keyPress = function(e){
22068             var k = e.getKey();
22069             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
22070                 return;
22071             }
22072             var c = e.getCharCode();
22073             if(allowed.indexOf(String.fromCharCode(c)) === -1){
22074                 e.stopEvent();
22075             }
22076         };
22077         this.el.on("keypress", keyPress, this);
22078     },
22079
22080     // private
22081     validateValue : function(value){
22082         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
22083             return false;
22084         }
22085         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22086              return true;
22087         }
22088         var num = this.parseValue(value);
22089         if(isNaN(num)){
22090             this.markInvalid(String.format(this.nanText, value));
22091             return false;
22092         }
22093         if(num < this.minValue){
22094             this.markInvalid(String.format(this.minText, this.minValue));
22095             return false;
22096         }
22097         if(num > this.maxValue){
22098             this.markInvalid(String.format(this.maxText, this.maxValue));
22099             return false;
22100         }
22101         return true;
22102     },
22103
22104     getValue : function(){
22105         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
22106     },
22107
22108     // private
22109     parseValue : function(value){
22110         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
22111         return isNaN(value) ? '' : value;
22112     },
22113
22114     // private
22115     fixPrecision : function(value){
22116         var nan = isNaN(value);
22117         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
22118             return nan ? '' : value;
22119         }
22120         return parseFloat(value).toFixed(this.decimalPrecision);
22121     },
22122
22123     setValue : function(v){
22124         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
22125     },
22126
22127     // private
22128     decimalPrecisionFcn : function(v){
22129         return Math.floor(v);
22130     },
22131
22132     beforeBlur : function(){
22133         var v = this.parseValue(this.getRawValue());
22134         if(v){
22135             this.setValue(this.fixPrecision(v));
22136         }
22137     }
22138 });/*
22139  * Based on:
22140  * Ext JS Library 1.1.1
22141  * Copyright(c) 2006-2007, Ext JS, LLC.
22142  *
22143  * Originally Released Under LGPL - original licence link has changed is not relivant.
22144  *
22145  * Fork - LGPL
22146  * <script type="text/javascript">
22147  */
22148  
22149 /**
22150  * @class Roo.form.DateField
22151  * @extends Roo.form.TriggerField
22152  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22153 * @constructor
22154 * Create a new DateField
22155 * @param {Object} config
22156  */
22157 Roo.form.DateField = function(config){
22158     Roo.form.DateField.superclass.constructor.call(this, config);
22159     
22160       this.addEvents({
22161          
22162         /**
22163          * @event select
22164          * Fires when a date is selected
22165              * @param {Roo.form.DateField} combo This combo box
22166              * @param {Date} date The date selected
22167              */
22168         'select' : true
22169          
22170     });
22171     
22172     
22173     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22174     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22175     this.ddMatch = null;
22176     if(this.disabledDates){
22177         var dd = this.disabledDates;
22178         var re = "(?:";
22179         for(var i = 0; i < dd.length; i++){
22180             re += dd[i];
22181             if(i != dd.length-1) re += "|";
22182         }
22183         this.ddMatch = new RegExp(re + ")");
22184     }
22185 };
22186
22187 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
22188     /**
22189      * @cfg {String} format
22190      * The default date format string which can be overriden for localization support.  The format must be
22191      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22192      */
22193     format : "m/d/y",
22194     /**
22195      * @cfg {String} altFormats
22196      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22197      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22198      */
22199     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22200     /**
22201      * @cfg {Array} disabledDays
22202      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22203      */
22204     disabledDays : null,
22205     /**
22206      * @cfg {String} disabledDaysText
22207      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22208      */
22209     disabledDaysText : "Disabled",
22210     /**
22211      * @cfg {Array} disabledDates
22212      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22213      * expression so they are very powerful. Some examples:
22214      * <ul>
22215      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22216      * <li>["03/08", "09/16"] would disable those days for every year</li>
22217      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22218      * <li>["03/../2006"] would disable every day in March 2006</li>
22219      * <li>["^03"] would disable every day in every March</li>
22220      * </ul>
22221      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22222      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22223      */
22224     disabledDates : null,
22225     /**
22226      * @cfg {String} disabledDatesText
22227      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22228      */
22229     disabledDatesText : "Disabled",
22230     /**
22231      * @cfg {Date/String} minValue
22232      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22233      * valid format (defaults to null).
22234      */
22235     minValue : null,
22236     /**
22237      * @cfg {Date/String} maxValue
22238      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22239      * valid format (defaults to null).
22240      */
22241     maxValue : null,
22242     /**
22243      * @cfg {String} minText
22244      * The error text to display when the date in the cell is before minValue (defaults to
22245      * 'The date in this field must be after {minValue}').
22246      */
22247     minText : "The date in this field must be equal to or after {0}",
22248     /**
22249      * @cfg {String} maxText
22250      * The error text to display when the date in the cell is after maxValue (defaults to
22251      * 'The date in this field must be before {maxValue}').
22252      */
22253     maxText : "The date in this field must be equal to or before {0}",
22254     /**
22255      * @cfg {String} invalidText
22256      * The error text to display when the date in the field is invalid (defaults to
22257      * '{value} is not a valid date - it must be in the format {format}').
22258      */
22259     invalidText : "{0} is not a valid date - it must be in the format {1}",
22260     /**
22261      * @cfg {String} triggerClass
22262      * An additional CSS class used to style the trigger button.  The trigger will always get the
22263      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22264      * which displays a calendar icon).
22265      */
22266     triggerClass : 'x-form-date-trigger',
22267     
22268
22269     /**
22270      * @cfg {bool} useIso
22271      * if enabled, then the date field will use a hidden field to store the 
22272      * real value as iso formated date. default (false)
22273      */ 
22274     useIso : false,
22275     /**
22276      * @cfg {String/Object} autoCreate
22277      * A DomHelper element spec, or true for a default element spec (defaults to
22278      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22279      */ 
22280     // private
22281     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
22282     
22283     // private
22284     hiddenField: false,
22285     
22286     onRender : function(ct, position)
22287     {
22288         Roo.form.DateField.superclass.onRender.call(this, ct, position);
22289         if (this.useIso) {
22290             this.el.dom.removeAttribute('name'); 
22291             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22292                     'before', true);
22293             this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
22294             // prevent input submission
22295             this.hiddenName = this.name;
22296         }
22297             
22298             
22299     },
22300     
22301     // private
22302     validateValue : function(value)
22303     {
22304         value = this.formatDate(value);
22305         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
22306             return false;
22307         }
22308         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22309              return true;
22310         }
22311         var svalue = value;
22312         value = this.parseDate(value);
22313         if(!value){
22314             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22315             return false;
22316         }
22317         var time = value.getTime();
22318         if(this.minValue && time < this.minValue.getTime()){
22319             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22320             return false;
22321         }
22322         if(this.maxValue && time > this.maxValue.getTime()){
22323             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22324             return false;
22325         }
22326         if(this.disabledDays){
22327             var day = value.getDay();
22328             for(var i = 0; i < this.disabledDays.length; i++) {
22329                 if(day === this.disabledDays[i]){
22330                     this.markInvalid(this.disabledDaysText);
22331                     return false;
22332                 }
22333             }
22334         }
22335         var fvalue = this.formatDate(value);
22336         if(this.ddMatch && this.ddMatch.test(fvalue)){
22337             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22338             return false;
22339         }
22340         return true;
22341     },
22342
22343     // private
22344     // Provides logic to override the default TriggerField.validateBlur which just returns true
22345     validateBlur : function(){
22346         return !this.menu || !this.menu.isVisible();
22347     },
22348
22349     /**
22350      * Returns the current date value of the date field.
22351      * @return {Date} The date value
22352      */
22353     getValue : function(){
22354         
22355         return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
22356     },
22357
22358     /**
22359      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22360      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
22361      * (the default format used is "m/d/y").
22362      * <br />Usage:
22363      * <pre><code>
22364 //All of these calls set the same date value (May 4, 2006)
22365
22366 //Pass a date object:
22367 var dt = new Date('5/4/06');
22368 dateField.setValue(dt);
22369
22370 //Pass a date string (default format):
22371 dateField.setValue('5/4/06');
22372
22373 //Pass a date string (custom format):
22374 dateField.format = 'Y-m-d';
22375 dateField.setValue('2006-5-4');
22376 </code></pre>
22377      * @param {String/Date} date The date or valid date string
22378      */
22379     setValue : function(date){
22380         if (this.hiddenField) {
22381             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22382         }
22383         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22384     },
22385
22386     // private
22387     parseDate : function(value){
22388         if(!value || value instanceof Date){
22389             return value;
22390         }
22391         var v = Date.parseDate(value, this.format);
22392         if(!v && this.altFormats){
22393             if(!this.altFormatsArray){
22394                 this.altFormatsArray = this.altFormats.split("|");
22395             }
22396             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22397                 v = Date.parseDate(value, this.altFormatsArray[i]);
22398             }
22399         }
22400         return v;
22401     },
22402
22403     // private
22404     formatDate : function(date, fmt){
22405         return (!date || !(date instanceof Date)) ?
22406                date : date.dateFormat(fmt || this.format);
22407     },
22408
22409     // private
22410     menuListeners : {
22411         select: function(m, d){
22412             this.setValue(d);
22413             this.fireEvent('select', this, d);
22414         },
22415         show : function(){ // retain focus styling
22416             this.onFocus();
22417         },
22418         hide : function(){
22419             this.focus.defer(10, this);
22420             var ml = this.menuListeners;
22421             this.menu.un("select", ml.select,  this);
22422             this.menu.un("show", ml.show,  this);
22423             this.menu.un("hide", ml.hide,  this);
22424         }
22425     },
22426
22427     // private
22428     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22429     onTriggerClick : function(){
22430         if(this.disabled){
22431             return;
22432         }
22433         if(this.menu == null){
22434             this.menu = new Roo.menu.DateMenu();
22435         }
22436         Roo.apply(this.menu.picker,  {
22437             showClear: this.allowBlank,
22438             minDate : this.minValue,
22439             maxDate : this.maxValue,
22440             disabledDatesRE : this.ddMatch,
22441             disabledDatesText : this.disabledDatesText,
22442             disabledDays : this.disabledDays,
22443             disabledDaysText : this.disabledDaysText,
22444             format : this.format,
22445             minText : String.format(this.minText, this.formatDate(this.minValue)),
22446             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22447         });
22448         this.menu.on(Roo.apply({}, this.menuListeners, {
22449             scope:this
22450         }));
22451         this.menu.picker.setValue(this.getValue() || new Date());
22452         this.menu.show(this.el, "tl-bl?");
22453     },
22454
22455     beforeBlur : function(){
22456         var v = this.parseDate(this.getRawValue());
22457         if(v){
22458             this.setValue(v);
22459         }
22460     }
22461
22462     /** @cfg {Boolean} grow @hide */
22463     /** @cfg {Number} growMin @hide */
22464     /** @cfg {Number} growMax @hide */
22465     /**
22466      * @hide
22467      * @method autoSize
22468      */
22469 });/*
22470  * Based on:
22471  * Ext JS Library 1.1.1
22472  * Copyright(c) 2006-2007, Ext JS, LLC.
22473  *
22474  * Originally Released Under LGPL - original licence link has changed is not relivant.
22475  *
22476  * Fork - LGPL
22477  * <script type="text/javascript">
22478  */
22479  
22480
22481 /**
22482  * @class Roo.form.ComboBox
22483  * @extends Roo.form.TriggerField
22484  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
22485  * @constructor
22486  * Create a new ComboBox.
22487  * @param {Object} config Configuration options
22488  */
22489 Roo.form.ComboBox = function(config){
22490     Roo.form.ComboBox.superclass.constructor.call(this, config);
22491     this.addEvents({
22492         /**
22493          * @event expand
22494          * Fires when the dropdown list is expanded
22495              * @param {Roo.form.ComboBox} combo This combo box
22496              */
22497         'expand' : true,
22498         /**
22499          * @event collapse
22500          * Fires when the dropdown list is collapsed
22501              * @param {Roo.form.ComboBox} combo This combo box
22502              */
22503         'collapse' : true,
22504         /**
22505          * @event beforeselect
22506          * Fires before a list item is selected. Return false to cancel the selection.
22507              * @param {Roo.form.ComboBox} combo This combo box
22508              * @param {Roo.data.Record} record The data record returned from the underlying store
22509              * @param {Number} index The index of the selected item in the dropdown list
22510              */
22511         'beforeselect' : true,
22512         /**
22513          * @event select
22514          * Fires when a list item is selected
22515              * @param {Roo.form.ComboBox} combo This combo box
22516              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
22517              * @param {Number} index The index of the selected item in the dropdown list
22518              */
22519         'select' : true,
22520         /**
22521          * @event beforequery
22522          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
22523          * The event object passed has these properties:
22524              * @param {Roo.form.ComboBox} combo This combo box
22525              * @param {String} query The query
22526              * @param {Boolean} forceAll true to force "all" query
22527              * @param {Boolean} cancel true to cancel the query
22528              * @param {Object} e The query event object
22529              */
22530         'beforequery': true,
22531          /**
22532          * @event add
22533          * Fires when the 'add' icon is pressed (add a listener to enable add button)
22534              * @param {Roo.form.ComboBox} combo This combo box
22535              */
22536         'add' : true,
22537         /**
22538          * @event edit
22539          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
22540              * @param {Roo.form.ComboBox} combo This combo box
22541              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
22542              */
22543         'edit' : true
22544         
22545         
22546     });
22547     if(this.transform){
22548         this.allowDomMove = false;
22549         var s = Roo.getDom(this.transform);
22550         if(!this.hiddenName){
22551             this.hiddenName = s.name;
22552         }
22553         if(!this.store){
22554             this.mode = 'local';
22555             var d = [], opts = s.options;
22556             for(var i = 0, len = opts.length;i < len; i++){
22557                 var o = opts[i];
22558                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
22559                 if(o.selected) {
22560                     this.value = value;
22561                 }
22562                 d.push([value, o.text]);
22563             }
22564             this.store = new Roo.data.SimpleStore({
22565                 'id': 0,
22566                 fields: ['value', 'text'],
22567                 data : d
22568             });
22569             this.valueField = 'value';
22570             this.displayField = 'text';
22571         }
22572         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
22573         if(!this.lazyRender){
22574             this.target = true;
22575             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
22576             s.parentNode.removeChild(s); // remove it
22577             this.render(this.el.parentNode);
22578         }else{
22579             s.parentNode.removeChild(s); // remove it
22580         }
22581
22582     }
22583     if (this.store) {
22584         this.store = Roo.factory(this.store, Roo.data);
22585     }
22586     
22587     this.selectedIndex = -1;
22588     if(this.mode == 'local'){
22589         if(config.queryDelay === undefined){
22590             this.queryDelay = 10;
22591         }
22592         if(config.minChars === undefined){
22593             this.minChars = 0;
22594         }
22595     }
22596 };
22597
22598 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
22599     /**
22600      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
22601      */
22602     /**
22603      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
22604      * rendering into an Roo.Editor, defaults to false)
22605      */
22606     /**
22607      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
22608      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
22609      */
22610     /**
22611      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
22612      */
22613     /**
22614      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
22615      * the dropdown list (defaults to undefined, with no header element)
22616      */
22617
22618      /**
22619      * @cfg {String/Roo.Template} tpl The template to use to render the output
22620      */
22621      
22622     // private
22623     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
22624     /**
22625      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
22626      */
22627     listWidth: undefined,
22628     /**
22629      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
22630      * mode = 'remote' or 'text' if mode = 'local')
22631      */
22632     displayField: undefined,
22633     /**
22634      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
22635      * mode = 'remote' or 'value' if mode = 'local'). 
22636      * Note: use of a valueField requires the user make a selection
22637      * in order for a value to be mapped.
22638      */
22639     valueField: undefined,
22640     /**
22641      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
22642      * field's data value (defaults to the underlying DOM element's name)
22643      */
22644     hiddenName: undefined,
22645     /**
22646      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
22647      */
22648     listClass: '',
22649     /**
22650      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
22651      */
22652     selectedClass: 'x-combo-selected',
22653     /**
22654      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
22655      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
22656      * which displays a downward arrow icon).
22657      */
22658     triggerClass : 'x-form-arrow-trigger',
22659     /**
22660      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
22661      */
22662     shadow:'sides',
22663     /**
22664      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
22665      * anchor positions (defaults to 'tl-bl')
22666      */
22667     listAlign: 'tl-bl?',
22668     /**
22669      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
22670      */
22671     maxHeight: 300,
22672     /**
22673      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
22674      * query specified by the allQuery config option (defaults to 'query')
22675      */
22676     triggerAction: 'query',
22677     /**
22678      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
22679      * (defaults to 4, does not apply if editable = false)
22680      */
22681     minChars : 4,
22682     /**
22683      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
22684      * delay (typeAheadDelay) if it matches a known value (defaults to false)
22685      */
22686     typeAhead: false,
22687     /**
22688      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
22689      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
22690      */
22691     queryDelay: 500,
22692     /**
22693      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
22694      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
22695      */
22696     pageSize: 0,
22697     /**
22698      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
22699      * when editable = true (defaults to false)
22700      */
22701     selectOnFocus:false,
22702     /**
22703      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
22704      */
22705     queryParam: 'query',
22706     /**
22707      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
22708      * when mode = 'remote' (defaults to 'Loading...')
22709      */
22710     loadingText: 'Loading...',
22711     /**
22712      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
22713      */
22714     resizable: false,
22715     /**
22716      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
22717      */
22718     handleHeight : 8,
22719     /**
22720      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
22721      * traditional select (defaults to true)
22722      */
22723     editable: true,
22724     /**
22725      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
22726      */
22727     allQuery: '',
22728     /**
22729      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
22730      */
22731     mode: 'remote',
22732     /**
22733      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
22734      * listWidth has a higher value)
22735      */
22736     minListWidth : 70,
22737     /**
22738      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
22739      * allow the user to set arbitrary text into the field (defaults to false)
22740      */
22741     forceSelection:false,
22742     /**
22743      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
22744      * if typeAhead = true (defaults to 250)
22745      */
22746     typeAheadDelay : 250,
22747     /**
22748      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
22749      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
22750      */
22751     valueNotFoundText : undefined,
22752     /**
22753      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
22754      */
22755     blockFocus : false,
22756     
22757     /**
22758      * @cfg {Boolean} disableClear Disable showing of clear button.
22759      */
22760     disableClear : false,
22761     /**
22762      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
22763      */
22764     alwaysQuery : false,
22765     
22766     //private
22767     addicon : false,
22768     editicon: false,
22769     
22770     
22771     // private
22772     onRender : function(ct, position){
22773         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
22774         if(this.hiddenName){
22775             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
22776                     'before', true);
22777             this.hiddenField.value =
22778                 this.hiddenValue !== undefined ? this.hiddenValue :
22779                 this.value !== undefined ? this.value : '';
22780
22781             // prevent input submission
22782             this.el.dom.removeAttribute('name');
22783         }
22784         if(Roo.isGecko){
22785             this.el.dom.setAttribute('autocomplete', 'off');
22786         }
22787
22788         var cls = 'x-combo-list';
22789
22790         this.list = new Roo.Layer({
22791             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
22792         });
22793
22794         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
22795         this.list.setWidth(lw);
22796         this.list.swallowEvent('mousewheel');
22797         this.assetHeight = 0;
22798
22799         if(this.title){
22800             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
22801             this.assetHeight += this.header.getHeight();
22802         }
22803
22804         this.innerList = this.list.createChild({cls:cls+'-inner'});
22805         this.innerList.on('mouseover', this.onViewOver, this);
22806         this.innerList.on('mousemove', this.onViewMove, this);
22807         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
22808         
22809         if(this.allowBlank && !this.pageSize && !this.disableClear){
22810             this.footer = this.list.createChild({cls:cls+'-ft'});
22811             this.pageTb = new Roo.Toolbar(this.footer);
22812            
22813         }
22814         if(this.pageSize){
22815             this.footer = this.list.createChild({cls:cls+'-ft'});
22816             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
22817                     {pageSize: this.pageSize});
22818             
22819         }
22820         
22821         if (this.pageTb && this.allowBlank && !this.disableClear) {
22822             var _this = this;
22823             this.pageTb.add(new Roo.Toolbar.Fill(), {
22824                 cls: 'x-btn-icon x-btn-clear',
22825                 text: '&#160;',
22826                 handler: function()
22827                 {
22828                     _this.collapse();
22829                     _this.clearValue();
22830                     _this.onSelect(false, -1);
22831                 }
22832             });
22833         }
22834         if (this.footer) {
22835             this.assetHeight += this.footer.getHeight();
22836         }
22837         
22838
22839         if(!this.tpl){
22840             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
22841         }
22842
22843         this.view = new Roo.View(this.innerList, this.tpl, {
22844             singleSelect:true, store: this.store, selectedClass: this.selectedClass
22845         });
22846
22847         this.view.on('click', this.onViewClick, this);
22848
22849         this.store.on('beforeload', this.onBeforeLoad, this);
22850         this.store.on('load', this.onLoad, this);
22851         this.store.on('loadexception', this.collapse, this);
22852
22853         if(this.resizable){
22854             this.resizer = new Roo.Resizable(this.list,  {
22855                pinned:true, handles:'se'
22856             });
22857             this.resizer.on('resize', function(r, w, h){
22858                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
22859                 this.listWidth = w;
22860                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
22861                 this.restrictHeight();
22862             }, this);
22863             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
22864         }
22865         if(!this.editable){
22866             this.editable = true;
22867             this.setEditable(false);
22868         }  
22869         
22870         
22871         if (typeof(this.events.add.listeners) != 'undefined') {
22872             
22873             this.addicon = this.wrap.createChild(
22874                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
22875        
22876             this.addicon.on('click', function(e) {
22877                 this.fireEvent('add', this);
22878             }, this);
22879         }
22880         if (typeof(this.events.edit.listeners) != 'undefined') {
22881             
22882             this.editicon = this.wrap.createChild(
22883                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
22884             if (this.addicon) {
22885                 this.editicon.setStyle('margin-left', '40px');
22886             }
22887             this.editicon.on('click', function(e) {
22888                 
22889                 // we fire even  if inothing is selected..
22890                 this.fireEvent('edit', this, this.lastData );
22891                 
22892             }, this);
22893         }
22894         
22895         
22896         
22897     },
22898
22899     // private
22900     initEvents : function(){
22901         Roo.form.ComboBox.superclass.initEvents.call(this);
22902
22903         this.keyNav = new Roo.KeyNav(this.el, {
22904             "up" : function(e){
22905                 this.inKeyMode = true;
22906                 this.selectPrev();
22907             },
22908
22909             "down" : function(e){
22910                 if(!this.isExpanded()){
22911                     this.onTriggerClick();
22912                 }else{
22913                     this.inKeyMode = true;
22914                     this.selectNext();
22915                 }
22916             },
22917
22918             "enter" : function(e){
22919                 this.onViewClick();
22920                 //return true;
22921             },
22922
22923             "esc" : function(e){
22924                 this.collapse();
22925             },
22926
22927             "tab" : function(e){
22928                 this.onViewClick(false);
22929                 return true;
22930             },
22931
22932             scope : this,
22933
22934             doRelay : function(foo, bar, hname){
22935                 if(hname == 'down' || this.scope.isExpanded()){
22936                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
22937                 }
22938                 return true;
22939             },
22940
22941             forceKeyDown: true
22942         });
22943         this.queryDelay = Math.max(this.queryDelay || 10,
22944                 this.mode == 'local' ? 10 : 250);
22945         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
22946         if(this.typeAhead){
22947             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
22948         }
22949         if(this.editable !== false){
22950             this.el.on("keyup", this.onKeyUp, this);
22951         }
22952         if(this.forceSelection){
22953             this.on('blur', this.doForce, this);
22954         }
22955     },
22956
22957     onDestroy : function(){
22958         if(this.view){
22959             this.view.setStore(null);
22960             this.view.el.removeAllListeners();
22961             this.view.el.remove();
22962             this.view.purgeListeners();
22963         }
22964         if(this.list){
22965             this.list.destroy();
22966         }
22967         if(this.store){
22968             this.store.un('beforeload', this.onBeforeLoad, this);
22969             this.store.un('load', this.onLoad, this);
22970             this.store.un('loadexception', this.collapse, this);
22971         }
22972         Roo.form.ComboBox.superclass.onDestroy.call(this);
22973     },
22974
22975     // private
22976     fireKey : function(e){
22977         if(e.isNavKeyPress() && !this.list.isVisible()){
22978             this.fireEvent("specialkey", this, e);
22979         }
22980     },
22981
22982     // private
22983     onResize: function(w, h){
22984         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
22985         
22986         if(typeof w != 'number'){
22987             // we do not handle it!?!?
22988             return;
22989         }
22990         var tw = this.trigger.getWidth();
22991         tw += this.addicon ? this.addicon.getWidth() : 0;
22992         tw += this.editicon ? this.editicon.getWidth() : 0;
22993         var x = w - tw;
22994         this.el.setWidth( this.adjustWidth('input', x));
22995             
22996         this.trigger.setStyle('left', x+'px');
22997         
22998         if(this.list && this.listWidth === undefined){
22999             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
23000             this.list.setWidth(lw);
23001             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23002         }
23003         
23004     
23005         
23006     },
23007
23008     /**
23009      * Allow or prevent the user from directly editing the field text.  If false is passed,
23010      * the user will only be able to select from the items defined in the dropdown list.  This method
23011      * is the runtime equivalent of setting the 'editable' config option at config time.
23012      * @param {Boolean} value True to allow the user to directly edit the field text
23013      */
23014     setEditable : function(value){
23015         if(value == this.editable){
23016             return;
23017         }
23018         this.editable = value;
23019         if(!value){
23020             this.el.dom.setAttribute('readOnly', true);
23021             this.el.on('mousedown', this.onTriggerClick,  this);
23022             this.el.addClass('x-combo-noedit');
23023         }else{
23024             this.el.dom.setAttribute('readOnly', false);
23025             this.el.un('mousedown', this.onTriggerClick,  this);
23026             this.el.removeClass('x-combo-noedit');
23027         }
23028     },
23029
23030     // private
23031     onBeforeLoad : function(){
23032         if(!this.hasFocus){
23033             return;
23034         }
23035         this.innerList.update(this.loadingText ?
23036                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
23037         this.restrictHeight();
23038         this.selectedIndex = -1;
23039     },
23040
23041     // private
23042     onLoad : function(){
23043         if(!this.hasFocus){
23044             return;
23045         }
23046         if(this.store.getCount() > 0){
23047             this.expand();
23048             this.restrictHeight();
23049             if(this.lastQuery == this.allQuery){
23050                 if(this.editable){
23051                     this.el.dom.select();
23052                 }
23053                 if(!this.selectByValue(this.value, true)){
23054                     this.select(0, true);
23055                 }
23056             }else{
23057                 this.selectNext();
23058                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
23059                     this.taTask.delay(this.typeAheadDelay);
23060                 }
23061             }
23062         }else{
23063             this.onEmptyResults();
23064         }
23065         //this.el.focus();
23066     },
23067
23068     // private
23069     onTypeAhead : function(){
23070         if(this.store.getCount() > 0){
23071             var r = this.store.getAt(0);
23072             var newValue = r.data[this.displayField];
23073             var len = newValue.length;
23074             var selStart = this.getRawValue().length;
23075             if(selStart != len){
23076                 this.setRawValue(newValue);
23077                 this.selectText(selStart, newValue.length);
23078             }
23079         }
23080     },
23081
23082     // private
23083     onSelect : function(record, index){
23084         if(this.fireEvent('beforeselect', this, record, index) !== false){
23085             this.setFromData(index > -1 ? record.data : false);
23086             this.collapse();
23087             this.fireEvent('select', this, record, index);
23088         }
23089     },
23090
23091     /**
23092      * Returns the currently selected field value or empty string if no value is set.
23093      * @return {String} value The selected value
23094      */
23095     getValue : function(){
23096         if(this.valueField){
23097             return typeof this.value != 'undefined' ? this.value : '';
23098         }else{
23099             return Roo.form.ComboBox.superclass.getValue.call(this);
23100         }
23101     },
23102
23103     /**
23104      * Clears any text/value currently set in the field
23105      */
23106     clearValue : function(){
23107         if(this.hiddenField){
23108             this.hiddenField.value = '';
23109         }
23110         this.value = '';
23111         this.setRawValue('');
23112         this.lastSelectionText = '';
23113         this.applyEmptyText();
23114     },
23115
23116     /**
23117      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
23118      * will be displayed in the field.  If the value does not match the data value of an existing item,
23119      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
23120      * Otherwise the field will be blank (although the value will still be set).
23121      * @param {String} value The value to match
23122      */
23123     setValue : function(v){
23124         var text = v;
23125         if(this.valueField){
23126             var r = this.findRecord(this.valueField, v);
23127             if(r){
23128                 text = r.data[this.displayField];
23129             }else if(this.valueNotFoundText !== undefined){
23130                 text = this.valueNotFoundText;
23131             }
23132         }
23133         this.lastSelectionText = text;
23134         if(this.hiddenField){
23135             this.hiddenField.value = v;
23136         }
23137         Roo.form.ComboBox.superclass.setValue.call(this, text);
23138         this.value = v;
23139     },
23140     /**
23141      * @property {Object} the last set data for the element
23142      */
23143     
23144     lastData : false,
23145     /**
23146      * Sets the value of the field based on a object which is related to the record format for the store.
23147      * @param {Object} value the value to set as. or false on reset?
23148      */
23149     setFromData : function(o){
23150         var dv = ''; // display value
23151         var vv = ''; // value value..
23152         this.lastData = o;
23153         if (this.displayField) {
23154             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
23155         } else {
23156             // this is an error condition!!!
23157             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
23158         }
23159         
23160         if(this.valueField){
23161             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
23162         }
23163         if(this.hiddenField){
23164             this.hiddenField.value = vv;
23165             
23166             this.lastSelectionText = dv;
23167             Roo.form.ComboBox.superclass.setValue.call(this, dv);
23168             this.value = vv;
23169             return;
23170         }
23171         // no hidden field.. - we store the value in 'value', but still display
23172         // display field!!!!
23173         this.lastSelectionText = dv;
23174         Roo.form.ComboBox.superclass.setValue.call(this, dv);
23175         this.value = vv;
23176         
23177         
23178     },
23179     // private
23180     reset : function(){
23181         // overridden so that last data is reset..
23182         this.setValue(this.originalValue);
23183         this.clearInvalid();
23184         this.lastData = false;
23185     },
23186     // private
23187     findRecord : function(prop, value){
23188         var record;
23189         if(this.store.getCount() > 0){
23190             this.store.each(function(r){
23191                 if(r.data[prop] == value){
23192                     record = r;
23193                     return false;
23194                 }
23195             });
23196         }
23197         return record;
23198     },
23199
23200     // private
23201     onViewMove : function(e, t){
23202         this.inKeyMode = false;
23203     },
23204
23205     // private
23206     onViewOver : function(e, t){
23207         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
23208             return;
23209         }
23210         var item = this.view.findItemFromChild(t);
23211         if(item){
23212             var index = this.view.indexOf(item);
23213             this.select(index, false);
23214         }
23215     },
23216
23217     // private
23218     onViewClick : function(doFocus){
23219         var index = this.view.getSelectedIndexes()[0];
23220         var r = this.store.getAt(index);
23221         if(r){
23222             this.onSelect(r, index);
23223         }
23224         if(doFocus !== false && !this.blockFocus){
23225             this.el.focus();
23226         }
23227     },
23228
23229     // private
23230     restrictHeight : function(){
23231         this.innerList.dom.style.height = '';
23232         var inner = this.innerList.dom;
23233         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
23234         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
23235         this.list.beginUpdate();
23236         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
23237         this.list.alignTo(this.el, this.listAlign);
23238         this.list.endUpdate();
23239     },
23240
23241     // private
23242     onEmptyResults : function(){
23243         this.collapse();
23244     },
23245
23246     /**
23247      * Returns true if the dropdown list is expanded, else false.
23248      */
23249     isExpanded : function(){
23250         return this.list.isVisible();
23251     },
23252
23253     /**
23254      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
23255      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23256      * @param {String} value The data value of the item to select
23257      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23258      * selected item if it is not currently in view (defaults to true)
23259      * @return {Boolean} True if the value matched an item in the list, else false
23260      */
23261     selectByValue : function(v, scrollIntoView){
23262         if(v !== undefined && v !== null){
23263             var r = this.findRecord(this.valueField || this.displayField, v);
23264             if(r){
23265                 this.select(this.store.indexOf(r), scrollIntoView);
23266                 return true;
23267             }
23268         }
23269         return false;
23270     },
23271
23272     /**
23273      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
23274      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23275      * @param {Number} index The zero-based index of the list item to select
23276      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23277      * selected item if it is not currently in view (defaults to true)
23278      */
23279     select : function(index, scrollIntoView){
23280         this.selectedIndex = index;
23281         this.view.select(index);
23282         if(scrollIntoView !== false){
23283             var el = this.view.getNode(index);
23284             if(el){
23285                 this.innerList.scrollChildIntoView(el, false);
23286             }
23287         }
23288     },
23289
23290     // private
23291     selectNext : function(){
23292         var ct = this.store.getCount();
23293         if(ct > 0){
23294             if(this.selectedIndex == -1){
23295                 this.select(0);
23296             }else if(this.selectedIndex < ct-1){
23297                 this.select(this.selectedIndex+1);
23298             }
23299         }
23300     },
23301
23302     // private
23303     selectPrev : function(){
23304         var ct = this.store.getCount();
23305         if(ct > 0){
23306             if(this.selectedIndex == -1){
23307                 this.select(0);
23308             }else if(this.selectedIndex != 0){
23309                 this.select(this.selectedIndex-1);
23310             }
23311         }
23312     },
23313
23314     // private
23315     onKeyUp : function(e){
23316         if(this.editable !== false && !e.isSpecialKey()){
23317             this.lastKey = e.getKey();
23318             this.dqTask.delay(this.queryDelay);
23319         }
23320     },
23321
23322     // private
23323     validateBlur : function(){
23324         return !this.list || !this.list.isVisible();   
23325     },
23326
23327     // private
23328     initQuery : function(){
23329         this.doQuery(this.getRawValue());
23330     },
23331
23332     // private
23333     doForce : function(){
23334         if(this.el.dom.value.length > 0){
23335             this.el.dom.value =
23336                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
23337             this.applyEmptyText();
23338         }
23339     },
23340
23341     /**
23342      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
23343      * query allowing the query action to be canceled if needed.
23344      * @param {String} query The SQL query to execute
23345      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
23346      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
23347      * saved in the current store (defaults to false)
23348      */
23349     doQuery : function(q, forceAll){
23350         if(q === undefined || q === null){
23351             q = '';
23352         }
23353         var qe = {
23354             query: q,
23355             forceAll: forceAll,
23356             combo: this,
23357             cancel:false
23358         };
23359         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
23360             return false;
23361         }
23362         q = qe.query;
23363         forceAll = qe.forceAll;
23364         if(forceAll === true || (q.length >= this.minChars)){
23365             if(this.lastQuery != q || this.alwaysQuery){
23366                 this.lastQuery = q;
23367                 if(this.mode == 'local'){
23368                     this.selectedIndex = -1;
23369                     if(forceAll){
23370                         this.store.clearFilter();
23371                     }else{
23372                         this.store.filter(this.displayField, q);
23373                     }
23374                     this.onLoad();
23375                 }else{
23376                     this.store.baseParams[this.queryParam] = q;
23377                     this.store.load({
23378                         params: this.getParams(q)
23379                     });
23380                     this.expand();
23381                 }
23382             }else{
23383                 this.selectedIndex = -1;
23384                 this.onLoad();   
23385             }
23386         }
23387     },
23388
23389     // private
23390     getParams : function(q){
23391         var p = {};
23392         //p[this.queryParam] = q;
23393         if(this.pageSize){
23394             p.start = 0;
23395             p.limit = this.pageSize;
23396         }
23397         return p;
23398     },
23399
23400     /**
23401      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
23402      */
23403     collapse : function(){
23404         if(!this.isExpanded()){
23405             return;
23406         }
23407         this.list.hide();
23408         Roo.get(document).un('mousedown', this.collapseIf, this);
23409         Roo.get(document).un('mousewheel', this.collapseIf, this);
23410         if (!this.editable) {
23411             Roo.get(document).un('keydown', this.listKeyPress, this);
23412         }
23413         this.fireEvent('collapse', this);
23414     },
23415
23416     // private
23417     collapseIf : function(e){
23418         if(!e.within(this.wrap) && !e.within(this.list)){
23419             this.collapse();
23420         }
23421     },
23422
23423     /**
23424      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
23425      */
23426     expand : function(){
23427         if(this.isExpanded() || !this.hasFocus){
23428             return;
23429         }
23430         this.list.alignTo(this.el, this.listAlign);
23431         this.list.show();
23432         Roo.get(document).on('mousedown', this.collapseIf, this);
23433         Roo.get(document).on('mousewheel', this.collapseIf, this);
23434         if (!this.editable) {
23435             Roo.get(document).on('keydown', this.listKeyPress, this);
23436         }
23437         
23438         this.fireEvent('expand', this);
23439     },
23440
23441     // private
23442     // Implements the default empty TriggerField.onTriggerClick function
23443     onTriggerClick : function(){
23444         if(this.disabled){
23445             return;
23446         }
23447         if(this.isExpanded()){
23448             this.collapse();
23449             if (!this.blockFocus) {
23450                 this.el.focus();
23451             }
23452             
23453         }else {
23454             this.hasFocus = true;
23455             if(this.triggerAction == 'all') {
23456                 this.doQuery(this.allQuery, true);
23457             } else {
23458                 this.doQuery(this.getRawValue());
23459             }
23460             if (!this.blockFocus) {
23461                 this.el.focus();
23462             }
23463         }
23464     },
23465     listKeyPress : function(e)
23466     {
23467         //Roo.log('listkeypress');
23468         // scroll to first matching element based on key pres..
23469         if (e.isSpecialKey()) {
23470             return false;
23471         }
23472         var k = String.fromCharCode(e.getKey()).toUpperCase();
23473         //Roo.log(k);
23474         var match  = false;
23475         var csel = this.view.getSelectedNodes();
23476         var cselitem = false;
23477         if (csel.length) {
23478             var ix = this.view.indexOf(csel[0]);
23479             cselitem  = this.store.getAt(ix);
23480             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
23481                 cselitem = false;
23482             }
23483             
23484         }
23485         
23486         this.store.each(function(v) { 
23487             if (cselitem) {
23488                 // start at existing selection.
23489                 if (cselitem.id == v.id) {
23490                     cselitem = false;
23491                 }
23492                 return;
23493             }
23494                 
23495             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
23496                 match = this.store.indexOf(v);
23497                 return false;
23498             }
23499         }, this);
23500         
23501         if (match === false) {
23502             return true; // no more action?
23503         }
23504         // scroll to?
23505         this.view.select(match);
23506         var sn = Roo.get(this.view.getSelectedNodes()[0])
23507         sn.scrollIntoView(sn.dom.parentNode, false);
23508     }
23509
23510     /** 
23511     * @cfg {Boolean} grow 
23512     * @hide 
23513     */
23514     /** 
23515     * @cfg {Number} growMin 
23516     * @hide 
23517     */
23518     /** 
23519     * @cfg {Number} growMax 
23520     * @hide 
23521     */
23522     /**
23523      * @hide
23524      * @method autoSize
23525      */
23526 });/*
23527  * Based on:
23528  * Ext JS Library 1.1.1
23529  * Copyright(c) 2006-2007, Ext JS, LLC.
23530  *
23531  * Originally Released Under LGPL - original licence link has changed is not relivant.
23532  *
23533  * Fork - LGPL
23534  * <script type="text/javascript">
23535  */
23536 /**
23537  * @class Roo.form.Checkbox
23538  * @extends Roo.form.Field
23539  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
23540  * @constructor
23541  * Creates a new Checkbox
23542  * @param {Object} config Configuration options
23543  */
23544 Roo.form.Checkbox = function(config){
23545     Roo.form.Checkbox.superclass.constructor.call(this, config);
23546     this.addEvents({
23547         /**
23548          * @event check
23549          * Fires when the checkbox is checked or unchecked.
23550              * @param {Roo.form.Checkbox} this This checkbox
23551              * @param {Boolean} checked The new checked value
23552              */
23553         check : true
23554     });
23555 };
23556
23557 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
23558     /**
23559      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
23560      */
23561     focusClass : undefined,
23562     /**
23563      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
23564      */
23565     fieldClass: "x-form-field",
23566     /**
23567      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
23568      */
23569     checked: false,
23570     /**
23571      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
23572      * {tag: "input", type: "checkbox", autocomplete: "off"})
23573      */
23574     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
23575     /**
23576      * @cfg {String} boxLabel The text that appears beside the checkbox
23577      */
23578     boxLabel : "",
23579     /**
23580      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
23581      */  
23582     inputValue : '1',
23583     /**
23584      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23585      */
23586      valueOff: '0', // value when not checked..
23587
23588     actionMode : 'viewEl', 
23589     //
23590     // private
23591     itemCls : 'x-menu-check-item x-form-item',
23592     groupClass : 'x-menu-group-item',
23593     inputType : 'hidden',
23594     
23595     
23596     inSetChecked: false, // check that we are not calling self...
23597     
23598     inputElement: false, // real input element?
23599     basedOn: false, // ????
23600     
23601     isFormField: true, // not sure where this is needed!!!!
23602
23603     onResize : function(){
23604         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
23605         if(!this.boxLabel){
23606             this.el.alignTo(this.wrap, 'c-c');
23607         }
23608     },
23609
23610     initEvents : function(){
23611         Roo.form.Checkbox.superclass.initEvents.call(this);
23612         this.el.on("click", this.onClick,  this);
23613         this.el.on("change", this.onClick,  this);
23614     },
23615
23616
23617     getResizeEl : function(){
23618         return this.wrap;
23619     },
23620
23621     getPositionEl : function(){
23622         return this.wrap;
23623     },
23624
23625     // private
23626     onRender : function(ct, position){
23627         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
23628         /*
23629         if(this.inputValue !== undefined){
23630             this.el.dom.value = this.inputValue;
23631         }
23632         */
23633         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
23634         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
23635         var viewEl = this.wrap.createChild({ 
23636             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
23637         this.viewEl = viewEl;   
23638         this.wrap.on('click', this.onClick,  this); 
23639         
23640         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
23641         this.el.on('propertychange', this.setFromHidden,  this);  //ie
23642         
23643         
23644         
23645         if(this.boxLabel){
23646             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
23647         //    viewEl.on('click', this.onClick,  this); 
23648         }
23649         //if(this.checked){
23650             this.setChecked(this.checked);
23651         //}else{
23652             //this.checked = this.el.dom;
23653         //}
23654
23655     },
23656
23657     // private
23658     initValue : Roo.emptyFn,
23659
23660     /**
23661      * Returns the checked state of the checkbox.
23662      * @return {Boolean} True if checked, else false
23663      */
23664     getValue : function(){
23665         if(this.el){
23666             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
23667         }
23668         return this.valueOff;
23669         
23670     },
23671
23672         // private
23673     onClick : function(){ 
23674         this.setChecked(!this.checked);
23675
23676         //if(this.el.dom.checked != this.checked){
23677         //    this.setValue(this.el.dom.checked);
23678        // }
23679     },
23680
23681     /**
23682      * Sets the checked state of the checkbox.
23683      * On is always based on a string comparison between inputValue and the param.
23684      * @param {Boolean/String} value - the value to set 
23685      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
23686      */
23687     setValue : function(v,suppressEvent){
23688         
23689         
23690         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
23691         //if(this.el && this.el.dom){
23692         //    this.el.dom.checked = this.checked;
23693         //    this.el.dom.defaultChecked = this.checked;
23694         //}
23695         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
23696         //this.fireEvent("check", this, this.checked);
23697     },
23698     // private..
23699     setChecked : function(state,suppressEvent)
23700     {
23701         if (this.inSetChecked) {
23702             this.checked = state;
23703             return;
23704         }
23705         
23706     
23707         if(this.wrap){
23708             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
23709         }
23710         this.checked = state;
23711         if(suppressEvent !== true){
23712             this.fireEvent('check', this, state);
23713         }
23714         this.inSetChecked = true;
23715         this.el.dom.value = state ? this.inputValue : this.valueOff;
23716         this.inSetChecked = false;
23717         
23718     },
23719     // handle setting of hidden value by some other method!!?!?
23720     setFromHidden: function()
23721     {
23722         if(!this.el){
23723             return;
23724         }
23725         //console.log("SET FROM HIDDEN");
23726         //alert('setFrom hidden');
23727         this.setValue(this.el.dom.value);
23728     },
23729     
23730     onDestroy : function()
23731     {
23732         if(this.viewEl){
23733             Roo.get(this.viewEl).remove();
23734         }
23735          
23736         Roo.form.Checkbox.superclass.onDestroy.call(this);
23737     }
23738
23739 });/*
23740  * Based on:
23741  * Ext JS Library 1.1.1
23742  * Copyright(c) 2006-2007, Ext JS, LLC.
23743  *
23744  * Originally Released Under LGPL - original licence link has changed is not relivant.
23745  *
23746  * Fork - LGPL
23747  * <script type="text/javascript">
23748  */
23749  
23750 /**
23751  * @class Roo.form.Radio
23752  * @extends Roo.form.Checkbox
23753  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
23754  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
23755  * @constructor
23756  * Creates a new Radio
23757  * @param {Object} config Configuration options
23758  */
23759 Roo.form.Radio = function(){
23760     Roo.form.Radio.superclass.constructor.apply(this, arguments);
23761 };
23762 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
23763     inputType: 'radio',
23764
23765     /**
23766      * If this radio is part of a group, it will return the selected value
23767      * @return {String}
23768      */
23769     getGroupValue : function(){
23770         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
23771     }
23772 });//<script type="text/javascript">
23773
23774 /*
23775  * Ext JS Library 1.1.1
23776  * Copyright(c) 2006-2007, Ext JS, LLC.
23777  * licensing@extjs.com
23778  * 
23779  * http://www.extjs.com/license
23780  */
23781  
23782  /*
23783   * 
23784   * Known bugs:
23785   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
23786   * - IE ? - no idea how much works there.
23787   * 
23788   * 
23789   * 
23790   */
23791  
23792
23793 /**
23794  * @class Ext.form.HtmlEditor
23795  * @extends Ext.form.Field
23796  * Provides a lightweight HTML Editor component.
23797  * WARNING - THIS CURRENTlY ONLY WORKS ON FIREFOX - USE FCKeditor for a cross platform version
23798  * 
23799  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
23800  * supported by this editor.</b><br/><br/>
23801  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
23802  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23803  */
23804 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
23805       /**
23806      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23807      */
23808     toolbars : false,
23809     /**
23810      * @cfg {String} createLinkText The default text for the create link prompt
23811      */
23812     createLinkText : 'Please enter the URL for the link:',
23813     /**
23814      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
23815      */
23816     defaultLinkValue : 'http:/'+'/',
23817    
23818     
23819     // id of frame..
23820     frameId: false,
23821     
23822     // private properties
23823     validationEvent : false,
23824     deferHeight: true,
23825     initialized : false,
23826     activated : false,
23827     sourceEditMode : false,
23828     onFocus : Roo.emptyFn,
23829     iframePad:3,
23830     hideMode:'offsets',
23831     defaultAutoCreate : {
23832         tag: "textarea",
23833         style:"width:500px;height:300px;",
23834         autocomplete: "off"
23835     },
23836
23837     // private
23838     initComponent : function(){
23839         this.addEvents({
23840             /**
23841              * @event initialize
23842              * Fires when the editor is fully initialized (including the iframe)
23843              * @param {HtmlEditor} this
23844              */
23845             initialize: true,
23846             /**
23847              * @event activate
23848              * Fires when the editor is first receives the focus. Any insertion must wait
23849              * until after this event.
23850              * @param {HtmlEditor} this
23851              */
23852             activate: true,
23853              /**
23854              * @event beforesync
23855              * Fires before the textarea is updated with content from the editor iframe. Return false
23856              * to cancel the sync.
23857              * @param {HtmlEditor} this
23858              * @param {String} html
23859              */
23860             beforesync: true,
23861              /**
23862              * @event beforepush
23863              * Fires before the iframe editor is updated with content from the textarea. Return false
23864              * to cancel the push.
23865              * @param {HtmlEditor} this
23866              * @param {String} html
23867              */
23868             beforepush: true,
23869              /**
23870              * @event sync
23871              * Fires when the textarea is updated with content from the editor iframe.
23872              * @param {HtmlEditor} this
23873              * @param {String} html
23874              */
23875             sync: true,
23876              /**
23877              * @event push
23878              * Fires when the iframe editor is updated with content from the textarea.
23879              * @param {HtmlEditor} this
23880              * @param {String} html
23881              */
23882             push: true,
23883              /**
23884              * @event editmodechange
23885              * Fires when the editor switches edit modes
23886              * @param {HtmlEditor} this
23887              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23888              */
23889             editmodechange: true,
23890             /**
23891              * @event editorevent
23892              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23893              * @param {HtmlEditor} this
23894              */
23895             editorevent: true
23896         })
23897     },
23898
23899     /**
23900      * Protected method that will not generally be called directly. It
23901      * is called when the editor creates its toolbar. Override this method if you need to
23902      * add custom toolbar buttons.
23903      * @param {HtmlEditor} editor
23904      */
23905     createToolbar : function(editor){
23906         if (!editor.toolbars || !editor.toolbars.length) {
23907             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
23908         }
23909         
23910         for (var i =0 ; i < editor.toolbars.length;i++) {
23911             editor.toolbars[i] = Roo.factory(editor.toolbars[i], Roo.form.HtmlEditor);
23912             editor.toolbars[i].init(editor);
23913         }
23914          
23915         
23916     },
23917
23918     /**
23919      * Protected method that will not generally be called directly. It
23920      * is called when the editor initializes the iframe with HTML contents. Override this method if you
23921      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
23922      */
23923     getDocMarkup : function(){
23924         return '<html><head><style type="text/css">body{border:0;margin:0;padding:3px;height:98%;cursor:text;}</style></head><body></body></html>';
23925     },
23926
23927     // private
23928     onRender : function(ct, position){
23929         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
23930         this.el.dom.style.border = '0 none';
23931         this.el.dom.setAttribute('tabIndex', -1);
23932         this.el.addClass('x-hidden');
23933         if(Roo.isIE){ // fix IE 1px bogus margin
23934             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
23935         }
23936         this.wrap = this.el.wrap({
23937             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23938         });
23939
23940         this.frameId = Roo.id();
23941         this.createToolbar(this);
23942         
23943         
23944         
23945         
23946       
23947         
23948         var iframe = this.wrap.createChild({
23949             tag: 'iframe',
23950             id: this.frameId,
23951             name: this.frameId,
23952             frameBorder : 'no',
23953             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
23954         });
23955         
23956        // console.log(iframe);
23957         //this.wrap.dom.appendChild(iframe);
23958
23959         this.iframe = iframe.dom;
23960
23961          this.assignDocWin();
23962         
23963         this.doc.designMode = 'on';
23964        
23965         this.doc.open();
23966         this.doc.write(this.getDocMarkup());
23967         this.doc.close();
23968
23969         
23970         var task = { // must defer to wait for browser to be ready
23971             run : function(){
23972                 //console.log("run task?" + this.doc.readyState);
23973                 this.assignDocWin();
23974                 if(this.doc.body || this.doc.readyState == 'complete'){
23975                     try {
23976                         this.doc.designMode="on";
23977                     } catch (e) {
23978                         return;
23979                     }
23980                     Roo.TaskMgr.stop(task);
23981                     this.initEditor.defer(10, this);
23982                 }
23983             },
23984             interval : 10,
23985             duration:10000,
23986             scope: this
23987         };
23988         Roo.TaskMgr.start(task);
23989
23990         if(!this.width){
23991             this.setSize(this.el.getSize());
23992         }
23993     },
23994
23995     // private
23996     onResize : function(w, h){
23997         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
23998         if(this.el && this.iframe){
23999             if(typeof w == 'number'){
24000                 var aw = w - this.wrap.getFrameWidth('lr');
24001                 this.el.setWidth(this.adjustWidth('textarea', aw));
24002                 this.iframe.style.width = aw + 'px';
24003             }
24004             if(typeof h == 'number'){
24005                 var tbh = 0;
24006                 for (var i =0; i < this.toolbars.length;i++) {
24007                     // fixme - ask toolbars for heights?
24008                     tbh += this.toolbars[i].tb.el.getHeight();
24009                 }
24010                 
24011                 
24012                 
24013                 
24014                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
24015                 this.el.setHeight(this.adjustWidth('textarea', ah));
24016                 this.iframe.style.height = ah + 'px';
24017                 if(this.doc){
24018                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
24019                 }
24020             }
24021         }
24022     },
24023
24024     /**
24025      * Toggles the editor between standard and source edit mode.
24026      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24027      */
24028     toggleSourceEdit : function(sourceEditMode){
24029         
24030         this.sourceEditMode = sourceEditMode === true;
24031         
24032         if(this.sourceEditMode){
24033           
24034             this.syncValue();
24035             this.iframe.className = 'x-hidden';
24036             this.el.removeClass('x-hidden');
24037             this.el.dom.removeAttribute('tabIndex');
24038             this.el.focus();
24039         }else{
24040              
24041             this.pushValue();
24042             this.iframe.className = '';
24043             this.el.addClass('x-hidden');
24044             this.el.dom.setAttribute('tabIndex', -1);
24045             this.deferFocus();
24046         }
24047         this.setSize(this.wrap.getSize());
24048         this.fireEvent('editmodechange', this, this.sourceEditMode);
24049     },
24050
24051     // private used internally
24052     createLink : function(){
24053         var url = prompt(this.createLinkText, this.defaultLinkValue);
24054         if(url && url != 'http:/'+'/'){
24055             this.relayCmd('createlink', url);
24056         }
24057     },
24058
24059     // private (for BoxComponent)
24060     adjustSize : Roo.BoxComponent.prototype.adjustSize,
24061
24062     // private (for BoxComponent)
24063     getResizeEl : function(){
24064         return this.wrap;
24065     },
24066
24067     // private (for BoxComponent)
24068     getPositionEl : function(){
24069         return this.wrap;
24070     },
24071
24072     // private
24073     initEvents : function(){
24074         this.originalValue = this.getValue();
24075     },
24076
24077     /**
24078      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24079      * @method
24080      */
24081     markInvalid : Roo.emptyFn,
24082     /**
24083      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24084      * @method
24085      */
24086     clearInvalid : Roo.emptyFn,
24087
24088     setValue : function(v){
24089         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
24090         this.pushValue();
24091     },
24092
24093     /**
24094      * Protected method that will not generally be called directly. If you need/want
24095      * custom HTML cleanup, this is the method you should override.
24096      * @param {String} html The HTML to be cleaned
24097      * return {String} The cleaned HTML
24098      */
24099     cleanHtml : function(html){
24100         html = String(html);
24101         if(html.length > 5){
24102             if(Roo.isSafari){ // strip safari nonsense
24103                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24104             }
24105         }
24106         if(html == '&nbsp;'){
24107             html = '';
24108         }
24109         return html;
24110     },
24111
24112     /**
24113      * Protected method that will not generally be called directly. Syncs the contents
24114      * of the editor iframe with the textarea.
24115      */
24116     syncValue : function(){
24117         if(this.initialized){
24118             var bd = (this.doc.body || this.doc.documentElement);
24119             var html = bd.innerHTML;
24120             if(Roo.isSafari){
24121                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24122                 var m = bs.match(/text-align:(.*?);/i);
24123                 if(m && m[1]){
24124                     html = '<div style="'+m[0]+'">' + html + '</div>';
24125                 }
24126             }
24127             html = this.cleanHtml(html);
24128             if(this.fireEvent('beforesync', this, html) !== false){
24129                 this.el.dom.value = html;
24130                 this.fireEvent('sync', this, html);
24131             }
24132         }
24133     },
24134
24135     /**
24136      * Protected method that will not generally be called directly. Pushes the value of the textarea
24137      * into the iframe editor.
24138      */
24139     pushValue : function(){
24140         if(this.initialized){
24141             var v = this.el.dom.value;
24142             if(v.length < 1){
24143                 v = '&#160;';
24144             }
24145             if(this.fireEvent('beforepush', this, v) !== false){
24146                 (this.doc.body || this.doc.documentElement).innerHTML = v;
24147                 this.fireEvent('push', this, v);
24148             }
24149         }
24150     },
24151
24152     // private
24153     deferFocus : function(){
24154         this.focus.defer(10, this);
24155     },
24156
24157     // doc'ed in Field
24158     focus : function(){
24159         if(this.win && !this.sourceEditMode){
24160             this.win.focus();
24161         }else{
24162             this.el.focus();
24163         }
24164     },
24165     
24166     assignDocWin: function()
24167     {
24168         var iframe = this.iframe;
24169         
24170          if(Roo.isIE){
24171             this.doc = iframe.contentWindow.document;
24172             this.win = iframe.contentWindow;
24173         } else {
24174             if (!Roo.get(this.frameId)) {
24175                 return;
24176             }
24177             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24178             this.win = Roo.get(this.frameId).dom.contentWindow;
24179         }
24180     },
24181     
24182     // private
24183     initEditor : function(){
24184         //console.log("INIT EDITOR");
24185         this.assignDocWin();
24186         
24187         
24188         
24189         this.doc.designMode="on";
24190         this.doc.open();
24191         this.doc.write(this.getDocMarkup());
24192         this.doc.close();
24193         
24194         var dbody = (this.doc.body || this.doc.documentElement);
24195         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24196         // this copies styles from the containing element into thsi one..
24197         // not sure why we need all of this..
24198         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24199         ss['background-attachment'] = 'fixed'; // w3c
24200         dbody.bgProperties = 'fixed'; // ie
24201         Roo.DomHelper.applyStyles(dbody, ss);
24202         Roo.EventManager.on(this.doc, {
24203             'mousedown': this.onEditorEvent,
24204             'dblclick': this.onEditorEvent,
24205             'click': this.onEditorEvent,
24206             'keyup': this.onEditorEvent,
24207             buffer:100,
24208             scope: this
24209         });
24210         if(Roo.isGecko){
24211             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24212         }
24213         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24214             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24215         }
24216         this.initialized = true;
24217
24218         this.fireEvent('initialize', this);
24219         this.pushValue();
24220     },
24221
24222     // private
24223     onDestroy : function(){
24224         
24225         
24226         
24227         if(this.rendered){
24228             
24229             for (var i =0; i < this.toolbars.length;i++) {
24230                 // fixme - ask toolbars for heights?
24231                 this.toolbars[i].onDestroy();
24232             }
24233             
24234             this.wrap.dom.innerHTML = '';
24235             this.wrap.remove();
24236         }
24237     },
24238
24239     // private
24240     onFirstFocus : function(){
24241         
24242         this.assignDocWin();
24243         
24244         
24245         this.activated = true;
24246         for (var i =0; i < this.toolbars.length;i++) {
24247             this.toolbars[i].onFirstFocus();
24248         }
24249        
24250         if(Roo.isGecko){ // prevent silly gecko errors
24251             this.win.focus();
24252             var s = this.win.getSelection();
24253             if(!s.focusNode || s.focusNode.nodeType != 3){
24254                 var r = s.getRangeAt(0);
24255                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24256                 r.collapse(true);
24257                 this.deferFocus();
24258             }
24259             try{
24260                 this.execCmd('useCSS', true);
24261                 this.execCmd('styleWithCSS', false);
24262             }catch(e){}
24263         }
24264         this.fireEvent('activate', this);
24265     },
24266
24267     // private
24268     adjustFont: function(btn){
24269         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24270         //if(Roo.isSafari){ // safari
24271         //    adjust *= 2;
24272        // }
24273         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24274         if(Roo.isSafari){ // safari
24275             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24276             v =  (v < 10) ? 10 : v;
24277             v =  (v > 48) ? 48 : v;
24278             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24279             
24280         }
24281         
24282         
24283         v = Math.max(1, v+adjust);
24284         
24285         this.execCmd('FontSize', v  );
24286     },
24287
24288     onEditorEvent : function(e){
24289         this.fireEvent('editorevent', this, e);
24290       //  this.updateToolbar();
24291         this.syncValue();
24292     },
24293
24294     insertTag : function(tg)
24295     {
24296         // could be a bit smarter... -> wrap the current selected tRoo..
24297         
24298         this.execCmd("formatblock",   tg);
24299         
24300     },
24301     
24302     insertText : function(txt)
24303     {
24304         
24305         
24306         range = this.createRange();
24307         range.deleteContents();
24308                //alert(Sender.getAttribute('label'));
24309                
24310         range.insertNode(this.doc.createTextNode(txt));
24311     } ,
24312     
24313     // private
24314     relayBtnCmd : function(btn){
24315         this.relayCmd(btn.cmd);
24316     },
24317
24318     /**
24319      * Executes a Midas editor command on the editor document and performs necessary focus and
24320      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24321      * @param {String} cmd The Midas command
24322      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24323      */
24324     relayCmd : function(cmd, value){
24325         this.win.focus();
24326         this.execCmd(cmd, value);
24327         this.fireEvent('editorevent', this);
24328         //this.updateToolbar();
24329         this.deferFocus();
24330     },
24331
24332     /**
24333      * Executes a Midas editor command directly on the editor document.
24334      * For visual commands, you should use {@link #relayCmd} instead.
24335      * <b>This should only be called after the editor is initialized.</b>
24336      * @param {String} cmd The Midas command
24337      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24338      */
24339     execCmd : function(cmd, value){
24340         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24341         this.syncValue();
24342     },
24343
24344    
24345     /**
24346      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24347      * to insert tRoo.
24348      * @param {String} text
24349      */
24350     insertAtCursor : function(text){
24351         if(!this.activated){
24352             return;
24353         }
24354         if(Roo.isIE){
24355             this.win.focus();
24356             var r = this.doc.selection.createRange();
24357             if(r){
24358                 r.collapse(true);
24359                 r.pasteHTML(text);
24360                 this.syncValue();
24361                 this.deferFocus();
24362             }
24363         }else if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24364             this.win.focus();
24365             this.execCmd('InsertHTML', text);
24366             this.deferFocus();
24367         }
24368     },
24369  // private
24370     mozKeyPress : function(e){
24371         if(e.ctrlKey){
24372             var c = e.getCharCode(), cmd;
24373           
24374             if(c > 0){
24375                 c = String.fromCharCode(c).toLowerCase();
24376                 switch(c){
24377                     case 'b':
24378                         cmd = 'bold';
24379                     break;
24380                     case 'i':
24381                         cmd = 'italic';
24382                     break;
24383                     case 'u':
24384                         cmd = 'underline';
24385                     case 'v':
24386                         this.cleanUpPaste.defer(100, this);
24387                         return;
24388                     break;
24389                 }
24390                 if(cmd){
24391                     this.win.focus();
24392                     this.execCmd(cmd);
24393                     this.deferFocus();
24394                     e.preventDefault();
24395                 }
24396                 
24397             }
24398         }
24399     },
24400
24401     // private
24402     fixKeys : function(){ // load time branching for fastest keydown performance
24403         if(Roo.isIE){
24404             return function(e){
24405                 var k = e.getKey(), r;
24406                 if(k == e.TAB){
24407                     e.stopEvent();
24408                     r = this.doc.selection.createRange();
24409                     if(r){
24410                         r.collapse(true);
24411                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24412                         this.deferFocus();
24413                     }
24414                     return;
24415                 }
24416                 
24417                 if(k == e.ENTER){
24418                     r = this.doc.selection.createRange();
24419                     if(r){
24420                         var target = r.parentElement();
24421                         if(!target || target.tagName.toLowerCase() != 'li'){
24422                             e.stopEvent();
24423                             r.pasteHTML('<br />');
24424                             r.collapse(false);
24425                             r.select();
24426                         }
24427                     }
24428                 }
24429                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24430                     this.cleanUpPaste.defer(100, this);
24431                     return;
24432                 }
24433                 
24434                 
24435             };
24436         }else if(Roo.isOpera){
24437             return function(e){
24438                 var k = e.getKey();
24439                 if(k == e.TAB){
24440                     e.stopEvent();
24441                     this.win.focus();
24442                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24443                     this.deferFocus();
24444                 }
24445                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24446                     this.cleanUpPaste.defer(100, this);
24447                     return;
24448                 }
24449                 
24450             };
24451         }else if(Roo.isSafari){
24452             return function(e){
24453                 var k = e.getKey();
24454                 
24455                 if(k == e.TAB){
24456                     e.stopEvent();
24457                     this.execCmd('InsertText','\t');
24458                     this.deferFocus();
24459                     return;
24460                 }
24461                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24462                     this.cleanUpPaste.defer(100, this);
24463                     return;
24464                 }
24465                 
24466              };
24467         }
24468     }(),
24469     
24470     getAllAncestors: function()
24471     {
24472         var p = this.getSelectedNode();
24473         var a = [];
24474         if (!p) {
24475             a.push(p); // push blank onto stack..
24476             p = this.getParentElement();
24477         }
24478         
24479         
24480         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24481             a.push(p);
24482             p = p.parentNode;
24483         }
24484         a.push(this.doc.body);
24485         return a;
24486     },
24487     lastSel : false,
24488     lastSelNode : false,
24489     
24490     
24491     getSelection : function() 
24492     {
24493         this.assignDocWin();
24494         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24495     },
24496     
24497     getSelectedNode: function() 
24498     {
24499         // this may only work on Gecko!!!
24500         
24501         // should we cache this!!!!
24502         
24503         
24504         
24505          
24506         var range = this.createRange(this.getSelection());
24507         
24508         if (Roo.isIE) {
24509             var parent = range.parentElement();
24510             while (true) {
24511                 var testRange = range.duplicate();
24512                 testRange.moveToElementText(parent);
24513                 if (testRange.inRange(range)) {
24514                     break;
24515                 }
24516                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24517                     break;
24518                 }
24519                 parent = parent.parentElement;
24520             }
24521             return parent;
24522         }
24523         
24524         
24525         var ar = range.endContainer.childNodes;
24526         if (!ar.length) {
24527             ar = range.commonAncestorContainer.childNodes;
24528             //alert(ar.length);
24529         }
24530         var nodes = [];
24531         var other_nodes = [];
24532         var has_other_nodes = false;
24533         for (var i=0;i<ar.length;i++) {
24534             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24535                 continue;
24536             }
24537             // fullly contained node.
24538             
24539             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24540                 nodes.push(ar[i]);
24541                 continue;
24542             }
24543             
24544             // probably selected..
24545             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24546                 other_nodes.push(ar[i]);
24547                 continue;
24548             }
24549             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24550                 continue;
24551             }
24552             
24553             
24554             has_other_nodes = true;
24555         }
24556         if (!nodes.length && other_nodes.length) {
24557             nodes= other_nodes;
24558         }
24559         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24560             return false;
24561         }
24562         
24563         return nodes[0];
24564     },
24565     createRange: function(sel)
24566     {
24567         // this has strange effects when using with 
24568         // top toolbar - not sure if it's a great idea.
24569         //this.editor.contentWindow.focus();
24570         if (typeof sel != "undefined") {
24571             try {
24572                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24573             } catch(e) {
24574                 return this.doc.createRange();
24575             }
24576         } else {
24577             return this.doc.createRange();
24578         }
24579     },
24580     getParentElement: function()
24581     {
24582         
24583         this.assignDocWin();
24584         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24585         
24586         var range = this.createRange(sel);
24587          
24588         try {
24589             var p = range.commonAncestorContainer;
24590             while (p.nodeType == 3) { // text node
24591                 p = p.parentNode;
24592             }
24593             return p;
24594         } catch (e) {
24595             return null;
24596         }
24597     
24598     },
24599     
24600     
24601     
24602     // BC Hacks - cause I cant work out what i was trying to do..
24603     rangeIntersectsNode : function(range, node)
24604     {
24605         var nodeRange = node.ownerDocument.createRange();
24606         try {
24607             nodeRange.selectNode(node);
24608         }
24609         catch (e) {
24610             nodeRange.selectNodeContents(node);
24611         }
24612
24613         return range.compareBoundaryPoints(Range.END_TO_START, nodeRange) == -1 &&
24614                  range.compareBoundaryPoints(Range.START_TO_END, nodeRange) == 1;
24615     },
24616     rangeCompareNode : function(range, node) {
24617         var nodeRange = node.ownerDocument.createRange();
24618         try {
24619             nodeRange.selectNode(node);
24620         } catch (e) {
24621             nodeRange.selectNodeContents(node);
24622         }
24623         var nodeIsBefore = range.compareBoundaryPoints(Range.START_TO_START, nodeRange) == 1;
24624         var nodeIsAfter = range.compareBoundaryPoints(Range.END_TO_END, nodeRange) == -1;
24625
24626         if (nodeIsBefore && !nodeIsAfter)
24627             return 0;
24628         if (!nodeIsBefore && nodeIsAfter)
24629             return 1;
24630         if (nodeIsBefore && nodeIsAfter)
24631             return 2;
24632
24633         return 3;
24634     },
24635
24636     // private? - in a new class?
24637     cleanUpPaste :  function()
24638     {
24639         // cleans up the whole document..
24640       //  console.log('cleanuppaste');
24641         this.cleanUpChildren(this.doc.body)
24642         
24643         
24644     },
24645     cleanUpChildren : function (n)
24646     {
24647         if (!n.childNodes.length) {
24648             return;
24649         }
24650         for (var i = n.childNodes.length-1; i > -1 ; i--) {
24651            this.cleanUpChild(n.childNodes[i]);
24652         }
24653     },
24654     
24655     
24656         
24657     
24658     cleanUpChild : function (node)
24659     {
24660         //console.log(node);
24661         if (node.nodeName == "#text") {
24662             // clean up silly Windows -- stuff?
24663             return; 
24664         }
24665         if (node.nodeName == "#comment") {
24666             node.parentNode.removeChild(node);
24667             // clean up silly Windows -- stuff?
24668             return; 
24669         }
24670         
24671         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
24672             // remove node.
24673             node.parentNode.removeChild(node);
24674             return;
24675             
24676         }
24677         if (!node.attributes || !node.attributes.length) {
24678             this.cleanUpChildren(node);
24679             return;
24680         }
24681         
24682         function cleanAttr(n,v)
24683         {
24684             
24685             if (v.match(/^\./) || v.match(/^\//)) {
24686                 return;
24687             }
24688             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
24689                 return;
24690             }
24691             Roo.log("(REMOVE)"+ node.tagName +'.' + n + '=' + v);
24692             node.removeAttribute(n);
24693             
24694         }
24695         
24696         function cleanStyle(n,v)
24697         {
24698             if (v.match(/expression/)) { //XSS?? should we even bother..
24699                 node.removeAttribute(n);
24700                 return;
24701             }
24702             
24703             
24704             var parts = v.split(/;/);
24705             Roo.each(parts, function(p) {
24706                 p = p.replace(/\s+/g,'');
24707                 if (!p.length) {
24708                     return;
24709                 }
24710                 var l = p.split(':').shift().replace(/\s+/g,'');
24711                 
24712                 if (Roo.form.HtmlEditor.cwhite.indexOf(l) < 0) {
24713                     Roo.log('(REMOVE)' + node.tagName +'.' + n + ':'+l + '=' + v);
24714                     node.removeAttribute(n);
24715                     return false;
24716                 }
24717             });
24718             
24719             
24720         }
24721         
24722         
24723         for (var i = node.attributes.length-1; i > -1 ; i--) {
24724             var a = node.attributes[i];
24725             //console.log(a);
24726             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
24727                 node.removeAttribute(a.name);
24728                 return;
24729             }
24730             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
24731                 cleanAttr(a.name,a.value); // fixme..
24732                 return;
24733             }
24734             if (a.name == 'style') {
24735                 cleanStyle(a.name,a.value);
24736             }
24737             /// clean up MS crap..
24738             if (a.name == 'class') {
24739                 if (a.value.match(/^Mso/)) {
24740                     node.className = '';
24741                 }
24742             }
24743             
24744             // style cleanup!?
24745             // class cleanup?
24746             
24747         }
24748         
24749         
24750         this.cleanUpChildren(node);
24751         
24752         
24753     }
24754     
24755     
24756     // hide stuff that is not compatible
24757     /**
24758      * @event blur
24759      * @hide
24760      */
24761     /**
24762      * @event change
24763      * @hide
24764      */
24765     /**
24766      * @event focus
24767      * @hide
24768      */
24769     /**
24770      * @event specialkey
24771      * @hide
24772      */
24773     /**
24774      * @cfg {String} fieldClass @hide
24775      */
24776     /**
24777      * @cfg {String} focusClass @hide
24778      */
24779     /**
24780      * @cfg {String} autoCreate @hide
24781      */
24782     /**
24783      * @cfg {String} inputType @hide
24784      */
24785     /**
24786      * @cfg {String} invalidClass @hide
24787      */
24788     /**
24789      * @cfg {String} invalidText @hide
24790      */
24791     /**
24792      * @cfg {String} msgFx @hide
24793      */
24794     /**
24795      * @cfg {String} validateOnBlur @hide
24796      */
24797 });
24798
24799 Roo.form.HtmlEditor.white = [
24800         'area', 'br', 'img', 'input', 'hr', 'wbr',
24801         
24802        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
24803        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
24804        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
24805        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
24806        'table',   'ul',         'xmp', 
24807        
24808        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
24809       'thead',   'tr', 
24810      
24811       'dir', 'menu', 'ol', 'ul', 'dl',
24812        
24813       'embed',  'object'
24814 ];
24815
24816
24817 Roo.form.HtmlEditor.black = [
24818     //    'embed',  'object', // enable - backend responsiblity to clean thiese
24819         'applet', // 
24820         'base',   'basefont', 'bgsound', 'blink',  'body', 
24821         'frame',  'frameset', 'head',    'html',   'ilayer', 
24822         'iframe', 'layer',  'link',     'meta',    'object',   
24823         'script', 'style' ,'title',  'xml' // clean later..
24824 ];
24825 Roo.form.HtmlEditor.clean = [
24826     'script', 'style', 'title', 'xml'
24827 ];
24828
24829 // attributes..
24830
24831 Roo.form.HtmlEditor.ablack = [
24832     'on'
24833 ];
24834     
24835 Roo.form.HtmlEditor.aclean = [ 
24836     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
24837 ];
24838
24839 // protocols..
24840 Roo.form.HtmlEditor.pwhite= [
24841         'http',  'https',  'mailto'
24842 ];
24843
24844 Roo.form.HtmlEditor.cwhite= [
24845         'text-align',
24846         'font-size'
24847 ];
24848
24849 // <script type="text/javascript">
24850 /*
24851  * Based on
24852  * Ext JS Library 1.1.1
24853  * Copyright(c) 2006-2007, Ext JS, LLC.
24854  *  
24855  
24856  */
24857
24858 /**
24859  * @class Roo.form.HtmlEditorToolbar1
24860  * Basic Toolbar
24861  * 
24862  * Usage:
24863  *
24864  new Roo.form.HtmlEditor({
24865     ....
24866     toolbars : [
24867         new Roo.form.HtmlEditorToolbar1({
24868             disable : { fonts: 1 , format: 1, ..., ... , ...],
24869             btns : [ .... ]
24870         })
24871     }
24872      
24873  * 
24874  * @cfg {Object} disable List of elements to disable..
24875  * @cfg {Array} btns List of additional buttons.
24876  * 
24877  * 
24878  * NEEDS Extra CSS? 
24879  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
24880  */
24881  
24882 Roo.form.HtmlEditor.ToolbarStandard = function(config)
24883 {
24884     
24885     Roo.apply(this, config);
24886     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
24887     // dont call parent... till later.
24888 }
24889
24890 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
24891     
24892     tb: false,
24893     
24894     rendered: false,
24895     
24896     editor : false,
24897     /**
24898      * @cfg {Object} disable  List of toolbar elements to disable
24899          
24900      */
24901     disable : false,
24902       /**
24903      * @cfg {Array} fontFamilies An array of available font families
24904      */
24905     fontFamilies : [
24906         'Arial',
24907         'Courier New',
24908         'Tahoma',
24909         'Times New Roman',
24910         'Verdana'
24911     ],
24912     
24913     specialChars : [
24914            "&#169;",
24915           "&#174;",     
24916           "&#8482;",    
24917           "&#163;" ,    
24918          // "&#8212;",    
24919           "&#8230;",    
24920           "&#247;" ,    
24921         //  "&#225;" ,     ?? a acute?
24922            "&#8364;"    , //Euro
24923        //   "&#8220;"    ,
24924         //  "&#8221;"    ,
24925         //  "&#8226;"    ,
24926           "&#176;"  //   , // degrees
24927
24928          // "&#233;"     , // e ecute
24929          // "&#250;"     , // u ecute?
24930     ],
24931     inputElements : [ 
24932             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
24933             "input:submit", "input:button", "select", "textarea", "label" ],
24934     formats : [
24935         ["p"] ,  
24936         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
24937         ["pre"],[ "code"], 
24938         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
24939     ],
24940      /**
24941      * @cfg {String} defaultFont default font to use.
24942      */
24943     defaultFont: 'tahoma',
24944    
24945     fontSelect : false,
24946     
24947     
24948     formatCombo : false,
24949     
24950     init : function(editor)
24951     {
24952         this.editor = editor;
24953         
24954         
24955         var fid = editor.frameId;
24956         var etb = this;
24957         function btn(id, toggle, handler){
24958             var xid = fid + '-'+ id ;
24959             return {
24960                 id : xid,
24961                 cmd : id,
24962                 cls : 'x-btn-icon x-edit-'+id,
24963                 enableToggle:toggle !== false,
24964                 scope: editor, // was editor...
24965                 handler:handler||editor.relayBtnCmd,
24966                 clickEvent:'mousedown',
24967                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
24968                 tabIndex:-1
24969             };
24970         }
24971         
24972         
24973         
24974         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
24975         this.tb = tb;
24976          // stop form submits
24977         tb.el.on('click', function(e){
24978             e.preventDefault(); // what does this do?
24979         });
24980
24981         if(!this.disable.font && !Roo.isSafari){
24982             /* why no safari for fonts
24983             editor.fontSelect = tb.el.createChild({
24984                 tag:'select',
24985                 tabIndex: -1,
24986                 cls:'x-font-select',
24987                 html: editor.createFontOptions()
24988             });
24989             editor.fontSelect.on('change', function(){
24990                 var font = editor.fontSelect.dom.value;
24991                 editor.relayCmd('fontname', font);
24992                 editor.deferFocus();
24993             }, editor);
24994             tb.add(
24995                 editor.fontSelect.dom,
24996                 '-'
24997             );
24998             */
24999         };
25000         if(!this.disable.formats){
25001             this.formatCombo = new Roo.form.ComboBox({
25002                 store: new Roo.data.SimpleStore({
25003                     id : 'tag',
25004                     fields: ['tag'],
25005                     data : this.formats // from states.js
25006                 }),
25007                 blockFocus : true,
25008                 //autoCreate : {tag: "div",  size: "20"},
25009                 displayField:'tag',
25010                 typeAhead: false,
25011                 mode: 'local',
25012                 editable : false,
25013                 triggerAction: 'all',
25014                 emptyText:'Add tag',
25015                 selectOnFocus:true,
25016                 width:135,
25017                 listeners : {
25018                     'select': function(c, r, i) {
25019                         editor.insertTag(r.get('tag'));
25020                         editor.focus();
25021                     }
25022                 }
25023
25024             });
25025             tb.addField(this.formatCombo);
25026             
25027         }
25028         
25029         if(!this.disable.format){
25030             tb.add(
25031                 btn('bold'),
25032                 btn('italic'),
25033                 btn('underline')
25034             );
25035         };
25036         if(!this.disable.fontSize){
25037             tb.add(
25038                 '-',
25039                 
25040                 
25041                 btn('increasefontsize', false, editor.adjustFont),
25042                 btn('decreasefontsize', false, editor.adjustFont)
25043             );
25044         };
25045         
25046         
25047         if(this.disable.colors){
25048             tb.add(
25049                 '-', {
25050                     id:editor.frameId +'-forecolor',
25051                     cls:'x-btn-icon x-edit-forecolor',
25052                     clickEvent:'mousedown',
25053                     tooltip: this.buttonTips['forecolor'] || undefined,
25054                     tabIndex:-1,
25055                     menu : new Roo.menu.ColorMenu({
25056                         allowReselect: true,
25057                         focus: Roo.emptyFn,
25058                         value:'000000',
25059                         plain:true,
25060                         selectHandler: function(cp, color){
25061                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
25062                             editor.deferFocus();
25063                         },
25064                         scope: editor,
25065                         clickEvent:'mousedown'
25066                     })
25067                 }, {
25068                     id:editor.frameId +'backcolor',
25069                     cls:'x-btn-icon x-edit-backcolor',
25070                     clickEvent:'mousedown',
25071                     tooltip: this.buttonTips['backcolor'] || undefined,
25072                     tabIndex:-1,
25073                     menu : new Roo.menu.ColorMenu({
25074                         focus: Roo.emptyFn,
25075                         value:'FFFFFF',
25076                         plain:true,
25077                         allowReselect: true,
25078                         selectHandler: function(cp, color){
25079                             if(Roo.isGecko){
25080                                 editor.execCmd('useCSS', false);
25081                                 editor.execCmd('hilitecolor', color);
25082                                 editor.execCmd('useCSS', true);
25083                                 editor.deferFocus();
25084                             }else{
25085                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
25086                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
25087                                 editor.deferFocus();
25088                             }
25089                         },
25090                         scope:editor,
25091                         clickEvent:'mousedown'
25092                     })
25093                 }
25094             );
25095         };
25096         // now add all the items...
25097         
25098
25099         if(!this.disable.alignments){
25100             tb.add(
25101                 '-',
25102                 btn('justifyleft'),
25103                 btn('justifycenter'),
25104                 btn('justifyright')
25105             );
25106         };
25107
25108         //if(!Roo.isSafari){
25109             if(!this.disable.links){
25110                 tb.add(
25111                     '-',
25112                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
25113                 );
25114             };
25115
25116             if(!this.disable.lists){
25117                 tb.add(
25118                     '-',
25119                     btn('insertorderedlist'),
25120                     btn('insertunorderedlist')
25121                 );
25122             }
25123             if(!this.disable.sourceEdit){
25124                 tb.add(
25125                     '-',
25126                     btn('sourceedit', true, function(btn){
25127                         this.toggleSourceEdit(btn.pressed);
25128                     })
25129                 );
25130             }
25131         //}
25132         
25133         var smenu = { };
25134         // special menu.. - needs to be tidied up..
25135         if (!this.disable.special) {
25136             smenu = {
25137                 text: "&#169;",
25138                 cls: 'x-edit-none',
25139                 menu : {
25140                     items : []
25141                    }
25142             };
25143             for (var i =0; i < this.specialChars.length; i++) {
25144                 smenu.menu.items.push({
25145                     
25146                     html: this.specialChars[i],
25147                     handler: function(a,b) {
25148                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
25149                         
25150                     },
25151                     tabIndex:-1
25152                 });
25153             }
25154             
25155             
25156             tb.add(smenu);
25157             
25158             
25159         }
25160         if (this.btns) {
25161             for(var i =0; i< this.btns.length;i++) {
25162                 var b = this.btns[i];
25163                 b.cls =  'x-edit-none';
25164                 b.scope = editor;
25165                 tb.add(b);
25166             }
25167         
25168         }
25169         
25170         
25171         
25172         // disable everything...
25173         
25174         this.tb.items.each(function(item){
25175            if(item.id != editor.frameId+ '-sourceedit'){
25176                 item.disable();
25177             }
25178         });
25179         this.rendered = true;
25180         
25181         // the all the btns;
25182         editor.on('editorevent', this.updateToolbar, this);
25183         // other toolbars need to implement this..
25184         //editor.on('editmodechange', this.updateToolbar, this);
25185     },
25186     
25187     
25188     
25189     /**
25190      * Protected method that will not generally be called directly. It triggers
25191      * a toolbar update by reading the markup state of the current selection in the editor.
25192      */
25193     updateToolbar: function(){
25194
25195         if(!this.editor.activated){
25196             this.editor.onFirstFocus();
25197             return;
25198         }
25199
25200         var btns = this.tb.items.map, 
25201             doc = this.editor.doc,
25202             frameId = this.editor.frameId;
25203
25204         if(!this.disable.font && !Roo.isSafari){
25205             /*
25206             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
25207             if(name != this.fontSelect.dom.value){
25208                 this.fontSelect.dom.value = name;
25209             }
25210             */
25211         }
25212         if(!this.disable.format){
25213             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
25214             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
25215             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
25216         }
25217         if(!this.disable.alignments){
25218             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
25219             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
25220             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
25221         }
25222         if(!Roo.isSafari && !this.disable.lists){
25223             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
25224             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
25225         }
25226         
25227         var ans = this.editor.getAllAncestors();
25228         if (this.formatCombo) {
25229             
25230             
25231             var store = this.formatCombo.store;
25232             this.formatCombo.setValue("");
25233             for (var i =0; i < ans.length;i++) {
25234                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
25235                     // select it..
25236                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
25237                     break;
25238                 }
25239             }
25240         }
25241         
25242         
25243         
25244         // hides menus... - so this cant be on a menu...
25245         Roo.menu.MenuMgr.hideAll();
25246
25247         //this.editorsyncValue();
25248     },
25249    
25250     
25251     createFontOptions : function(){
25252         var buf = [], fs = this.fontFamilies, ff, lc;
25253         for(var i = 0, len = fs.length; i< len; i++){
25254             ff = fs[i];
25255             lc = ff.toLowerCase();
25256             buf.push(
25257                 '<option value="',lc,'" style="font-family:',ff,';"',
25258                     (this.defaultFont == lc ? ' selected="true">' : '>'),
25259                     ff,
25260                 '</option>'
25261             );
25262         }
25263         return buf.join('');
25264     },
25265     
25266     toggleSourceEdit : function(sourceEditMode){
25267         if(sourceEditMode === undefined){
25268             sourceEditMode = !this.sourceEditMode;
25269         }
25270         this.sourceEditMode = sourceEditMode === true;
25271         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
25272         // just toggle the button?
25273         if(btn.pressed !== this.editor.sourceEditMode){
25274             btn.toggle(this.editor.sourceEditMode);
25275             return;
25276         }
25277         
25278         if(this.sourceEditMode){
25279             this.tb.items.each(function(item){
25280                 if(item.cmd != 'sourceedit'){
25281                     item.disable();
25282                 }
25283             });
25284           
25285         }else{
25286             if(this.initialized){
25287                 this.tb.items.each(function(item){
25288                     item.enable();
25289                 });
25290             }
25291             
25292         }
25293         // tell the editor that it's been pressed..
25294         this.editor.toggleSourceEdit(sourceEditMode);
25295        
25296     },
25297      /**
25298      * Object collection of toolbar tooltips for the buttons in the editor. The key
25299      * is the command id associated with that button and the value is a valid QuickTips object.
25300      * For example:
25301 <pre><code>
25302 {
25303     bold : {
25304         title: 'Bold (Ctrl+B)',
25305         text: 'Make the selected text bold.',
25306         cls: 'x-html-editor-tip'
25307     },
25308     italic : {
25309         title: 'Italic (Ctrl+I)',
25310         text: 'Make the selected text italic.',
25311         cls: 'x-html-editor-tip'
25312     },
25313     ...
25314 </code></pre>
25315     * @type Object
25316      */
25317     buttonTips : {
25318         bold : {
25319             title: 'Bold (Ctrl+B)',
25320             text: 'Make the selected text bold.',
25321             cls: 'x-html-editor-tip'
25322         },
25323         italic : {
25324             title: 'Italic (Ctrl+I)',
25325             text: 'Make the selected text italic.',
25326             cls: 'x-html-editor-tip'
25327         },
25328         underline : {
25329             title: 'Underline (Ctrl+U)',
25330             text: 'Underline the selected text.',
25331             cls: 'x-html-editor-tip'
25332         },
25333         increasefontsize : {
25334             title: 'Grow Text',
25335             text: 'Increase the font size.',
25336             cls: 'x-html-editor-tip'
25337         },
25338         decreasefontsize : {
25339             title: 'Shrink Text',
25340             text: 'Decrease the font size.',
25341             cls: 'x-html-editor-tip'
25342         },
25343         backcolor : {
25344             title: 'Text Highlight Color',
25345             text: 'Change the background color of the selected text.',
25346             cls: 'x-html-editor-tip'
25347         },
25348         forecolor : {
25349             title: 'Font Color',
25350             text: 'Change the color of the selected text.',
25351             cls: 'x-html-editor-tip'
25352         },
25353         justifyleft : {
25354             title: 'Align Text Left',
25355             text: 'Align text to the left.',
25356             cls: 'x-html-editor-tip'
25357         },
25358         justifycenter : {
25359             title: 'Center Text',
25360             text: 'Center text in the editor.',
25361             cls: 'x-html-editor-tip'
25362         },
25363         justifyright : {
25364             title: 'Align Text Right',
25365             text: 'Align text to the right.',
25366             cls: 'x-html-editor-tip'
25367         },
25368         insertunorderedlist : {
25369             title: 'Bullet List',
25370             text: 'Start a bulleted list.',
25371             cls: 'x-html-editor-tip'
25372         },
25373         insertorderedlist : {
25374             title: 'Numbered List',
25375             text: 'Start a numbered list.',
25376             cls: 'x-html-editor-tip'
25377         },
25378         createlink : {
25379             title: 'Hyperlink',
25380             text: 'Make the selected text a hyperlink.',
25381             cls: 'x-html-editor-tip'
25382         },
25383         sourceedit : {
25384             title: 'Source Edit',
25385             text: 'Switch to source editing mode.',
25386             cls: 'x-html-editor-tip'
25387         }
25388     },
25389     // private
25390     onDestroy : function(){
25391         if(this.rendered){
25392             
25393             this.tb.items.each(function(item){
25394                 if(item.menu){
25395                     item.menu.removeAll();
25396                     if(item.menu.el){
25397                         item.menu.el.destroy();
25398                     }
25399                 }
25400                 item.destroy();
25401             });
25402              
25403         }
25404     },
25405     onFirstFocus: function() {
25406         this.tb.items.each(function(item){
25407            item.enable();
25408         });
25409     }
25410 });
25411
25412
25413
25414
25415 // <script type="text/javascript">
25416 /*
25417  * Based on
25418  * Ext JS Library 1.1.1
25419  * Copyright(c) 2006-2007, Ext JS, LLC.
25420  *  
25421  
25422  */
25423
25424  
25425 /**
25426  * @class Roo.form.HtmlEditor.ToolbarContext
25427  * Context Toolbar
25428  * 
25429  * Usage:
25430  *
25431  new Roo.form.HtmlEditor({
25432     ....
25433     toolbars : [
25434         new Roo.form.HtmlEditor.ToolbarStandard(),
25435         new Roo.form.HtmlEditor.ToolbarContext()
25436         })
25437     }
25438      
25439  * 
25440  * @config : {Object} disable List of elements to disable.. (not done yet.)
25441  * 
25442  * 
25443  */
25444
25445 Roo.form.HtmlEditor.ToolbarContext = function(config)
25446 {
25447     
25448     Roo.apply(this, config);
25449     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
25450     // dont call parent... till later.
25451 }
25452 Roo.form.HtmlEditor.ToolbarContext.types = {
25453     'IMG' : {
25454         width : {
25455             title: "Width",
25456             width: 40
25457         },
25458         height:  {
25459             title: "Height",
25460             width: 40
25461         },
25462         align: {
25463             title: "Align",
25464             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
25465             width : 80
25466             
25467         },
25468         border: {
25469             title: "Border",
25470             width: 40
25471         },
25472         alt: {
25473             title: "Alt",
25474             width: 120
25475         },
25476         src : {
25477             title: "Src",
25478             width: 220
25479         }
25480         
25481     },
25482     'A' : {
25483         name : {
25484             title: "Name",
25485             width: 50
25486         },
25487         href:  {
25488             title: "Href",
25489             width: 220
25490         } // border?
25491         
25492     },
25493     'TABLE' : {
25494         rows : {
25495             title: "Rows",
25496             width: 20
25497         },
25498         cols : {
25499             title: "Cols",
25500             width: 20
25501         },
25502         width : {
25503             title: "Width",
25504             width: 40
25505         },
25506         height : {
25507             title: "Height",
25508             width: 40
25509         },
25510         border : {
25511             title: "Border",
25512             width: 20
25513         }
25514     },
25515     'TD' : {
25516         width : {
25517             title: "Width",
25518             width: 40
25519         },
25520         height : {
25521             title: "Height",
25522             width: 40
25523         },   
25524         align: {
25525             title: "Align",
25526             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
25527             width: 40
25528         },
25529         valign: {
25530             title: "Valign",
25531             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
25532             width: 40
25533         },
25534         colspan: {
25535             title: "Colspan",
25536             width: 20
25537             
25538         }
25539     },
25540     'INPUT' : {
25541         name : {
25542             title: "name",
25543             width: 120
25544         },
25545         value : {
25546             title: "Value",
25547             width: 120
25548         },
25549         width : {
25550             title: "Width",
25551             width: 40
25552         }
25553     },
25554     'LABEL' : {
25555         'for' : {
25556             title: "For",
25557             width: 120
25558         }
25559     },
25560     'TEXTAREA' : {
25561           name : {
25562             title: "name",
25563             width: 120
25564         },
25565         rows : {
25566             title: "Rows",
25567             width: 20
25568         },
25569         cols : {
25570             title: "Cols",
25571             width: 20
25572         }
25573     },
25574     'SELECT' : {
25575         name : {
25576             title: "name",
25577             width: 120
25578         },
25579         selectoptions : {
25580             title: "Options",
25581             width: 200
25582         }
25583     },
25584     'BODY' : {
25585         title : {
25586             title: "title",
25587             width: 120,
25588             disabled : true
25589         }
25590     }
25591 };
25592
25593
25594
25595 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
25596     
25597     tb: false,
25598     
25599     rendered: false,
25600     
25601     editor : false,
25602     /**
25603      * @cfg {Object} disable  List of toolbar elements to disable
25604          
25605      */
25606     disable : false,
25607     
25608     
25609     
25610     toolbars : false,
25611     
25612     init : function(editor)
25613     {
25614         this.editor = editor;
25615         
25616         
25617         var fid = editor.frameId;
25618         var etb = this;
25619         function btn(id, toggle, handler){
25620             var xid = fid + '-'+ id ;
25621             return {
25622                 id : xid,
25623                 cmd : id,
25624                 cls : 'x-btn-icon x-edit-'+id,
25625                 enableToggle:toggle !== false,
25626                 scope: editor, // was editor...
25627                 handler:handler||editor.relayBtnCmd,
25628                 clickEvent:'mousedown',
25629                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
25630                 tabIndex:-1
25631             };
25632         }
25633         // create a new element.
25634         var wdiv = editor.wrap.createChild({
25635                 tag: 'div'
25636             }, editor.wrap.dom.firstChild.nextSibling, true);
25637         
25638         // can we do this more than once??
25639         
25640          // stop form submits
25641       
25642  
25643         // disable everything...
25644         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
25645         this.toolbars = {};
25646            
25647         for (var i in  ty) {
25648           
25649             this.toolbars[i] = this.buildToolbar(ty[i],i);
25650         }
25651         this.tb = this.toolbars.BODY;
25652         this.tb.el.show();
25653         
25654          
25655         this.rendered = true;
25656         
25657         // the all the btns;
25658         editor.on('editorevent', this.updateToolbar, this);
25659         // other toolbars need to implement this..
25660         //editor.on('editmodechange', this.updateToolbar, this);
25661     },
25662     
25663     
25664     
25665     /**
25666      * Protected method that will not generally be called directly. It triggers
25667      * a toolbar update by reading the markup state of the current selection in the editor.
25668      */
25669     updateToolbar: function(){
25670
25671         if(!this.editor.activated){
25672             this.editor.onFirstFocus();
25673             return;
25674         }
25675
25676         
25677         var ans = this.editor.getAllAncestors();
25678         
25679         // pick
25680         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
25681         var sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
25682         sel = sel ? sel : this.editor.doc.body;
25683         sel = sel.tagName.length ? sel : this.editor.doc.body;
25684         var tn = sel.tagName.toUpperCase();
25685         sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
25686         tn = sel.tagName.toUpperCase();
25687         if (this.tb.name  == tn) {
25688             return; // no change
25689         }
25690         this.tb.el.hide();
25691         ///console.log("show: " + tn);
25692         this.tb =  this.toolbars[tn];
25693         this.tb.el.show();
25694         this.tb.fields.each(function(e) {
25695             e.setValue(sel.getAttribute(e.name));
25696         });
25697         this.tb.selectedNode = sel;
25698         
25699         
25700         Roo.menu.MenuMgr.hideAll();
25701
25702         //this.editorsyncValue();
25703     },
25704    
25705        
25706     // private
25707     onDestroy : function(){
25708         if(this.rendered){
25709             
25710             this.tb.items.each(function(item){
25711                 if(item.menu){
25712                     item.menu.removeAll();
25713                     if(item.menu.el){
25714                         item.menu.el.destroy();
25715                     }
25716                 }
25717                 item.destroy();
25718             });
25719              
25720         }
25721     },
25722     onFirstFocus: function() {
25723         // need to do this for all the toolbars..
25724         this.tb.items.each(function(item){
25725            item.enable();
25726         });
25727     },
25728     buildToolbar: function(tlist, nm)
25729     {
25730         var editor = this.editor;
25731          // create a new element.
25732         var wdiv = editor.wrap.createChild({
25733                 tag: 'div'
25734             }, editor.wrap.dom.firstChild.nextSibling, true);
25735         
25736        
25737         var tb = new Roo.Toolbar(wdiv);
25738         tb.add(nm+ ":&nbsp;");
25739         for (var i in tlist) {
25740             var item = tlist[i];
25741             tb.add(item.title + ":&nbsp;");
25742             if (item.opts) {
25743                 // fixme
25744                 
25745               
25746                 tb.addField( new Roo.form.ComboBox({
25747                     store: new Roo.data.SimpleStore({
25748                         id : 'val',
25749                         fields: ['val'],
25750                         data : item.opts // from states.js
25751                     }),
25752                     name : i,
25753                     displayField:'val',
25754                     typeAhead: false,
25755                     mode: 'local',
25756                     editable : false,
25757                     triggerAction: 'all',
25758                     emptyText:'Select',
25759                     selectOnFocus:true,
25760                     width: item.width ? item.width  : 130,
25761                     listeners : {
25762                         'select': function(c, r, i) {
25763                             tb.selectedNode.setAttribute(c.name, r.get('val'));
25764                         }
25765                     }
25766
25767                 }));
25768                 continue;
25769                     
25770                 
25771                 
25772                 
25773                 
25774                 tb.addField( new Roo.form.TextField({
25775                     name: i,
25776                     width: 100,
25777                     //allowBlank:false,
25778                     value: ''
25779                 }));
25780                 continue;
25781             }
25782             tb.addField( new Roo.form.TextField({
25783                 name: i,
25784                 width: item.width,
25785                 //allowBlank:true,
25786                 value: '',
25787                 listeners: {
25788                     'change' : function(f, nv, ov) {
25789                         tb.selectedNode.setAttribute(f.name, nv);
25790                     }
25791                 }
25792             }));
25793              
25794         }
25795         tb.el.on('click', function(e){
25796             e.preventDefault(); // what does this do?
25797         });
25798         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
25799         tb.el.hide();
25800         tb.name = nm;
25801         // dont need to disable them... as they will get hidden
25802         return tb;
25803          
25804         
25805     }
25806     
25807     
25808     
25809     
25810 });
25811
25812
25813
25814
25815
25816 /*
25817  * Based on:
25818  * Ext JS Library 1.1.1
25819  * Copyright(c) 2006-2007, Ext JS, LLC.
25820  *
25821  * Originally Released Under LGPL - original licence link has changed is not relivant.
25822  *
25823  * Fork - LGPL
25824  * <script type="text/javascript">
25825  */
25826  
25827 /**
25828  * @class Roo.form.BasicForm
25829  * @extends Roo.util.Observable
25830  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
25831  * @constructor
25832  * @param {String/HTMLElement/Roo.Element} el The form element or its id
25833  * @param {Object} config Configuration options
25834  */
25835 Roo.form.BasicForm = function(el, config){
25836     this.allItems = [];
25837     this.childForms = [];
25838     Roo.apply(this, config);
25839     /*
25840      * The Roo.form.Field items in this form.
25841      * @type MixedCollection
25842      */
25843      
25844      
25845     this.items = new Roo.util.MixedCollection(false, function(o){
25846         return o.id || (o.id = Roo.id());
25847     });
25848     this.addEvents({
25849         /**
25850          * @event beforeaction
25851          * Fires before any action is performed. Return false to cancel the action.
25852          * @param {Form} this
25853          * @param {Action} action The action to be performed
25854          */
25855         beforeaction: true,
25856         /**
25857          * @event actionfailed
25858          * Fires when an action fails.
25859          * @param {Form} this
25860          * @param {Action} action The action that failed
25861          */
25862         actionfailed : true,
25863         /**
25864          * @event actioncomplete
25865          * Fires when an action is completed.
25866          * @param {Form} this
25867          * @param {Action} action The action that completed
25868          */
25869         actioncomplete : true
25870     });
25871     if(el){
25872         this.initEl(el);
25873     }
25874     Roo.form.BasicForm.superclass.constructor.call(this);
25875 };
25876
25877 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
25878     /**
25879      * @cfg {String} method
25880      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
25881      */
25882     /**
25883      * @cfg {DataReader} reader
25884      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
25885      * This is optional as there is built-in support for processing JSON.
25886      */
25887     /**
25888      * @cfg {DataReader} errorReader
25889      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
25890      * This is completely optional as there is built-in support for processing JSON.
25891      */
25892     /**
25893      * @cfg {String} url
25894      * The URL to use for form actions if one isn't supplied in the action options.
25895      */
25896     /**
25897      * @cfg {Boolean} fileUpload
25898      * Set to true if this form is a file upload.
25899      */
25900     /**
25901      * @cfg {Object} baseParams
25902      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
25903      */
25904     /**
25905      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
25906      */
25907     timeout: 30,
25908
25909     // private
25910     activeAction : null,
25911
25912     /**
25913      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
25914      * or setValues() data instead of when the form was first created.
25915      */
25916     trackResetOnLoad : false,
25917     
25918     
25919     /**
25920      * childForms - used for multi-tab forms
25921      * @type {Array}
25922      */
25923     childForms : false,
25924     
25925     /**
25926      * allItems - full list of fields.
25927      * @type {Array}
25928      */
25929     allItems : false,
25930     
25931     /**
25932      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
25933      * element by passing it or its id or mask the form itself by passing in true.
25934      * @type Mixed
25935      */
25936     waitMsgTarget : undefined,
25937
25938     // private
25939     initEl : function(el){
25940         this.el = Roo.get(el);
25941         this.id = this.el.id || Roo.id();
25942         this.el.on('submit', this.onSubmit, this);
25943         this.el.addClass('x-form');
25944     },
25945
25946     // private
25947     onSubmit : function(e){
25948         e.stopEvent();
25949     },
25950
25951     /**
25952      * Returns true if client-side validation on the form is successful.
25953      * @return Boolean
25954      */
25955     isValid : function(){
25956         var valid = true;
25957         this.items.each(function(f){
25958            if(!f.validate()){
25959                valid = false;
25960            }
25961         });
25962         return valid;
25963     },
25964
25965     /**
25966      * Returns true if any fields in this form have changed since their original load.
25967      * @return Boolean
25968      */
25969     isDirty : function(){
25970         var dirty = false;
25971         this.items.each(function(f){
25972            if(f.isDirty()){
25973                dirty = true;
25974                return false;
25975            }
25976         });
25977         return dirty;
25978     },
25979
25980     /**
25981      * Performs a predefined action (submit or load) or custom actions you define on this form.
25982      * @param {String} actionName The name of the action type
25983      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
25984      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
25985      * accept other config options):
25986      * <pre>
25987 Property          Type             Description
25988 ----------------  ---------------  ----------------------------------------------------------------------------------
25989 url               String           The url for the action (defaults to the form's url)
25990 method            String           The form method to use (defaults to the form's method, or POST if not defined)
25991 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
25992 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
25993                                    validate the form on the client (defaults to false)
25994      * </pre>
25995      * @return {BasicForm} this
25996      */
25997     doAction : function(action, options){
25998         if(typeof action == 'string'){
25999             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
26000         }
26001         if(this.fireEvent('beforeaction', this, action) !== false){
26002             this.beforeAction(action);
26003             action.run.defer(100, action);
26004         }
26005         return this;
26006     },
26007
26008     /**
26009      * Shortcut to do a submit action.
26010      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
26011      * @return {BasicForm} this
26012      */
26013     submit : function(options){
26014         this.doAction('submit', options);
26015         return this;
26016     },
26017
26018     /**
26019      * Shortcut to do a load action.
26020      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
26021      * @return {BasicForm} this
26022      */
26023     load : function(options){
26024         this.doAction('load', options);
26025         return this;
26026     },
26027
26028     /**
26029      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
26030      * @param {Record} record The record to edit
26031      * @return {BasicForm} this
26032      */
26033     updateRecord : function(record){
26034         record.beginEdit();
26035         var fs = record.fields;
26036         fs.each(function(f){
26037             var field = this.findField(f.name);
26038             if(field){
26039                 record.set(f.name, field.getValue());
26040             }
26041         }, this);
26042         record.endEdit();
26043         return this;
26044     },
26045
26046     /**
26047      * Loads an Roo.data.Record into this form.
26048      * @param {Record} record The record to load
26049      * @return {BasicForm} this
26050      */
26051     loadRecord : function(record){
26052         this.setValues(record.data);
26053         return this;
26054     },
26055
26056     // private
26057     beforeAction : function(action){
26058         var o = action.options;
26059         if(o.waitMsg){
26060             if(this.waitMsgTarget === true){
26061                 this.el.mask(o.waitMsg, 'x-mask-loading');
26062             }else if(this.waitMsgTarget){
26063                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
26064                 this.waitMsgTarget.mask(o.waitMsg, 'x-mask-loading');
26065             }else{
26066                 Roo.MessageBox.wait(o.waitMsg, o.waitTitle || this.waitTitle || 'Please Wait...');
26067             }
26068         }
26069     },
26070
26071     // private
26072     afterAction : function(action, success){
26073         this.activeAction = null;
26074         var o = action.options;
26075         if(o.waitMsg){
26076             if(this.waitMsgTarget === true){
26077                 this.el.unmask();
26078             }else if(this.waitMsgTarget){
26079                 this.waitMsgTarget.unmask();
26080             }else{
26081                 Roo.MessageBox.updateProgress(1);
26082                 Roo.MessageBox.hide();
26083             }
26084         }
26085         if(success){
26086             if(o.reset){
26087                 this.reset();
26088             }
26089             Roo.callback(o.success, o.scope, [this, action]);
26090             this.fireEvent('actioncomplete', this, action);
26091         }else{
26092             Roo.callback(o.failure, o.scope, [this, action]);
26093             this.fireEvent('actionfailed', this, action);
26094         }
26095     },
26096
26097     /**
26098      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
26099      * @param {String} id The value to search for
26100      * @return Field
26101      */
26102     findField : function(id){
26103         var field = this.items.get(id);
26104         if(!field){
26105             this.items.each(function(f){
26106                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
26107                     field = f;
26108                     return false;
26109                 }
26110             });
26111         }
26112         return field || null;
26113     },
26114
26115     /**
26116      * Add a secondary form to this one, 
26117      * Used to provide tabbed forms. One form is primary, with hidden values 
26118      * which mirror the elements from the other forms.
26119      * 
26120      * @param {Roo.form.Form} form to add.
26121      * 
26122      */
26123     addForm : function(form)
26124     {
26125        
26126         if (this.childForms.indexOf(form) > -1) {
26127             // already added..
26128             return;
26129         }
26130         this.childForms.push(form);
26131         var n = '';
26132         Roo.each(form.allItems, function (fe) {
26133             
26134             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
26135             if (this.findField(n)) { // already added..
26136                 return;
26137             }
26138             var add = new Roo.form.Hidden({
26139                 name : n
26140             });
26141             add.render(this.el);
26142             
26143             this.add( add );
26144         }, this);
26145         
26146     },
26147     /**
26148      * Mark fields in this form invalid in bulk.
26149      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
26150      * @return {BasicForm} this
26151      */
26152     markInvalid : function(errors){
26153         if(errors instanceof Array){
26154             for(var i = 0, len = errors.length; i < len; i++){
26155                 var fieldError = errors[i];
26156                 var f = this.findField(fieldError.id);
26157                 if(f){
26158                     f.markInvalid(fieldError.msg);
26159                 }
26160             }
26161         }else{
26162             var field, id;
26163             for(id in errors){
26164                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
26165                     field.markInvalid(errors[id]);
26166                 }
26167             }
26168         }
26169         Roo.each(this.childForms || [], function (f) {
26170             f.markInvalid(errors);
26171         });
26172         
26173         return this;
26174     },
26175
26176     /**
26177      * Set values for fields in this form in bulk.
26178      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
26179      * @return {BasicForm} this
26180      */
26181     setValues : function(values){
26182         if(values instanceof Array){ // array of objects
26183             for(var i = 0, len = values.length; i < len; i++){
26184                 var v = values[i];
26185                 var f = this.findField(v.id);
26186                 if(f){
26187                     f.setValue(v.value);
26188                     if(this.trackResetOnLoad){
26189                         f.originalValue = f.getValue();
26190                     }
26191                 }
26192             }
26193         }else{ // object hash
26194             var field, id;
26195             for(id in values){
26196                 if(typeof values[id] != 'function' && (field = this.findField(id))){
26197                     
26198                     if (field.setFromData && 
26199                         field.valueField && 
26200                         field.displayField &&
26201                         // combos' with local stores can 
26202                         // be queried via setValue()
26203                         // to set their value..
26204                         (field.store && !field.store.isLocal)
26205                         ) {
26206                         // it's a combo
26207                         var sd = { };
26208                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
26209                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
26210                         field.setFromData(sd);
26211                         
26212                     } else {
26213                         field.setValue(values[id]);
26214                     }
26215                     
26216                     
26217                     if(this.trackResetOnLoad){
26218                         field.originalValue = field.getValue();
26219                     }
26220                 }
26221             }
26222         }
26223          
26224         Roo.each(this.childForms || [], function (f) {
26225             f.setValues(values);
26226         });
26227                 
26228         return this;
26229     },
26230
26231     /**
26232      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
26233      * they are returned as an array.
26234      * @param {Boolean} asString
26235      * @return {Object}
26236      */
26237     getValues : function(asString){
26238         if (this.childForms) {
26239             // copy values from the child forms
26240             Roo.each(this.childForms, function (f) {
26241                 this.setValues(f.getValues());
26242             }, this);
26243         }
26244         
26245         
26246         
26247         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
26248         if(asString === true){
26249             return fs;
26250         }
26251         return Roo.urlDecode(fs);
26252     },
26253     
26254     /**
26255      * Returns the fields in this form as an object with key/value pairs. 
26256      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
26257      * @return {Object}
26258      */
26259     getFieldValues : function()
26260     {
26261         if (this.childForms) {
26262             // copy values from the child forms
26263             Roo.each(this.childForms, function (f) {
26264                 this.setValues(f.getValues());
26265             }, this);
26266         }
26267         
26268         var ret = {};
26269         this.items.each(function(f){
26270             if (!f.getName()) {
26271                 return;
26272             }
26273             var v = f.getValue();
26274             if ((typeof(v) == 'object') && f.getRawValue) {
26275                 v = f.getRawValue() ; // dates..
26276             }
26277             ret[f.getName()] = v;
26278         });
26279         
26280         return ret;
26281     },
26282
26283     /**
26284      * Clears all invalid messages in this form.
26285      * @return {BasicForm} this
26286      */
26287     clearInvalid : function(){
26288         this.items.each(function(f){
26289            f.clearInvalid();
26290         });
26291         
26292         Roo.each(this.childForms || [], function (f) {
26293             f.clearInvalid();
26294         });
26295         
26296         
26297         return this;
26298     },
26299
26300     /**
26301      * Resets this form.
26302      * @return {BasicForm} this
26303      */
26304     reset : function(){
26305         this.items.each(function(f){
26306             f.reset();
26307         });
26308         
26309         Roo.each(this.childForms || [], function (f) {
26310             f.reset();
26311         });
26312        
26313         
26314         return this;
26315     },
26316
26317     /**
26318      * Add Roo.form components to this form.
26319      * @param {Field} field1
26320      * @param {Field} field2 (optional)
26321      * @param {Field} etc (optional)
26322      * @return {BasicForm} this
26323      */
26324     add : function(){
26325         this.items.addAll(Array.prototype.slice.call(arguments, 0));
26326         return this;
26327     },
26328
26329
26330     /**
26331      * Removes a field from the items collection (does NOT remove its markup).
26332      * @param {Field} field
26333      * @return {BasicForm} this
26334      */
26335     remove : function(field){
26336         this.items.remove(field);
26337         return this;
26338     },
26339
26340     /**
26341      * Looks at the fields in this form, checks them for an id attribute,
26342      * and calls applyTo on the existing dom element with that id.
26343      * @return {BasicForm} this
26344      */
26345     render : function(){
26346         this.items.each(function(f){
26347             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
26348                 f.applyTo(f.id);
26349             }
26350         });
26351         return this;
26352     },
26353
26354     /**
26355      * Calls {@link Ext#apply} for all fields in this form with the passed object.
26356      * @param {Object} values
26357      * @return {BasicForm} this
26358      */
26359     applyToFields : function(o){
26360         this.items.each(function(f){
26361            Roo.apply(f, o);
26362         });
26363         return this;
26364     },
26365
26366     /**
26367      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
26368      * @param {Object} values
26369      * @return {BasicForm} this
26370      */
26371     applyIfToFields : function(o){
26372         this.items.each(function(f){
26373            Roo.applyIf(f, o);
26374         });
26375         return this;
26376     }
26377 });
26378
26379 // back compat
26380 Roo.BasicForm = Roo.form.BasicForm;/*
26381  * Based on:
26382  * Ext JS Library 1.1.1
26383  * Copyright(c) 2006-2007, Ext JS, LLC.
26384  *
26385  * Originally Released Under LGPL - original licence link has changed is not relivant.
26386  *
26387  * Fork - LGPL
26388  * <script type="text/javascript">
26389  */
26390
26391 /**
26392  * @class Roo.form.Form
26393  * @extends Roo.form.BasicForm
26394  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
26395  * @constructor
26396  * @param {Object} config Configuration options
26397  */
26398 Roo.form.Form = function(config){
26399     var xitems =  [];
26400     if (config.items) {
26401         xitems = config.items;
26402         delete config.items;
26403     }
26404    
26405     
26406     Roo.form.Form.superclass.constructor.call(this, null, config);
26407     this.url = this.url || this.action;
26408     if(!this.root){
26409         this.root = new Roo.form.Layout(Roo.applyIf({
26410             id: Roo.id()
26411         }, config));
26412     }
26413     this.active = this.root;
26414     /**
26415      * Array of all the buttons that have been added to this form via {@link addButton}
26416      * @type Array
26417      */
26418     this.buttons = [];
26419     this.allItems = [];
26420     this.addEvents({
26421         /**
26422          * @event clientvalidation
26423          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
26424          * @param {Form} this
26425          * @param {Boolean} valid true if the form has passed client-side validation
26426          */
26427         clientvalidation: true,
26428         /**
26429          * @event rendered
26430          * Fires when the form is rendered
26431          * @param {Roo.form.Form} form
26432          */
26433         rendered : true
26434     });
26435     
26436     if (this.progressUrl) {
26437             // push a hidden field onto the list of fields..
26438             this.addxtype( {
26439                     xns: Roo.form, 
26440                     xtype : 'Hidden', 
26441                     name : 'UPLOAD_IDENTIFIER' 
26442             });
26443         }
26444         
26445     
26446     Roo.each(xitems, this.addxtype, this);
26447     
26448     
26449     
26450 };
26451
26452 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
26453     /**
26454      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
26455      */
26456     /**
26457      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
26458      */
26459     /**
26460      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
26461      */
26462     buttonAlign:'center',
26463
26464     /**
26465      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
26466      */
26467     minButtonWidth:75,
26468
26469     /**
26470      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
26471      * This property cascades to child containers if not set.
26472      */
26473     labelAlign:'left',
26474
26475     /**
26476      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
26477      * fires a looping event with that state. This is required to bind buttons to the valid
26478      * state using the config value formBind:true on the button.
26479      */
26480     monitorValid : false,
26481
26482     /**
26483      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
26484      */
26485     monitorPoll : 200,
26486     
26487     /**
26488      * @cfg {String} progressUrl - Url to return progress data 
26489      */
26490     
26491     progressUrl : false,
26492   
26493     /**
26494      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
26495      * fields are added and the column is closed. If no fields are passed the column remains open
26496      * until end() is called.
26497      * @param {Object} config The config to pass to the column
26498      * @param {Field} field1 (optional)
26499      * @param {Field} field2 (optional)
26500      * @param {Field} etc (optional)
26501      * @return Column The column container object
26502      */
26503     column : function(c){
26504         var col = new Roo.form.Column(c);
26505         this.start(col);
26506         if(arguments.length > 1){ // duplicate code required because of Opera
26507             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
26508             this.end();
26509         }
26510         return col;
26511     },
26512
26513     /**
26514      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
26515      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
26516      * until end() is called.
26517      * @param {Object} config The config to pass to the fieldset
26518      * @param {Field} field1 (optional)
26519      * @param {Field} field2 (optional)
26520      * @param {Field} etc (optional)
26521      * @return FieldSet The fieldset container object
26522      */
26523     fieldset : function(c){
26524         var fs = new Roo.form.FieldSet(c);
26525         this.start(fs);
26526         if(arguments.length > 1){ // duplicate code required because of Opera
26527             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
26528             this.end();
26529         }
26530         return fs;
26531     },
26532
26533     /**
26534      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
26535      * fields are added and the container is closed. If no fields are passed the container remains open
26536      * until end() is called.
26537      * @param {Object} config The config to pass to the Layout
26538      * @param {Field} field1 (optional)
26539      * @param {Field} field2 (optional)
26540      * @param {Field} etc (optional)
26541      * @return Layout The container object
26542      */
26543     container : function(c){
26544         var l = new Roo.form.Layout(c);
26545         this.start(l);
26546         if(arguments.length > 1){ // duplicate code required because of Opera
26547             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
26548             this.end();
26549         }
26550         return l;
26551     },
26552
26553     /**
26554      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
26555      * @param {Object} container A Roo.form.Layout or subclass of Layout
26556      * @return {Form} this
26557      */
26558     start : function(c){
26559         // cascade label info
26560         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
26561         this.active.stack.push(c);
26562         c.ownerCt = this.active;
26563         this.active = c;
26564         return this;
26565     },
26566
26567     /**
26568      * Closes the current open container
26569      * @return {Form} this
26570      */
26571     end : function(){
26572         if(this.active == this.root){
26573             return this;
26574         }
26575         this.active = this.active.ownerCt;
26576         return this;
26577     },
26578
26579     /**
26580      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
26581      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
26582      * as the label of the field.
26583      * @param {Field} field1
26584      * @param {Field} field2 (optional)
26585      * @param {Field} etc. (optional)
26586      * @return {Form} this
26587      */
26588     add : function(){
26589         this.active.stack.push.apply(this.active.stack, arguments);
26590         this.allItems.push.apply(this.allItems,arguments);
26591         var r = [];
26592         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
26593             if(a[i].isFormField){
26594                 r.push(a[i]);
26595             }
26596         }
26597         if(r.length > 0){
26598             Roo.form.Form.superclass.add.apply(this, r);
26599         }
26600         return this;
26601     },
26602     
26603
26604     
26605     
26606     
26607      /**
26608      * Find any element that has been added to a form, using it's ID or name
26609      * This can include framesets, columns etc. along with regular fields..
26610      * @param {String} id - id or name to find.
26611      
26612      * @return {Element} e - or false if nothing found.
26613      */
26614     findbyId : function(id)
26615     {
26616         var ret = false;
26617         if (!id) {
26618             return ret;
26619         }
26620         Ext.each(this.allItems, function(f){
26621             if (f.id == id || f.name == id ){
26622                 ret = f;
26623                 return false;
26624             }
26625         });
26626         return ret;
26627     },
26628
26629     
26630     
26631     /**
26632      * Render this form into the passed container. This should only be called once!
26633      * @param {String/HTMLElement/Element} container The element this component should be rendered into
26634      * @return {Form} this
26635      */
26636     render : function(ct)
26637     {
26638         
26639         
26640         
26641         ct = Roo.get(ct);
26642         var o = this.autoCreate || {
26643             tag: 'form',
26644             method : this.method || 'POST',
26645             id : this.id || Roo.id()
26646         };
26647         this.initEl(ct.createChild(o));
26648
26649         this.root.render(this.el);
26650         
26651        
26652              
26653         this.items.each(function(f){
26654             f.render('x-form-el-'+f.id);
26655         });
26656
26657         if(this.buttons.length > 0){
26658             // tables are required to maintain order and for correct IE layout
26659             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
26660                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
26661                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
26662             }}, null, true);
26663             var tr = tb.getElementsByTagName('tr')[0];
26664             for(var i = 0, len = this.buttons.length; i < len; i++) {
26665                 var b = this.buttons[i];
26666                 var td = document.createElement('td');
26667                 td.className = 'x-form-btn-td';
26668                 b.render(tr.appendChild(td));
26669             }
26670         }
26671         if(this.monitorValid){ // initialize after render
26672             this.startMonitoring();
26673         }
26674         this.fireEvent('rendered', this);
26675         return this;
26676     },
26677
26678     /**
26679      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
26680      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
26681      * object or a valid Roo.DomHelper element config
26682      * @param {Function} handler The function called when the button is clicked
26683      * @param {Object} scope (optional) The scope of the handler function
26684      * @return {Roo.Button}
26685      */
26686     addButton : function(config, handler, scope){
26687         var bc = {
26688             handler: handler,
26689             scope: scope,
26690             minWidth: this.minButtonWidth,
26691             hideParent:true
26692         };
26693         if(typeof config == "string"){
26694             bc.text = config;
26695         }else{
26696             Roo.apply(bc, config);
26697         }
26698         var btn = new Roo.Button(null, bc);
26699         this.buttons.push(btn);
26700         return btn;
26701     },
26702
26703      /**
26704      * Adds a series of form elements (using the xtype property as the factory method.
26705      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
26706      * @param {Object} config 
26707      */
26708     
26709     addxtype : function()
26710     {
26711         var ar = Array.prototype.slice.call(arguments, 0);
26712         var ret = false;
26713         for(var i = 0; i < ar.length; i++) {
26714             if (!ar[i]) {
26715                 continue; // skip -- if this happends something invalid got sent, we 
26716                 // should ignore it, as basically that interface element will not show up
26717                 // and that should be pretty obvious!!
26718             }
26719             
26720             if (Roo.form[ar[i].xtype]) {
26721                 ar[i].form = this;
26722                 var fe = Roo.factory(ar[i], Roo.form);
26723                 if (!ret) {
26724                     ret = fe;
26725                 }
26726                 fe.form = this;
26727                 if (fe.store) {
26728                     fe.store.form = this;
26729                 }
26730                 if (fe.isLayout) {  
26731                          
26732                     this.start(fe);
26733                     this.allItems.push(fe);
26734                     if (fe.items && fe.addxtype) {
26735                         fe.addxtype.apply(fe, fe.items);
26736                         delete fe.items;
26737                     }
26738                      this.end();
26739                     continue;
26740                 }
26741                 
26742                 
26743                  
26744                 this.add(fe);
26745               //  console.log('adding ' + ar[i].xtype);
26746             }
26747             if (ar[i].xtype == 'Button') {  
26748                 //console.log('adding button');
26749                 //console.log(ar[i]);
26750                 this.addButton(ar[i]);
26751                 this.allItems.push(fe);
26752                 continue;
26753             }
26754             
26755             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
26756                 alert('end is not supported on xtype any more, use items');
26757             //    this.end();
26758             //    //console.log('adding end');
26759             }
26760             
26761         }
26762         return ret;
26763     },
26764     
26765     /**
26766      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
26767      * option "monitorValid"
26768      */
26769     startMonitoring : function(){
26770         if(!this.bound){
26771             this.bound = true;
26772             Roo.TaskMgr.start({
26773                 run : this.bindHandler,
26774                 interval : this.monitorPoll || 200,
26775                 scope: this
26776             });
26777         }
26778     },
26779
26780     /**
26781      * Stops monitoring of the valid state of this form
26782      */
26783     stopMonitoring : function(){
26784         this.bound = false;
26785     },
26786
26787     // private
26788     bindHandler : function(){
26789         if(!this.bound){
26790             return false; // stops binding
26791         }
26792         var valid = true;
26793         this.items.each(function(f){
26794             if(!f.isValid(true)){
26795                 valid = false;
26796                 return false;
26797             }
26798         });
26799         for(var i = 0, len = this.buttons.length; i < len; i++){
26800             var btn = this.buttons[i];
26801             if(btn.formBind === true && btn.disabled === valid){
26802                 btn.setDisabled(!valid);
26803             }
26804         }
26805         this.fireEvent('clientvalidation', this, valid);
26806     }
26807     
26808     
26809     
26810     
26811     
26812     
26813     
26814     
26815 });
26816
26817
26818 // back compat
26819 Roo.Form = Roo.form.Form;
26820 /*
26821  * Based on:
26822  * Ext JS Library 1.1.1
26823  * Copyright(c) 2006-2007, Ext JS, LLC.
26824  *
26825  * Originally Released Under LGPL - original licence link has changed is not relivant.
26826  *
26827  * Fork - LGPL
26828  * <script type="text/javascript">
26829  */
26830  
26831  /**
26832  * @class Roo.form.Action
26833  * Internal Class used to handle form actions
26834  * @constructor
26835  * @param {Roo.form.BasicForm} el The form element or its id
26836  * @param {Object} config Configuration options
26837  */
26838  
26839  
26840 // define the action interface
26841 Roo.form.Action = function(form, options){
26842     this.form = form;
26843     this.options = options || {};
26844 };
26845 /**
26846  * Client Validation Failed
26847  * @const 
26848  */
26849 Roo.form.Action.CLIENT_INVALID = 'client';
26850 /**
26851  * Server Validation Failed
26852  * @const 
26853  */
26854  Roo.form.Action.SERVER_INVALID = 'server';
26855  /**
26856  * Connect to Server Failed
26857  * @const 
26858  */
26859 Roo.form.Action.CONNECT_FAILURE = 'connect';
26860 /**
26861  * Reading Data from Server Failed
26862  * @const 
26863  */
26864 Roo.form.Action.LOAD_FAILURE = 'load';
26865
26866 Roo.form.Action.prototype = {
26867     type : 'default',
26868     failureType : undefined,
26869     response : undefined,
26870     result : undefined,
26871
26872     // interface method
26873     run : function(options){
26874
26875     },
26876
26877     // interface method
26878     success : function(response){
26879
26880     },
26881
26882     // interface method
26883     handleResponse : function(response){
26884
26885     },
26886
26887     // default connection failure
26888     failure : function(response){
26889         this.response = response;
26890         this.failureType = Roo.form.Action.CONNECT_FAILURE;
26891         this.form.afterAction(this, false);
26892     },
26893
26894     processResponse : function(response){
26895         this.response = response;
26896         if(!response.responseText){
26897             return true;
26898         }
26899         this.result = this.handleResponse(response);
26900         return this.result;
26901     },
26902
26903     // utility functions used internally
26904     getUrl : function(appendParams){
26905         var url = this.options.url || this.form.url || this.form.el.dom.action;
26906         if(appendParams){
26907             var p = this.getParams();
26908             if(p){
26909                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
26910             }
26911         }
26912         return url;
26913     },
26914
26915     getMethod : function(){
26916         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
26917     },
26918
26919     getParams : function(){
26920         var bp = this.form.baseParams;
26921         var p = this.options.params;
26922         if(p){
26923             if(typeof p == "object"){
26924                 p = Roo.urlEncode(Roo.applyIf(p, bp));
26925             }else if(typeof p == 'string' && bp){
26926                 p += '&' + Roo.urlEncode(bp);
26927             }
26928         }else if(bp){
26929             p = Roo.urlEncode(bp);
26930         }
26931         return p;
26932     },
26933
26934     createCallback : function(){
26935         return {
26936             success: this.success,
26937             failure: this.failure,
26938             scope: this,
26939             timeout: (this.form.timeout*1000),
26940             upload: this.form.fileUpload ? this.success : undefined
26941         };
26942     }
26943 };
26944
26945 Roo.form.Action.Submit = function(form, options){
26946     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
26947 };
26948
26949 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
26950     type : 'submit',
26951
26952     haveProgress : false,
26953     uploadComplete : false,
26954     
26955     // uploadProgress indicator.
26956     uploadProgress : function()
26957     {
26958         if (!this.form.progressUrl) {
26959             return;
26960         }
26961         
26962         if (!this.haveProgress) {
26963             Roo.MessageBox.progress("Uploading", "Uploading");
26964         }
26965         if (this.uploadComplete) {
26966            Roo.MessageBox.hide();
26967            return;
26968         }
26969         
26970         this.haveProgress = true;
26971    
26972         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
26973         
26974         var c = new Roo.data.Connection();
26975         c.request({
26976             url : this.form.progressUrl,
26977             params: {
26978                 id : uid
26979             },
26980             method: 'GET',
26981             success : function(req){
26982                //console.log(data);
26983                 var rdata = false;
26984                 var edata;
26985                 try  {
26986                    rdata = Roo.decode(req.responseText)
26987                 } catch (e) {
26988                     Roo.log("Invalid data from server..");
26989                     Roo.log(edata);
26990                     return;
26991                 }
26992                 if (!rdata || !rdata.success) {
26993                     Roo.log(rdata);
26994                     return;
26995                 }
26996                 var data = rdata.data;
26997                 
26998                 if (this.uploadComplete) {
26999                    Roo.MessageBox.hide();
27000                    return;
27001                 }
27002                    
27003                 if (data){
27004                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
27005                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
27006                     );
27007                 }
27008                 this.uploadProgress.defer(2000,this);
27009             },
27010        
27011             failure: function(data) {
27012                 Roo.log('progress url failed ');
27013                 Roo.log(data);
27014             },
27015             scope : this
27016         });
27017            
27018     },
27019     
27020     
27021     run : function()
27022     {
27023         // run get Values on the form, so it syncs any secondary forms.
27024         this.form.getValues();
27025         
27026         var o = this.options;
27027         var method = this.getMethod();
27028         var isPost = method == 'POST';
27029         if(o.clientValidation === false || this.form.isValid()){
27030             
27031             if (this.form.progressUrl) {
27032                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
27033                     (new Date() * 1) + '' + Math.random());
27034                     
27035             } 
27036             
27037             Roo.Ajax.request(Roo.apply(this.createCallback(), {
27038                 form:this.form.el.dom,
27039                 url:this.getUrl(!isPost),
27040                 method: method,
27041                 params:isPost ? this.getParams() : null,
27042                 isUpload: this.form.fileUpload
27043             }));
27044             
27045             this.uploadProgress();
27046
27047         }else if (o.clientValidation !== false){ // client validation failed
27048             this.failureType = Roo.form.Action.CLIENT_INVALID;
27049             this.form.afterAction(this, false);
27050         }
27051     },
27052
27053     success : function(response)
27054     {
27055         this.uploadComplete= true;
27056         if (this.haveProgress) {
27057             Roo.MessageBox.hide();
27058         }
27059         
27060         var result = this.processResponse(response);
27061         if(result === true || result.success){
27062             this.form.afterAction(this, true);
27063             return;
27064         }
27065         if(result.errors){
27066             this.form.markInvalid(result.errors);
27067             this.failureType = Roo.form.Action.SERVER_INVALID;
27068         }
27069         this.form.afterAction(this, false);
27070     },
27071     failure : function(response)
27072     {
27073         this.uploadComplete= true;
27074         if (this.haveProgress) {
27075             Roo.MessageBox.hide();
27076         }
27077         
27078         this.response = response;
27079         this.failureType = Roo.form.Action.CONNECT_FAILURE;
27080         this.form.afterAction(this, false);
27081     },
27082     
27083     handleResponse : function(response){
27084         if(this.form.errorReader){
27085             var rs = this.form.errorReader.read(response);
27086             var errors = [];
27087             if(rs.records){
27088                 for(var i = 0, len = rs.records.length; i < len; i++) {
27089                     var r = rs.records[i];
27090                     errors[i] = r.data;
27091                 }
27092             }
27093             if(errors.length < 1){
27094                 errors = null;
27095             }
27096             return {
27097                 success : rs.success,
27098                 errors : errors
27099             };
27100         }
27101         var ret = false;
27102         try {
27103             ret = Roo.decode(response.responseText);
27104         } catch (e) {
27105             ret = {
27106                 success: false,
27107                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
27108                 errors : []
27109             };
27110         }
27111         return ret;
27112         
27113     }
27114 });
27115
27116
27117 Roo.form.Action.Load = function(form, options){
27118     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
27119     this.reader = this.form.reader;
27120 };
27121
27122 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
27123     type : 'load',
27124
27125     run : function(){
27126         Roo.Ajax.request(Roo.apply(
27127                 this.createCallback(), {
27128                     method:this.getMethod(),
27129                     url:this.getUrl(false),
27130                     params:this.getParams()
27131         }));
27132     },
27133
27134     success : function(response){
27135         var result = this.processResponse(response);
27136         if(result === true || !result.success || !result.data){
27137             this.failureType = Roo.form.Action.LOAD_FAILURE;
27138             this.form.afterAction(this, false);
27139             return;
27140         }
27141         this.form.clearInvalid();
27142         this.form.setValues(result.data);
27143         this.form.afterAction(this, true);
27144     },
27145
27146     handleResponse : function(response){
27147         if(this.form.reader){
27148             var rs = this.form.reader.read(response);
27149             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
27150             return {
27151                 success : rs.success,
27152                 data : data
27153             };
27154         }
27155         return Roo.decode(response.responseText);
27156     }
27157 });
27158
27159 Roo.form.Action.ACTION_TYPES = {
27160     'load' : Roo.form.Action.Load,
27161     'submit' : Roo.form.Action.Submit
27162 };/*
27163  * Based on:
27164  * Ext JS Library 1.1.1
27165  * Copyright(c) 2006-2007, Ext JS, LLC.
27166  *
27167  * Originally Released Under LGPL - original licence link has changed is not relivant.
27168  *
27169  * Fork - LGPL
27170  * <script type="text/javascript">
27171  */
27172  
27173 /**
27174  * @class Roo.form.Layout
27175  * @extends Roo.Component
27176  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
27177  * @constructor
27178  * @param {Object} config Configuration options
27179  */
27180 Roo.form.Layout = function(config){
27181     var xitems = [];
27182     if (config.items) {
27183         xitems = config.items;
27184         delete config.items;
27185     }
27186     Roo.form.Layout.superclass.constructor.call(this, config);
27187     this.stack = [];
27188     Roo.each(xitems, this.addxtype, this);
27189      
27190 };
27191
27192 Roo.extend(Roo.form.Layout, Roo.Component, {
27193     /**
27194      * @cfg {String/Object} autoCreate
27195      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
27196      */
27197     /**
27198      * @cfg {String/Object/Function} style
27199      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
27200      * a function which returns such a specification.
27201      */
27202     /**
27203      * @cfg {String} labelAlign
27204      * Valid values are "left," "top" and "right" (defaults to "left")
27205      */
27206     /**
27207      * @cfg {Number} labelWidth
27208      * Fixed width in pixels of all field labels (defaults to undefined)
27209      */
27210     /**
27211      * @cfg {Boolean} clear
27212      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
27213      */
27214     clear : true,
27215     /**
27216      * @cfg {String} labelSeparator
27217      * The separator to use after field labels (defaults to ':')
27218      */
27219     labelSeparator : ':',
27220     /**
27221      * @cfg {Boolean} hideLabels
27222      * True to suppress the display of field labels in this layout (defaults to false)
27223      */
27224     hideLabels : false,
27225
27226     // private
27227     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
27228     
27229     isLayout : true,
27230     
27231     // private
27232     onRender : function(ct, position){
27233         if(this.el){ // from markup
27234             this.el = Roo.get(this.el);
27235         }else {  // generate
27236             var cfg = this.getAutoCreate();
27237             this.el = ct.createChild(cfg, position);
27238         }
27239         if(this.style){
27240             this.el.applyStyles(this.style);
27241         }
27242         if(this.labelAlign){
27243             this.el.addClass('x-form-label-'+this.labelAlign);
27244         }
27245         if(this.hideLabels){
27246             this.labelStyle = "display:none";
27247             this.elementStyle = "padding-left:0;";
27248         }else{
27249             if(typeof this.labelWidth == 'number'){
27250                 this.labelStyle = "width:"+this.labelWidth+"px;";
27251                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
27252             }
27253             if(this.labelAlign == 'top'){
27254                 this.labelStyle = "width:auto;";
27255                 this.elementStyle = "padding-left:0;";
27256             }
27257         }
27258         var stack = this.stack;
27259         var slen = stack.length;
27260         if(slen > 0){
27261             if(!this.fieldTpl){
27262                 var t = new Roo.Template(
27263                     '<div class="x-form-item {5}">',
27264                         '<label for="{0}" style="{2}">{1}{4}</label>',
27265                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
27266                         '</div>',
27267                     '</div><div class="x-form-clear-left"></div>'
27268                 );
27269                 t.disableFormats = true;
27270                 t.compile();
27271                 Roo.form.Layout.prototype.fieldTpl = t;
27272             }
27273             for(var i = 0; i < slen; i++) {
27274                 if(stack[i].isFormField){
27275                     this.renderField(stack[i]);
27276                 }else{
27277                     this.renderComponent(stack[i]);
27278                 }
27279             }
27280         }
27281         if(this.clear){
27282             this.el.createChild({cls:'x-form-clear'});
27283         }
27284     },
27285
27286     // private
27287     renderField : function(f){
27288         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
27289                f.id, //0
27290                f.fieldLabel, //1
27291                f.labelStyle||this.labelStyle||'', //2
27292                this.elementStyle||'', //3
27293                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
27294                f.itemCls||this.itemCls||''  //5
27295        ], true).getPrevSibling());
27296     },
27297
27298     // private
27299     renderComponent : function(c){
27300         c.render(c.isLayout ? this.el : this.el.createChild());    
27301     },
27302     /**
27303      * Adds a object form elements (using the xtype property as the factory method.)
27304      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
27305      * @param {Object} config 
27306      */
27307     addxtype : function(o)
27308     {
27309         // create the lement.
27310         o.form = this.form;
27311         var fe = Roo.factory(o, Roo.form);
27312         this.form.allItems.push(fe);
27313         this.stack.push(fe);
27314         
27315         if (fe.isFormField) {
27316             this.form.items.add(fe);
27317         }
27318          
27319         return fe;
27320     }
27321 });
27322
27323 /**
27324  * @class Roo.form.Column
27325  * @extends Roo.form.Layout
27326  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
27327  * @constructor
27328  * @param {Object} config Configuration options
27329  */
27330 Roo.form.Column = function(config){
27331     Roo.form.Column.superclass.constructor.call(this, config);
27332 };
27333
27334 Roo.extend(Roo.form.Column, Roo.form.Layout, {
27335     /**
27336      * @cfg {Number/String} width
27337      * The fixed width of the column in pixels or CSS value (defaults to "auto")
27338      */
27339     /**
27340      * @cfg {String/Object} autoCreate
27341      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
27342      */
27343
27344     // private
27345     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
27346
27347     // private
27348     onRender : function(ct, position){
27349         Roo.form.Column.superclass.onRender.call(this, ct, position);
27350         if(this.width){
27351             this.el.setWidth(this.width);
27352         }
27353     }
27354 });
27355
27356
27357 /**
27358  * @class Roo.form.Row
27359  * @extends Roo.form.Layout
27360  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
27361  * @constructor
27362  * @param {Object} config Configuration options
27363  */
27364
27365  
27366 Roo.form.Row = function(config){
27367     Roo.form.Row.superclass.constructor.call(this, config);
27368 };
27369  
27370 Roo.extend(Roo.form.Row, Roo.form.Layout, {
27371       /**
27372      * @cfg {Number/String} width
27373      * The fixed width of the column in pixels or CSS value (defaults to "auto")
27374      */
27375     /**
27376      * @cfg {Number/String} height
27377      * The fixed height of the column in pixels or CSS value (defaults to "auto")
27378      */
27379     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
27380     
27381     padWidth : 20,
27382     // private
27383     onRender : function(ct, position){
27384         //console.log('row render');
27385         if(!this.rowTpl){
27386             var t = new Roo.Template(
27387                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
27388                     '<label for="{0}" style="{2}">{1}{4}</label>',
27389                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
27390                     '</div>',
27391                 '</div>'
27392             );
27393             t.disableFormats = true;
27394             t.compile();
27395             Roo.form.Layout.prototype.rowTpl = t;
27396         }
27397         this.fieldTpl = this.rowTpl;
27398         
27399         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
27400         var labelWidth = 100;
27401         
27402         if ((this.labelAlign != 'top')) {
27403             if (typeof this.labelWidth == 'number') {
27404                 labelWidth = this.labelWidth
27405             }
27406             this.padWidth =  20 + labelWidth;
27407             
27408         }
27409         
27410         Roo.form.Column.superclass.onRender.call(this, ct, position);
27411         if(this.width){
27412             this.el.setWidth(this.width);
27413         }
27414         if(this.height){
27415             this.el.setHeight(this.height);
27416         }
27417     },
27418     
27419     // private
27420     renderField : function(f){
27421         f.fieldEl = this.fieldTpl.append(this.el, [
27422                f.id, f.fieldLabel,
27423                f.labelStyle||this.labelStyle||'',
27424                this.elementStyle||'',
27425                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
27426                f.itemCls||this.itemCls||'',
27427                f.width ? f.width + this.padWidth : 160 + this.padWidth
27428        ],true);
27429     }
27430 });
27431  
27432
27433 /**
27434  * @class Roo.form.FieldSet
27435  * @extends Roo.form.Layout
27436  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
27437  * @constructor
27438  * @param {Object} config Configuration options
27439  */
27440 Roo.form.FieldSet = function(config){
27441     Roo.form.FieldSet.superclass.constructor.call(this, config);
27442 };
27443
27444 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
27445     /**
27446      * @cfg {String} legend
27447      * The text to display as the legend for the FieldSet (defaults to '')
27448      */
27449     /**
27450      * @cfg {String/Object} autoCreate
27451      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
27452      */
27453
27454     // private
27455     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
27456
27457     // private
27458     onRender : function(ct, position){
27459         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
27460         if(this.legend){
27461             this.setLegend(this.legend);
27462         }
27463     },
27464
27465     // private
27466     setLegend : function(text){
27467         if(this.rendered){
27468             this.el.child('legend').update(text);
27469         }
27470     }
27471 });/*
27472  * Based on:
27473  * Ext JS Library 1.1.1
27474  * Copyright(c) 2006-2007, Ext JS, LLC.
27475  *
27476  * Originally Released Under LGPL - original licence link has changed is not relivant.
27477  *
27478  * Fork - LGPL
27479  * <script type="text/javascript">
27480  */
27481 /**
27482  * @class Roo.form.VTypes
27483  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
27484  * @singleton
27485  */
27486 Roo.form.VTypes = function(){
27487     // closure these in so they are only created once.
27488     var alpha = /^[a-zA-Z_]+$/;
27489     var alphanum = /^[a-zA-Z0-9_]+$/;
27490     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
27491     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
27492
27493     // All these messages and functions are configurable
27494     return {
27495         /**
27496          * The function used to validate email addresses
27497          * @param {String} value The email address
27498          */
27499         'email' : function(v){
27500             return email.test(v);
27501         },
27502         /**
27503          * The error text to display when the email validation function returns false
27504          * @type String
27505          */
27506         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
27507         /**
27508          * The keystroke filter mask to be applied on email input
27509          * @type RegExp
27510          */
27511         'emailMask' : /[a-z0-9_\.\-@]/i,
27512
27513         /**
27514          * The function used to validate URLs
27515          * @param {String} value The URL
27516          */
27517         'url' : function(v){
27518             return url.test(v);
27519         },
27520         /**
27521          * The error text to display when the url validation function returns false
27522          * @type String
27523          */
27524         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
27525         
27526         /**
27527          * The function used to validate alpha values
27528          * @param {String} value The value
27529          */
27530         'alpha' : function(v){
27531             return alpha.test(v);
27532         },
27533         /**
27534          * The error text to display when the alpha validation function returns false
27535          * @type String
27536          */
27537         'alphaText' : 'This field should only contain letters and _',
27538         /**
27539          * The keystroke filter mask to be applied on alpha input
27540          * @type RegExp
27541          */
27542         'alphaMask' : /[a-z_]/i,
27543
27544         /**
27545          * The function used to validate alphanumeric values
27546          * @param {String} value The value
27547          */
27548         'alphanum' : function(v){
27549             return alphanum.test(v);
27550         },
27551         /**
27552          * The error text to display when the alphanumeric validation function returns false
27553          * @type String
27554          */
27555         'alphanumText' : 'This field should only contain letters, numbers and _',
27556         /**
27557          * The keystroke filter mask to be applied on alphanumeric input
27558          * @type RegExp
27559          */
27560         'alphanumMask' : /[a-z0-9_]/i
27561     };
27562 }();//<script type="text/javascript">
27563
27564 /**
27565  * @class Roo.form.FCKeditor
27566  * @extends Roo.form.TextArea
27567  * Wrapper around the FCKEditor http://www.fckeditor.net
27568  * @constructor
27569  * Creates a new FCKeditor
27570  * @param {Object} config Configuration options
27571  */
27572 Roo.form.FCKeditor = function(config){
27573     Roo.form.FCKeditor.superclass.constructor.call(this, config);
27574     this.addEvents({
27575          /**
27576          * @event editorinit
27577          * Fired when the editor is initialized - you can add extra handlers here..
27578          * @param {FCKeditor} this
27579          * @param {Object} the FCK object.
27580          */
27581         editorinit : true
27582     });
27583     
27584     
27585 };
27586 Roo.form.FCKeditor.editors = { };
27587 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
27588 {
27589     //defaultAutoCreate : {
27590     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
27591     //},
27592     // private
27593     /**
27594      * @cfg {Object} fck options - see fck manual for details.
27595      */
27596     fckconfig : false,
27597     
27598     /**
27599      * @cfg {Object} fck toolbar set (Basic or Default)
27600      */
27601     toolbarSet : 'Basic',
27602     /**
27603      * @cfg {Object} fck BasePath
27604      */ 
27605     basePath : '/fckeditor/',
27606     
27607     
27608     frame : false,
27609     
27610     value : '',
27611     
27612    
27613     onRender : function(ct, position)
27614     {
27615         if(!this.el){
27616             this.defaultAutoCreate = {
27617                 tag: "textarea",
27618                 style:"width:300px;height:60px;",
27619                 autocomplete: "off"
27620             };
27621         }
27622         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
27623         /*
27624         if(this.grow){
27625             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
27626             if(this.preventScrollbars){
27627                 this.el.setStyle("overflow", "hidden");
27628             }
27629             this.el.setHeight(this.growMin);
27630         }
27631         */
27632         //console.log('onrender' + this.getId() );
27633         Roo.form.FCKeditor.editors[this.getId()] = this;
27634          
27635
27636         this.replaceTextarea() ;
27637         
27638     },
27639     
27640     getEditor : function() {
27641         return this.fckEditor;
27642     },
27643     /**
27644      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
27645      * @param {Mixed} value The value to set
27646      */
27647     
27648     
27649     setValue : function(value)
27650     {
27651         //console.log('setValue: ' + value);
27652         
27653         if(typeof(value) == 'undefined') { // not sure why this is happending...
27654             return;
27655         }
27656         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
27657         
27658         //if(!this.el || !this.getEditor()) {
27659         //    this.value = value;
27660             //this.setValue.defer(100,this,[value]);    
27661         //    return;
27662         //} 
27663         
27664         if(!this.getEditor()) {
27665             return;
27666         }
27667         
27668         this.getEditor().SetData(value);
27669         
27670         //
27671
27672     },
27673
27674     /**
27675      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
27676      * @return {Mixed} value The field value
27677      */
27678     getValue : function()
27679     {
27680         
27681         if (this.frame && this.frame.dom.style.display == 'none') {
27682             return Roo.form.FCKeditor.superclass.getValue.call(this);
27683         }
27684         
27685         if(!this.el || !this.getEditor()) {
27686            
27687            // this.getValue.defer(100,this); 
27688             return this.value;
27689         }
27690        
27691         
27692         var value=this.getEditor().GetData();
27693         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
27694         return Roo.form.FCKeditor.superclass.getValue.call(this);
27695         
27696
27697     },
27698
27699     /**
27700      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
27701      * @return {Mixed} value The field value
27702      */
27703     getRawValue : function()
27704     {
27705         if (this.frame && this.frame.dom.style.display == 'none') {
27706             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
27707         }
27708         
27709         if(!this.el || !this.getEditor()) {
27710             //this.getRawValue.defer(100,this); 
27711             return this.value;
27712             return;
27713         }
27714         
27715         
27716         
27717         var value=this.getEditor().GetData();
27718         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
27719         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
27720          
27721     },
27722     
27723     setSize : function(w,h) {
27724         
27725         
27726         
27727         //if (this.frame && this.frame.dom.style.display == 'none') {
27728         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
27729         //    return;
27730         //}
27731         //if(!this.el || !this.getEditor()) {
27732         //    this.setSize.defer(100,this, [w,h]); 
27733         //    return;
27734         //}
27735         
27736         
27737         
27738         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
27739         
27740         this.frame.dom.setAttribute('width', w);
27741         this.frame.dom.setAttribute('height', h);
27742         this.frame.setSize(w,h);
27743         
27744     },
27745     
27746     toggleSourceEdit : function(value) {
27747         
27748       
27749          
27750         this.el.dom.style.display = value ? '' : 'none';
27751         this.frame.dom.style.display = value ?  'none' : '';
27752         
27753     },
27754     
27755     
27756     focus: function(tag)
27757     {
27758         if (this.frame.dom.style.display == 'none') {
27759             return Roo.form.FCKeditor.superclass.focus.call(this);
27760         }
27761         if(!this.el || !this.getEditor()) {
27762             this.focus.defer(100,this, [tag]); 
27763             return;
27764         }
27765         
27766         
27767         
27768         
27769         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
27770         this.getEditor().Focus();
27771         if (tgs.length) {
27772             if (!this.getEditor().Selection.GetSelection()) {
27773                 this.focus.defer(100,this, [tag]); 
27774                 return;
27775             }
27776             
27777             
27778             var r = this.getEditor().EditorDocument.createRange();
27779             r.setStart(tgs[0],0);
27780             r.setEnd(tgs[0],0);
27781             this.getEditor().Selection.GetSelection().removeAllRanges();
27782             this.getEditor().Selection.GetSelection().addRange(r);
27783             this.getEditor().Focus();
27784         }
27785         
27786     },
27787     
27788     
27789     
27790     replaceTextarea : function()
27791     {
27792         if ( document.getElementById( this.getId() + '___Frame' ) )
27793             return ;
27794         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
27795         //{
27796             // We must check the elements firstly using the Id and then the name.
27797         var oTextarea = document.getElementById( this.getId() );
27798         
27799         var colElementsByName = document.getElementsByName( this.getId() ) ;
27800          
27801         oTextarea.style.display = 'none' ;
27802
27803         if ( oTextarea.tabIndex ) {            
27804             this.TabIndex = oTextarea.tabIndex ;
27805         }
27806         
27807         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
27808         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
27809         this.frame = Roo.get(this.getId() + '___Frame')
27810     },
27811     
27812     _getConfigHtml : function()
27813     {
27814         var sConfig = '' ;
27815
27816         for ( var o in this.fckconfig ) {
27817             sConfig += sConfig.length > 0  ? '&amp;' : '';
27818             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
27819         }
27820
27821         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
27822     },
27823     
27824     
27825     _getIFrameHtml : function()
27826     {
27827         var sFile = 'fckeditor.html' ;
27828         /* no idea what this is about..
27829         try
27830         {
27831             if ( (/fcksource=true/i).test( window.top.location.search ) )
27832                 sFile = 'fckeditor.original.html' ;
27833         }
27834         catch (e) { 
27835         */
27836
27837         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
27838         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
27839         
27840         
27841         var html = '<iframe id="' + this.getId() +
27842             '___Frame" src="' + sLink +
27843             '" width="' + this.width +
27844             '" height="' + this.height + '"' +
27845             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
27846             ' frameborder="0" scrolling="no"></iframe>' ;
27847
27848         return html ;
27849     },
27850     
27851     _insertHtmlBefore : function( html, element )
27852     {
27853         if ( element.insertAdjacentHTML )       {
27854             // IE
27855             element.insertAdjacentHTML( 'beforeBegin', html ) ;
27856         } else { // Gecko
27857             var oRange = document.createRange() ;
27858             oRange.setStartBefore( element ) ;
27859             var oFragment = oRange.createContextualFragment( html );
27860             element.parentNode.insertBefore( oFragment, element ) ;
27861         }
27862     }
27863     
27864     
27865   
27866     
27867     
27868     
27869     
27870
27871 });
27872
27873 //Roo.reg('fckeditor', Roo.form.FCKeditor);
27874
27875 function FCKeditor_OnComplete(editorInstance){
27876     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
27877     f.fckEditor = editorInstance;
27878     //console.log("loaded");
27879     f.fireEvent('editorinit', f, editorInstance);
27880
27881   
27882
27883  
27884
27885
27886
27887
27888
27889
27890
27891
27892
27893
27894
27895
27896
27897
27898
27899 //<script type="text/javascript">
27900 /**
27901  * @class Roo.form.GridField
27902  * @extends Roo.form.Field
27903  * Embed a grid (or editable grid into a form)
27904  * STATUS ALPHA
27905  * 
27906  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
27907  * it needs 
27908  * xgrid.store = Roo.data.Store
27909  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
27910  * xgrid.store.reader = Roo.data.JsonReader 
27911  * 
27912  * 
27913  * @constructor
27914  * Creates a new GridField
27915  * @param {Object} config Configuration options
27916  */
27917 Roo.form.GridField = function(config){
27918     Roo.form.GridField.superclass.constructor.call(this, config);
27919      
27920 };
27921
27922 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
27923     /**
27924      * @cfg {Number} width  - used to restrict width of grid..
27925      */
27926     width : 100,
27927     /**
27928      * @cfg {Number} height - used to restrict height of grid..
27929      */
27930     height : 50,
27931      /**
27932      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
27933          * 
27934          *}
27935      */
27936     xgrid : false, 
27937     /**
27938      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
27939      * {tag: "input", type: "checkbox", autocomplete: "off"})
27940      */
27941    // defaultAutoCreate : { tag: 'div' },
27942     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
27943     /**
27944      * @cfg {String} addTitle Text to include for adding a title.
27945      */
27946     addTitle : false,
27947     //
27948     onResize : function(){
27949         Roo.form.Field.superclass.onResize.apply(this, arguments);
27950     },
27951
27952     initEvents : function(){
27953         // Roo.form.Checkbox.superclass.initEvents.call(this);
27954         // has no events...
27955        
27956     },
27957
27958
27959     getResizeEl : function(){
27960         return this.wrap;
27961     },
27962
27963     getPositionEl : function(){
27964         return this.wrap;
27965     },
27966
27967     // private
27968     onRender : function(ct, position){
27969         
27970         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
27971         var style = this.style;
27972         delete this.style;
27973         
27974         Roo.form.GridField.superclass.onRender.call(this, ct, position);
27975         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
27976         this.viewEl = this.wrap.createChild({ tag: 'div' });
27977         if (style) {
27978             this.viewEl.applyStyles(style);
27979         }
27980         if (this.width) {
27981             this.viewEl.setWidth(this.width);
27982         }
27983         if (this.height) {
27984             this.viewEl.setHeight(this.height);
27985         }
27986         //if(this.inputValue !== undefined){
27987         //this.setValue(this.value);
27988         
27989         
27990         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
27991         
27992         
27993         this.grid.render();
27994         this.grid.getDataSource().on('remove', this.refreshValue, this);
27995         this.grid.getDataSource().on('update', this.refreshValue, this);
27996         this.grid.on('afteredit', this.refreshValue, this);
27997  
27998     },
27999      
28000     
28001     /**
28002      * Sets the value of the item. 
28003      * @param {String} either an object  or a string..
28004      */
28005     setValue : function(v){
28006         //this.value = v;
28007         v = v || []; // empty set..
28008         // this does not seem smart - it really only affects memoryproxy grids..
28009         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
28010             var ds = this.grid.getDataSource();
28011             // assumes a json reader..
28012             var data = {}
28013             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
28014             ds.loadData( data);
28015         }
28016         Roo.form.GridField.superclass.setValue.call(this, v);
28017         this.refreshValue();
28018         // should load data in the grid really....
28019     },
28020     
28021     // private
28022     refreshValue: function() {
28023          var val = [];
28024         this.grid.getDataSource().each(function(r) {
28025             val.push(r.data);
28026         });
28027         this.el.dom.value = Roo.encode(val);
28028     }
28029     
28030      
28031     
28032     
28033 });/*
28034  * Based on:
28035  * Ext JS Library 1.1.1
28036  * Copyright(c) 2006-2007, Ext JS, LLC.
28037  *
28038  * Originally Released Under LGPL - original licence link has changed is not relivant.
28039  *
28040  * Fork - LGPL
28041  * <script type="text/javascript">
28042  */
28043 /**
28044  * @class Roo.form.DisplayField
28045  * @extends Roo.form.Field
28046  * A generic Field to display non-editable data.
28047  * @constructor
28048  * Creates a new Display Field item.
28049  * @param {Object} config Configuration options
28050  */
28051 Roo.form.DisplayField = function(config){
28052     Roo.form.DisplayField.superclass.constructor.call(this, config);
28053     
28054 };
28055
28056 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
28057     inputType:      'hidden',
28058     allowBlank:     true,
28059     readOnly:         true,
28060     
28061  
28062     /**
28063      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
28064      */
28065     focusClass : undefined,
28066     /**
28067      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
28068      */
28069     fieldClass: 'x-form-field',
28070     
28071      /**
28072      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
28073      */
28074     valueRenderer: undefined,
28075     
28076     width: 100,
28077     /**
28078      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
28079      * {tag: "input", type: "checkbox", autocomplete: "off"})
28080      */
28081      
28082  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
28083
28084     onResize : function(){
28085         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
28086         
28087     },
28088
28089     initEvents : function(){
28090         // Roo.form.Checkbox.superclass.initEvents.call(this);
28091         // has no events...
28092        
28093     },
28094
28095
28096     getResizeEl : function(){
28097         return this.wrap;
28098     },
28099
28100     getPositionEl : function(){
28101         return this.wrap;
28102     },
28103
28104     // private
28105     onRender : function(ct, position){
28106         
28107         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
28108         //if(this.inputValue !== undefined){
28109         this.wrap = this.el.wrap();
28110         
28111         this.viewEl = this.wrap.createChild({ tag: 'div'});
28112         
28113         if (this.bodyStyle) {
28114             this.viewEl.applyStyles(this.bodyStyle);
28115         }
28116         //this.viewEl.setStyle('padding', '2px');
28117         
28118         this.setValue(this.value);
28119         
28120     },
28121 /*
28122     // private
28123     initValue : Roo.emptyFn,
28124
28125   */
28126
28127         // private
28128     onClick : function(){
28129         
28130     },
28131
28132     /**
28133      * Sets the checked state of the checkbox.
28134      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
28135      */
28136     setValue : function(v){
28137         this.value = v;
28138         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
28139         // this might be called before we have a dom element..
28140         if (!this.viewEl) {
28141             return;
28142         }
28143         this.viewEl.dom.innerHTML = html;
28144         Roo.form.DisplayField.superclass.setValue.call(this, v);
28145
28146     }
28147 });//<script type="text/javasscript">
28148  
28149
28150 /**
28151  * @class Roo.DDView
28152  * A DnD enabled version of Roo.View.
28153  * @param {Element/String} container The Element in which to create the View.
28154  * @param {String} tpl The template string used to create the markup for each element of the View
28155  * @param {Object} config The configuration properties. These include all the config options of
28156  * {@link Roo.View} plus some specific to this class.<br>
28157  * <p>
28158  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
28159  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
28160  * <p>
28161  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
28162 .x-view-drag-insert-above {
28163         border-top:1px dotted #3366cc;
28164 }
28165 .x-view-drag-insert-below {
28166         border-bottom:1px dotted #3366cc;
28167 }
28168 </code></pre>
28169  * 
28170  */
28171  
28172 Roo.DDView = function(container, tpl, config) {
28173     Roo.DDView.superclass.constructor.apply(this, arguments);
28174     this.getEl().setStyle("outline", "0px none");
28175     this.getEl().unselectable();
28176     if (this.dragGroup) {
28177                 this.setDraggable(this.dragGroup.split(","));
28178     }
28179     if (this.dropGroup) {
28180                 this.setDroppable(this.dropGroup.split(","));
28181     }
28182     if (this.deletable) {
28183         this.setDeletable();
28184     }
28185     this.isDirtyFlag = false;
28186         this.addEvents({
28187                 "drop" : true
28188         });
28189 };
28190
28191 Roo.extend(Roo.DDView, Roo.View, {
28192 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
28193 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
28194 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
28195 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
28196
28197         isFormField: true,
28198
28199         reset: Roo.emptyFn,
28200         
28201         clearInvalid: Roo.form.Field.prototype.clearInvalid,
28202
28203         validate: function() {
28204                 return true;
28205         },
28206         
28207         destroy: function() {
28208                 this.purgeListeners();
28209                 this.getEl.removeAllListeners();
28210                 this.getEl().remove();
28211                 if (this.dragZone) {
28212                         if (this.dragZone.destroy) {
28213                                 this.dragZone.destroy();
28214                         }
28215                 }
28216                 if (this.dropZone) {
28217                         if (this.dropZone.destroy) {
28218                                 this.dropZone.destroy();
28219                         }
28220                 }
28221         },
28222
28223 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
28224         getName: function() {
28225                 return this.name;
28226         },
28227
28228 /**     Loads the View from a JSON string representing the Records to put into the Store. */
28229         setValue: function(v) {
28230                 if (!this.store) {
28231                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
28232                 }
28233                 var data = {};
28234                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
28235                 this.store.proxy = new Roo.data.MemoryProxy(data);
28236                 this.store.load();
28237         },
28238
28239 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
28240         getValue: function() {
28241                 var result = '(';
28242                 this.store.each(function(rec) {
28243                         result += rec.id + ',';
28244                 });
28245                 return result.substr(0, result.length - 1) + ')';
28246         },
28247         
28248         getIds: function() {
28249                 var i = 0, result = new Array(this.store.getCount());
28250                 this.store.each(function(rec) {
28251                         result[i++] = rec.id;
28252                 });
28253                 return result;
28254         },
28255         
28256         isDirty: function() {
28257                 return this.isDirtyFlag;
28258         },
28259
28260 /**
28261  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
28262  *      whole Element becomes the target, and this causes the drop gesture to append.
28263  */
28264     getTargetFromEvent : function(e) {
28265                 var target = e.getTarget();
28266                 while ((target !== null) && (target.parentNode != this.el.dom)) {
28267                 target = target.parentNode;
28268                 }
28269                 if (!target) {
28270                         target = this.el.dom.lastChild || this.el.dom;
28271                 }
28272                 return target;
28273     },
28274
28275 /**
28276  *      Create the drag data which consists of an object which has the property "ddel" as
28277  *      the drag proxy element. 
28278  */
28279     getDragData : function(e) {
28280         var target = this.findItemFromChild(e.getTarget());
28281                 if(target) {
28282                         this.handleSelection(e);
28283                         var selNodes = this.getSelectedNodes();
28284             var dragData = {
28285                 source: this,
28286                 copy: this.copy || (this.allowCopy && e.ctrlKey),
28287                 nodes: selNodes,
28288                 records: []
28289                         };
28290                         var selectedIndices = this.getSelectedIndexes();
28291                         for (var i = 0; i < selectedIndices.length; i++) {
28292                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
28293                         }
28294                         if (selNodes.length == 1) {
28295                                 dragData.ddel = target.cloneNode(true); // the div element
28296                         } else {
28297                                 var div = document.createElement('div'); // create the multi element drag "ghost"
28298                                 div.className = 'multi-proxy';
28299                                 for (var i = 0, len = selNodes.length; i < len; i++) {
28300                                         div.appendChild(selNodes[i].cloneNode(true));
28301                                 }
28302                                 dragData.ddel = div;
28303                         }
28304             //console.log(dragData)
28305             //console.log(dragData.ddel.innerHTML)
28306                         return dragData;
28307                 }
28308         //console.log('nodragData')
28309                 return false;
28310     },
28311     
28312 /**     Specify to which ddGroup items in this DDView may be dragged. */
28313     setDraggable: function(ddGroup) {
28314         if (ddGroup instanceof Array) {
28315                 Roo.each(ddGroup, this.setDraggable, this);
28316                 return;
28317         }
28318         if (this.dragZone) {
28319                 this.dragZone.addToGroup(ddGroup);
28320         } else {
28321                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
28322                                 containerScroll: true,
28323                                 ddGroup: ddGroup 
28324
28325                         });
28326 //                      Draggability implies selection. DragZone's mousedown selects the element.
28327                         if (!this.multiSelect) { this.singleSelect = true; }
28328
28329 //                      Wire the DragZone's handlers up to methods in *this*
28330                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
28331                 }
28332     },
28333
28334 /**     Specify from which ddGroup this DDView accepts drops. */
28335     setDroppable: function(ddGroup) {
28336         if (ddGroup instanceof Array) {
28337                 Roo.each(ddGroup, this.setDroppable, this);
28338                 return;
28339         }
28340         if (this.dropZone) {
28341                 this.dropZone.addToGroup(ddGroup);
28342         } else {
28343                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
28344                                 containerScroll: true,
28345                                 ddGroup: ddGroup
28346                         });
28347
28348 //                      Wire the DropZone's handlers up to methods in *this*
28349                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
28350                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
28351                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
28352                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
28353                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
28354                 }
28355     },
28356
28357 /**     Decide whether to drop above or below a View node. */
28358     getDropPoint : function(e, n, dd){
28359         if (n == this.el.dom) { return "above"; }
28360                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
28361                 var c = t + (b - t) / 2;
28362                 var y = Roo.lib.Event.getPageY(e);
28363                 if(y <= c) {
28364                         return "above";
28365                 }else{
28366                         return "below";
28367                 }
28368     },
28369
28370     onNodeEnter : function(n, dd, e, data){
28371                 return false;
28372     },
28373     
28374     onNodeOver : function(n, dd, e, data){
28375                 var pt = this.getDropPoint(e, n, dd);
28376                 // set the insert point style on the target node
28377                 var dragElClass = this.dropNotAllowed;
28378                 if (pt) {
28379                         var targetElClass;
28380                         if (pt == "above"){
28381                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
28382                                 targetElClass = "x-view-drag-insert-above";
28383                         } else {
28384                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
28385                                 targetElClass = "x-view-drag-insert-below";
28386                         }
28387                         if (this.lastInsertClass != targetElClass){
28388                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
28389                                 this.lastInsertClass = targetElClass;
28390                         }
28391                 }
28392                 return dragElClass;
28393         },
28394
28395     onNodeOut : function(n, dd, e, data){
28396                 this.removeDropIndicators(n);
28397     },
28398
28399     onNodeDrop : function(n, dd, e, data){
28400         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
28401                 return false;
28402         }
28403         var pt = this.getDropPoint(e, n, dd);
28404                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
28405                 if (pt == "below") { insertAt++; }
28406                 for (var i = 0; i < data.records.length; i++) {
28407                         var r = data.records[i];
28408                         var dup = this.store.getById(r.id);
28409                         if (dup && (dd != this.dragZone)) {
28410                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
28411                         } else {
28412                                 if (data.copy) {
28413                                         this.store.insert(insertAt++, r.copy());
28414                                 } else {
28415                                         data.source.isDirtyFlag = true;
28416                                         r.store.remove(r);
28417                                         this.store.insert(insertAt++, r);
28418                                 }
28419                                 this.isDirtyFlag = true;
28420                         }
28421                 }
28422                 this.dragZone.cachedTarget = null;
28423                 return true;
28424     },
28425
28426     removeDropIndicators : function(n){
28427                 if(n){
28428                         Roo.fly(n).removeClass([
28429                                 "x-view-drag-insert-above",
28430                                 "x-view-drag-insert-below"]);
28431                         this.lastInsertClass = "_noclass";
28432                 }
28433     },
28434
28435 /**
28436  *      Utility method. Add a delete option to the DDView's context menu.
28437  *      @param {String} imageUrl The URL of the "delete" icon image.
28438  */
28439         setDeletable: function(imageUrl) {
28440                 if (!this.singleSelect && !this.multiSelect) {
28441                         this.singleSelect = true;
28442                 }
28443                 var c = this.getContextMenu();
28444                 this.contextMenu.on("itemclick", function(item) {
28445                         switch (item.id) {
28446                                 case "delete":
28447                                         this.remove(this.getSelectedIndexes());
28448                                         break;
28449                         }
28450                 }, this);
28451                 this.contextMenu.add({
28452                         icon: imageUrl,
28453                         id: "delete",
28454                         text: 'Delete'
28455                 });
28456         },
28457         
28458 /**     Return the context menu for this DDView. */
28459         getContextMenu: function() {
28460                 if (!this.contextMenu) {
28461 //                      Create the View's context menu
28462                         this.contextMenu = new Roo.menu.Menu({
28463                                 id: this.id + "-contextmenu"
28464                         });
28465                         this.el.on("contextmenu", this.showContextMenu, this);
28466                 }
28467                 return this.contextMenu;
28468         },
28469         
28470         disableContextMenu: function() {
28471                 if (this.contextMenu) {
28472                         this.el.un("contextmenu", this.showContextMenu, this);
28473                 }
28474         },
28475
28476         showContextMenu: function(e, item) {
28477         item = this.findItemFromChild(e.getTarget());
28478                 if (item) {
28479                         e.stopEvent();
28480                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
28481                         this.contextMenu.showAt(e.getXY());
28482             }
28483     },
28484
28485 /**
28486  *      Remove {@link Roo.data.Record}s at the specified indices.
28487  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
28488  */
28489     remove: function(selectedIndices) {
28490                 selectedIndices = [].concat(selectedIndices);
28491                 for (var i = 0; i < selectedIndices.length; i++) {
28492                         var rec = this.store.getAt(selectedIndices[i]);
28493                         this.store.remove(rec);
28494                 }
28495     },
28496
28497 /**
28498  *      Double click fires the event, but also, if this is draggable, and there is only one other
28499  *      related DropZone, it transfers the selected node.
28500  */
28501     onDblClick : function(e){
28502         var item = this.findItemFromChild(e.getTarget());
28503         if(item){
28504             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
28505                 return false;
28506             }
28507             if (this.dragGroup) {
28508                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
28509                     while (targets.indexOf(this.dropZone) > -1) {
28510                             targets.remove(this.dropZone);
28511                                 }
28512                     if (targets.length == 1) {
28513                                         this.dragZone.cachedTarget = null;
28514                         var el = Roo.get(targets[0].getEl());
28515                         var box = el.getBox(true);
28516                         targets[0].onNodeDrop(el.dom, {
28517                                 target: el.dom,
28518                                 xy: [box.x, box.y + box.height - 1]
28519                         }, null, this.getDragData(e));
28520                     }
28521                 }
28522         }
28523     },
28524     
28525     handleSelection: function(e) {
28526                 this.dragZone.cachedTarget = null;
28527         var item = this.findItemFromChild(e.getTarget());
28528         if (!item) {
28529                 this.clearSelections(true);
28530                 return;
28531         }
28532                 if (item && (this.multiSelect || this.singleSelect)){
28533                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
28534                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
28535                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
28536                                 this.unselect(item);
28537                         } else {
28538                                 this.select(item, this.multiSelect && e.ctrlKey);
28539                                 this.lastSelection = item;
28540                         }
28541                 }
28542     },
28543
28544     onItemClick : function(item, index, e){
28545                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
28546                         return false;
28547                 }
28548                 return true;
28549     },
28550
28551     unselect : function(nodeInfo, suppressEvent){
28552                 var node = this.getNode(nodeInfo);
28553                 if(node && this.isSelected(node)){
28554                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28555                                 Roo.fly(node).removeClass(this.selectedClass);
28556                                 this.selections.remove(node);
28557                                 if(!suppressEvent){
28558                                         this.fireEvent("selectionchange", this, this.selections);
28559                                 }
28560                         }
28561                 }
28562     }
28563 });
28564 /*
28565  * Based on:
28566  * Ext JS Library 1.1.1
28567  * Copyright(c) 2006-2007, Ext JS, LLC.
28568  *
28569  * Originally Released Under LGPL - original licence link has changed is not relivant.
28570  *
28571  * Fork - LGPL
28572  * <script type="text/javascript">
28573  */
28574  
28575 /**
28576  * @class Roo.LayoutManager
28577  * @extends Roo.util.Observable
28578  * Base class for layout managers.
28579  */
28580 Roo.LayoutManager = function(container, config){
28581     Roo.LayoutManager.superclass.constructor.call(this);
28582     this.el = Roo.get(container);
28583     // ie scrollbar fix
28584     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
28585         document.body.scroll = "no";
28586     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
28587         this.el.position('relative');
28588     }
28589     this.id = this.el.id;
28590     this.el.addClass("x-layout-container");
28591     /** false to disable window resize monitoring @type Boolean */
28592     this.monitorWindowResize = true;
28593     this.regions = {};
28594     this.addEvents({
28595         /**
28596          * @event layout
28597          * Fires when a layout is performed. 
28598          * @param {Roo.LayoutManager} this
28599          */
28600         "layout" : true,
28601         /**
28602          * @event regionresized
28603          * Fires when the user resizes a region. 
28604          * @param {Roo.LayoutRegion} region The resized region
28605          * @param {Number} newSize The new size (width for east/west, height for north/south)
28606          */
28607         "regionresized" : true,
28608         /**
28609          * @event regioncollapsed
28610          * Fires when a region is collapsed. 
28611          * @param {Roo.LayoutRegion} region The collapsed region
28612          */
28613         "regioncollapsed" : true,
28614         /**
28615          * @event regionexpanded
28616          * Fires when a region is expanded.  
28617          * @param {Roo.LayoutRegion} region The expanded region
28618          */
28619         "regionexpanded" : true
28620     });
28621     this.updating = false;
28622     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
28623 };
28624
28625 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
28626     /**
28627      * Returns true if this layout is currently being updated
28628      * @return {Boolean}
28629      */
28630     isUpdating : function(){
28631         return this.updating; 
28632     },
28633     
28634     /**
28635      * Suspend the LayoutManager from doing auto-layouts while
28636      * making multiple add or remove calls
28637      */
28638     beginUpdate : function(){
28639         this.updating = true;    
28640     },
28641     
28642     /**
28643      * Restore auto-layouts and optionally disable the manager from performing a layout
28644      * @param {Boolean} noLayout true to disable a layout update 
28645      */
28646     endUpdate : function(noLayout){
28647         this.updating = false;
28648         if(!noLayout){
28649             this.layout();
28650         }    
28651     },
28652     
28653     layout: function(){
28654         
28655     },
28656     
28657     onRegionResized : function(region, newSize){
28658         this.fireEvent("regionresized", region, newSize);
28659         this.layout();
28660     },
28661     
28662     onRegionCollapsed : function(region){
28663         this.fireEvent("regioncollapsed", region);
28664     },
28665     
28666     onRegionExpanded : function(region){
28667         this.fireEvent("regionexpanded", region);
28668     },
28669         
28670     /**
28671      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
28672      * performs box-model adjustments.
28673      * @return {Object} The size as an object {width: (the width), height: (the height)}
28674      */
28675     getViewSize : function(){
28676         var size;
28677         if(this.el.dom != document.body){
28678             size = this.el.getSize();
28679         }else{
28680             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
28681         }
28682         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
28683         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
28684         return size;
28685     },
28686     
28687     /**
28688      * Returns the Element this layout is bound to.
28689      * @return {Roo.Element}
28690      */
28691     getEl : function(){
28692         return this.el;
28693     },
28694     
28695     /**
28696      * Returns the specified region.
28697      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
28698      * @return {Roo.LayoutRegion}
28699      */
28700     getRegion : function(target){
28701         return this.regions[target.toLowerCase()];
28702     },
28703     
28704     onWindowResize : function(){
28705         if(this.monitorWindowResize){
28706             this.layout();
28707         }
28708     }
28709 });/*
28710  * Based on:
28711  * Ext JS Library 1.1.1
28712  * Copyright(c) 2006-2007, Ext JS, LLC.
28713  *
28714  * Originally Released Under LGPL - original licence link has changed is not relivant.
28715  *
28716  * Fork - LGPL
28717  * <script type="text/javascript">
28718  */
28719 /**
28720  * @class Roo.BorderLayout
28721  * @extends Roo.LayoutManager
28722  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
28723  * please see: <br><br>
28724  * <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>
28725  * <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>
28726  * Example:
28727  <pre><code>
28728  var layout = new Roo.BorderLayout(document.body, {
28729     north: {
28730         initialSize: 25,
28731         titlebar: false
28732     },
28733     west: {
28734         split:true,
28735         initialSize: 200,
28736         minSize: 175,
28737         maxSize: 400,
28738         titlebar: true,
28739         collapsible: true
28740     },
28741     east: {
28742         split:true,
28743         initialSize: 202,
28744         minSize: 175,
28745         maxSize: 400,
28746         titlebar: true,
28747         collapsible: true
28748     },
28749     south: {
28750         split:true,
28751         initialSize: 100,
28752         minSize: 100,
28753         maxSize: 200,
28754         titlebar: true,
28755         collapsible: true
28756     },
28757     center: {
28758         titlebar: true,
28759         autoScroll:true,
28760         resizeTabs: true,
28761         minTabWidth: 50,
28762         preferredTabWidth: 150
28763     }
28764 });
28765
28766 // shorthand
28767 var CP = Roo.ContentPanel;
28768
28769 layout.beginUpdate();
28770 layout.add("north", new CP("north", "North"));
28771 layout.add("south", new CP("south", {title: "South", closable: true}));
28772 layout.add("west", new CP("west", {title: "West"}));
28773 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
28774 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
28775 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
28776 layout.getRegion("center").showPanel("center1");
28777 layout.endUpdate();
28778 </code></pre>
28779
28780 <b>The container the layout is rendered into can be either the body element or any other element.
28781 If it is not the body element, the container needs to either be an absolute positioned element,
28782 or you will need to add "position:relative" to the css of the container.  You will also need to specify
28783 the container size if it is not the body element.</b>
28784
28785 * @constructor
28786 * Create a new BorderLayout
28787 * @param {String/HTMLElement/Element} container The container this layout is bound to
28788 * @param {Object} config Configuration options
28789  */
28790 Roo.BorderLayout = function(container, config){
28791     config = config || {};
28792     Roo.BorderLayout.superclass.constructor.call(this, container, config);
28793     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
28794     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
28795         var target = this.factory.validRegions[i];
28796         if(config[target]){
28797             this.addRegion(target, config[target]);
28798         }
28799     }
28800 };
28801
28802 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
28803     /**
28804      * Creates and adds a new region if it doesn't already exist.
28805      * @param {String} target The target region key (north, south, east, west or center).
28806      * @param {Object} config The regions config object
28807      * @return {BorderLayoutRegion} The new region
28808      */
28809     addRegion : function(target, config){
28810         if(!this.regions[target]){
28811             var r = this.factory.create(target, this, config);
28812             this.bindRegion(target, r);
28813         }
28814         return this.regions[target];
28815     },
28816
28817     // private (kinda)
28818     bindRegion : function(name, r){
28819         this.regions[name] = r;
28820         r.on("visibilitychange", this.layout, this);
28821         r.on("paneladded", this.layout, this);
28822         r.on("panelremoved", this.layout, this);
28823         r.on("invalidated", this.layout, this);
28824         r.on("resized", this.onRegionResized, this);
28825         r.on("collapsed", this.onRegionCollapsed, this);
28826         r.on("expanded", this.onRegionExpanded, this);
28827     },
28828
28829     /**
28830      * Performs a layout update.
28831      */
28832     layout : function(){
28833         if(this.updating) return;
28834         var size = this.getViewSize();
28835         var w = size.width;
28836         var h = size.height;
28837         var centerW = w;
28838         var centerH = h;
28839         var centerY = 0;
28840         var centerX = 0;
28841         //var x = 0, y = 0;
28842
28843         var rs = this.regions;
28844         var north = rs["north"];
28845         var south = rs["south"]; 
28846         var west = rs["west"];
28847         var east = rs["east"];
28848         var center = rs["center"];
28849         //if(this.hideOnLayout){ // not supported anymore
28850             //c.el.setStyle("display", "none");
28851         //}
28852         if(north && north.isVisible()){
28853             var b = north.getBox();
28854             var m = north.getMargins();
28855             b.width = w - (m.left+m.right);
28856             b.x = m.left;
28857             b.y = m.top;
28858             centerY = b.height + b.y + m.bottom;
28859             centerH -= centerY;
28860             north.updateBox(this.safeBox(b));
28861         }
28862         if(south && south.isVisible()){
28863             var b = south.getBox();
28864             var m = south.getMargins();
28865             b.width = w - (m.left+m.right);
28866             b.x = m.left;
28867             var totalHeight = (b.height + m.top + m.bottom);
28868             b.y = h - totalHeight + m.top;
28869             centerH -= totalHeight;
28870             south.updateBox(this.safeBox(b));
28871         }
28872         if(west && west.isVisible()){
28873             var b = west.getBox();
28874             var m = west.getMargins();
28875             b.height = centerH - (m.top+m.bottom);
28876             b.x = m.left;
28877             b.y = centerY + m.top;
28878             var totalWidth = (b.width + m.left + m.right);
28879             centerX += totalWidth;
28880             centerW -= totalWidth;
28881             west.updateBox(this.safeBox(b));
28882         }
28883         if(east && east.isVisible()){
28884             var b = east.getBox();
28885             var m = east.getMargins();
28886             b.height = centerH - (m.top+m.bottom);
28887             var totalWidth = (b.width + m.left + m.right);
28888             b.x = w - totalWidth + m.left;
28889             b.y = centerY + m.top;
28890             centerW -= totalWidth;
28891             east.updateBox(this.safeBox(b));
28892         }
28893         if(center){
28894             var m = center.getMargins();
28895             var centerBox = {
28896                 x: centerX + m.left,
28897                 y: centerY + m.top,
28898                 width: centerW - (m.left+m.right),
28899                 height: centerH - (m.top+m.bottom)
28900             };
28901             //if(this.hideOnLayout){
28902                 //center.el.setStyle("display", "block");
28903             //}
28904             center.updateBox(this.safeBox(centerBox));
28905         }
28906         this.el.repaint();
28907         this.fireEvent("layout", this);
28908     },
28909
28910     // private
28911     safeBox : function(box){
28912         box.width = Math.max(0, box.width);
28913         box.height = Math.max(0, box.height);
28914         return box;
28915     },
28916
28917     /**
28918      * Adds a ContentPanel (or subclass) to this layout.
28919      * @param {String} target The target region key (north, south, east, west or center).
28920      * @param {Roo.ContentPanel} panel The panel to add
28921      * @return {Roo.ContentPanel} The added panel
28922      */
28923     add : function(target, panel){
28924          
28925         target = target.toLowerCase();
28926         return this.regions[target].add(panel);
28927     },
28928
28929     /**
28930      * Remove a ContentPanel (or subclass) to this layout.
28931      * @param {String} target The target region key (north, south, east, west or center).
28932      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
28933      * @return {Roo.ContentPanel} The removed panel
28934      */
28935     remove : function(target, panel){
28936         target = target.toLowerCase();
28937         return this.regions[target].remove(panel);
28938     },
28939
28940     /**
28941      * Searches all regions for a panel with the specified id
28942      * @param {String} panelId
28943      * @return {Roo.ContentPanel} The panel or null if it wasn't found
28944      */
28945     findPanel : function(panelId){
28946         var rs = this.regions;
28947         for(var target in rs){
28948             if(typeof rs[target] != "function"){
28949                 var p = rs[target].getPanel(panelId);
28950                 if(p){
28951                     return p;
28952                 }
28953             }
28954         }
28955         return null;
28956     },
28957
28958     /**
28959      * Searches all regions for a panel with the specified id and activates (shows) it.
28960      * @param {String/ContentPanel} panelId The panels id or the panel itself
28961      * @return {Roo.ContentPanel} The shown panel or null
28962      */
28963     showPanel : function(panelId) {
28964       var rs = this.regions;
28965       for(var target in rs){
28966          var r = rs[target];
28967          if(typeof r != "function"){
28968             if(r.hasPanel(panelId)){
28969                return r.showPanel(panelId);
28970             }
28971          }
28972       }
28973       return null;
28974    },
28975
28976    /**
28977      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
28978      * @param {Roo.state.Provider} provider (optional) An alternate state provider
28979      */
28980     restoreState : function(provider){
28981         if(!provider){
28982             provider = Roo.state.Manager;
28983         }
28984         var sm = new Roo.LayoutStateManager();
28985         sm.init(this, provider);
28986     },
28987
28988     /**
28989      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
28990      * object should contain properties for each region to add ContentPanels to, and each property's value should be
28991      * a valid ContentPanel config object.  Example:
28992      * <pre><code>
28993 // Create the main layout
28994 var layout = new Roo.BorderLayout('main-ct', {
28995     west: {
28996         split:true,
28997         minSize: 175,
28998         titlebar: true
28999     },
29000     center: {
29001         title:'Components'
29002     }
29003 }, 'main-ct');
29004
29005 // Create and add multiple ContentPanels at once via configs
29006 layout.batchAdd({
29007    west: {
29008        id: 'source-files',
29009        autoCreate:true,
29010        title:'Ext Source Files',
29011        autoScroll:true,
29012        fitToFrame:true
29013    },
29014    center : {
29015        el: cview,
29016        autoScroll:true,
29017        fitToFrame:true,
29018        toolbar: tb,
29019        resizeEl:'cbody'
29020    }
29021 });
29022 </code></pre>
29023      * @param {Object} regions An object containing ContentPanel configs by region name
29024      */
29025     batchAdd : function(regions){
29026         this.beginUpdate();
29027         for(var rname in regions){
29028             var lr = this.regions[rname];
29029             if(lr){
29030                 this.addTypedPanels(lr, regions[rname]);
29031             }
29032         }
29033         this.endUpdate();
29034     },
29035
29036     // private
29037     addTypedPanels : function(lr, ps){
29038         if(typeof ps == 'string'){
29039             lr.add(new Roo.ContentPanel(ps));
29040         }
29041         else if(ps instanceof Array){
29042             for(var i =0, len = ps.length; i < len; i++){
29043                 this.addTypedPanels(lr, ps[i]);
29044             }
29045         }
29046         else if(!ps.events){ // raw config?
29047             var el = ps.el;
29048             delete ps.el; // prevent conflict
29049             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
29050         }
29051         else {  // panel object assumed!
29052             lr.add(ps);
29053         }
29054     },
29055     /**
29056      * Adds a xtype elements to the layout.
29057      * <pre><code>
29058
29059 layout.addxtype({
29060        xtype : 'ContentPanel',
29061        region: 'west',
29062        items: [ .... ]
29063    }
29064 );
29065
29066 layout.addxtype({
29067         xtype : 'NestedLayoutPanel',
29068         region: 'west',
29069         layout: {
29070            center: { },
29071            west: { }   
29072         },
29073         items : [ ... list of content panels or nested layout panels.. ]
29074    }
29075 );
29076 </code></pre>
29077      * @param {Object} cfg Xtype definition of item to add.
29078      */
29079     addxtype : function(cfg)
29080     {
29081         // basically accepts a pannel...
29082         // can accept a layout region..!?!?
29083        // console.log('BorderLayout add ' + cfg.xtype)
29084         
29085         if (!cfg.xtype.match(/Panel$/)) {
29086             return false;
29087         }
29088         var ret = false;
29089         var region = cfg.region;
29090         delete cfg.region;
29091         
29092           
29093         var xitems = [];
29094         if (cfg.items) {
29095             xitems = cfg.items;
29096             delete cfg.items;
29097         }
29098         
29099         
29100         switch(cfg.xtype) 
29101         {
29102             case 'ContentPanel':  // ContentPanel (el, cfg)
29103             case 'ScrollPanel':  // ContentPanel (el, cfg)
29104                 if(cfg.autoCreate) {
29105                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29106                 } else {
29107                     var el = this.el.createChild();
29108                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
29109                 }
29110                 
29111                 this.add(region, ret);
29112                 break;
29113             
29114             
29115             case 'TreePanel': // our new panel!
29116                 cfg.el = this.el.createChild();
29117                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29118                 this.add(region, ret);
29119                 break;
29120             
29121             case 'NestedLayoutPanel': 
29122                 // create a new Layout (which is  a Border Layout...
29123                 var el = this.el.createChild();
29124                 var clayout = cfg.layout;
29125                 delete cfg.layout;
29126                 clayout.items   = clayout.items  || [];
29127                 // replace this exitems with the clayout ones..
29128                 xitems = clayout.items;
29129                  
29130                 
29131                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
29132                     cfg.background = false;
29133                 }
29134                 var layout = new Roo.BorderLayout(el, clayout);
29135                 
29136                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
29137                 //console.log('adding nested layout panel '  + cfg.toSource());
29138                 this.add(region, ret);
29139                 
29140                 break;
29141                 
29142             case 'GridPanel': 
29143             
29144                 // needs grid and region
29145                 
29146                 //var el = this.getRegion(region).el.createChild();
29147                 var el = this.el.createChild();
29148                 // create the grid first...
29149                 
29150                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
29151                 delete cfg.grid;
29152                 if (region == 'center' && this.active ) {
29153                     cfg.background = false;
29154                 }
29155                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
29156                 
29157                 this.add(region, ret);
29158                 if (cfg.background) {
29159                     ret.on('activate', function(gp) {
29160                         if (!gp.grid.rendered) {
29161                             gp.grid.render();
29162                         }
29163                     });
29164                 } else {
29165                     grid.render();
29166                 }
29167                 break;
29168            
29169                
29170                 
29171                 
29172             default: 
29173                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
29174                 return;
29175              // GridPanel (grid, cfg)
29176             
29177         }
29178         this.beginUpdate();
29179         // add children..
29180         Roo.each(xitems, function(i)  {
29181             ret.addxtype(i);
29182         });
29183         this.endUpdate();
29184         return ret;
29185         
29186     }
29187 });
29188
29189 /**
29190  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
29191  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
29192  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
29193  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
29194  * <pre><code>
29195 // shorthand
29196 var CP = Roo.ContentPanel;
29197
29198 var layout = Roo.BorderLayout.create({
29199     north: {
29200         initialSize: 25,
29201         titlebar: false,
29202         panels: [new CP("north", "North")]
29203     },
29204     west: {
29205         split:true,
29206         initialSize: 200,
29207         minSize: 175,
29208         maxSize: 400,
29209         titlebar: true,
29210         collapsible: true,
29211         panels: [new CP("west", {title: "West"})]
29212     },
29213     east: {
29214         split:true,
29215         initialSize: 202,
29216         minSize: 175,
29217         maxSize: 400,
29218         titlebar: true,
29219         collapsible: true,
29220         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
29221     },
29222     south: {
29223         split:true,
29224         initialSize: 100,
29225         minSize: 100,
29226         maxSize: 200,
29227         titlebar: true,
29228         collapsible: true,
29229         panels: [new CP("south", {title: "South", closable: true})]
29230     },
29231     center: {
29232         titlebar: true,
29233         autoScroll:true,
29234         resizeTabs: true,
29235         minTabWidth: 50,
29236         preferredTabWidth: 150,
29237         panels: [
29238             new CP("center1", {title: "Close Me", closable: true}),
29239             new CP("center2", {title: "Center Panel", closable: false})
29240         ]
29241     }
29242 }, document.body);
29243
29244 layout.getRegion("center").showPanel("center1");
29245 </code></pre>
29246  * @param config
29247  * @param targetEl
29248  */
29249 Roo.BorderLayout.create = function(config, targetEl){
29250     var layout = new Roo.BorderLayout(targetEl || document.body, config);
29251     layout.beginUpdate();
29252     var regions = Roo.BorderLayout.RegionFactory.validRegions;
29253     for(var j = 0, jlen = regions.length; j < jlen; j++){
29254         var lr = regions[j];
29255         if(layout.regions[lr] && config[lr].panels){
29256             var r = layout.regions[lr];
29257             var ps = config[lr].panels;
29258             layout.addTypedPanels(r, ps);
29259         }
29260     }
29261     layout.endUpdate();
29262     return layout;
29263 };
29264
29265 // private
29266 Roo.BorderLayout.RegionFactory = {
29267     // private
29268     validRegions : ["north","south","east","west","center"],
29269
29270     // private
29271     create : function(target, mgr, config){
29272         target = target.toLowerCase();
29273         if(config.lightweight || config.basic){
29274             return new Roo.BasicLayoutRegion(mgr, config, target);
29275         }
29276         switch(target){
29277             case "north":
29278                 return new Roo.NorthLayoutRegion(mgr, config);
29279             case "south":
29280                 return new Roo.SouthLayoutRegion(mgr, config);
29281             case "east":
29282                 return new Roo.EastLayoutRegion(mgr, config);
29283             case "west":
29284                 return new Roo.WestLayoutRegion(mgr, config);
29285             case "center":
29286                 return new Roo.CenterLayoutRegion(mgr, config);
29287         }
29288         throw 'Layout region "'+target+'" not supported.';
29289     }
29290 };/*
29291  * Based on:
29292  * Ext JS Library 1.1.1
29293  * Copyright(c) 2006-2007, Ext JS, LLC.
29294  *
29295  * Originally Released Under LGPL - original licence link has changed is not relivant.
29296  *
29297  * Fork - LGPL
29298  * <script type="text/javascript">
29299  */
29300  
29301 /**
29302  * @class Roo.BasicLayoutRegion
29303  * @extends Roo.util.Observable
29304  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
29305  * and does not have a titlebar, tabs or any other features. All it does is size and position 
29306  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
29307  */
29308 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
29309     this.mgr = mgr;
29310     this.position  = pos;
29311     this.events = {
29312         /**
29313          * @scope Roo.BasicLayoutRegion
29314          */
29315         
29316         /**
29317          * @event beforeremove
29318          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
29319          * @param {Roo.LayoutRegion} this
29320          * @param {Roo.ContentPanel} panel The panel
29321          * @param {Object} e The cancel event object
29322          */
29323         "beforeremove" : true,
29324         /**
29325          * @event invalidated
29326          * Fires when the layout for this region is changed.
29327          * @param {Roo.LayoutRegion} this
29328          */
29329         "invalidated" : true,
29330         /**
29331          * @event visibilitychange
29332          * Fires when this region is shown or hidden 
29333          * @param {Roo.LayoutRegion} this
29334          * @param {Boolean} visibility true or false
29335          */
29336         "visibilitychange" : true,
29337         /**
29338          * @event paneladded
29339          * Fires when a panel is added. 
29340          * @param {Roo.LayoutRegion} this
29341          * @param {Roo.ContentPanel} panel The panel
29342          */
29343         "paneladded" : true,
29344         /**
29345          * @event panelremoved
29346          * Fires when a panel is removed. 
29347          * @param {Roo.LayoutRegion} this
29348          * @param {Roo.ContentPanel} panel The panel
29349          */
29350         "panelremoved" : true,
29351         /**
29352          * @event collapsed
29353          * Fires when this region is collapsed.
29354          * @param {Roo.LayoutRegion} this
29355          */
29356         "collapsed" : true,
29357         /**
29358          * @event expanded
29359          * Fires when this region is expanded.
29360          * @param {Roo.LayoutRegion} this
29361          */
29362         "expanded" : true,
29363         /**
29364          * @event slideshow
29365          * Fires when this region is slid into view.
29366          * @param {Roo.LayoutRegion} this
29367          */
29368         "slideshow" : true,
29369         /**
29370          * @event slidehide
29371          * Fires when this region slides out of view. 
29372          * @param {Roo.LayoutRegion} this
29373          */
29374         "slidehide" : true,
29375         /**
29376          * @event panelactivated
29377          * Fires when a panel is activated. 
29378          * @param {Roo.LayoutRegion} this
29379          * @param {Roo.ContentPanel} panel The activated panel
29380          */
29381         "panelactivated" : true,
29382         /**
29383          * @event resized
29384          * Fires when the user resizes this region. 
29385          * @param {Roo.LayoutRegion} this
29386          * @param {Number} newSize The new size (width for east/west, height for north/south)
29387          */
29388         "resized" : true
29389     };
29390     /** A collection of panels in this region. @type Roo.util.MixedCollection */
29391     this.panels = new Roo.util.MixedCollection();
29392     this.panels.getKey = this.getPanelId.createDelegate(this);
29393     this.box = null;
29394     this.activePanel = null;
29395     // ensure listeners are added...
29396     
29397     if (config.listeners || config.events) {
29398         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
29399             listeners : config.listeners || {},
29400             events : config.events || {}
29401         });
29402     }
29403     
29404     if(skipConfig !== true){
29405         this.applyConfig(config);
29406     }
29407 };
29408
29409 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
29410     getPanelId : function(p){
29411         return p.getId();
29412     },
29413     
29414     applyConfig : function(config){
29415         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
29416         this.config = config;
29417         
29418     },
29419     
29420     /**
29421      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
29422      * the width, for horizontal (north, south) the height.
29423      * @param {Number} newSize The new width or height
29424      */
29425     resizeTo : function(newSize){
29426         var el = this.el ? this.el :
29427                  (this.activePanel ? this.activePanel.getEl() : null);
29428         if(el){
29429             switch(this.position){
29430                 case "east":
29431                 case "west":
29432                     el.setWidth(newSize);
29433                     this.fireEvent("resized", this, newSize);
29434                 break;
29435                 case "north":
29436                 case "south":
29437                     el.setHeight(newSize);
29438                     this.fireEvent("resized", this, newSize);
29439                 break;                
29440             }
29441         }
29442     },
29443     
29444     getBox : function(){
29445         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
29446     },
29447     
29448     getMargins : function(){
29449         return this.margins;
29450     },
29451     
29452     updateBox : function(box){
29453         this.box = box;
29454         var el = this.activePanel.getEl();
29455         el.dom.style.left = box.x + "px";
29456         el.dom.style.top = box.y + "px";
29457         this.activePanel.setSize(box.width, box.height);
29458     },
29459     
29460     /**
29461      * Returns the container element for this region.
29462      * @return {Roo.Element}
29463      */
29464     getEl : function(){
29465         return this.activePanel;
29466     },
29467     
29468     /**
29469      * Returns true if this region is currently visible.
29470      * @return {Boolean}
29471      */
29472     isVisible : function(){
29473         return this.activePanel ? true : false;
29474     },
29475     
29476     setActivePanel : function(panel){
29477         panel = this.getPanel(panel);
29478         if(this.activePanel && this.activePanel != panel){
29479             this.activePanel.setActiveState(false);
29480             this.activePanel.getEl().setLeftTop(-10000,-10000);
29481         }
29482         this.activePanel = panel;
29483         panel.setActiveState(true);
29484         if(this.box){
29485             panel.setSize(this.box.width, this.box.height);
29486         }
29487         this.fireEvent("panelactivated", this, panel);
29488         this.fireEvent("invalidated");
29489     },
29490     
29491     /**
29492      * Show the specified panel.
29493      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
29494      * @return {Roo.ContentPanel} The shown panel or null
29495      */
29496     showPanel : function(panel){
29497         if(panel = this.getPanel(panel)){
29498             this.setActivePanel(panel);
29499         }
29500         return panel;
29501     },
29502     
29503     /**
29504      * Get the active panel for this region.
29505      * @return {Roo.ContentPanel} The active panel or null
29506      */
29507     getActivePanel : function(){
29508         return this.activePanel;
29509     },
29510     
29511     /**
29512      * Add the passed ContentPanel(s)
29513      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
29514      * @return {Roo.ContentPanel} The panel added (if only one was added)
29515      */
29516     add : function(panel){
29517         if(arguments.length > 1){
29518             for(var i = 0, len = arguments.length; i < len; i++) {
29519                 this.add(arguments[i]);
29520             }
29521             return null;
29522         }
29523         if(this.hasPanel(panel)){
29524             this.showPanel(panel);
29525             return panel;
29526         }
29527         var el = panel.getEl();
29528         if(el.dom.parentNode != this.mgr.el.dom){
29529             this.mgr.el.dom.appendChild(el.dom);
29530         }
29531         if(panel.setRegion){
29532             panel.setRegion(this);
29533         }
29534         this.panels.add(panel);
29535         el.setStyle("position", "absolute");
29536         if(!panel.background){
29537             this.setActivePanel(panel);
29538             if(this.config.initialSize && this.panels.getCount()==1){
29539                 this.resizeTo(this.config.initialSize);
29540             }
29541         }
29542         this.fireEvent("paneladded", this, panel);
29543         return panel;
29544     },
29545     
29546     /**
29547      * Returns true if the panel is in this region.
29548      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29549      * @return {Boolean}
29550      */
29551     hasPanel : function(panel){
29552         if(typeof panel == "object"){ // must be panel obj
29553             panel = panel.getId();
29554         }
29555         return this.getPanel(panel) ? true : false;
29556     },
29557     
29558     /**
29559      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
29560      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29561      * @param {Boolean} preservePanel Overrides the config preservePanel option
29562      * @return {Roo.ContentPanel} The panel that was removed
29563      */
29564     remove : function(panel, preservePanel){
29565         panel = this.getPanel(panel);
29566         if(!panel){
29567             return null;
29568         }
29569         var e = {};
29570         this.fireEvent("beforeremove", this, panel, e);
29571         if(e.cancel === true){
29572             return null;
29573         }
29574         var panelId = panel.getId();
29575         this.panels.removeKey(panelId);
29576         return panel;
29577     },
29578     
29579     /**
29580      * Returns the panel specified or null if it's not in this region.
29581      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29582      * @return {Roo.ContentPanel}
29583      */
29584     getPanel : function(id){
29585         if(typeof id == "object"){ // must be panel obj
29586             return id;
29587         }
29588         return this.panels.get(id);
29589     },
29590     
29591     /**
29592      * Returns this regions position (north/south/east/west/center).
29593      * @return {String} 
29594      */
29595     getPosition: function(){
29596         return this.position;    
29597     }
29598 });/*
29599  * Based on:
29600  * Ext JS Library 1.1.1
29601  * Copyright(c) 2006-2007, Ext JS, LLC.
29602  *
29603  * Originally Released Under LGPL - original licence link has changed is not relivant.
29604  *
29605  * Fork - LGPL
29606  * <script type="text/javascript">
29607  */
29608  
29609 /**
29610  * @class Roo.LayoutRegion
29611  * @extends Roo.BasicLayoutRegion
29612  * This class represents a region in a layout manager.
29613  * @cfg {Boolean} collapsible False to disable collapsing (defaults to true)
29614  * @cfg {Boolean} collapsed True to set the initial display to collapsed (defaults to false)
29615  * @cfg {Boolean} floatable False to disable floating (defaults to true)
29616  * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
29617  * @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})
29618  * @cfg {String} tabPosition "top" or "bottom" (defaults to "bottom")
29619  * @cfg {String} collapsedTitle Optional string message to display in the collapsed block of a north or south region
29620  * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
29621  * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
29622  * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
29623  * @cfg {String} title The title for the region (overrides panel titles)
29624  * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
29625  * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
29626  * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
29627  * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
29628  * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
29629  * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
29630  * the space available, similar to FireFox 1.5 tabs (defaults to false)
29631  * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
29632  * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
29633  * @cfg {Boolean} showPin True to show a pin button
29634 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
29635 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
29636 * @cfg {Boolean} disableTabTips True to disable tab tooltips
29637 * @cfg {Number} width  For East/West panels
29638 * @cfg {Number} height For North/South panels
29639 * @cfg {Boolean} split To show the splitter
29640  */
29641 Roo.LayoutRegion = function(mgr, config, pos){
29642     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
29643     var dh = Roo.DomHelper;
29644     /** This region's container element 
29645     * @type Roo.Element */
29646     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
29647     /** This region's title element 
29648     * @type Roo.Element */
29649
29650     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
29651         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
29652         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
29653     ]}, true);
29654     this.titleEl.enableDisplayMode();
29655     /** This region's title text element 
29656     * @type HTMLElement */
29657     this.titleTextEl = this.titleEl.dom.firstChild;
29658     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
29659     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
29660     this.closeBtn.enableDisplayMode();
29661     this.closeBtn.on("click", this.closeClicked, this);
29662     this.closeBtn.hide();
29663
29664     this.createBody(config);
29665     this.visible = true;
29666     this.collapsed = false;
29667
29668     if(config.hideWhenEmpty){
29669         this.hide();
29670         this.on("paneladded", this.validateVisibility, this);
29671         this.on("panelremoved", this.validateVisibility, this);
29672     }
29673     this.applyConfig(config);
29674 };
29675
29676 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
29677
29678     createBody : function(){
29679         /** This region's body element 
29680         * @type Roo.Element */
29681         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
29682     },
29683
29684     applyConfig : function(c){
29685         if(c.collapsible && this.position != "center" && !this.collapsedEl){
29686             var dh = Roo.DomHelper;
29687             if(c.titlebar !== false){
29688                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
29689                 this.collapseBtn.on("click", this.collapse, this);
29690                 this.collapseBtn.enableDisplayMode();
29691
29692                 if(c.showPin === true || this.showPin){
29693                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
29694                     this.stickBtn.enableDisplayMode();
29695                     this.stickBtn.on("click", this.expand, this);
29696                     this.stickBtn.hide();
29697                 }
29698             }
29699             /** This region's collapsed element
29700             * @type Roo.Element */
29701             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
29702                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
29703             ]}, true);
29704             if(c.floatable !== false){
29705                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
29706                this.collapsedEl.on("click", this.collapseClick, this);
29707             }
29708
29709             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
29710                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
29711                    id: "message", unselectable: "on", style:{"float":"left"}});
29712                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
29713              }
29714             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
29715             this.expandBtn.on("click", this.expand, this);
29716         }
29717         if(this.collapseBtn){
29718             this.collapseBtn.setVisible(c.collapsible == true);
29719         }
29720         this.cmargins = c.cmargins || this.cmargins ||
29721                          (this.position == "west" || this.position == "east" ?
29722                              {top: 0, left: 2, right:2, bottom: 0} :
29723                              {top: 2, left: 0, right:0, bottom: 2});
29724         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
29725         this.bottomTabs = c.tabPosition != "top";
29726         this.autoScroll = c.autoScroll || false;
29727         if(this.autoScroll){
29728             this.bodyEl.setStyle("overflow", "auto");
29729         }else{
29730             this.bodyEl.setStyle("overflow", "hidden");
29731         }
29732         //if(c.titlebar !== false){
29733             if((!c.titlebar && !c.title) || c.titlebar === false){
29734                 this.titleEl.hide();
29735             }else{
29736                 this.titleEl.show();
29737                 if(c.title){
29738                     this.titleTextEl.innerHTML = c.title;
29739                 }
29740             }
29741         //}
29742         this.duration = c.duration || .30;
29743         this.slideDuration = c.slideDuration || .45;
29744         this.config = c;
29745         if(c.collapsed){
29746             this.collapse(true);
29747         }
29748         if(c.hidden){
29749             this.hide();
29750         }
29751     },
29752     /**
29753      * Returns true if this region is currently visible.
29754      * @return {Boolean}
29755      */
29756     isVisible : function(){
29757         return this.visible;
29758     },
29759
29760     /**
29761      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
29762      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
29763      */
29764     setCollapsedTitle : function(title){
29765         title = title || "&#160;";
29766         if(this.collapsedTitleTextEl){
29767             this.collapsedTitleTextEl.innerHTML = title;
29768         }
29769     },
29770
29771     getBox : function(){
29772         var b;
29773         if(!this.collapsed){
29774             b = this.el.getBox(false, true);
29775         }else{
29776             b = this.collapsedEl.getBox(false, true);
29777         }
29778         return b;
29779     },
29780
29781     getMargins : function(){
29782         return this.collapsed ? this.cmargins : this.margins;
29783     },
29784
29785     highlight : function(){
29786         this.el.addClass("x-layout-panel-dragover");
29787     },
29788
29789     unhighlight : function(){
29790         this.el.removeClass("x-layout-panel-dragover");
29791     },
29792
29793     updateBox : function(box){
29794         this.box = box;
29795         if(!this.collapsed){
29796             this.el.dom.style.left = box.x + "px";
29797             this.el.dom.style.top = box.y + "px";
29798             this.updateBody(box.width, box.height);
29799         }else{
29800             this.collapsedEl.dom.style.left = box.x + "px";
29801             this.collapsedEl.dom.style.top = box.y + "px";
29802             this.collapsedEl.setSize(box.width, box.height);
29803         }
29804         if(this.tabs){
29805             this.tabs.autoSizeTabs();
29806         }
29807     },
29808
29809     updateBody : function(w, h){
29810         if(w !== null){
29811             this.el.setWidth(w);
29812             w -= this.el.getBorderWidth("rl");
29813             if(this.config.adjustments){
29814                 w += this.config.adjustments[0];
29815             }
29816         }
29817         if(h !== null){
29818             this.el.setHeight(h);
29819             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
29820             h -= this.el.getBorderWidth("tb");
29821             if(this.config.adjustments){
29822                 h += this.config.adjustments[1];
29823             }
29824             this.bodyEl.setHeight(h);
29825             if(this.tabs){
29826                 h = this.tabs.syncHeight(h);
29827             }
29828         }
29829         if(this.panelSize){
29830             w = w !== null ? w : this.panelSize.width;
29831             h = h !== null ? h : this.panelSize.height;
29832         }
29833         if(this.activePanel){
29834             var el = this.activePanel.getEl();
29835             w = w !== null ? w : el.getWidth();
29836             h = h !== null ? h : el.getHeight();
29837             this.panelSize = {width: w, height: h};
29838             this.activePanel.setSize(w, h);
29839         }
29840         if(Roo.isIE && this.tabs){
29841             this.tabs.el.repaint();
29842         }
29843     },
29844
29845     /**
29846      * Returns the container element for this region.
29847      * @return {Roo.Element}
29848      */
29849     getEl : function(){
29850         return this.el;
29851     },
29852
29853     /**
29854      * Hides this region.
29855      */
29856     hide : function(){
29857         if(!this.collapsed){
29858             this.el.dom.style.left = "-2000px";
29859             this.el.hide();
29860         }else{
29861             this.collapsedEl.dom.style.left = "-2000px";
29862             this.collapsedEl.hide();
29863         }
29864         this.visible = false;
29865         this.fireEvent("visibilitychange", this, false);
29866     },
29867
29868     /**
29869      * Shows this region if it was previously hidden.
29870      */
29871     show : function(){
29872         if(!this.collapsed){
29873             this.el.show();
29874         }else{
29875             this.collapsedEl.show();
29876         }
29877         this.visible = true;
29878         this.fireEvent("visibilitychange", this, true);
29879     },
29880
29881     closeClicked : function(){
29882         if(this.activePanel){
29883             this.remove(this.activePanel);
29884         }
29885     },
29886
29887     collapseClick : function(e){
29888         if(this.isSlid){
29889            e.stopPropagation();
29890            this.slideIn();
29891         }else{
29892            e.stopPropagation();
29893            this.slideOut();
29894         }
29895     },
29896
29897     /**
29898      * Collapses this region.
29899      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
29900      */
29901     collapse : function(skipAnim){
29902         if(this.collapsed) return;
29903         this.collapsed = true;
29904         if(this.split){
29905             this.split.el.hide();
29906         }
29907         if(this.config.animate && skipAnim !== true){
29908             this.fireEvent("invalidated", this);
29909             this.animateCollapse();
29910         }else{
29911             this.el.setLocation(-20000,-20000);
29912             this.el.hide();
29913             this.collapsedEl.show();
29914             this.fireEvent("collapsed", this);
29915             this.fireEvent("invalidated", this);
29916         }
29917     },
29918
29919     animateCollapse : function(){
29920         // overridden
29921     },
29922
29923     /**
29924      * Expands this region if it was previously collapsed.
29925      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
29926      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
29927      */
29928     expand : function(e, skipAnim){
29929         if(e) e.stopPropagation();
29930         if(!this.collapsed || this.el.hasActiveFx()) return;
29931         if(this.isSlid){
29932             this.afterSlideIn();
29933             skipAnim = true;
29934         }
29935         this.collapsed = false;
29936         if(this.config.animate && skipAnim !== true){
29937             this.animateExpand();
29938         }else{
29939             this.el.show();
29940             if(this.split){
29941                 this.split.el.show();
29942             }
29943             this.collapsedEl.setLocation(-2000,-2000);
29944             this.collapsedEl.hide();
29945             this.fireEvent("invalidated", this);
29946             this.fireEvent("expanded", this);
29947         }
29948     },
29949
29950     animateExpand : function(){
29951         // overridden
29952     },
29953
29954     initTabs : function(){
29955         this.bodyEl.setStyle("overflow", "hidden");
29956         var ts = new Roo.TabPanel(this.bodyEl.dom, {
29957             tabPosition: this.bottomTabs ? 'bottom' : 'top',
29958             disableTooltips: this.config.disableTabTips
29959         });
29960         if(this.config.hideTabs){
29961             ts.stripWrap.setDisplayed(false);
29962         }
29963         this.tabs = ts;
29964         ts.resizeTabs = this.config.resizeTabs === true;
29965         ts.minTabWidth = this.config.minTabWidth || 40;
29966         ts.maxTabWidth = this.config.maxTabWidth || 250;
29967         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
29968         ts.monitorResize = false;
29969         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
29970         ts.bodyEl.addClass('x-layout-tabs-body');
29971         this.panels.each(this.initPanelAsTab, this);
29972     },
29973
29974     initPanelAsTab : function(panel){
29975         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
29976                     this.config.closeOnTab && panel.isClosable());
29977         if(panel.tabTip !== undefined){
29978             ti.setTooltip(panel.tabTip);
29979         }
29980         ti.on("activate", function(){
29981               this.setActivePanel(panel);
29982         }, this);
29983         if(this.config.closeOnTab){
29984             ti.on("beforeclose", function(t, e){
29985                 e.cancel = true;
29986                 this.remove(panel);
29987             }, this);
29988         }
29989         return ti;
29990     },
29991
29992     updatePanelTitle : function(panel, title){
29993         if(this.activePanel == panel){
29994             this.updateTitle(title);
29995         }
29996         if(this.tabs){
29997             var ti = this.tabs.getTab(panel.getEl().id);
29998             ti.setText(title);
29999             if(panel.tabTip !== undefined){
30000                 ti.setTooltip(panel.tabTip);
30001             }
30002         }
30003     },
30004
30005     updateTitle : function(title){
30006         if(this.titleTextEl && !this.config.title){
30007             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
30008         }
30009     },
30010
30011     setActivePanel : function(panel){
30012         panel = this.getPanel(panel);
30013         if(this.activePanel && this.activePanel != panel){
30014             this.activePanel.setActiveState(false);
30015         }
30016         this.activePanel = panel;
30017         panel.setActiveState(true);
30018         if(this.panelSize){
30019             panel.setSize(this.panelSize.width, this.panelSize.height);
30020         }
30021         if(this.closeBtn){
30022             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
30023         }
30024         this.updateTitle(panel.getTitle());
30025         if(this.tabs){
30026             this.fireEvent("invalidated", this);
30027         }
30028         this.fireEvent("panelactivated", this, panel);
30029     },
30030
30031     /**
30032      * Shows the specified panel.
30033      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
30034      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
30035      */
30036     showPanel : function(panel){
30037         if(panel = this.getPanel(panel)){
30038             if(this.tabs){
30039                 var tab = this.tabs.getTab(panel.getEl().id);
30040                 if(tab.isHidden()){
30041                     this.tabs.unhideTab(tab.id);
30042                 }
30043                 tab.activate();
30044             }else{
30045                 this.setActivePanel(panel);
30046             }
30047         }
30048         return panel;
30049     },
30050
30051     /**
30052      * Get the active panel for this region.
30053      * @return {Roo.ContentPanel} The active panel or null
30054      */
30055     getActivePanel : function(){
30056         return this.activePanel;
30057     },
30058
30059     validateVisibility : function(){
30060         if(this.panels.getCount() < 1){
30061             this.updateTitle("&#160;");
30062             this.closeBtn.hide();
30063             this.hide();
30064         }else{
30065             if(!this.isVisible()){
30066                 this.show();
30067             }
30068         }
30069     },
30070
30071     /**
30072      * Adds the passed ContentPanel(s) to this region.
30073      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
30074      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
30075      */
30076     add : function(panel){
30077         if(arguments.length > 1){
30078             for(var i = 0, len = arguments.length; i < len; i++) {
30079                 this.add(arguments[i]);
30080             }
30081             return null;
30082         }
30083         if(this.hasPanel(panel)){
30084             this.showPanel(panel);
30085             return panel;
30086         }
30087         panel.setRegion(this);
30088         this.panels.add(panel);
30089         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
30090             this.bodyEl.dom.appendChild(panel.getEl().dom);
30091             if(panel.background !== true){
30092                 this.setActivePanel(panel);
30093             }
30094             this.fireEvent("paneladded", this, panel);
30095             return panel;
30096         }
30097         if(!this.tabs){
30098             this.initTabs();
30099         }else{
30100             this.initPanelAsTab(panel);
30101         }
30102         if(panel.background !== true){
30103             this.tabs.activate(panel.getEl().id);
30104         }
30105         this.fireEvent("paneladded", this, panel);
30106         return panel;
30107     },
30108
30109     /**
30110      * Hides the tab for the specified panel.
30111      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30112      */
30113     hidePanel : function(panel){
30114         if(this.tabs && (panel = this.getPanel(panel))){
30115             this.tabs.hideTab(panel.getEl().id);
30116         }
30117     },
30118
30119     /**
30120      * Unhides the tab for a previously hidden panel.
30121      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30122      */
30123     unhidePanel : function(panel){
30124         if(this.tabs && (panel = this.getPanel(panel))){
30125             this.tabs.unhideTab(panel.getEl().id);
30126         }
30127     },
30128
30129     clearPanels : function(){
30130         while(this.panels.getCount() > 0){
30131              this.remove(this.panels.first());
30132         }
30133     },
30134
30135     /**
30136      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
30137      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30138      * @param {Boolean} preservePanel Overrides the config preservePanel option
30139      * @return {Roo.ContentPanel} The panel that was removed
30140      */
30141     remove : function(panel, preservePanel){
30142         panel = this.getPanel(panel);
30143         if(!panel){
30144             return null;
30145         }
30146         var e = {};
30147         this.fireEvent("beforeremove", this, panel, e);
30148         if(e.cancel === true){
30149             return null;
30150         }
30151         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
30152         var panelId = panel.getId();
30153         this.panels.removeKey(panelId);
30154         if(preservePanel){
30155             document.body.appendChild(panel.getEl().dom);
30156         }
30157         if(this.tabs){
30158             this.tabs.removeTab(panel.getEl().id);
30159         }else if (!preservePanel){
30160             this.bodyEl.dom.removeChild(panel.getEl().dom);
30161         }
30162         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
30163             var p = this.panels.first();
30164             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
30165             tempEl.appendChild(p.getEl().dom);
30166             this.bodyEl.update("");
30167             this.bodyEl.dom.appendChild(p.getEl().dom);
30168             tempEl = null;
30169             this.updateTitle(p.getTitle());
30170             this.tabs = null;
30171             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30172             this.setActivePanel(p);
30173         }
30174         panel.setRegion(null);
30175         if(this.activePanel == panel){
30176             this.activePanel = null;
30177         }
30178         if(this.config.autoDestroy !== false && preservePanel !== true){
30179             try{panel.destroy();}catch(e){}
30180         }
30181         this.fireEvent("panelremoved", this, panel);
30182         return panel;
30183     },
30184
30185     /**
30186      * Returns the TabPanel component used by this region
30187      * @return {Roo.TabPanel}
30188      */
30189     getTabs : function(){
30190         return this.tabs;
30191     },
30192
30193     createTool : function(parentEl, className){
30194         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
30195             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
30196         btn.addClassOnOver("x-layout-tools-button-over");
30197         return btn;
30198     }
30199 });/*
30200  * Based on:
30201  * Ext JS Library 1.1.1
30202  * Copyright(c) 2006-2007, Ext JS, LLC.
30203  *
30204  * Originally Released Under LGPL - original licence link has changed is not relivant.
30205  *
30206  * Fork - LGPL
30207  * <script type="text/javascript">
30208  */
30209  
30210
30211
30212 /**
30213  * @class Roo.SplitLayoutRegion
30214  * @extends Roo.LayoutRegion
30215  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
30216  */
30217 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
30218     this.cursor = cursor;
30219     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
30220 };
30221
30222 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
30223     splitTip : "Drag to resize.",
30224     collapsibleSplitTip : "Drag to resize. Double click to hide.",
30225     useSplitTips : false,
30226
30227     applyConfig : function(config){
30228         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
30229         if(config.split){
30230             if(!this.split){
30231                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
30232                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
30233                 /** The SplitBar for this region 
30234                 * @type Roo.SplitBar */
30235                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
30236                 this.split.on("moved", this.onSplitMove, this);
30237                 this.split.useShim = config.useShim === true;
30238                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
30239                 if(this.useSplitTips){
30240                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
30241                 }
30242                 if(config.collapsible){
30243                     this.split.el.on("dblclick", this.collapse,  this);
30244                 }
30245             }
30246             if(typeof config.minSize != "undefined"){
30247                 this.split.minSize = config.minSize;
30248             }
30249             if(typeof config.maxSize != "undefined"){
30250                 this.split.maxSize = config.maxSize;
30251             }
30252             if(config.hideWhenEmpty || config.hidden || config.collapsed){
30253                 this.hideSplitter();
30254             }
30255         }
30256     },
30257
30258     getHMaxSize : function(){
30259          var cmax = this.config.maxSize || 10000;
30260          var center = this.mgr.getRegion("center");
30261          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
30262     },
30263
30264     getVMaxSize : function(){
30265          var cmax = this.config.maxSize || 10000;
30266          var center = this.mgr.getRegion("center");
30267          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
30268     },
30269
30270     onSplitMove : function(split, newSize){
30271         this.fireEvent("resized", this, newSize);
30272     },
30273     
30274     /** 
30275      * Returns the {@link Roo.SplitBar} for this region.
30276      * @return {Roo.SplitBar}
30277      */
30278     getSplitBar : function(){
30279         return this.split;
30280     },
30281     
30282     hide : function(){
30283         this.hideSplitter();
30284         Roo.SplitLayoutRegion.superclass.hide.call(this);
30285     },
30286
30287     hideSplitter : function(){
30288         if(this.split){
30289             this.split.el.setLocation(-2000,-2000);
30290             this.split.el.hide();
30291         }
30292     },
30293
30294     show : function(){
30295         if(this.split){
30296             this.split.el.show();
30297         }
30298         Roo.SplitLayoutRegion.superclass.show.call(this);
30299     },
30300     
30301     beforeSlide: function(){
30302         if(Roo.isGecko){// firefox overflow auto bug workaround
30303             this.bodyEl.clip();
30304             if(this.tabs) this.tabs.bodyEl.clip();
30305             if(this.activePanel){
30306                 this.activePanel.getEl().clip();
30307                 
30308                 if(this.activePanel.beforeSlide){
30309                     this.activePanel.beforeSlide();
30310                 }
30311             }
30312         }
30313     },
30314     
30315     afterSlide : function(){
30316         if(Roo.isGecko){// firefox overflow auto bug workaround
30317             this.bodyEl.unclip();
30318             if(this.tabs) this.tabs.bodyEl.unclip();
30319             if(this.activePanel){
30320                 this.activePanel.getEl().unclip();
30321                 if(this.activePanel.afterSlide){
30322                     this.activePanel.afterSlide();
30323                 }
30324             }
30325         }
30326     },
30327
30328     initAutoHide : function(){
30329         if(this.autoHide !== false){
30330             if(!this.autoHideHd){
30331                 var st = new Roo.util.DelayedTask(this.slideIn, this);
30332                 this.autoHideHd = {
30333                     "mouseout": function(e){
30334                         if(!e.within(this.el, true)){
30335                             st.delay(500);
30336                         }
30337                     },
30338                     "mouseover" : function(e){
30339                         st.cancel();
30340                     },
30341                     scope : this
30342                 };
30343             }
30344             this.el.on(this.autoHideHd);
30345         }
30346     },
30347
30348     clearAutoHide : function(){
30349         if(this.autoHide !== false){
30350             this.el.un("mouseout", this.autoHideHd.mouseout);
30351             this.el.un("mouseover", this.autoHideHd.mouseover);
30352         }
30353     },
30354
30355     clearMonitor : function(){
30356         Roo.get(document).un("click", this.slideInIf, this);
30357     },
30358
30359     // these names are backwards but not changed for compat
30360     slideOut : function(){
30361         if(this.isSlid || this.el.hasActiveFx()){
30362             return;
30363         }
30364         this.isSlid = true;
30365         if(this.collapseBtn){
30366             this.collapseBtn.hide();
30367         }
30368         this.closeBtnState = this.closeBtn.getStyle('display');
30369         this.closeBtn.hide();
30370         if(this.stickBtn){
30371             this.stickBtn.show();
30372         }
30373         this.el.show();
30374         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
30375         this.beforeSlide();
30376         this.el.setStyle("z-index", 10001);
30377         this.el.slideIn(this.getSlideAnchor(), {
30378             callback: function(){
30379                 this.afterSlide();
30380                 this.initAutoHide();
30381                 Roo.get(document).on("click", this.slideInIf, this);
30382                 this.fireEvent("slideshow", this);
30383             },
30384             scope: this,
30385             block: true
30386         });
30387     },
30388
30389     afterSlideIn : function(){
30390         this.clearAutoHide();
30391         this.isSlid = false;
30392         this.clearMonitor();
30393         this.el.setStyle("z-index", "");
30394         if(this.collapseBtn){
30395             this.collapseBtn.show();
30396         }
30397         this.closeBtn.setStyle('display', this.closeBtnState);
30398         if(this.stickBtn){
30399             this.stickBtn.hide();
30400         }
30401         this.fireEvent("slidehide", this);
30402     },
30403
30404     slideIn : function(cb){
30405         if(!this.isSlid || this.el.hasActiveFx()){
30406             Roo.callback(cb);
30407             return;
30408         }
30409         this.isSlid = false;
30410         this.beforeSlide();
30411         this.el.slideOut(this.getSlideAnchor(), {
30412             callback: function(){
30413                 this.el.setLeftTop(-10000, -10000);
30414                 this.afterSlide();
30415                 this.afterSlideIn();
30416                 Roo.callback(cb);
30417             },
30418             scope: this,
30419             block: true
30420         });
30421     },
30422     
30423     slideInIf : function(e){
30424         if(!e.within(this.el)){
30425             this.slideIn();
30426         }
30427     },
30428
30429     animateCollapse : function(){
30430         this.beforeSlide();
30431         this.el.setStyle("z-index", 20000);
30432         var anchor = this.getSlideAnchor();
30433         this.el.slideOut(anchor, {
30434             callback : function(){
30435                 this.el.setStyle("z-index", "");
30436                 this.collapsedEl.slideIn(anchor, {duration:.3});
30437                 this.afterSlide();
30438                 this.el.setLocation(-10000,-10000);
30439                 this.el.hide();
30440                 this.fireEvent("collapsed", this);
30441             },
30442             scope: this,
30443             block: true
30444         });
30445     },
30446
30447     animateExpand : function(){
30448         this.beforeSlide();
30449         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
30450         this.el.setStyle("z-index", 20000);
30451         this.collapsedEl.hide({
30452             duration:.1
30453         });
30454         this.el.slideIn(this.getSlideAnchor(), {
30455             callback : function(){
30456                 this.el.setStyle("z-index", "");
30457                 this.afterSlide();
30458                 if(this.split){
30459                     this.split.el.show();
30460                 }
30461                 this.fireEvent("invalidated", this);
30462                 this.fireEvent("expanded", this);
30463             },
30464             scope: this,
30465             block: true
30466         });
30467     },
30468
30469     anchors : {
30470         "west" : "left",
30471         "east" : "right",
30472         "north" : "top",
30473         "south" : "bottom"
30474     },
30475
30476     sanchors : {
30477         "west" : "l",
30478         "east" : "r",
30479         "north" : "t",
30480         "south" : "b"
30481     },
30482
30483     canchors : {
30484         "west" : "tl-tr",
30485         "east" : "tr-tl",
30486         "north" : "tl-bl",
30487         "south" : "bl-tl"
30488     },
30489
30490     getAnchor : function(){
30491         return this.anchors[this.position];
30492     },
30493
30494     getCollapseAnchor : function(){
30495         return this.canchors[this.position];
30496     },
30497
30498     getSlideAnchor : function(){
30499         return this.sanchors[this.position];
30500     },
30501
30502     getAlignAdj : function(){
30503         var cm = this.cmargins;
30504         switch(this.position){
30505             case "west":
30506                 return [0, 0];
30507             break;
30508             case "east":
30509                 return [0, 0];
30510             break;
30511             case "north":
30512                 return [0, 0];
30513             break;
30514             case "south":
30515                 return [0, 0];
30516             break;
30517         }
30518     },
30519
30520     getExpandAdj : function(){
30521         var c = this.collapsedEl, cm = this.cmargins;
30522         switch(this.position){
30523             case "west":
30524                 return [-(cm.right+c.getWidth()+cm.left), 0];
30525             break;
30526             case "east":
30527                 return [cm.right+c.getWidth()+cm.left, 0];
30528             break;
30529             case "north":
30530                 return [0, -(cm.top+cm.bottom+c.getHeight())];
30531             break;
30532             case "south":
30533                 return [0, cm.top+cm.bottom+c.getHeight()];
30534             break;
30535         }
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  * These classes are private internal classes
30549  */
30550 Roo.CenterLayoutRegion = function(mgr, config){
30551     Roo.LayoutRegion.call(this, mgr, config, "center");
30552     this.visible = true;
30553     this.minWidth = config.minWidth || 20;
30554     this.minHeight = config.minHeight || 20;
30555 };
30556
30557 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
30558     hide : function(){
30559         // center panel can't be hidden
30560     },
30561     
30562     show : function(){
30563         // center panel can't be hidden
30564     },
30565     
30566     getMinWidth: function(){
30567         return this.minWidth;
30568     },
30569     
30570     getMinHeight: function(){
30571         return this.minHeight;
30572     }
30573 });
30574
30575
30576 Roo.NorthLayoutRegion = function(mgr, config){
30577     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
30578     if(this.split){
30579         this.split.placement = Roo.SplitBar.TOP;
30580         this.split.orientation = Roo.SplitBar.VERTICAL;
30581         this.split.el.addClass("x-layout-split-v");
30582     }
30583     var size = config.initialSize || config.height;
30584     if(typeof size != "undefined"){
30585         this.el.setHeight(size);
30586     }
30587 };
30588 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
30589     orientation: Roo.SplitBar.VERTICAL,
30590     getBox : function(){
30591         if(this.collapsed){
30592             return this.collapsedEl.getBox();
30593         }
30594         var box = this.el.getBox();
30595         if(this.split){
30596             box.height += this.split.el.getHeight();
30597         }
30598         return box;
30599     },
30600     
30601     updateBox : function(box){
30602         if(this.split && !this.collapsed){
30603             box.height -= this.split.el.getHeight();
30604             this.split.el.setLeft(box.x);
30605             this.split.el.setTop(box.y+box.height);
30606             this.split.el.setWidth(box.width);
30607         }
30608         if(this.collapsed){
30609             this.updateBody(box.width, null);
30610         }
30611         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30612     }
30613 });
30614
30615 Roo.SouthLayoutRegion = function(mgr, config){
30616     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
30617     if(this.split){
30618         this.split.placement = Roo.SplitBar.BOTTOM;
30619         this.split.orientation = Roo.SplitBar.VERTICAL;
30620         this.split.el.addClass("x-layout-split-v");
30621     }
30622     var size = config.initialSize || config.height;
30623     if(typeof size != "undefined"){
30624         this.el.setHeight(size);
30625     }
30626 };
30627 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
30628     orientation: Roo.SplitBar.VERTICAL,
30629     getBox : function(){
30630         if(this.collapsed){
30631             return this.collapsedEl.getBox();
30632         }
30633         var box = this.el.getBox();
30634         if(this.split){
30635             var sh = this.split.el.getHeight();
30636             box.height += sh;
30637             box.y -= sh;
30638         }
30639         return box;
30640     },
30641     
30642     updateBox : function(box){
30643         if(this.split && !this.collapsed){
30644             var sh = this.split.el.getHeight();
30645             box.height -= sh;
30646             box.y += sh;
30647             this.split.el.setLeft(box.x);
30648             this.split.el.setTop(box.y-sh);
30649             this.split.el.setWidth(box.width);
30650         }
30651         if(this.collapsed){
30652             this.updateBody(box.width, null);
30653         }
30654         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30655     }
30656 });
30657
30658 Roo.EastLayoutRegion = function(mgr, config){
30659     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
30660     if(this.split){
30661         this.split.placement = Roo.SplitBar.RIGHT;
30662         this.split.orientation = Roo.SplitBar.HORIZONTAL;
30663         this.split.el.addClass("x-layout-split-h");
30664     }
30665     var size = config.initialSize || config.width;
30666     if(typeof size != "undefined"){
30667         this.el.setWidth(size);
30668     }
30669 };
30670 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
30671     orientation: Roo.SplitBar.HORIZONTAL,
30672     getBox : function(){
30673         if(this.collapsed){
30674             return this.collapsedEl.getBox();
30675         }
30676         var box = this.el.getBox();
30677         if(this.split){
30678             var sw = this.split.el.getWidth();
30679             box.width += sw;
30680             box.x -= sw;
30681         }
30682         return box;
30683     },
30684
30685     updateBox : function(box){
30686         if(this.split && !this.collapsed){
30687             var sw = this.split.el.getWidth();
30688             box.width -= sw;
30689             this.split.el.setLeft(box.x);
30690             this.split.el.setTop(box.y);
30691             this.split.el.setHeight(box.height);
30692             box.x += sw;
30693         }
30694         if(this.collapsed){
30695             this.updateBody(null, box.height);
30696         }
30697         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30698     }
30699 });
30700
30701 Roo.WestLayoutRegion = function(mgr, config){
30702     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
30703     if(this.split){
30704         this.split.placement = Roo.SplitBar.LEFT;
30705         this.split.orientation = Roo.SplitBar.HORIZONTAL;
30706         this.split.el.addClass("x-layout-split-h");
30707     }
30708     var size = config.initialSize || config.width;
30709     if(typeof size != "undefined"){
30710         this.el.setWidth(size);
30711     }
30712 };
30713 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
30714     orientation: Roo.SplitBar.HORIZONTAL,
30715     getBox : function(){
30716         if(this.collapsed){
30717             return this.collapsedEl.getBox();
30718         }
30719         var box = this.el.getBox();
30720         if(this.split){
30721             box.width += this.split.el.getWidth();
30722         }
30723         return box;
30724     },
30725     
30726     updateBox : function(box){
30727         if(this.split && !this.collapsed){
30728             var sw = this.split.el.getWidth();
30729             box.width -= sw;
30730             this.split.el.setLeft(box.x+box.width);
30731             this.split.el.setTop(box.y);
30732             this.split.el.setHeight(box.height);
30733         }
30734         if(this.collapsed){
30735             this.updateBody(null, box.height);
30736         }
30737         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30738     }
30739 });
30740 /*
30741  * Based on:
30742  * Ext JS Library 1.1.1
30743  * Copyright(c) 2006-2007, Ext JS, LLC.
30744  *
30745  * Originally Released Under LGPL - original licence link has changed is not relivant.
30746  *
30747  * Fork - LGPL
30748  * <script type="text/javascript">
30749  */
30750  
30751  
30752 /*
30753  * Private internal class for reading and applying state
30754  */
30755 Roo.LayoutStateManager = function(layout){
30756      // default empty state
30757      this.state = {
30758         north: {},
30759         south: {},
30760         east: {},
30761         west: {}       
30762     };
30763 };
30764
30765 Roo.LayoutStateManager.prototype = {
30766     init : function(layout, provider){
30767         this.provider = provider;
30768         var state = provider.get(layout.id+"-layout-state");
30769         if(state){
30770             var wasUpdating = layout.isUpdating();
30771             if(!wasUpdating){
30772                 layout.beginUpdate();
30773             }
30774             for(var key in state){
30775                 if(typeof state[key] != "function"){
30776                     var rstate = state[key];
30777                     var r = layout.getRegion(key);
30778                     if(r && rstate){
30779                         if(rstate.size){
30780                             r.resizeTo(rstate.size);
30781                         }
30782                         if(rstate.collapsed == true){
30783                             r.collapse(true);
30784                         }else{
30785                             r.expand(null, true);
30786                         }
30787                     }
30788                 }
30789             }
30790             if(!wasUpdating){
30791                 layout.endUpdate();
30792             }
30793             this.state = state; 
30794         }
30795         this.layout = layout;
30796         layout.on("regionresized", this.onRegionResized, this);
30797         layout.on("regioncollapsed", this.onRegionCollapsed, this);
30798         layout.on("regionexpanded", this.onRegionExpanded, this);
30799     },
30800     
30801     storeState : function(){
30802         this.provider.set(this.layout.id+"-layout-state", this.state);
30803     },
30804     
30805     onRegionResized : function(region, newSize){
30806         this.state[region.getPosition()].size = newSize;
30807         this.storeState();
30808     },
30809     
30810     onRegionCollapsed : function(region){
30811         this.state[region.getPosition()].collapsed = true;
30812         this.storeState();
30813     },
30814     
30815     onRegionExpanded : function(region){
30816         this.state[region.getPosition()].collapsed = false;
30817         this.storeState();
30818     }
30819 };/*
30820  * Based on:
30821  * Ext JS Library 1.1.1
30822  * Copyright(c) 2006-2007, Ext JS, LLC.
30823  *
30824  * Originally Released Under LGPL - original licence link has changed is not relivant.
30825  *
30826  * Fork - LGPL
30827  * <script type="text/javascript">
30828  */
30829 /**
30830  * @class Roo.ContentPanel
30831  * @extends Roo.util.Observable
30832  * A basic ContentPanel element.
30833  * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes  (defaults to false)
30834  * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
30835  * @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
30836  * @cfg {Boolean} closable True if the panel can be closed/removed
30837  * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
30838  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
30839  * @cfg {Toolbar} toolbar A toolbar for this panel
30840  * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
30841  * @cfg {String} title The title for this panel
30842  * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
30843  * @cfg {String} url Calls {@link #setUrl} with this value
30844  * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
30845  * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
30846  * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
30847  * @constructor
30848  * Create a new ContentPanel.
30849  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
30850  * @param {String/Object} config A string to set only the title or a config object
30851  * @param {String} content (optional) Set the HTML content for this panel
30852  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
30853  */
30854 Roo.ContentPanel = function(el, config, content){
30855     
30856      
30857     /*
30858     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
30859         config = el;
30860         el = Roo.id();
30861     }
30862     if (config && config.parentLayout) { 
30863         el = config.parentLayout.el.createChild(); 
30864     }
30865     */
30866     if(el.autoCreate){ // xtype is available if this is called from factory
30867         config = el;
30868         el = Roo.id();
30869     }
30870     this.el = Roo.get(el);
30871     if(!this.el && config && config.autoCreate){
30872         if(typeof config.autoCreate == "object"){
30873             if(!config.autoCreate.id){
30874                 config.autoCreate.id = config.id||el;
30875             }
30876             this.el = Roo.DomHelper.append(document.body,
30877                         config.autoCreate, true);
30878         }else{
30879             this.el = Roo.DomHelper.append(document.body,
30880                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
30881         }
30882     }
30883     this.closable = false;
30884     this.loaded = false;
30885     this.active = false;
30886     if(typeof config == "string"){
30887         this.title = config;
30888     }else{
30889         Roo.apply(this, config);
30890     }
30891     
30892     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
30893         this.wrapEl = this.el.wrap();    
30894         this.toolbar = new Roo.Toolbar(this.el.insertSibling(false, 'before'), [] , this.toolbar);
30895         
30896     }
30897     
30898     
30899     
30900     if(this.resizeEl){
30901         this.resizeEl = Roo.get(this.resizeEl, true);
30902     }else{
30903         this.resizeEl = this.el;
30904     }
30905     this.addEvents({
30906         /**
30907          * @event activate
30908          * Fires when this panel is activated. 
30909          * @param {Roo.ContentPanel} this
30910          */
30911         "activate" : true,
30912         /**
30913          * @event deactivate
30914          * Fires when this panel is activated. 
30915          * @param {Roo.ContentPanel} this
30916          */
30917         "deactivate" : true,
30918
30919         /**
30920          * @event resize
30921          * Fires when this panel is resized if fitToFrame is true.
30922          * @param {Roo.ContentPanel} this
30923          * @param {Number} width The width after any component adjustments
30924          * @param {Number} height The height after any component adjustments
30925          */
30926         "resize" : true
30927     });
30928     if(this.autoScroll){
30929         this.resizeEl.setStyle("overflow", "auto");
30930     } else {
30931         // fix randome scrolling
30932         this.el.on('scroll', function() {
30933             this.scrollTo('top',0); 
30934         });
30935     }
30936     content = content || this.content;
30937     if(content){
30938         this.setContent(content);
30939     }
30940     if(config && config.url){
30941         this.setUrl(this.url, this.params, this.loadOnce);
30942     }
30943     
30944     
30945     
30946     Roo.ContentPanel.superclass.constructor.call(this);
30947 };
30948
30949 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
30950     tabTip:'',
30951     setRegion : function(region){
30952         this.region = region;
30953         if(region){
30954            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
30955         }else{
30956            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
30957         } 
30958     },
30959     
30960     /**
30961      * Returns the toolbar for this Panel if one was configured. 
30962      * @return {Roo.Toolbar} 
30963      */
30964     getToolbar : function(){
30965         return this.toolbar;
30966     },
30967     
30968     setActiveState : function(active){
30969         this.active = active;
30970         if(!active){
30971             this.fireEvent("deactivate", this);
30972         }else{
30973             this.fireEvent("activate", this);
30974         }
30975     },
30976     /**
30977      * Updates this panel's element
30978      * @param {String} content The new content
30979      * @param {Boolean} loadScripts (optional) true to look for and process scripts
30980     */
30981     setContent : function(content, loadScripts){
30982         this.el.update(content, loadScripts);
30983     },
30984
30985     ignoreResize : function(w, h){
30986         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
30987             return true;
30988         }else{
30989             this.lastSize = {width: w, height: h};
30990             return false;
30991         }
30992     },
30993     /**
30994      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
30995      * @return {Roo.UpdateManager} The UpdateManager
30996      */
30997     getUpdateManager : function(){
30998         return this.el.getUpdateManager();
30999     },
31000      /**
31001      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
31002      * @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:
31003 <pre><code>
31004 panel.load({
31005     url: "your-url.php",
31006     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
31007     callback: yourFunction,
31008     scope: yourObject, //(optional scope)
31009     discardUrl: false,
31010     nocache: false,
31011     text: "Loading...",
31012     timeout: 30,
31013     scripts: false
31014 });
31015 </code></pre>
31016      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
31017      * 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.
31018      * @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}
31019      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
31020      * @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.
31021      * @return {Roo.ContentPanel} this
31022      */
31023     load : function(){
31024         var um = this.el.getUpdateManager();
31025         um.update.apply(um, arguments);
31026         return this;
31027     },
31028
31029
31030     /**
31031      * 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.
31032      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
31033      * @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)
31034      * @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)
31035      * @return {Roo.UpdateManager} The UpdateManager
31036      */
31037     setUrl : function(url, params, loadOnce){
31038         if(this.refreshDelegate){
31039             this.removeListener("activate", this.refreshDelegate);
31040         }
31041         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
31042         this.on("activate", this.refreshDelegate);
31043         return this.el.getUpdateManager();
31044     },
31045     
31046     _handleRefresh : function(url, params, loadOnce){
31047         if(!loadOnce || !this.loaded){
31048             var updater = this.el.getUpdateManager();
31049             updater.update(url, params, this._setLoaded.createDelegate(this));
31050         }
31051     },
31052     
31053     _setLoaded : function(){
31054         this.loaded = true;
31055     }, 
31056     
31057     /**
31058      * Returns this panel's id
31059      * @return {String} 
31060      */
31061     getId : function(){
31062         return this.el.id;
31063     },
31064     
31065     /** 
31066      * Returns this panel's element - used by regiosn to add.
31067      * @return {Roo.Element} 
31068      */
31069     getEl : function(){
31070         return this.wrapEl || this.el;
31071     },
31072     
31073     adjustForComponents : function(width, height){
31074         if(this.resizeEl != this.el){
31075             width -= this.el.getFrameWidth('lr');
31076             height -= this.el.getFrameWidth('tb');
31077         }
31078         if(this.toolbar){
31079             var te = this.toolbar.getEl();
31080             height -= te.getHeight();
31081             te.setWidth(width);
31082         }
31083         if(this.adjustments){
31084             width += this.adjustments[0];
31085             height += this.adjustments[1];
31086         }
31087         return {"width": width, "height": height};
31088     },
31089     
31090     setSize : function(width, height){
31091         if(this.fitToFrame && !this.ignoreResize(width, height)){
31092             if(this.fitContainer && this.resizeEl != this.el){
31093                 this.el.setSize(width, height);
31094             }
31095             var size = this.adjustForComponents(width, height);
31096             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
31097             this.fireEvent('resize', this, size.width, size.height);
31098         }
31099     },
31100     
31101     /**
31102      * Returns this panel's title
31103      * @return {String} 
31104      */
31105     getTitle : function(){
31106         return this.title;
31107     },
31108     
31109     /**
31110      * Set this panel's title
31111      * @param {String} title
31112      */
31113     setTitle : function(title){
31114         this.title = title;
31115         if(this.region){
31116             this.region.updatePanelTitle(this, title);
31117         }
31118     },
31119     
31120     /**
31121      * Returns true is this panel was configured to be closable
31122      * @return {Boolean} 
31123      */
31124     isClosable : function(){
31125         return this.closable;
31126     },
31127     
31128     beforeSlide : function(){
31129         this.el.clip();
31130         this.resizeEl.clip();
31131     },
31132     
31133     afterSlide : function(){
31134         this.el.unclip();
31135         this.resizeEl.unclip();
31136     },
31137     
31138     /**
31139      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
31140      *   Will fail silently if the {@link #setUrl} method has not been called.
31141      *   This does not activate the panel, just updates its content.
31142      */
31143     refresh : function(){
31144         if(this.refreshDelegate){
31145            this.loaded = false;
31146            this.refreshDelegate();
31147         }
31148     },
31149     
31150     /**
31151      * Destroys this panel
31152      */
31153     destroy : function(){
31154         this.el.removeAllListeners();
31155         var tempEl = document.createElement("span");
31156         tempEl.appendChild(this.el.dom);
31157         tempEl.innerHTML = "";
31158         this.el.remove();
31159         this.el = null;
31160     },
31161     
31162       /**
31163      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
31164      * <pre><code>
31165
31166 layout.addxtype({
31167        xtype : 'Form',
31168        items: [ .... ]
31169    }
31170 );
31171
31172 </code></pre>
31173      * @param {Object} cfg Xtype definition of item to add.
31174      */
31175     
31176     addxtype : function(cfg) {
31177         // add form..
31178         if (cfg.xtype.match(/^Form$/)) {
31179             var el = this.el.createChild();
31180
31181             this.form = new  Roo.form.Form(cfg);
31182             
31183             
31184             if ( this.form.allItems.length) this.form.render(el.dom);
31185             return this.form;
31186         }
31187         if (['View', 'JsonView'].indexOf(cfg.xtype) > -1) {
31188             // views..
31189             cfg.el = this.el.appendChild(document.createElement("div"));
31190             // factory?
31191             var ret = new Roo[cfg.xtype](cfg);
31192             ret.render(false, ''); // render blank..
31193             return ret;
31194             
31195         }
31196         return false;
31197         
31198     }
31199 });
31200
31201 /**
31202  * @class Roo.GridPanel
31203  * @extends Roo.ContentPanel
31204  * @constructor
31205  * Create a new GridPanel.
31206  * @param {Roo.grid.Grid} grid The grid for this panel
31207  * @param {String/Object} config A string to set only the panel's title, or a config object
31208  */
31209 Roo.GridPanel = function(grid, config){
31210     
31211   
31212     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
31213         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
31214         
31215     this.wrapper.dom.appendChild(grid.getGridEl().dom);
31216     
31217     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
31218     
31219     if(this.toolbar){
31220         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
31221     }
31222     // xtype created footer. - not sure if will work as we normally have to render first..
31223     if (this.footer && !this.footer.el && this.footer.xtype) {
31224         
31225         this.footer.container = this.grid.getView().getFooterPanel(true);
31226         this.footer.dataSource = this.grid.dataSource;
31227         this.footer = Roo.factory(this.footer, Roo);
31228         
31229     }
31230     
31231     grid.monitorWindowResize = false; // turn off autosizing
31232     grid.autoHeight = false;
31233     grid.autoWidth = false;
31234     this.grid = grid;
31235     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
31236 };
31237
31238 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
31239     getId : function(){
31240         return this.grid.id;
31241     },
31242     
31243     /**
31244      * Returns the grid for this panel
31245      * @return {Roo.grid.Grid} 
31246      */
31247     getGrid : function(){
31248         return this.grid;    
31249     },
31250     
31251     setSize : function(width, height){
31252         if(!this.ignoreResize(width, height)){
31253             var grid = this.grid;
31254             var size = this.adjustForComponents(width, height);
31255             grid.getGridEl().setSize(size.width, size.height);
31256             grid.autoSize();
31257         }
31258     },
31259     
31260     beforeSlide : function(){
31261         this.grid.getView().scroller.clip();
31262     },
31263     
31264     afterSlide : function(){
31265         this.grid.getView().scroller.unclip();
31266     },
31267     
31268     destroy : function(){
31269         this.grid.destroy();
31270         delete this.grid;
31271         Roo.GridPanel.superclass.destroy.call(this); 
31272     }
31273 });
31274
31275
31276 /**
31277  * @class Roo.NestedLayoutPanel
31278  * @extends Roo.ContentPanel
31279  * @constructor
31280  * Create a new NestedLayoutPanel.
31281  * 
31282  * 
31283  * @param {Roo.BorderLayout} layout The layout for this panel
31284  * @param {String/Object} config A string to set only the title or a config object
31285  */
31286 Roo.NestedLayoutPanel = function(layout, config)
31287 {
31288     // construct with only one argument..
31289     /* FIXME - implement nicer consturctors
31290     if (layout.layout) {
31291         config = layout;
31292         layout = config.layout;
31293         delete config.layout;
31294     }
31295     if (layout.xtype && !layout.getEl) {
31296         // then layout needs constructing..
31297         layout = Roo.factory(layout, Roo);
31298     }
31299     */
31300     
31301     
31302     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
31303     
31304     layout.monitorWindowResize = false; // turn off autosizing
31305     this.layout = layout;
31306     this.layout.getEl().addClass("x-layout-nested-layout");
31307     
31308     
31309     
31310     
31311 };
31312
31313 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
31314
31315     setSize : function(width, height){
31316         if(!this.ignoreResize(width, height)){
31317             var size = this.adjustForComponents(width, height);
31318             var el = this.layout.getEl();
31319             el.setSize(size.width, size.height);
31320             var touch = el.dom.offsetWidth;
31321             this.layout.layout();
31322             // ie requires a double layout on the first pass
31323             if(Roo.isIE && !this.initialized){
31324                 this.initialized = true;
31325                 this.layout.layout();
31326             }
31327         }
31328     },
31329     
31330     // activate all subpanels if not currently active..
31331     
31332     setActiveState : function(active){
31333         this.active = active;
31334         if(!active){
31335             this.fireEvent("deactivate", this);
31336             return;
31337         }
31338         
31339         this.fireEvent("activate", this);
31340         // not sure if this should happen before or after..
31341         if (!this.layout) {
31342             return; // should not happen..
31343         }
31344         var reg = false;
31345         for (var r in this.layout.regions) {
31346             reg = this.layout.getRegion(r);
31347             if (reg.getActivePanel()) {
31348                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
31349                 reg.setActivePanel(reg.getActivePanel());
31350                 continue;
31351             }
31352             if (!reg.panels.length) {
31353                 continue;
31354             }
31355             reg.showPanel(reg.getPanel(0));
31356         }
31357         
31358         
31359         
31360         
31361     },
31362     
31363     /**
31364      * Returns the nested BorderLayout for this panel
31365      * @return {Roo.BorderLayout} 
31366      */
31367     getLayout : function(){
31368         return this.layout;
31369     },
31370     
31371      /**
31372      * Adds a xtype elements to the layout of the nested panel
31373      * <pre><code>
31374
31375 panel.addxtype({
31376        xtype : 'ContentPanel',
31377        region: 'west',
31378        items: [ .... ]
31379    }
31380 );
31381
31382 panel.addxtype({
31383         xtype : 'NestedLayoutPanel',
31384         region: 'west',
31385         layout: {
31386            center: { },
31387            west: { }   
31388         },
31389         items : [ ... list of content panels or nested layout panels.. ]
31390    }
31391 );
31392 </code></pre>
31393      * @param {Object} cfg Xtype definition of item to add.
31394      */
31395     addxtype : function(cfg) {
31396         return this.layout.addxtype(cfg);
31397     
31398     }
31399 });
31400
31401 Roo.ScrollPanel = function(el, config, content){
31402     config = config || {};
31403     config.fitToFrame = true;
31404     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
31405     
31406     this.el.dom.style.overflow = "hidden";
31407     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
31408     this.el.removeClass("x-layout-inactive-content");
31409     this.el.on("mousewheel", this.onWheel, this);
31410
31411     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
31412     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
31413     up.unselectable(); down.unselectable();
31414     up.on("click", this.scrollUp, this);
31415     down.on("click", this.scrollDown, this);
31416     up.addClassOnOver("x-scroller-btn-over");
31417     down.addClassOnOver("x-scroller-btn-over");
31418     up.addClassOnClick("x-scroller-btn-click");
31419     down.addClassOnClick("x-scroller-btn-click");
31420     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
31421
31422     this.resizeEl = this.el;
31423     this.el = wrap; this.up = up; this.down = down;
31424 };
31425
31426 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
31427     increment : 100,
31428     wheelIncrement : 5,
31429     scrollUp : function(){
31430         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
31431     },
31432
31433     scrollDown : function(){
31434         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
31435     },
31436
31437     afterScroll : function(){
31438         var el = this.resizeEl;
31439         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
31440         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31441         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31442     },
31443
31444     setSize : function(){
31445         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
31446         this.afterScroll();
31447     },
31448
31449     onWheel : function(e){
31450         var d = e.getWheelDelta();
31451         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
31452         this.afterScroll();
31453         e.stopEvent();
31454     },
31455
31456     setContent : function(content, loadScripts){
31457         this.resizeEl.update(content, loadScripts);
31458     }
31459
31460 });
31461
31462
31463
31464
31465
31466
31467
31468
31469
31470 /**
31471  * @class Roo.TreePanel
31472  * @extends Roo.ContentPanel
31473  * @constructor
31474  * Create a new TreePanel. - defaults to fit/scoll contents.
31475  * @param {String/Object} config A string to set only the panel's title, or a config object
31476  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
31477  */
31478 Roo.TreePanel = function(config){
31479     var el = config.el;
31480     var tree = config.tree;
31481     delete config.tree; 
31482     delete config.el; // hopefull!
31483     
31484     // wrapper for IE7 strict & safari scroll issue
31485     
31486     var treeEl = el.createChild();
31487     config.resizeEl = treeEl;
31488     
31489     
31490     
31491     Roo.TreePanel.superclass.constructor.call(this, el, config);
31492  
31493  
31494     this.tree = new Roo.tree.TreePanel(treeEl , tree);
31495     //console.log(tree);
31496     this.on('activate', function()
31497     {
31498         if (this.tree.rendered) {
31499             return;
31500         }
31501         //console.log('render tree');
31502         this.tree.render();
31503     });
31504     
31505     this.on('resize',  function (cp, w, h) {
31506             this.tree.innerCt.setWidth(w);
31507             this.tree.innerCt.setHeight(h);
31508             this.tree.innerCt.setStyle('overflow-y', 'auto');
31509     });
31510
31511         
31512     
31513 };
31514
31515 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
31516     fitToFrame : true,
31517     autoScroll : true
31518 });
31519
31520
31521
31522
31523
31524
31525
31526
31527
31528
31529
31530 /*
31531  * Based on:
31532  * Ext JS Library 1.1.1
31533  * Copyright(c) 2006-2007, Ext JS, LLC.
31534  *
31535  * Originally Released Under LGPL - original licence link has changed is not relivant.
31536  *
31537  * Fork - LGPL
31538  * <script type="text/javascript">
31539  */
31540  
31541
31542 /**
31543  * @class Roo.ReaderLayout
31544  * @extends Roo.BorderLayout
31545  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
31546  * center region containing two nested regions (a top one for a list view and one for item preview below),
31547  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
31548  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
31549  * expedites the setup of the overall layout and regions for this common application style.
31550  * Example:
31551  <pre><code>
31552 var reader = new Roo.ReaderLayout();
31553 var CP = Roo.ContentPanel;  // shortcut for adding
31554
31555 reader.beginUpdate();
31556 reader.add("north", new CP("north", "North"));
31557 reader.add("west", new CP("west", {title: "West"}));
31558 reader.add("east", new CP("east", {title: "East"}));
31559
31560 reader.regions.listView.add(new CP("listView", "List"));
31561 reader.regions.preview.add(new CP("preview", "Preview"));
31562 reader.endUpdate();
31563 </code></pre>
31564 * @constructor
31565 * Create a new ReaderLayout
31566 * @param {Object} config Configuration options
31567 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
31568 * document.body if omitted)
31569 */
31570 Roo.ReaderLayout = function(config, renderTo){
31571     var c = config || {size:{}};
31572     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
31573         north: c.north !== false ? Roo.apply({
31574             split:false,
31575             initialSize: 32,
31576             titlebar: false
31577         }, c.north) : false,
31578         west: c.west !== false ? Roo.apply({
31579             split:true,
31580             initialSize: 200,
31581             minSize: 175,
31582             maxSize: 400,
31583             titlebar: true,
31584             collapsible: true,
31585             animate: true,
31586             margins:{left:5,right:0,bottom:5,top:5},
31587             cmargins:{left:5,right:5,bottom:5,top:5}
31588         }, c.west) : false,
31589         east: c.east !== false ? Roo.apply({
31590             split:true,
31591             initialSize: 200,
31592             minSize: 175,
31593             maxSize: 400,
31594             titlebar: true,
31595             collapsible: true,
31596             animate: true,
31597             margins:{left:0,right:5,bottom:5,top:5},
31598             cmargins:{left:5,right:5,bottom:5,top:5}
31599         }, c.east) : false,
31600         center: Roo.apply({
31601             tabPosition: 'top',
31602             autoScroll:false,
31603             closeOnTab: true,
31604             titlebar:false,
31605             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
31606         }, c.center)
31607     });
31608
31609     this.el.addClass('x-reader');
31610
31611     this.beginUpdate();
31612
31613     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
31614         south: c.preview !== false ? Roo.apply({
31615             split:true,
31616             initialSize: 200,
31617             minSize: 100,
31618             autoScroll:true,
31619             collapsible:true,
31620             titlebar: true,
31621             cmargins:{top:5,left:0, right:0, bottom:0}
31622         }, c.preview) : false,
31623         center: Roo.apply({
31624             autoScroll:false,
31625             titlebar:false,
31626             minHeight:200
31627         }, c.listView)
31628     });
31629     this.add('center', new Roo.NestedLayoutPanel(inner,
31630             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
31631
31632     this.endUpdate();
31633
31634     this.regions.preview = inner.getRegion('south');
31635     this.regions.listView = inner.getRegion('center');
31636 };
31637
31638 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
31639  * Based on:
31640  * Ext JS Library 1.1.1
31641  * Copyright(c) 2006-2007, Ext JS, LLC.
31642  *
31643  * Originally Released Under LGPL - original licence link has changed is not relivant.
31644  *
31645  * Fork - LGPL
31646  * <script type="text/javascript">
31647  */
31648  
31649 /**
31650  * @class Roo.grid.Grid
31651  * @extends Roo.util.Observable
31652  * This class represents the primary interface of a component based grid control.
31653  * <br><br>Usage:<pre><code>
31654  var grid = new Roo.grid.Grid("my-container-id", {
31655      ds: myDataStore,
31656      cm: myColModel,
31657      selModel: mySelectionModel,
31658      autoSizeColumns: true,
31659      monitorWindowResize: false,
31660      trackMouseOver: true
31661  });
31662  // set any options
31663  grid.render();
31664  * </code></pre>
31665  * <b>Common Problems:</b><br/>
31666  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
31667  * element will correct this<br/>
31668  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
31669  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
31670  * are unpredictable.<br/>
31671  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
31672  * grid to calculate dimensions/offsets.<br/>
31673   * @constructor
31674  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
31675  * The container MUST have some type of size defined for the grid to fill. The container will be
31676  * automatically set to position relative if it isn't already.
31677  * @param {Object} config A config object that sets properties on this grid.
31678  */
31679 Roo.grid.Grid = function(container, config){
31680         // initialize the container
31681         this.container = Roo.get(container);
31682         this.container.update("");
31683         this.container.setStyle("overflow", "hidden");
31684     this.container.addClass('x-grid-container');
31685
31686     this.id = this.container.id;
31687
31688     Roo.apply(this, config);
31689     // check and correct shorthanded configs
31690     if(this.ds){
31691         this.dataSource = this.ds;
31692         delete this.ds;
31693     }
31694     if(this.cm){
31695         this.colModel = this.cm;
31696         delete this.cm;
31697     }
31698     if(this.sm){
31699         this.selModel = this.sm;
31700         delete this.sm;
31701     }
31702
31703     if (this.selModel) {
31704         this.selModel = Roo.factory(this.selModel, Roo.grid);
31705         this.sm = this.selModel;
31706         this.sm.xmodule = this.xmodule || false;
31707     }
31708     if (typeof(this.colModel.config) == 'undefined') {
31709         this.colModel = new Roo.grid.ColumnModel(this.colModel);
31710         this.cm = this.colModel;
31711         this.cm.xmodule = this.xmodule || false;
31712     }
31713     if (this.dataSource) {
31714         this.dataSource= Roo.factory(this.dataSource, Roo.data);
31715         this.ds = this.dataSource;
31716         this.ds.xmodule = this.xmodule || false;
31717         
31718     }
31719     
31720     
31721     
31722     if(this.width){
31723         this.container.setWidth(this.width);
31724     }
31725
31726     if(this.height){
31727         this.container.setHeight(this.height);
31728     }
31729     /** @private */
31730         this.addEvents({
31731             // raw events
31732             /**
31733              * @event click
31734              * The raw click event for the entire grid.
31735              * @param {Roo.EventObject} e
31736              */
31737             "click" : true,
31738             /**
31739              * @event dblclick
31740              * The raw dblclick event for the entire grid.
31741              * @param {Roo.EventObject} e
31742              */
31743             "dblclick" : true,
31744             /**
31745              * @event contextmenu
31746              * The raw contextmenu event for the entire grid.
31747              * @param {Roo.EventObject} e
31748              */
31749             "contextmenu" : true,
31750             /**
31751              * @event mousedown
31752              * The raw mousedown event for the entire grid.
31753              * @param {Roo.EventObject} e
31754              */
31755             "mousedown" : true,
31756             /**
31757              * @event mouseup
31758              * The raw mouseup event for the entire grid.
31759              * @param {Roo.EventObject} e
31760              */
31761             "mouseup" : true,
31762             /**
31763              * @event mouseover
31764              * The raw mouseover event for the entire grid.
31765              * @param {Roo.EventObject} e
31766              */
31767             "mouseover" : true,
31768             /**
31769              * @event mouseout
31770              * The raw mouseout event for the entire grid.
31771              * @param {Roo.EventObject} e
31772              */
31773             "mouseout" : true,
31774             /**
31775              * @event keypress
31776              * The raw keypress event for the entire grid.
31777              * @param {Roo.EventObject} e
31778              */
31779             "keypress" : true,
31780             /**
31781              * @event keydown
31782              * The raw keydown event for the entire grid.
31783              * @param {Roo.EventObject} e
31784              */
31785             "keydown" : true,
31786
31787             // custom events
31788
31789             /**
31790              * @event cellclick
31791              * Fires when a cell is clicked
31792              * @param {Grid} this
31793              * @param {Number} rowIndex
31794              * @param {Number} columnIndex
31795              * @param {Roo.EventObject} e
31796              */
31797             "cellclick" : true,
31798             /**
31799              * @event celldblclick
31800              * Fires when a cell is double clicked
31801              * @param {Grid} this
31802              * @param {Number} rowIndex
31803              * @param {Number} columnIndex
31804              * @param {Roo.EventObject} e
31805              */
31806             "celldblclick" : true,
31807             /**
31808              * @event rowclick
31809              * Fires when a row is clicked
31810              * @param {Grid} this
31811              * @param {Number} rowIndex
31812              * @param {Roo.EventObject} e
31813              */
31814             "rowclick" : true,
31815             /**
31816              * @event rowdblclick
31817              * Fires when a row is double clicked
31818              * @param {Grid} this
31819              * @param {Number} rowIndex
31820              * @param {Roo.EventObject} e
31821              */
31822             "rowdblclick" : true,
31823             /**
31824              * @event headerclick
31825              * Fires when a header is clicked
31826              * @param {Grid} this
31827              * @param {Number} columnIndex
31828              * @param {Roo.EventObject} e
31829              */
31830             "headerclick" : true,
31831             /**
31832              * @event headerdblclick
31833              * Fires when a header cell is double clicked
31834              * @param {Grid} this
31835              * @param {Number} columnIndex
31836              * @param {Roo.EventObject} e
31837              */
31838             "headerdblclick" : true,
31839             /**
31840              * @event rowcontextmenu
31841              * Fires when a row is right clicked
31842              * @param {Grid} this
31843              * @param {Number} rowIndex
31844              * @param {Roo.EventObject} e
31845              */
31846             "rowcontextmenu" : true,
31847             /**
31848          * @event cellcontextmenu
31849          * Fires when a cell is right clicked
31850          * @param {Grid} this
31851          * @param {Number} rowIndex
31852          * @param {Number} cellIndex
31853          * @param {Roo.EventObject} e
31854          */
31855          "cellcontextmenu" : true,
31856             /**
31857              * @event headercontextmenu
31858              * Fires when a header is right clicked
31859              * @param {Grid} this
31860              * @param {Number} columnIndex
31861              * @param {Roo.EventObject} e
31862              */
31863             "headercontextmenu" : true,
31864             /**
31865              * @event bodyscroll
31866              * Fires when the body element is scrolled
31867              * @param {Number} scrollLeft
31868              * @param {Number} scrollTop
31869              */
31870             "bodyscroll" : true,
31871             /**
31872              * @event columnresize
31873              * Fires when the user resizes a column
31874              * @param {Number} columnIndex
31875              * @param {Number} newSize
31876              */
31877             "columnresize" : true,
31878             /**
31879              * @event columnmove
31880              * Fires when the user moves a column
31881              * @param {Number} oldIndex
31882              * @param {Number} newIndex
31883              */
31884             "columnmove" : true,
31885             /**
31886              * @event startdrag
31887              * Fires when row(s) start being dragged
31888              * @param {Grid} this
31889              * @param {Roo.GridDD} dd The drag drop object
31890              * @param {event} e The raw browser event
31891              */
31892             "startdrag" : true,
31893             /**
31894              * @event enddrag
31895              * Fires when a drag operation is complete
31896              * @param {Grid} this
31897              * @param {Roo.GridDD} dd The drag drop object
31898              * @param {event} e The raw browser event
31899              */
31900             "enddrag" : true,
31901             /**
31902              * @event dragdrop
31903              * Fires when dragged row(s) are dropped on a valid DD target
31904              * @param {Grid} this
31905              * @param {Roo.GridDD} dd The drag drop object
31906              * @param {String} targetId The target drag drop object
31907              * @param {event} e The raw browser event
31908              */
31909             "dragdrop" : true,
31910             /**
31911              * @event dragover
31912              * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
31913              * @param {Grid} this
31914              * @param {Roo.GridDD} dd The drag drop object
31915              * @param {String} targetId The target drag drop object
31916              * @param {event} e The raw browser event
31917              */
31918             "dragover" : true,
31919             /**
31920              * @event dragenter
31921              *  Fires when the dragged row(s) first cross another DD target while being dragged
31922              * @param {Grid} this
31923              * @param {Roo.GridDD} dd The drag drop object
31924              * @param {String} targetId The target drag drop object
31925              * @param {event} e The raw browser event
31926              */
31927             "dragenter" : true,
31928             /**
31929              * @event dragout
31930              * Fires when the dragged row(s) leave another DD target while being dragged
31931              * @param {Grid} this
31932              * @param {Roo.GridDD} dd The drag drop object
31933              * @param {String} targetId The target drag drop object
31934              * @param {event} e The raw browser event
31935              */
31936             "dragout" : true,
31937         /**
31938          * @event render
31939          * Fires when the grid is rendered
31940          * @param {Grid} grid
31941          */
31942         render : true
31943     });
31944
31945     Roo.grid.Grid.superclass.constructor.call(this);
31946 };
31947 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
31948     
31949     /**
31950      * @cfg {String} ddGroup - drag drop group.
31951          */
31952     
31953     /**
31954      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
31955          */
31956         minColumnWidth : 25,
31957
31958     /**
31959          * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
31960          * <b>on initial render.</b> It is more efficient to explicitly size the columns
31961          * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
31962          */
31963         autoSizeColumns : false,
31964
31965         /**
31966          * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
31967          */
31968         autoSizeHeaders : true,
31969
31970         /**
31971          * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
31972          */
31973         monitorWindowResize : true,
31974
31975         /**
31976          * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
31977          * rows measured to get a columns size. Default is 0 (all rows).
31978          */
31979         maxRowsToMeasure : 0,
31980
31981         /**
31982          * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
31983          */
31984         trackMouseOver : true,
31985
31986     /**
31987          * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
31988          */
31989     
31990         /**
31991          * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
31992          */
31993         enableDragDrop : false,
31994
31995         /**
31996          * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
31997          */
31998         enableColumnMove : true,
31999
32000         /**
32001          * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
32002          */
32003         enableColumnHide : true,
32004
32005         /**
32006          * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
32007          */
32008         enableRowHeightSync : false,
32009
32010         /**
32011          * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
32012          */
32013         stripeRows : true,
32014
32015         /**
32016          * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
32017          */
32018         autoHeight : false,
32019
32020     /**
32021      * @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.
32022      */
32023     autoExpandColumn : false,
32024
32025     /**
32026     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
32027     * Default is 50.
32028     */
32029     autoExpandMin : 50,
32030
32031     /**
32032     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
32033     */
32034     autoExpandMax : 1000,
32035
32036     /**
32037          * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
32038          */
32039         view : null,
32040
32041         /**
32042      * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
32043          */
32044         loadMask : false,
32045
32046     // private
32047     rendered : false,
32048
32049     /**
32050     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
32051     * of a fixed width. Default is false.
32052     */
32053     /**
32054     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
32055     */
32056     /**
32057      * Called once after all setup has been completed and the grid is ready to be rendered.
32058      * @return {Roo.grid.Grid} this
32059      */
32060     render : function(){
32061         var c = this.container;
32062         // try to detect autoHeight/width mode
32063         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
32064             this.autoHeight = true;
32065         }
32066         var view = this.getView();
32067         view.init(this);
32068
32069         c.on("click", this.onClick, this);
32070         c.on("dblclick", this.onDblClick, this);
32071         c.on("contextmenu", this.onContextMenu, this);
32072         c.on("keydown", this.onKeyDown, this);
32073
32074         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
32075
32076         this.getSelectionModel().init(this);
32077
32078         view.render();
32079
32080         if(this.loadMask){
32081             this.loadMask = new Roo.LoadMask(this.container,
32082                     Roo.apply({store:this.dataSource}, this.loadMask));
32083         }
32084         
32085         
32086         if (this.toolbar && this.toolbar.xtype) {
32087             this.toolbar.container = this.getView().getHeaderPanel(true);
32088             this.toolbar = new Ext.Toolbar(this.toolbar);
32089         }
32090         if (this.footer && this.footer.xtype) {
32091             this.footer.dataSource = this.getDataSource();
32092             this.footer.container = this.getView().getFooterPanel(true);
32093             this.footer = Roo.factory(this.footer, Roo);
32094         }
32095         this.rendered = true;
32096         this.fireEvent('render', this);
32097         return this;
32098     },
32099
32100         /**
32101          * Reconfigures the grid to use a different Store and Column Model.
32102          * The View will be bound to the new objects and refreshed.
32103          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
32104          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
32105          */
32106     reconfigure : function(dataSource, colModel){
32107         if(this.loadMask){
32108             this.loadMask.destroy();
32109             this.loadMask = new Roo.LoadMask(this.container,
32110                     Roo.apply({store:dataSource}, this.loadMask));
32111         }
32112         this.view.bind(dataSource, colModel);
32113         this.dataSource = dataSource;
32114         this.colModel = colModel;
32115         this.view.refresh(true);
32116     },
32117
32118     // private
32119     onKeyDown : function(e){
32120         this.fireEvent("keydown", e);
32121     },
32122
32123     /**
32124      * Destroy this grid.
32125      * @param {Boolean} removeEl True to remove the element
32126      */
32127     destroy : function(removeEl, keepListeners){
32128         if(this.loadMask){
32129             this.loadMask.destroy();
32130         }
32131         var c = this.container;
32132         c.removeAllListeners();
32133         this.view.destroy();
32134         this.colModel.purgeListeners();
32135         if(!keepListeners){
32136             this.purgeListeners();
32137         }
32138         c.update("");
32139         if(removeEl === true){
32140             c.remove();
32141         }
32142     },
32143
32144     // private
32145     processEvent : function(name, e){
32146         this.fireEvent(name, e);
32147         var t = e.getTarget();
32148         var v = this.view;
32149         var header = v.findHeaderIndex(t);
32150         if(header !== false){
32151             this.fireEvent("header" + name, this, header, e);
32152         }else{
32153             var row = v.findRowIndex(t);
32154             var cell = v.findCellIndex(t);
32155             if(row !== false){
32156                 this.fireEvent("row" + name, this, row, e);
32157                 if(cell !== false){
32158                     this.fireEvent("cell" + name, this, row, cell, e);
32159                 }
32160             }
32161         }
32162     },
32163
32164     // private
32165     onClick : function(e){
32166         this.processEvent("click", e);
32167     },
32168
32169     // private
32170     onContextMenu : function(e, t){
32171         this.processEvent("contextmenu", e);
32172     },
32173
32174     // private
32175     onDblClick : function(e){
32176         this.processEvent("dblclick", e);
32177     },
32178
32179     // private
32180     walkCells : function(row, col, step, fn, scope){
32181         var cm = this.colModel, clen = cm.getColumnCount();
32182         var ds = this.dataSource, rlen = ds.getCount(), first = true;
32183         if(step < 0){
32184             if(col < 0){
32185                 row--;
32186                 first = false;
32187             }
32188             while(row >= 0){
32189                 if(!first){
32190                     col = clen-1;
32191                 }
32192                 first = false;
32193                 while(col >= 0){
32194                     if(fn.call(scope || this, row, col, cm) === true){
32195                         return [row, col];
32196                     }
32197                     col--;
32198                 }
32199                 row--;
32200             }
32201         } else {
32202             if(col >= clen){
32203                 row++;
32204                 first = false;
32205             }
32206             while(row < rlen){
32207                 if(!first){
32208                     col = 0;
32209                 }
32210                 first = false;
32211                 while(col < clen){
32212                     if(fn.call(scope || this, row, col, cm) === true){
32213                         return [row, col];
32214                     }
32215                     col++;
32216                 }
32217                 row++;
32218             }
32219         }
32220         return null;
32221     },
32222
32223     // private
32224     getSelections : function(){
32225         return this.selModel.getSelections();
32226     },
32227
32228     /**
32229      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
32230      * but if manual update is required this method will initiate it.
32231      */
32232     autoSize : function(){
32233         if(this.rendered){
32234             this.view.layout();
32235             if(this.view.adjustForScroll){
32236                 this.view.adjustForScroll();
32237             }
32238         }
32239     },
32240
32241     /**
32242      * Returns the grid's underlying element.
32243      * @return {Element} The element
32244      */
32245     getGridEl : function(){
32246         return this.container;
32247     },
32248
32249     // private for compatibility, overridden by editor grid
32250     stopEditing : function(){},
32251
32252     /**
32253      * Returns the grid's SelectionModel.
32254      * @return {SelectionModel}
32255      */
32256     getSelectionModel : function(){
32257         if(!this.selModel){
32258             this.selModel = new Roo.grid.RowSelectionModel();
32259         }
32260         return this.selModel;
32261     },
32262
32263     /**
32264      * Returns the grid's DataSource.
32265      * @return {DataSource}
32266      */
32267     getDataSource : function(){
32268         return this.dataSource;
32269     },
32270
32271     /**
32272      * Returns the grid's ColumnModel.
32273      * @return {ColumnModel}
32274      */
32275     getColumnModel : function(){
32276         return this.colModel;
32277     },
32278
32279     /**
32280      * Returns the grid's GridView object.
32281      * @return {GridView}
32282      */
32283     getView : function(){
32284         if(!this.view){
32285             this.view = new Roo.grid.GridView(this.viewConfig);
32286         }
32287         return this.view;
32288     },
32289     /**
32290      * Called to get grid's drag proxy text, by default returns this.ddText.
32291      * @return {String}
32292      */
32293     getDragDropText : function(){
32294         var count = this.selModel.getCount();
32295         return String.format(this.ddText, count, count == 1 ? '' : 's');
32296     }
32297 });
32298 /**
32299  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
32300  * %0 is replaced with the number of selected rows.
32301  * @type String
32302  */
32303 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
32304  * Based on:
32305  * Ext JS Library 1.1.1
32306  * Copyright(c) 2006-2007, Ext JS, LLC.
32307  *
32308  * Originally Released Under LGPL - original licence link has changed is not relivant.
32309  *
32310  * Fork - LGPL
32311  * <script type="text/javascript">
32312  */
32313  
32314 Roo.grid.AbstractGridView = function(){
32315         this.grid = null;
32316         
32317         this.events = {
32318             "beforerowremoved" : true,
32319             "beforerowsinserted" : true,
32320             "beforerefresh" : true,
32321             "rowremoved" : true,
32322             "rowsinserted" : true,
32323             "rowupdated" : true,
32324             "refresh" : true
32325         };
32326     Roo.grid.AbstractGridView.superclass.constructor.call(this);
32327 };
32328
32329 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
32330     rowClass : "x-grid-row",
32331     cellClass : "x-grid-cell",
32332     tdClass : "x-grid-td",
32333     hdClass : "x-grid-hd",
32334     splitClass : "x-grid-hd-split",
32335     
32336         init: function(grid){
32337         this.grid = grid;
32338                 var cid = this.grid.getGridEl().id;
32339         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
32340         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
32341         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
32342         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
32343         },
32344         
32345         getColumnRenderers : function(){
32346         var renderers = [];
32347         var cm = this.grid.colModel;
32348         var colCount = cm.getColumnCount();
32349         for(var i = 0; i < colCount; i++){
32350             renderers[i] = cm.getRenderer(i);
32351         }
32352         return renderers;
32353     },
32354     
32355     getColumnIds : function(){
32356         var ids = [];
32357         var cm = this.grid.colModel;
32358         var colCount = cm.getColumnCount();
32359         for(var i = 0; i < colCount; i++){
32360             ids[i] = cm.getColumnId(i);
32361         }
32362         return ids;
32363     },
32364     
32365     getDataIndexes : function(){
32366         if(!this.indexMap){
32367             this.indexMap = this.buildIndexMap();
32368         }
32369         return this.indexMap.colToData;
32370     },
32371     
32372     getColumnIndexByDataIndex : function(dataIndex){
32373         if(!this.indexMap){
32374             this.indexMap = this.buildIndexMap();
32375         }
32376         return this.indexMap.dataToCol[dataIndex];
32377     },
32378     
32379     /**
32380      * Set a css style for a column dynamically. 
32381      * @param {Number} colIndex The index of the column
32382      * @param {String} name The css property name
32383      * @param {String} value The css value
32384      */
32385     setCSSStyle : function(colIndex, name, value){
32386         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
32387         Roo.util.CSS.updateRule(selector, name, value);
32388     },
32389     
32390     generateRules : function(cm){
32391         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
32392         Roo.util.CSS.removeStyleSheet(rulesId);
32393         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
32394             var cid = cm.getColumnId(i);
32395             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
32396                          this.tdSelector, cid, " {\n}\n",
32397                          this.hdSelector, cid, " {\n}\n",
32398                          this.splitSelector, cid, " {\n}\n");
32399         }
32400         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
32401     }
32402 });/*
32403  * Based on:
32404  * Ext JS Library 1.1.1
32405  * Copyright(c) 2006-2007, Ext JS, LLC.
32406  *
32407  * Originally Released Under LGPL - original licence link has changed is not relivant.
32408  *
32409  * Fork - LGPL
32410  * <script type="text/javascript">
32411  */
32412
32413 // private
32414 // This is a support class used internally by the Grid components
32415 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
32416     this.grid = grid;
32417     this.view = grid.getView();
32418     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
32419     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
32420     if(hd2){
32421         this.setHandleElId(Roo.id(hd));
32422         this.setOuterHandleElId(Roo.id(hd2));
32423     }
32424     this.scroll = false;
32425 };
32426 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
32427     maxDragWidth: 120,
32428     getDragData : function(e){
32429         var t = Roo.lib.Event.getTarget(e);
32430         var h = this.view.findHeaderCell(t);
32431         if(h){
32432             return {ddel: h.firstChild, header:h};
32433         }
32434         return false;
32435     },
32436
32437     onInitDrag : function(e){
32438         this.view.headersDisabled = true;
32439         var clone = this.dragData.ddel.cloneNode(true);
32440         clone.id = Roo.id();
32441         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
32442         this.proxy.update(clone);
32443         return true;
32444     },
32445
32446     afterValidDrop : function(){
32447         var v = this.view;
32448         setTimeout(function(){
32449             v.headersDisabled = false;
32450         }, 50);
32451     },
32452
32453     afterInvalidDrop : function(){
32454         var v = this.view;
32455         setTimeout(function(){
32456             v.headersDisabled = false;
32457         }, 50);
32458     }
32459 });
32460 /*
32461  * Based on:
32462  * Ext JS Library 1.1.1
32463  * Copyright(c) 2006-2007, Ext JS, LLC.
32464  *
32465  * Originally Released Under LGPL - original licence link has changed is not relivant.
32466  *
32467  * Fork - LGPL
32468  * <script type="text/javascript">
32469  */
32470 // private
32471 // This is a support class used internally by the Grid components
32472 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
32473     this.grid = grid;
32474     this.view = grid.getView();
32475     // split the proxies so they don't interfere with mouse events
32476     this.proxyTop = Roo.DomHelper.append(document.body, {
32477         cls:"col-move-top", html:"&#160;"
32478     }, true);
32479     this.proxyBottom = Roo.DomHelper.append(document.body, {
32480         cls:"col-move-bottom", html:"&#160;"
32481     }, true);
32482     this.proxyTop.hide = this.proxyBottom.hide = function(){
32483         this.setLeftTop(-100,-100);
32484         this.setStyle("visibility", "hidden");
32485     };
32486     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
32487     // temporarily disabled
32488     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
32489     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
32490 };
32491 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
32492     proxyOffsets : [-4, -9],
32493     fly: Roo.Element.fly,
32494
32495     getTargetFromEvent : function(e){
32496         var t = Roo.lib.Event.getTarget(e);
32497         var cindex = this.view.findCellIndex(t);
32498         if(cindex !== false){
32499             return this.view.getHeaderCell(cindex);
32500         }
32501     },
32502
32503     nextVisible : function(h){
32504         var v = this.view, cm = this.grid.colModel;
32505         h = h.nextSibling;
32506         while(h){
32507             if(!cm.isHidden(v.getCellIndex(h))){
32508                 return h;
32509             }
32510             h = h.nextSibling;
32511         }
32512         return null;
32513     },
32514
32515     prevVisible : function(h){
32516         var v = this.view, cm = this.grid.colModel;
32517         h = h.prevSibling;
32518         while(h){
32519             if(!cm.isHidden(v.getCellIndex(h))){
32520                 return h;
32521             }
32522             h = h.prevSibling;
32523         }
32524         return null;
32525     },
32526
32527     positionIndicator : function(h, n, e){
32528         var x = Roo.lib.Event.getPageX(e);
32529         var r = Roo.lib.Dom.getRegion(n.firstChild);
32530         var px, pt, py = r.top + this.proxyOffsets[1];
32531         if((r.right - x) <= (r.right-r.left)/2){
32532             px = r.right+this.view.borderWidth;
32533             pt = "after";
32534         }else{
32535             px = r.left;
32536             pt = "before";
32537         }
32538         var oldIndex = this.view.getCellIndex(h);
32539         var newIndex = this.view.getCellIndex(n);
32540
32541         if(this.grid.colModel.isFixed(newIndex)){
32542             return false;
32543         }
32544
32545         var locked = this.grid.colModel.isLocked(newIndex);
32546
32547         if(pt == "after"){
32548             newIndex++;
32549         }
32550         if(oldIndex < newIndex){
32551             newIndex--;
32552         }
32553         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
32554             return false;
32555         }
32556         px +=  this.proxyOffsets[0];
32557         this.proxyTop.setLeftTop(px, py);
32558         this.proxyTop.show();
32559         if(!this.bottomOffset){
32560             this.bottomOffset = this.view.mainHd.getHeight();
32561         }
32562         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
32563         this.proxyBottom.show();
32564         return pt;
32565     },
32566
32567     onNodeEnter : function(n, dd, e, data){
32568         if(data.header != n){
32569             this.positionIndicator(data.header, n, e);
32570         }
32571     },
32572
32573     onNodeOver : function(n, dd, e, data){
32574         var result = false;
32575         if(data.header != n){
32576             result = this.positionIndicator(data.header, n, e);
32577         }
32578         if(!result){
32579             this.proxyTop.hide();
32580             this.proxyBottom.hide();
32581         }
32582         return result ? this.dropAllowed : this.dropNotAllowed;
32583     },
32584
32585     onNodeOut : function(n, dd, e, data){
32586         this.proxyTop.hide();
32587         this.proxyBottom.hide();
32588     },
32589
32590     onNodeDrop : function(n, dd, e, data){
32591         var h = data.header;
32592         if(h != n){
32593             var cm = this.grid.colModel;
32594             var x = Roo.lib.Event.getPageX(e);
32595             var r = Roo.lib.Dom.getRegion(n.firstChild);
32596             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
32597             var oldIndex = this.view.getCellIndex(h);
32598             var newIndex = this.view.getCellIndex(n);
32599             var locked = cm.isLocked(newIndex);
32600             if(pt == "after"){
32601                 newIndex++;
32602             }
32603             if(oldIndex < newIndex){
32604                 newIndex--;
32605             }
32606             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
32607                 return false;
32608             }
32609             cm.setLocked(oldIndex, locked, true);
32610             cm.moveColumn(oldIndex, newIndex);
32611             this.grid.fireEvent("columnmove", oldIndex, newIndex);
32612             return true;
32613         }
32614         return false;
32615     }
32616 });
32617 /*
32618  * Based on:
32619  * Ext JS Library 1.1.1
32620  * Copyright(c) 2006-2007, Ext JS, LLC.
32621  *
32622  * Originally Released Under LGPL - original licence link has changed is not relivant.
32623  *
32624  * Fork - LGPL
32625  * <script type="text/javascript">
32626  */
32627   
32628 /**
32629  * @class Roo.grid.GridView
32630  * @extends Roo.util.Observable
32631  *
32632  * @constructor
32633  * @param {Object} config
32634  */
32635 Roo.grid.GridView = function(config){
32636     Roo.grid.GridView.superclass.constructor.call(this);
32637     this.el = null;
32638
32639     Roo.apply(this, config);
32640 };
32641
32642 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
32643
32644     /**
32645      * Override this function to apply custom css classes to rows during rendering
32646      * @param {Record} record The record
32647      * @param {Number} index
32648      * @method getRowClass
32649      */
32650     rowClass : "x-grid-row",
32651
32652     cellClass : "x-grid-col",
32653
32654     tdClass : "x-grid-td",
32655
32656     hdClass : "x-grid-hd",
32657
32658     splitClass : "x-grid-split",
32659
32660     sortClasses : ["sort-asc", "sort-desc"],
32661
32662     enableMoveAnim : false,
32663
32664     hlColor: "C3DAF9",
32665
32666     dh : Roo.DomHelper,
32667
32668     fly : Roo.Element.fly,
32669
32670     css : Roo.util.CSS,
32671
32672     borderWidth: 1,
32673
32674     splitOffset: 3,
32675
32676     scrollIncrement : 22,
32677
32678     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
32679
32680     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
32681
32682     bind : function(ds, cm){
32683         if(this.ds){
32684             this.ds.un("load", this.onLoad, this);
32685             this.ds.un("datachanged", this.onDataChange, this);
32686             this.ds.un("add", this.onAdd, this);
32687             this.ds.un("remove", this.onRemove, this);
32688             this.ds.un("update", this.onUpdate, this);
32689             this.ds.un("clear", this.onClear, this);
32690         }
32691         if(ds){
32692             ds.on("load", this.onLoad, this);
32693             ds.on("datachanged", this.onDataChange, this);
32694             ds.on("add", this.onAdd, this);
32695             ds.on("remove", this.onRemove, this);
32696             ds.on("update", this.onUpdate, this);
32697             ds.on("clear", this.onClear, this);
32698         }
32699         this.ds = ds;
32700
32701         if(this.cm){
32702             this.cm.un("widthchange", this.onColWidthChange, this);
32703             this.cm.un("headerchange", this.onHeaderChange, this);
32704             this.cm.un("hiddenchange", this.onHiddenChange, this);
32705             this.cm.un("columnmoved", this.onColumnMove, this);
32706             this.cm.un("columnlockchange", this.onColumnLock, this);
32707         }
32708         if(cm){
32709             this.generateRules(cm);
32710             cm.on("widthchange", this.onColWidthChange, this);
32711             cm.on("headerchange", this.onHeaderChange, this);
32712             cm.on("hiddenchange", this.onHiddenChange, this);
32713             cm.on("columnmoved", this.onColumnMove, this);
32714             cm.on("columnlockchange", this.onColumnLock, this);
32715         }
32716         this.cm = cm;
32717     },
32718
32719     init: function(grid){
32720                 Roo.grid.GridView.superclass.init.call(this, grid);
32721
32722                 this.bind(grid.dataSource, grid.colModel);
32723
32724             grid.on("headerclick", this.handleHeaderClick, this);
32725
32726         if(grid.trackMouseOver){
32727             grid.on("mouseover", this.onRowOver, this);
32728                 grid.on("mouseout", this.onRowOut, this);
32729             }
32730             grid.cancelTextSelection = function(){};
32731                 this.gridId = grid.id;
32732
32733                 var tpls = this.templates || {};
32734
32735                 if(!tpls.master){
32736                     tpls.master = new Roo.Template(
32737                        '<div class="x-grid" hidefocus="true">',
32738                           '<div class="x-grid-topbar"></div>',
32739                           '<div class="x-grid-scroller"><div></div></div>',
32740                           '<div class="x-grid-locked">',
32741                               '<div class="x-grid-header">{lockedHeader}</div>',
32742                               '<div class="x-grid-body">{lockedBody}</div>',
32743                           "</div>",
32744                           '<div class="x-grid-viewport">',
32745                               '<div class="x-grid-header">{header}</div>',
32746                               '<div class="x-grid-body">{body}</div>',
32747                           "</div>",
32748                           '<div class="x-grid-bottombar"></div>',
32749                           '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
32750                           '<div class="x-grid-resize-proxy">&#160;</div>',
32751                        "</div>"
32752                     );
32753                     tpls.master.disableformats = true;
32754                 }
32755
32756                 if(!tpls.header){
32757                     tpls.header = new Roo.Template(
32758                        '<table border="0" cellspacing="0" cellpadding="0">',
32759                        '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
32760                        "</table>{splits}"
32761                     );
32762                     tpls.header.disableformats = true;
32763                 }
32764                 tpls.header.compile();
32765
32766                 if(!tpls.hcell){
32767                     tpls.hcell = new Roo.Template(
32768                         '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
32769                         '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
32770                         "</div></td>"
32771                      );
32772                      tpls.hcell.disableFormats = true;
32773                 }
32774                 tpls.hcell.compile();
32775
32776                 if(!tpls.hsplit){
32777                     tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
32778                     tpls.hsplit.disableFormats = true;
32779                 }
32780                 tpls.hsplit.compile();
32781
32782                 if(!tpls.body){
32783                     tpls.body = new Roo.Template(
32784                        '<table border="0" cellspacing="0" cellpadding="0">',
32785                        "<tbody>{rows}</tbody>",
32786                        "</table>"
32787                     );
32788                     tpls.body.disableFormats = true;
32789                 }
32790                 tpls.body.compile();
32791
32792                 if(!tpls.row){
32793                     tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
32794                     tpls.row.disableFormats = true;
32795                 }
32796                 tpls.row.compile();
32797
32798                 if(!tpls.cell){
32799                     tpls.cell = new Roo.Template(
32800                         '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
32801                         '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
32802                         "</td>"
32803                     );
32804             tpls.cell.disableFormats = true;
32805         }
32806                 tpls.cell.compile();
32807
32808                 this.templates = tpls;
32809         },
32810
32811         // remap these for backwards compat
32812     onColWidthChange : function(){
32813         this.updateColumns.apply(this, arguments);
32814     },
32815     onHeaderChange : function(){
32816         this.updateHeaders.apply(this, arguments);
32817     }, 
32818     onHiddenChange : function(){
32819         this.handleHiddenChange.apply(this, arguments);
32820     },
32821     onColumnMove : function(){
32822         this.handleColumnMove.apply(this, arguments);
32823     },
32824     onColumnLock : function(){
32825         this.handleLockChange.apply(this, arguments);
32826     },
32827
32828     onDataChange : function(){
32829         this.refresh();
32830         this.updateHeaderSortState();
32831     },
32832
32833         onClear : function(){
32834         this.refresh();
32835     },
32836
32837         onUpdate : function(ds, record){
32838         this.refreshRow(record);
32839     },
32840
32841     refreshRow : function(record){
32842         var ds = this.ds, index;
32843         if(typeof record == 'number'){
32844             index = record;
32845             record = ds.getAt(index);
32846         }else{
32847             index = ds.indexOf(record);
32848         }
32849         this.insertRows(ds, index, index, true);
32850         this.onRemove(ds, record, index+1, true);
32851         this.syncRowHeights(index, index);
32852         this.layout();
32853         this.fireEvent("rowupdated", this, index, record);
32854     },
32855
32856     onAdd : function(ds, records, index){
32857         this.insertRows(ds, index, index + (records.length-1));
32858     },
32859
32860     onRemove : function(ds, record, index, isUpdate){
32861         if(isUpdate !== true){
32862             this.fireEvent("beforerowremoved", this, index, record);
32863         }
32864         var bt = this.getBodyTable(), lt = this.getLockedTable();
32865         if(bt.rows[index]){
32866             bt.firstChild.removeChild(bt.rows[index]);
32867         }
32868         if(lt.rows[index]){
32869             lt.firstChild.removeChild(lt.rows[index]);
32870         }
32871         if(isUpdate !== true){
32872             this.stripeRows(index);
32873             this.syncRowHeights(index, index);
32874             this.layout();
32875             this.fireEvent("rowremoved", this, index, record);
32876         }
32877     },
32878
32879     onLoad : function(){
32880         this.scrollToTop();
32881     },
32882
32883     /**
32884      * Scrolls the grid to the top
32885      */
32886     scrollToTop : function(){
32887         if(this.scroller){
32888             this.scroller.dom.scrollTop = 0;
32889             this.syncScroll();
32890         }
32891     },
32892
32893     /**
32894      * Gets a panel in the header of the grid that can be used for toolbars etc.
32895      * After modifying the contents of this panel a call to grid.autoSize() may be
32896      * required to register any changes in size.
32897      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
32898      * @return Roo.Element
32899      */
32900     getHeaderPanel : function(doShow){
32901         if(doShow){
32902             this.headerPanel.show();
32903         }
32904         return this.headerPanel;
32905         },
32906
32907         /**
32908      * Gets a panel in the footer of the grid that can be used for toolbars etc.
32909      * After modifying the contents of this panel a call to grid.autoSize() may be
32910      * required to register any changes in size.
32911      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
32912      * @return Roo.Element
32913      */
32914     getFooterPanel : function(doShow){
32915         if(doShow){
32916             this.footerPanel.show();
32917         }
32918         return this.footerPanel;
32919         },
32920
32921         initElements : function(){
32922             var E = Roo.Element;
32923             var el = this.grid.getGridEl().dom.firstChild;
32924             var cs = el.childNodes;
32925
32926             this.el = new E(el);
32927             this.headerPanel = new E(el.firstChild);
32928             this.headerPanel.enableDisplayMode("block");
32929
32930         this.scroller = new E(cs[1]);
32931             this.scrollSizer = new E(this.scroller.dom.firstChild);
32932
32933             this.lockedWrap = new E(cs[2]);
32934             this.lockedHd = new E(this.lockedWrap.dom.firstChild);
32935             this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
32936
32937             this.mainWrap = new E(cs[3]);
32938             this.mainHd = new E(this.mainWrap.dom.firstChild);
32939             this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
32940
32941             this.footerPanel = new E(cs[4]);
32942             this.footerPanel.enableDisplayMode("block");
32943
32944         this.focusEl = new E(cs[5]);
32945         this.focusEl.swallowEvent("click", true);
32946         this.resizeProxy = new E(cs[6]);
32947
32948             this.headerSelector = String.format(
32949                '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
32950                this.lockedHd.id, this.mainHd.id
32951             );
32952
32953             this.splitterSelector = String.format(
32954                '#{0} div.x-grid-split, #{1} div.x-grid-split',
32955                this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
32956             );
32957     },
32958     idToCssName : function(s)
32959     {
32960         return s.replace(/[^a-z0-9]+/ig, '-');
32961     },
32962
32963         getHeaderCell : function(index){
32964             return Roo.DomQuery.select(this.headerSelector)[index];
32965         },
32966
32967         getHeaderCellMeasure : function(index){
32968             return this.getHeaderCell(index).firstChild;
32969         },
32970
32971         getHeaderCellText : function(index){
32972             return this.getHeaderCell(index).firstChild.firstChild;
32973         },
32974
32975         getLockedTable : function(){
32976             return this.lockedBody.dom.firstChild;
32977         },
32978
32979         getBodyTable : function(){
32980             return this.mainBody.dom.firstChild;
32981         },
32982
32983         getLockedRow : function(index){
32984             return this.getLockedTable().rows[index];
32985         },
32986
32987         getRow : function(index){
32988             return this.getBodyTable().rows[index];
32989         },
32990
32991         getRowComposite : function(index){
32992             if(!this.rowEl){
32993                 this.rowEl = new Roo.CompositeElementLite();
32994             }
32995         var els = [], lrow, mrow;
32996         if(lrow = this.getLockedRow(index)){
32997             els.push(lrow);
32998         }
32999         if(mrow = this.getRow(index)){
33000             els.push(mrow);
33001         }
33002         this.rowEl.elements = els;
33003             return this.rowEl;
33004         },
33005
33006         getCell : function(rowIndex, colIndex){
33007             var locked = this.cm.getLockedCount();
33008             var source;
33009             if(colIndex < locked){
33010                 source = this.lockedBody.dom.firstChild;
33011             }else{
33012                 source = this.mainBody.dom.firstChild;
33013                 colIndex -= locked;
33014             }
33015         return source.rows[rowIndex].childNodes[colIndex];
33016         },
33017
33018         getCellText : function(rowIndex, colIndex){
33019             return this.getCell(rowIndex, colIndex).firstChild.firstChild;
33020         },
33021
33022         getCellBox : function(cell){
33023             var b = this.fly(cell).getBox();
33024         if(Roo.isOpera){ // opera fails to report the Y
33025             b.y = cell.offsetTop + this.mainBody.getY();
33026         }
33027         return b;
33028     },
33029
33030     getCellIndex : function(cell){
33031         var id = String(cell.className).match(this.cellRE);
33032         if(id){
33033             return parseInt(id[1], 10);
33034         }
33035         return 0;
33036     },
33037
33038     findHeaderIndex : function(n){
33039         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33040         return r ? this.getCellIndex(r) : false;
33041     },
33042
33043     findHeaderCell : function(n){
33044         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33045         return r ? r : false;
33046     },
33047
33048     findRowIndex : function(n){
33049         if(!n){
33050             return false;
33051         }
33052         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
33053         return r ? r.rowIndex : false;
33054     },
33055
33056     findCellIndex : function(node){
33057         var stop = this.el.dom;
33058         while(node && node != stop){
33059             if(this.findRE.test(node.className)){
33060                 return this.getCellIndex(node);
33061             }
33062             node = node.parentNode;
33063         }
33064         return false;
33065     },
33066
33067     getColumnId : function(index){
33068             return this.cm.getColumnId(index);
33069         },
33070
33071         getSplitters : function(){
33072             if(this.splitterSelector){
33073                return Roo.DomQuery.select(this.splitterSelector);
33074             }else{
33075                 return null;
33076             }
33077         },
33078
33079         getSplitter : function(index){
33080             return this.getSplitters()[index];
33081         },
33082
33083     onRowOver : function(e, t){
33084         var row;
33085         if((row = this.findRowIndex(t)) !== false){
33086             this.getRowComposite(row).addClass("x-grid-row-over");
33087         }
33088     },
33089
33090     onRowOut : function(e, t){
33091         var row;
33092         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
33093             this.getRowComposite(row).removeClass("x-grid-row-over");
33094         }
33095     },
33096
33097     renderHeaders : function(){
33098             var cm = this.cm;
33099         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
33100         var cb = [], lb = [], sb = [], lsb = [], p = {};
33101         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33102             p.cellId = "x-grid-hd-0-" + i;
33103             p.splitId = "x-grid-csplit-0-" + i;
33104             p.id = cm.getColumnId(i);
33105             p.title = cm.getColumnTooltip(i) || "";
33106             p.value = cm.getColumnHeader(i) || "";
33107             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
33108             if(!cm.isLocked(i)){
33109                 cb[cb.length] = ct.apply(p);
33110                 sb[sb.length] = st.apply(p);
33111             }else{
33112                 lb[lb.length] = ct.apply(p);
33113                 lsb[lsb.length] = st.apply(p);
33114             }
33115         }
33116         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
33117                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
33118         },
33119
33120         updateHeaders : function(){
33121         var html = this.renderHeaders();
33122         this.lockedHd.update(html[0]);
33123         this.mainHd.update(html[1]);
33124     },
33125
33126     /**
33127      * Focuses the specified row.
33128      * @param {Number} row The row index
33129      */
33130     focusRow : function(row){
33131         var x = this.scroller.dom.scrollLeft;
33132         this.focusCell(row, 0, false);
33133         this.scroller.dom.scrollLeft = x;
33134     },
33135
33136     /**
33137      * Focuses the specified cell.
33138      * @param {Number} row The row index
33139      * @param {Number} col The column index
33140      * @param {Boolean} hscroll false to disable horizontal scrolling
33141      */
33142     focusCell : function(row, col, hscroll){
33143         var el = this.ensureVisible(row, col, hscroll);
33144         this.focusEl.alignTo(el, "tl-tl");
33145         if(Roo.isGecko){
33146             this.focusEl.focus();
33147         }else{
33148             this.focusEl.focus.defer(1, this.focusEl);
33149         }
33150     },
33151
33152     /**
33153      * Scrolls the specified cell into view
33154      * @param {Number} row The row index
33155      * @param {Number} col The column index
33156      * @param {Boolean} hscroll false to disable horizontal scrolling
33157      */
33158     ensureVisible : function(row, col, hscroll){
33159         if(typeof row != "number"){
33160             row = row.rowIndex;
33161         }
33162         if(row < 0 && row >= this.ds.getCount()){
33163             return;
33164         }
33165         col = (col !== undefined ? col : 0);
33166         var cm = this.grid.colModel;
33167         while(cm.isHidden(col)){
33168             col++;
33169         }
33170
33171         var el = this.getCell(row, col);
33172         if(!el){
33173             return;
33174         }
33175         var c = this.scroller.dom;
33176
33177         var ctop = parseInt(el.offsetTop, 10);
33178         var cleft = parseInt(el.offsetLeft, 10);
33179         var cbot = ctop + el.offsetHeight;
33180         var cright = cleft + el.offsetWidth;
33181
33182         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
33183         var stop = parseInt(c.scrollTop, 10);
33184         var sleft = parseInt(c.scrollLeft, 10);
33185         var sbot = stop + ch;
33186         var sright = sleft + c.clientWidth;
33187
33188         if(ctop < stop){
33189                 c.scrollTop = ctop;
33190         }else if(cbot > sbot){
33191             c.scrollTop = cbot-ch;
33192         }
33193
33194         if(hscroll !== false){
33195             if(cleft < sleft){
33196                 c.scrollLeft = cleft;
33197             }else if(cright > sright){
33198                 c.scrollLeft = cright-c.clientWidth;
33199             }
33200         }
33201         return el;
33202     },
33203
33204     updateColumns : function(){
33205         this.grid.stopEditing();
33206         var cm = this.grid.colModel, colIds = this.getColumnIds();
33207         //var totalWidth = cm.getTotalWidth();
33208         var pos = 0;
33209         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33210             //if(cm.isHidden(i)) continue;
33211             var w = cm.getColumnWidth(i);
33212             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33213             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33214         }
33215         this.updateSplitters();
33216     },
33217
33218     generateRules : function(cm){
33219         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
33220         Roo.util.CSS.removeStyleSheet(rulesId);
33221         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33222             var cid = cm.getColumnId(i);
33223             var align = '';
33224             if(cm.config[i].align){
33225                 align = 'text-align:'+cm.config[i].align+';';
33226             }
33227             var hidden = '';
33228             if(cm.isHidden(i)){
33229                 hidden = 'display:none;';
33230             }
33231             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
33232             ruleBuf.push(
33233                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
33234                     this.hdSelector, cid, " {\n", align, width, "}\n",
33235                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
33236                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
33237         }
33238         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
33239     },
33240
33241     updateSplitters : function(){
33242         var cm = this.cm, s = this.getSplitters();
33243         if(s){ // splitters not created yet
33244             var pos = 0, locked = true;
33245             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33246                 if(cm.isHidden(i)) continue;
33247                 var w = cm.getColumnWidth(i);
33248                 if(!cm.isLocked(i) && locked){
33249                     pos = 0;
33250                     locked = false;
33251                 }
33252                 pos += w;
33253                 s[i].style.left = (pos-this.splitOffset) + "px";
33254             }
33255         }
33256     },
33257
33258     handleHiddenChange : function(colModel, colIndex, hidden){
33259         if(hidden){
33260             this.hideColumn(colIndex);
33261         }else{
33262             this.unhideColumn(colIndex);
33263         }
33264     },
33265
33266     hideColumn : function(colIndex){
33267         var cid = this.getColumnId(colIndex);
33268         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
33269         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
33270         if(Roo.isSafari){
33271             this.updateHeaders();
33272         }
33273         this.updateSplitters();
33274         this.layout();
33275     },
33276
33277     unhideColumn : function(colIndex){
33278         var cid = this.getColumnId(colIndex);
33279         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
33280         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
33281
33282         if(Roo.isSafari){
33283             this.updateHeaders();
33284         }
33285         this.updateSplitters();
33286         this.layout();
33287     },
33288
33289     insertRows : function(dm, firstRow, lastRow, isUpdate){
33290         if(firstRow == 0 && lastRow == dm.getCount()-1){
33291             this.refresh();
33292         }else{
33293             if(!isUpdate){
33294                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
33295             }
33296             var s = this.getScrollState();
33297             var markup = this.renderRows(firstRow, lastRow);
33298             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
33299             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
33300             this.restoreScroll(s);
33301             if(!isUpdate){
33302                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
33303                 this.syncRowHeights(firstRow, lastRow);
33304                 this.stripeRows(firstRow);
33305                 this.layout();
33306             }
33307         }
33308     },
33309
33310     bufferRows : function(markup, target, index){
33311         var before = null, trows = target.rows, tbody = target.tBodies[0];
33312         if(index < trows.length){
33313             before = trows[index];
33314         }
33315         var b = document.createElement("div");
33316         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
33317         var rows = b.firstChild.rows;
33318         for(var i = 0, len = rows.length; i < len; i++){
33319             if(before){
33320                 tbody.insertBefore(rows[0], before);
33321             }else{
33322                 tbody.appendChild(rows[0]);
33323             }
33324         }
33325         b.innerHTML = "";
33326         b = null;
33327     },
33328
33329     deleteRows : function(dm, firstRow, lastRow){
33330         if(dm.getRowCount()<1){
33331             this.fireEvent("beforerefresh", this);
33332             this.mainBody.update("");
33333             this.lockedBody.update("");
33334             this.fireEvent("refresh", this);
33335         }else{
33336             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
33337             var bt = this.getBodyTable();
33338             var tbody = bt.firstChild;
33339             var rows = bt.rows;
33340             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
33341                 tbody.removeChild(rows[firstRow]);
33342             }
33343             this.stripeRows(firstRow);
33344             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
33345         }
33346     },
33347
33348     updateRows : function(dataSource, firstRow, lastRow){
33349         var s = this.getScrollState();
33350         this.refresh();
33351         this.restoreScroll(s);
33352     },
33353
33354     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
33355         if(!noRefresh){
33356            this.refresh();
33357         }
33358         this.updateHeaderSortState();
33359     },
33360
33361     getScrollState : function(){
33362         var sb = this.scroller.dom;
33363         return {left: sb.scrollLeft, top: sb.scrollTop};
33364     },
33365
33366     stripeRows : function(startRow){
33367         if(!this.grid.stripeRows || this.ds.getCount() < 1){
33368             return;
33369         }
33370         startRow = startRow || 0;
33371         var rows = this.getBodyTable().rows;
33372         var lrows = this.getLockedTable().rows;
33373         var cls = ' x-grid-row-alt ';
33374         for(var i = startRow, len = rows.length; i < len; i++){
33375             var row = rows[i], lrow = lrows[i];
33376             var isAlt = ((i+1) % 2 == 0);
33377             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
33378             if(isAlt == hasAlt){
33379                 continue;
33380             }
33381             if(isAlt){
33382                 row.className += " x-grid-row-alt";
33383             }else{
33384                 row.className = row.className.replace("x-grid-row-alt", "");
33385             }
33386             if(lrow){
33387                 lrow.className = row.className;
33388             }
33389         }
33390     },
33391
33392     restoreScroll : function(state){
33393         var sb = this.scroller.dom;
33394         sb.scrollLeft = state.left;
33395         sb.scrollTop = state.top;
33396         this.syncScroll();
33397     },
33398
33399     syncScroll : function(){
33400         var sb = this.scroller.dom;
33401         var sh = this.mainHd.dom;
33402         var bs = this.mainBody.dom;
33403         var lv = this.lockedBody.dom;
33404         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
33405         lv.scrollTop = bs.scrollTop = sb.scrollTop;
33406     },
33407
33408     handleScroll : function(e){
33409         this.syncScroll();
33410         var sb = this.scroller.dom;
33411         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
33412         e.stopEvent();
33413     },
33414
33415     handleWheel : function(e){
33416         var d = e.getWheelDelta();
33417         this.scroller.dom.scrollTop -= d*22;
33418         // set this here to prevent jumpy scrolling on large tables
33419         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
33420         e.stopEvent();
33421     },
33422
33423     renderRows : function(startRow, endRow){
33424         // pull in all the crap needed to render rows
33425         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
33426         var colCount = cm.getColumnCount();
33427
33428         if(ds.getCount() < 1){
33429             return ["", ""];
33430         }
33431
33432         // build a map for all the columns
33433         var cs = [];
33434         for(var i = 0; i < colCount; i++){
33435             var name = cm.getDataIndex(i);
33436             cs[i] = {
33437                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
33438                 renderer : cm.getRenderer(i),
33439                 id : cm.getColumnId(i),
33440                 locked : cm.isLocked(i)
33441             };
33442         }
33443
33444         startRow = startRow || 0;
33445         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
33446
33447         // records to render
33448         var rs = ds.getRange(startRow, endRow);
33449
33450         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
33451     },
33452
33453     // As much as I hate to duplicate code, this was branched because FireFox really hates
33454     // [].join("") on strings. The performance difference was substantial enough to
33455     // branch this function
33456     doRender : Roo.isGecko ?
33457             function(cs, rs, ds, startRow, colCount, stripe){
33458                 var ts = this.templates, ct = ts.cell, rt = ts.row;
33459                 // buffers
33460                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
33461                 for(var j = 0, len = rs.length; j < len; j++){
33462                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
33463                     for(var i = 0; i < colCount; i++){
33464                         c = cs[i];
33465                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
33466                         p.id = c.id;
33467                         p.css = p.attr = "";
33468                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
33469                         if(p.value == undefined || p.value === "") p.value = "&#160;";
33470                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
33471                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
33472                         }
33473                         var markup = ct.apply(p);
33474                         if(!c.locked){
33475                             cb+= markup;
33476                         }else{
33477                             lcb+= markup;
33478                         }
33479                     }
33480                     var alt = [];
33481                     if(stripe && ((rowIndex+1) % 2 == 0)){
33482                         alt[0] = "x-grid-row-alt";
33483                     }
33484                     if(r.dirty){
33485                         alt[1] = " x-grid-dirty-row";
33486                     }
33487                     rp.cells = lcb;
33488                     if(this.getRowClass){
33489                         alt[2] = this.getRowClass(r, rowIndex);
33490                     }
33491                     rp.alt = alt.join(" ");
33492                     lbuf+= rt.apply(rp);
33493                     rp.cells = cb;
33494                     buf+=  rt.apply(rp);
33495                 }
33496                 return [lbuf, buf];
33497             } :
33498             function(cs, rs, ds, startRow, colCount, stripe){
33499                 var ts = this.templates, ct = ts.cell, rt = ts.row;
33500                 // buffers
33501                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
33502                 for(var j = 0, len = rs.length; j < len; j++){
33503                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
33504                     for(var i = 0; i < colCount; i++){
33505                         c = cs[i];
33506                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
33507                         p.id = c.id;
33508                         p.css = p.attr = "";
33509                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
33510                         if(p.value == undefined || p.value === "") p.value = "&#160;";
33511                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
33512                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
33513                         }
33514                         var markup = ct.apply(p);
33515                         if(!c.locked){
33516                             cb[cb.length] = markup;
33517                         }else{
33518                             lcb[lcb.length] = markup;
33519                         }
33520                     }
33521                     var alt = [];
33522                     if(stripe && ((rowIndex+1) % 2 == 0)){
33523                         alt[0] = "x-grid-row-alt";
33524                     }
33525                     if(r.dirty){
33526                         alt[1] = " x-grid-dirty-row";
33527                     }
33528                     rp.cells = lcb;
33529                     if(this.getRowClass){
33530                         alt[2] = this.getRowClass(r, rowIndex);
33531                     }
33532                     rp.alt = alt.join(" ");
33533                     rp.cells = lcb.join("");
33534                     lbuf[lbuf.length] = rt.apply(rp);
33535                     rp.cells = cb.join("");
33536                     buf[buf.length] =  rt.apply(rp);
33537                 }
33538                 return [lbuf.join(""), buf.join("")];
33539             },
33540
33541     renderBody : function(){
33542         var markup = this.renderRows();
33543         var bt = this.templates.body;
33544         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
33545     },
33546
33547     /**
33548      * Refreshes the grid
33549      * @param {Boolean} headersToo
33550      */
33551     refresh : function(headersToo){
33552         this.fireEvent("beforerefresh", this);
33553         this.grid.stopEditing();
33554         var result = this.renderBody();
33555         this.lockedBody.update(result[0]);
33556         this.mainBody.update(result[1]);
33557         if(headersToo === true){
33558             this.updateHeaders();
33559             this.updateColumns();
33560             this.updateSplitters();
33561             this.updateHeaderSortState();
33562         }
33563         this.syncRowHeights();
33564         this.layout();
33565         this.fireEvent("refresh", this);
33566     },
33567
33568     handleColumnMove : function(cm, oldIndex, newIndex){
33569         this.indexMap = null;
33570         var s = this.getScrollState();
33571         this.refresh(true);
33572         this.restoreScroll(s);
33573         this.afterMove(newIndex);
33574     },
33575
33576     afterMove : function(colIndex){
33577         if(this.enableMoveAnim && Roo.enableFx){
33578             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
33579         }
33580     },
33581
33582     updateCell : function(dm, rowIndex, dataIndex){
33583         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
33584         if(typeof colIndex == "undefined"){ // not present in grid
33585             return;
33586         }
33587         var cm = this.grid.colModel;
33588         var cell = this.getCell(rowIndex, colIndex);
33589         var cellText = this.getCellText(rowIndex, colIndex);
33590
33591         var p = {
33592             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
33593             id : cm.getColumnId(colIndex),
33594             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
33595         };
33596         var renderer = cm.getRenderer(colIndex);
33597         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
33598         if(typeof val == "undefined" || val === "") val = "&#160;";
33599         cellText.innerHTML = val;
33600         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
33601         this.syncRowHeights(rowIndex, rowIndex);
33602     },
33603
33604     calcColumnWidth : function(colIndex, maxRowsToMeasure){
33605         var maxWidth = 0;
33606         if(this.grid.autoSizeHeaders){
33607             var h = this.getHeaderCellMeasure(colIndex);
33608             maxWidth = Math.max(maxWidth, h.scrollWidth);
33609         }
33610         var tb, index;
33611         if(this.cm.isLocked(colIndex)){
33612             tb = this.getLockedTable();
33613             index = colIndex;
33614         }else{
33615             tb = this.getBodyTable();
33616             index = colIndex - this.cm.getLockedCount();
33617         }
33618         if(tb && tb.rows){
33619             var rows = tb.rows;
33620             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
33621             for(var i = 0; i < stopIndex; i++){
33622                 var cell = rows[i].childNodes[index].firstChild;
33623                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
33624             }
33625         }
33626         return maxWidth + /*margin for error in IE*/ 5;
33627     },
33628     /**
33629      * Autofit a column to its content.
33630      * @param {Number} colIndex
33631      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
33632      */
33633      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
33634          if(this.cm.isHidden(colIndex)){
33635              return; // can't calc a hidden column
33636          }
33637         if(forceMinSize){
33638             var cid = this.cm.getColumnId(colIndex);
33639             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
33640            if(this.grid.autoSizeHeaders){
33641                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
33642            }
33643         }
33644         var newWidth = this.calcColumnWidth(colIndex);
33645         this.cm.setColumnWidth(colIndex,
33646             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
33647         if(!suppressEvent){
33648             this.grid.fireEvent("columnresize", colIndex, newWidth);
33649         }
33650     },
33651
33652     /**
33653      * Autofits all columns to their content and then expands to fit any extra space in the grid
33654      */
33655      autoSizeColumns : function(){
33656         var cm = this.grid.colModel;
33657         var colCount = cm.getColumnCount();
33658         for(var i = 0; i < colCount; i++){
33659             this.autoSizeColumn(i, true, true);
33660         }
33661         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
33662             this.fitColumns();
33663         }else{
33664             this.updateColumns();
33665             this.layout();
33666         }
33667     },
33668
33669     /**
33670      * Autofits all columns to the grid's width proportionate with their current size
33671      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
33672      */
33673     fitColumns : function(reserveScrollSpace){
33674         var cm = this.grid.colModel;
33675         var colCount = cm.getColumnCount();
33676         var cols = [];
33677         var width = 0;
33678         var i, w;
33679         for (i = 0; i < colCount; i++){
33680             if(!cm.isHidden(i) && !cm.isFixed(i)){
33681                 w = cm.getColumnWidth(i);
33682                 cols.push(i);
33683                 cols.push(w);
33684                 width += w;
33685             }
33686         }
33687         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
33688         if(reserveScrollSpace){
33689             avail -= 17;
33690         }
33691         var frac = (avail - cm.getTotalWidth())/width;
33692         while (cols.length){
33693             w = cols.pop();
33694             i = cols.pop();
33695             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
33696         }
33697         this.updateColumns();
33698         this.layout();
33699     },
33700
33701     onRowSelect : function(rowIndex){
33702         var row = this.getRowComposite(rowIndex);
33703         row.addClass("x-grid-row-selected");
33704     },
33705
33706     onRowDeselect : function(rowIndex){
33707         var row = this.getRowComposite(rowIndex);
33708         row.removeClass("x-grid-row-selected");
33709     },
33710
33711     onCellSelect : function(row, col){
33712         var cell = this.getCell(row, col);
33713         if(cell){
33714             Roo.fly(cell).addClass("x-grid-cell-selected");
33715         }
33716     },
33717
33718     onCellDeselect : function(row, col){
33719         var cell = this.getCell(row, col);
33720         if(cell){
33721             Roo.fly(cell).removeClass("x-grid-cell-selected");
33722         }
33723     },
33724
33725     updateHeaderSortState : function(){
33726         var state = this.ds.getSortState();
33727         if(!state){
33728             return;
33729         }
33730         this.sortState = state;
33731         var sortColumn = this.cm.findColumnIndex(state.field);
33732         if(sortColumn != -1){
33733             var sortDir = state.direction;
33734             var sc = this.sortClasses;
33735             var hds = this.el.select(this.headerSelector).removeClass(sc);
33736             hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
33737         }
33738     },
33739
33740     handleHeaderClick : function(g, index){
33741         if(this.headersDisabled){
33742             return;
33743         }
33744         var dm = g.dataSource, cm = g.colModel;
33745             if(!cm.isSortable(index)){
33746             return;
33747         }
33748             g.stopEditing();
33749         dm.sort(cm.getDataIndex(index));
33750     },
33751
33752
33753     destroy : function(){
33754         if(this.colMenu){
33755             this.colMenu.removeAll();
33756             Roo.menu.MenuMgr.unregister(this.colMenu);
33757             this.colMenu.getEl().remove();
33758             delete this.colMenu;
33759         }
33760         if(this.hmenu){
33761             this.hmenu.removeAll();
33762             Roo.menu.MenuMgr.unregister(this.hmenu);
33763             this.hmenu.getEl().remove();
33764             delete this.hmenu;
33765         }
33766         if(this.grid.enableColumnMove){
33767             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
33768             if(dds){
33769                 for(var dd in dds){
33770                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
33771                         var elid = dds[dd].dragElId;
33772                         dds[dd].unreg();
33773                         Roo.get(elid).remove();
33774                     } else if(dds[dd].config.isTarget){
33775                         dds[dd].proxyTop.remove();
33776                         dds[dd].proxyBottom.remove();
33777                         dds[dd].unreg();
33778                     }
33779                     if(Roo.dd.DDM.locationCache[dd]){
33780                         delete Roo.dd.DDM.locationCache[dd];
33781                     }
33782                 }
33783                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
33784             }
33785         }
33786         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
33787         this.bind(null, null);
33788         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
33789     },
33790
33791     handleLockChange : function(){
33792         this.refresh(true);
33793     },
33794
33795     onDenyColumnLock : function(){
33796
33797     },
33798
33799     onDenyColumnHide : function(){
33800
33801     },
33802
33803     handleHdMenuClick : function(item){
33804         var index = this.hdCtxIndex;
33805         var cm = this.cm, ds = this.ds;
33806         switch(item.id){
33807             case "asc":
33808                 ds.sort(cm.getDataIndex(index), "ASC");
33809                 break;
33810             case "desc":
33811                 ds.sort(cm.getDataIndex(index), "DESC");
33812                 break;
33813             case "lock":
33814                 var lc = cm.getLockedCount();
33815                 if(cm.getColumnCount(true) <= lc+1){
33816                     this.onDenyColumnLock();
33817                     return;
33818                 }
33819                 if(lc != index){
33820                     cm.setLocked(index, true, true);
33821                     cm.moveColumn(index, lc);
33822                     this.grid.fireEvent("columnmove", index, lc);
33823                 }else{
33824                     cm.setLocked(index, true);
33825                 }
33826             break;
33827             case "unlock":
33828                 var lc = cm.getLockedCount();
33829                 if((lc-1) != index){
33830                     cm.setLocked(index, false, true);
33831                     cm.moveColumn(index, lc-1);
33832                     this.grid.fireEvent("columnmove", index, lc-1);
33833                 }else{
33834                     cm.setLocked(index, false);
33835                 }
33836             break;
33837             default:
33838                 index = cm.getIndexById(item.id.substr(4));
33839                 if(index != -1){
33840                     if(item.checked && cm.getColumnCount(true) <= 1){
33841                         this.onDenyColumnHide();
33842                         return false;
33843                     }
33844                     cm.setHidden(index, item.checked);
33845                 }
33846         }
33847         return true;
33848     },
33849
33850     beforeColMenuShow : function(){
33851         var cm = this.cm,  colCount = cm.getColumnCount();
33852         this.colMenu.removeAll();
33853         for(var i = 0; i < colCount; i++){
33854             this.colMenu.add(new Roo.menu.CheckItem({
33855                 id: "col-"+cm.getColumnId(i),
33856                 text: cm.getColumnHeader(i),
33857                 checked: !cm.isHidden(i),
33858                 hideOnClick:false
33859             }));
33860         }
33861     },
33862
33863     handleHdCtx : function(g, index, e){
33864         e.stopEvent();
33865         var hd = this.getHeaderCell(index);
33866         this.hdCtxIndex = index;
33867         var ms = this.hmenu.items, cm = this.cm;
33868         ms.get("asc").setDisabled(!cm.isSortable(index));
33869         ms.get("desc").setDisabled(!cm.isSortable(index));
33870         if(this.grid.enableColLock !== false){
33871             ms.get("lock").setDisabled(cm.isLocked(index));
33872             ms.get("unlock").setDisabled(!cm.isLocked(index));
33873         }
33874         this.hmenu.show(hd, "tl-bl");
33875     },
33876
33877     handleHdOver : function(e){
33878         var hd = this.findHeaderCell(e.getTarget());
33879         if(hd && !this.headersDisabled){
33880             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
33881                this.fly(hd).addClass("x-grid-hd-over");
33882             }
33883         }
33884     },
33885
33886     handleHdOut : function(e){
33887         var hd = this.findHeaderCell(e.getTarget());
33888         if(hd){
33889             this.fly(hd).removeClass("x-grid-hd-over");
33890         }
33891     },
33892
33893     handleSplitDblClick : function(e, t){
33894         var i = this.getCellIndex(t);
33895         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
33896             this.autoSizeColumn(i, true);
33897             this.layout();
33898         }
33899     },
33900
33901     render : function(){
33902
33903         var cm = this.cm;
33904         var colCount = cm.getColumnCount();
33905
33906         if(this.grid.monitorWindowResize === true){
33907             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
33908         }
33909         var header = this.renderHeaders();
33910         var body = this.templates.body.apply({rows:""});
33911         var html = this.templates.master.apply({
33912             lockedBody: body,
33913             body: body,
33914             lockedHeader: header[0],
33915             header: header[1]
33916         });
33917
33918         //this.updateColumns();
33919
33920         this.grid.getGridEl().dom.innerHTML = html;
33921
33922         this.initElements();
33923         
33924         // a kludge to fix the random scolling effect in webkit
33925         this.el.on("scroll", function() {
33926             this.el.dom.scrollTop=0; // hopefully not recursive..
33927         },this);
33928
33929         this.scroller.on("scroll", this.handleScroll, this);
33930         this.lockedBody.on("mousewheel", this.handleWheel, this);
33931         this.mainBody.on("mousewheel", this.handleWheel, this);
33932
33933         this.mainHd.on("mouseover", this.handleHdOver, this);
33934         this.mainHd.on("mouseout", this.handleHdOut, this);
33935         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
33936                 {delegate: "."+this.splitClass});
33937
33938         this.lockedHd.on("mouseover", this.handleHdOver, this);
33939         this.lockedHd.on("mouseout", this.handleHdOut, this);
33940         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
33941                 {delegate: "."+this.splitClass});
33942
33943         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
33944             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
33945         }
33946
33947         this.updateSplitters();
33948
33949         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
33950             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
33951             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
33952         }
33953
33954         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
33955             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
33956             this.hmenu.add(
33957                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
33958                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
33959             );
33960             if(this.grid.enableColLock !== false){
33961                 this.hmenu.add('-',
33962                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
33963                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
33964                 );
33965             }
33966             if(this.grid.enableColumnHide !== false){
33967
33968                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
33969                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
33970                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
33971
33972                 this.hmenu.add('-',
33973                     {id:"columns", text: this.columnsText, menu: this.colMenu}
33974                 );
33975             }
33976             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
33977
33978             this.grid.on("headercontextmenu", this.handleHdCtx, this);
33979         }
33980
33981         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
33982             this.dd = new Roo.grid.GridDragZone(this.grid, {
33983                 ddGroup : this.grid.ddGroup || 'GridDD'
33984             });
33985         }
33986
33987         /*
33988         for(var i = 0; i < colCount; i++){
33989             if(cm.isHidden(i)){
33990                 this.hideColumn(i);
33991             }
33992             if(cm.config[i].align){
33993                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
33994                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
33995             }
33996         }*/
33997         
33998         this.updateHeaderSortState();
33999
34000         this.beforeInitialResize();
34001         this.layout(true);
34002
34003         // two part rendering gives faster view to the user
34004         this.renderPhase2.defer(1, this);
34005     },
34006
34007     renderPhase2 : function(){
34008         // render the rows now
34009         this.refresh();
34010         if(this.grid.autoSizeColumns){
34011             this.autoSizeColumns();
34012         }
34013     },
34014
34015     beforeInitialResize : function(){
34016
34017     },
34018
34019     onColumnSplitterMoved : function(i, w){
34020         this.userResized = true;
34021         var cm = this.grid.colModel;
34022         cm.setColumnWidth(i, w, true);
34023         var cid = cm.getColumnId(i);
34024         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34025         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34026         this.updateSplitters();
34027         this.layout();
34028         this.grid.fireEvent("columnresize", i, w);
34029     },
34030
34031     syncRowHeights : function(startIndex, endIndex){
34032         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
34033             startIndex = startIndex || 0;
34034             var mrows = this.getBodyTable().rows;
34035             var lrows = this.getLockedTable().rows;
34036             var len = mrows.length-1;
34037             endIndex = Math.min(endIndex || len, len);
34038             for(var i = startIndex; i <= endIndex; i++){
34039                 var m = mrows[i], l = lrows[i];
34040                 var h = Math.max(m.offsetHeight, l.offsetHeight);
34041                 m.style.height = l.style.height = h + "px";
34042             }
34043         }
34044     },
34045
34046     layout : function(initialRender, is2ndPass){
34047         var g = this.grid;
34048         var auto = g.autoHeight;
34049         var scrollOffset = 16;
34050         var c = g.getGridEl(), cm = this.cm,
34051                 expandCol = g.autoExpandColumn,
34052                 gv = this;
34053         //c.beginMeasure();
34054
34055         if(!c.dom.offsetWidth){ // display:none?
34056             if(initialRender){
34057                 this.lockedWrap.show();
34058                 this.mainWrap.show();
34059             }
34060             return;
34061         }
34062
34063         var hasLock = this.cm.isLocked(0);
34064
34065         var tbh = this.headerPanel.getHeight();
34066         var bbh = this.footerPanel.getHeight();
34067
34068         if(auto){
34069             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
34070             var newHeight = ch + c.getBorderWidth("tb");
34071             if(g.maxHeight){
34072                 newHeight = Math.min(g.maxHeight, newHeight);
34073             }
34074             c.setHeight(newHeight);
34075         }
34076
34077         if(g.autoWidth){
34078             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
34079         }
34080
34081         var s = this.scroller;
34082
34083         var csize = c.getSize(true);
34084
34085         this.el.setSize(csize.width, csize.height);
34086
34087         this.headerPanel.setWidth(csize.width);
34088         this.footerPanel.setWidth(csize.width);
34089
34090         var hdHeight = this.mainHd.getHeight();
34091         var vw = csize.width;
34092         var vh = csize.height - (tbh + bbh);
34093
34094         s.setSize(vw, vh);
34095
34096         var bt = this.getBodyTable();
34097         var ltWidth = hasLock ?
34098                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
34099
34100         var scrollHeight = bt.offsetHeight;
34101         var scrollWidth = ltWidth + bt.offsetWidth;
34102         var vscroll = false, hscroll = false;
34103
34104         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
34105
34106         var lw = this.lockedWrap, mw = this.mainWrap;
34107         var lb = this.lockedBody, mb = this.mainBody;
34108
34109         setTimeout(function(){
34110             var t = s.dom.offsetTop;
34111             var w = s.dom.clientWidth,
34112                 h = s.dom.clientHeight;
34113
34114             lw.setTop(t);
34115             lw.setSize(ltWidth, h);
34116
34117             mw.setLeftTop(ltWidth, t);
34118             mw.setSize(w-ltWidth, h);
34119
34120             lb.setHeight(h-hdHeight);
34121             mb.setHeight(h-hdHeight);
34122
34123             if(is2ndPass !== true && !gv.userResized && expandCol){
34124                 // high speed resize without full column calculation
34125                 
34126                 var ci = cm.getIndexById(expandCol);
34127                 if (ci < 0) {
34128                     ci = cm.findColumnIndex(expandCol);
34129                 }
34130                 ci = Math.max(0, ci); // make sure it's got at least the first col.
34131                 var expandId = cm.getColumnId(ci);
34132                 var  tw = cm.getTotalWidth(false);
34133                 var currentWidth = cm.getColumnWidth(ci);
34134                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
34135                 if(currentWidth != cw){
34136                     cm.setColumnWidth(ci, cw, true);
34137                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34138                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34139                     gv.updateSplitters();
34140                     gv.layout(false, true);
34141                 }
34142             }
34143
34144             if(initialRender){
34145                 lw.show();
34146                 mw.show();
34147             }
34148             //c.endMeasure();
34149         }, 10);
34150     },
34151
34152     onWindowResize : function(){
34153         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
34154             return;
34155         }
34156         this.layout();
34157     },
34158
34159     appendFooter : function(parentEl){
34160         return null;
34161     },
34162
34163     sortAscText : "Sort Ascending",
34164     sortDescText : "Sort Descending",
34165     lockText : "Lock Column",
34166     unlockText : "Unlock Column",
34167     columnsText : "Columns"
34168 });
34169
34170
34171 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
34172     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
34173     this.proxy.el.addClass('x-grid3-col-dd');
34174 };
34175
34176 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
34177     handleMouseDown : function(e){
34178
34179     },
34180
34181     callHandleMouseDown : function(e){
34182         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
34183     }
34184 });
34185 /*
34186  * Based on:
34187  * Ext JS Library 1.1.1
34188  * Copyright(c) 2006-2007, Ext JS, LLC.
34189  *
34190  * Originally Released Under LGPL - original licence link has changed is not relivant.
34191  *
34192  * Fork - LGPL
34193  * <script type="text/javascript">
34194  */
34195  
34196 // private
34197 // This is a support class used internally by the Grid components
34198 Roo.grid.SplitDragZone = function(grid, hd, hd2){
34199     this.grid = grid;
34200     this.view = grid.getView();
34201     this.proxy = this.view.resizeProxy;
34202     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
34203         "gridSplitters" + this.grid.getGridEl().id, {
34204         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
34205     });
34206     this.setHandleElId(Roo.id(hd));
34207     this.setOuterHandleElId(Roo.id(hd2));
34208     this.scroll = false;
34209 };
34210 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
34211     fly: Roo.Element.fly,
34212
34213     b4StartDrag : function(x, y){
34214         this.view.headersDisabled = true;
34215         this.proxy.setHeight(this.view.mainWrap.getHeight());
34216         var w = this.cm.getColumnWidth(this.cellIndex);
34217         var minw = Math.max(w-this.grid.minColumnWidth, 0);
34218         this.resetConstraints();
34219         this.setXConstraint(minw, 1000);
34220         this.setYConstraint(0, 0);
34221         this.minX = x - minw;
34222         this.maxX = x + 1000;
34223         this.startPos = x;
34224         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
34225     },
34226
34227
34228     handleMouseDown : function(e){
34229         ev = Roo.EventObject.setEvent(e);
34230         var t = this.fly(ev.getTarget());
34231         if(t.hasClass("x-grid-split")){
34232             this.cellIndex = this.view.getCellIndex(t.dom);
34233             this.split = t.dom;
34234             this.cm = this.grid.colModel;
34235             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
34236                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
34237             }
34238         }
34239     },
34240
34241     endDrag : function(e){
34242         this.view.headersDisabled = false;
34243         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
34244         var diff = endX - this.startPos;
34245         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
34246     },
34247
34248     autoOffset : function(){
34249         this.setDelta(0,0);
34250     }
34251 });/*
34252  * Based on:
34253  * Ext JS Library 1.1.1
34254  * Copyright(c) 2006-2007, Ext JS, LLC.
34255  *
34256  * Originally Released Under LGPL - original licence link has changed is not relivant.
34257  *
34258  * Fork - LGPL
34259  * <script type="text/javascript">
34260  */
34261  
34262 // private
34263 // This is a support class used internally by the Grid components
34264 Roo.grid.GridDragZone = function(grid, config){
34265     this.view = grid.getView();
34266     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
34267     if(this.view.lockedBody){
34268         this.setHandleElId(Roo.id(this.view.mainBody.dom));
34269         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
34270     }
34271     this.scroll = false;
34272     this.grid = grid;
34273     this.ddel = document.createElement('div');
34274     this.ddel.className = 'x-grid-dd-wrap';
34275 };
34276
34277 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
34278     ddGroup : "GridDD",
34279
34280     getDragData : function(e){
34281         var t = Roo.lib.Event.getTarget(e);
34282         var rowIndex = this.view.findRowIndex(t);
34283         if(rowIndex !== false){
34284             var sm = this.grid.selModel;
34285             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
34286               //  sm.mouseDown(e, t);
34287             //}
34288             if (e.hasModifier()){
34289                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
34290             }
34291             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
34292         }
34293         return false;
34294     },
34295
34296     onInitDrag : function(e){
34297         var data = this.dragData;
34298         this.ddel.innerHTML = this.grid.getDragDropText();
34299         this.proxy.update(this.ddel);
34300         // fire start drag?
34301     },
34302
34303     afterRepair : function(){
34304         this.dragging = false;
34305     },
34306
34307     getRepairXY : function(e, data){
34308         return false;
34309     },
34310
34311     onEndDrag : function(data, e){
34312         // fire end drag?
34313     },
34314
34315     onValidDrop : function(dd, e, id){
34316         // fire drag drop?
34317         this.hideProxy();
34318     },
34319
34320     beforeInvalidDrop : function(e, id){
34321
34322     }
34323 });