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     if (config.listeners || config.events) { 
76         Roo.BasicLayoutRegion.superclass.constructor.call(this,  { 
77             listeners : config.listeners || {}, 
78             events : config.events || {} 
79         });    
80     }
81 };
82
83 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
84
85     /**
86      * The id of the element associated with this object.  This is what we
87      * refer to as the "linked element" because the size and position of
88      * this element is used to determine when the drag and drop objects have
89      * interacted.
90      * @property id
91      * @type String
92      */
93     id: null,
94
95     /**
96      * Configuration attributes passed into the constructor
97      * @property config
98      * @type object
99      */
100     config: null,
101
102     /**
103      * The id of the element that will be dragged.  By default this is same
104      * as the linked element , but could be changed to another element. Ex:
105      * Roo.dd.DDProxy
106      * @property dragElId
107      * @type String
108      * @private
109      */
110     dragElId: null,
111
112     /**
113      * the id of the element that initiates the drag operation.  By default
114      * this is the linked element, but could be changed to be a child of this
115      * element.  This lets us do things like only starting the drag when the
116      * header element within the linked html element is clicked.
117      * @property handleElId
118      * @type String
119      * @private
120      */
121     handleElId: null,
122
123     /**
124      * An associative array of HTML tags that will be ignored if clicked.
125      * @property invalidHandleTypes
126      * @type {string: string}
127      */
128     invalidHandleTypes: null,
129
130     /**
131      * An associative array of ids for elements that will be ignored if clicked
132      * @property invalidHandleIds
133      * @type {string: string}
134      */
135     invalidHandleIds: null,
136
137     /**
138      * An indexted array of css class names for elements that will be ignored
139      * if clicked.
140      * @property invalidHandleClasses
141      * @type string[]
142      */
143     invalidHandleClasses: null,
144
145     /**
146      * The linked element's absolute X position at the time the drag was
147      * started
148      * @property startPageX
149      * @type int
150      * @private
151      */
152     startPageX: 0,
153
154     /**
155      * The linked element's absolute X position at the time the drag was
156      * started
157      * @property startPageY
158      * @type int
159      * @private
160      */
161     startPageY: 0,
162
163     /**
164      * The group defines a logical collection of DragDrop objects that are
165      * related.  Instances only get events when interacting with other
166      * DragDrop object in the same group.  This lets us define multiple
167      * groups using a single DragDrop subclass if we want.
168      * @property groups
169      * @type {string: string}
170      */
171     groups: null,
172
173     /**
174      * Individual drag/drop instances can be locked.  This will prevent
175      * onmousedown start drag.
176      * @property locked
177      * @type boolean
178      * @private
179      */
180     locked: false,
181
182     /**
183      * Lock this instance
184      * @method lock
185      */
186     lock: function() { this.locked = true; },
187
188     /**
189      * Unlock this instace
190      * @method unlock
191      */
192     unlock: function() { this.locked = false; },
193
194     /**
195      * By default, all insances can be a drop target.  This can be disabled by
196      * setting isTarget to false.
197      * @method isTarget
198      * @type boolean
199      */
200     isTarget: true,
201
202     /**
203      * The padding configured for this drag and drop object for calculating
204      * the drop zone intersection with this object.
205      * @method padding
206      * @type int[]
207      */
208     padding: null,
209
210     /**
211      * Cached reference to the linked element
212      * @property _domRef
213      * @private
214      */
215     _domRef: null,
216
217     /**
218      * Internal typeof flag
219      * @property __ygDragDrop
220      * @private
221      */
222     __ygDragDrop: true,
223
224     /**
225      * Set to true when horizontal contraints are applied
226      * @property constrainX
227      * @type boolean
228      * @private
229      */
230     constrainX: false,
231
232     /**
233      * Set to true when vertical contraints are applied
234      * @property constrainY
235      * @type boolean
236      * @private
237      */
238     constrainY: false,
239
240     /**
241      * The left constraint
242      * @property minX
243      * @type int
244      * @private
245      */
246     minX: 0,
247
248     /**
249      * The right constraint
250      * @property maxX
251      * @type int
252      * @private
253      */
254     maxX: 0,
255
256     /**
257      * The up constraint
258      * @property minY
259      * @type int
260      * @type int
261      * @private
262      */
263     minY: 0,
264
265     /**
266      * The down constraint
267      * @property maxY
268      * @type int
269      * @private
270      */
271     maxY: 0,
272
273     /**
274      * Maintain offsets when we resetconstraints.  Set to true when you want
275      * the position of the element relative to its parent to stay the same
276      * when the page changes
277      *
278      * @property maintainOffset
279      * @type boolean
280      */
281     maintainOffset: false,
282
283     /**
284      * Array of pixel locations the element will snap to if we specified a
285      * horizontal graduation/interval.  This array is generated automatically
286      * when you define a tick interval.
287      * @property xTicks
288      * @type int[]
289      */
290     xTicks: null,
291
292     /**
293      * Array of pixel locations the element will snap to if we specified a
294      * vertical graduation/interval.  This array is generated automatically
295      * when you define a tick interval.
296      * @property yTicks
297      * @type int[]
298      */
299     yTicks: null,
300
301     /**
302      * By default the drag and drop instance will only respond to the primary
303      * button click (left button for a right-handed mouse).  Set to true to
304      * allow drag and drop to start with any mouse click that is propogated
305      * by the browser
306      * @property primaryButtonOnly
307      * @type boolean
308      */
309     primaryButtonOnly: true,
310
311     /**
312      * The availabe property is false until the linked dom element is accessible.
313      * @property available
314      * @type boolean
315      */
316     available: false,
317
318     /**
319      * By default, drags can only be initiated if the mousedown occurs in the
320      * region the linked element is.  This is done in part to work around a
321      * bug in some browsers that mis-report the mousedown if the previous
322      * mouseup happened outside of the window.  This property is set to true
323      * if outer handles are defined.
324      *
325      * @property hasOuterHandles
326      * @type boolean
327      * @default false
328      */
329     hasOuterHandles: false,
330
331     /**
332      * Code that executes immediately before the startDrag event
333      * @method b4StartDrag
334      * @private
335      */
336     b4StartDrag: function(x, y) { },
337
338     /**
339      * Abstract method called after a drag/drop object is clicked
340      * and the drag or mousedown time thresholds have beeen met.
341      * @method startDrag
342      * @param {int} X click location
343      * @param {int} Y click location
344      */
345     startDrag: function(x, y) { /* override this */ },
346
347     /**
348      * Code that executes immediately before the onDrag event
349      * @method b4Drag
350      * @private
351      */
352     b4Drag: function(e) { },
353
354     /**
355      * Abstract method called during the onMouseMove event while dragging an
356      * object.
357      * @method onDrag
358      * @param {Event} e the mousemove event
359      */
360     onDrag: function(e) { /* override this */ },
361
362     /**
363      * Abstract method called when this element fist begins hovering over
364      * another DragDrop obj
365      * @method onDragEnter
366      * @param {Event} e the mousemove event
367      * @param {String|DragDrop[]} id In POINT mode, the element
368      * id this is hovering over.  In INTERSECT mode, an array of one or more
369      * dragdrop items being hovered over.
370      */
371     onDragEnter: function(e, id) { /* override this */ },
372
373     /**
374      * Code that executes immediately before the onDragOver event
375      * @method b4DragOver
376      * @private
377      */
378     b4DragOver: function(e) { },
379
380     /**
381      * Abstract method called when this element is hovering over another
382      * DragDrop obj
383      * @method onDragOver
384      * @param {Event} e the mousemove event
385      * @param {String|DragDrop[]} id In POINT mode, the element
386      * id this is hovering over.  In INTERSECT mode, an array of dd items
387      * being hovered over.
388      */
389     onDragOver: function(e, id) { /* override this */ },
390
391     /**
392      * Code that executes immediately before the onDragOut event
393      * @method b4DragOut
394      * @private
395      */
396     b4DragOut: function(e) { },
397
398     /**
399      * Abstract method called when we are no longer hovering over an element
400      * @method onDragOut
401      * @param {Event} e the mousemove event
402      * @param {String|DragDrop[]} id In POINT mode, the element
403      * id this was hovering over.  In INTERSECT mode, an array of dd items
404      * that the mouse is no longer over.
405      */
406     onDragOut: function(e, id) { /* override this */ },
407
408     /**
409      * Code that executes immediately before the onDragDrop event
410      * @method b4DragDrop
411      * @private
412      */
413     b4DragDrop: function(e) { },
414
415     /**
416      * Abstract method called when this item is dropped on another DragDrop
417      * obj
418      * @method onDragDrop
419      * @param {Event} e the mouseup event
420      * @param {String|DragDrop[]} id In POINT mode, the element
421      * id this was dropped on.  In INTERSECT mode, an array of dd items this
422      * was dropped on.
423      */
424     onDragDrop: function(e, id) { /* override this */ },
425
426     /**
427      * Abstract method called when this item is dropped on an area with no
428      * drop target
429      * @method onInvalidDrop
430      * @param {Event} e the mouseup event
431      */
432     onInvalidDrop: function(e) { /* override this */ },
433
434     /**
435      * Code that executes immediately before the endDrag event
436      * @method b4EndDrag
437      * @private
438      */
439     b4EndDrag: function(e) { },
440
441     /**
442      * Fired when we are done dragging the object
443      * @method endDrag
444      * @param {Event} e the mouseup event
445      */
446     endDrag: function(e) { /* override this */ },
447
448     /**
449      * Code executed immediately before the onMouseDown event
450      * @method b4MouseDown
451      * @param {Event} e the mousedown event
452      * @private
453      */
454     b4MouseDown: function(e) {  },
455
456     /**
457      * Event handler that fires when a drag/drop obj gets a mousedown
458      * @method onMouseDown
459      * @param {Event} e the mousedown event
460      */
461     onMouseDown: function(e) { /* override this */ },
462
463     /**
464      * Event handler that fires when a drag/drop obj gets a mouseup
465      * @method onMouseUp
466      * @param {Event} e the mouseup event
467      */
468     onMouseUp: function(e) { /* override this */ },
469
470     /**
471      * Override the onAvailable method to do what is needed after the initial
472      * position was determined.
473      * @method onAvailable
474      */
475     onAvailable: function () {
476     },
477
478     /*
479      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
480      * @type Object
481      */
482     defaultPadding : {left:0, right:0, top:0, bottom:0},
483
484     /*
485      * Initializes the drag drop object's constraints to restrict movement to a certain element.
486  *
487  * Usage:
488  <pre><code>
489  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
490                 { dragElId: "existingProxyDiv" });
491  dd.startDrag = function(){
492      this.constrainTo("parent-id");
493  };
494  </code></pre>
495  * Or you can initalize it using the {@link Roo.Element} object:
496  <pre><code>
497  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
498      startDrag : function(){
499          this.constrainTo("parent-id");
500      }
501  });
502  </code></pre>
503      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
504      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
505      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
506      * an object containing the sides to pad. For example: {right:10, bottom:10}
507      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
508      */
509     constrainTo : function(constrainTo, pad, inContent){
510         if(typeof pad == "number"){
511             pad = {left: pad, right:pad, top:pad, bottom:pad};
512         }
513         pad = pad || this.defaultPadding;
514         var b = Roo.get(this.getEl()).getBox();
515         var ce = Roo.get(constrainTo);
516         var s = ce.getScroll();
517         var c, cd = ce.dom;
518         if(cd == document.body){
519             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
520         }else{
521             xy = ce.getXY();
522             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
523         }
524
525
526         var topSpace = b.y - c.y;
527         var leftSpace = b.x - c.x;
528
529         this.resetConstraints();
530         this.setXConstraint(leftSpace - (pad.left||0), // left
531                 c.width - leftSpace - b.width - (pad.right||0) //right
532         );
533         this.setYConstraint(topSpace - (pad.top||0), //top
534                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
535         );
536     },
537
538     /**
539      * Returns a reference to the linked element
540      * @method getEl
541      * @return {HTMLElement} the html element
542      */
543     getEl: function() {
544         if (!this._domRef) {
545             this._domRef = Roo.getDom(this.id);
546         }
547
548         return this._domRef;
549     },
550
551     /**
552      * Returns a reference to the actual element to drag.  By default this is
553      * the same as the html element, but it can be assigned to another
554      * element. An example of this can be found in Roo.dd.DDProxy
555      * @method getDragEl
556      * @return {HTMLElement} the html element
557      */
558     getDragEl: function() {
559         return Roo.getDom(this.dragElId);
560     },
561
562     /**
563      * Sets up the DragDrop object.  Must be called in the constructor of any
564      * Roo.dd.DragDrop subclass
565      * @method init
566      * @param id the id of the linked element
567      * @param {String} sGroup the group of related items
568      * @param {object} config configuration attributes
569      */
570     init: function(id, sGroup, config) {
571         this.initTarget(id, sGroup, config);
572         Event.on(this.id, "mousedown", this.handleMouseDown, this);
573         // Event.on(this.id, "selectstart", Event.preventDefault);
574     },
575
576     /**
577      * Initializes Targeting functionality only... the object does not
578      * get a mousedown handler.
579      * @method initTarget
580      * @param id the id of the linked element
581      * @param {String} sGroup the group of related items
582      * @param {object} config configuration attributes
583      */
584     initTarget: function(id, sGroup, config) {
585
586         // configuration attributes
587         this.config = config || {};
588
589         // create a local reference to the drag and drop manager
590         this.DDM = Roo.dd.DDM;
591         // initialize the groups array
592         this.groups = {};
593
594         // assume that we have an element reference instead of an id if the
595         // parameter is not a string
596         if (typeof id !== "string") {
597             id = Roo.id(id);
598         }
599
600         // set the id
601         this.id = id;
602
603         // add to an interaction group
604         this.addToGroup((sGroup) ? sGroup : "default");
605
606         // We don't want to register this as the handle with the manager
607         // so we just set the id rather than calling the setter.
608         this.handleElId = id;
609
610         // the linked element is the element that gets dragged by default
611         this.setDragElId(id);
612
613         // by default, clicked anchors will not start drag operations.
614         this.invalidHandleTypes = { A: "A" };
615         this.invalidHandleIds = {};
616         this.invalidHandleClasses = [];
617
618         this.applyConfig();
619
620         this.handleOnAvailable();
621     },
622
623     /**
624      * Applies the configuration parameters that were passed into the constructor.
625      * This is supposed to happen at each level through the inheritance chain.  So
626      * a DDProxy implentation will execute apply config on DDProxy, DD, and
627      * DragDrop in order to get all of the parameters that are available in
628      * each object.
629      * @method applyConfig
630      */
631     applyConfig: function() {
632
633         // configurable properties:
634         //    padding, isTarget, maintainOffset, primaryButtonOnly
635         this.padding           = this.config.padding || [0, 0, 0, 0];
636         this.isTarget          = (this.config.isTarget !== false);
637         this.maintainOffset    = (this.config.maintainOffset);
638         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
639
640     },
641
642     /**
643      * Executed when the linked element is available
644      * @method handleOnAvailable
645      * @private
646      */
647     handleOnAvailable: function() {
648         this.available = true;
649         this.resetConstraints();
650         this.onAvailable();
651     },
652
653      /**
654      * Configures the padding for the target zone in px.  Effectively expands
655      * (or reduces) the virtual object size for targeting calculations.
656      * Supports css-style shorthand; if only one parameter is passed, all sides
657      * will have that padding, and if only two are passed, the top and bottom
658      * will have the first param, the left and right the second.
659      * @method setPadding
660      * @param {int} iTop    Top pad
661      * @param {int} iRight  Right pad
662      * @param {int} iBot    Bot pad
663      * @param {int} iLeft   Left pad
664      */
665     setPadding: function(iTop, iRight, iBot, iLeft) {
666         // this.padding = [iLeft, iRight, iTop, iBot];
667         if (!iRight && 0 !== iRight) {
668             this.padding = [iTop, iTop, iTop, iTop];
669         } else if (!iBot && 0 !== iBot) {
670             this.padding = [iTop, iRight, iTop, iRight];
671         } else {
672             this.padding = [iTop, iRight, iBot, iLeft];
673         }
674     },
675
676     /**
677      * Stores the initial placement of the linked element.
678      * @method setInitialPosition
679      * @param {int} diffX   the X offset, default 0
680      * @param {int} diffY   the Y offset, default 0
681      */
682     setInitPosition: function(diffX, diffY) {
683         var el = this.getEl();
684
685         if (!this.DDM.verifyEl(el)) {
686             return;
687         }
688
689         var dx = diffX || 0;
690         var dy = diffY || 0;
691
692         var p = Dom.getXY( el );
693
694         this.initPageX = p[0] - dx;
695         this.initPageY = p[1] - dy;
696
697         this.lastPageX = p[0];
698         this.lastPageY = p[1];
699
700
701         this.setStartPosition(p);
702     },
703
704     /**
705      * Sets the start position of the element.  This is set when the obj
706      * is initialized, the reset when a drag is started.
707      * @method setStartPosition
708      * @param pos current position (from previous lookup)
709      * @private
710      */
711     setStartPosition: function(pos) {
712         var p = pos || Dom.getXY( this.getEl() );
713         this.deltaSetXY = null;
714
715         this.startPageX = p[0];
716         this.startPageY = p[1];
717     },
718
719     /**
720      * Add this instance to a group of related drag/drop objects.  All
721      * instances belong to at least one group, and can belong to as many
722      * groups as needed.
723      * @method addToGroup
724      * @param sGroup {string} the name of the group
725      */
726     addToGroup: function(sGroup) {
727         this.groups[sGroup] = true;
728         this.DDM.regDragDrop(this, sGroup);
729     },
730
731     /**
732      * Remove's this instance from the supplied interaction group
733      * @method removeFromGroup
734      * @param {string}  sGroup  The group to drop
735      */
736     removeFromGroup: function(sGroup) {
737         if (this.groups[sGroup]) {
738             delete this.groups[sGroup];
739         }
740
741         this.DDM.removeDDFromGroup(this, sGroup);
742     },
743
744     /**
745      * Allows you to specify that an element other than the linked element
746      * will be moved with the cursor during a drag
747      * @method setDragElId
748      * @param id {string} the id of the element that will be used to initiate the drag
749      */
750     setDragElId: function(id) {
751         this.dragElId = id;
752     },
753
754     /**
755      * Allows you to specify a child of the linked element that should be
756      * used to initiate the drag operation.  An example of this would be if
757      * you have a content div with text and links.  Clicking anywhere in the
758      * content area would normally start the drag operation.  Use this method
759      * to specify that an element inside of the content div is the element
760      * that starts the drag operation.
761      * @method setHandleElId
762      * @param id {string} the id of the element that will be used to
763      * initiate the drag.
764      */
765     setHandleElId: function(id) {
766         if (typeof id !== "string") {
767             id = Roo.id(id);
768         }
769         this.handleElId = id;
770         this.DDM.regHandle(this.id, id);
771     },
772
773     /**
774      * Allows you to set an element outside of the linked element as a drag
775      * handle
776      * @method setOuterHandleElId
777      * @param id the id of the element that will be used to initiate the drag
778      */
779     setOuterHandleElId: function(id) {
780         if (typeof id !== "string") {
781             id = Roo.id(id);
782         }
783         Event.on(id, "mousedown",
784                 this.handleMouseDown, this);
785         this.setHandleElId(id);
786
787         this.hasOuterHandles = true;
788     },
789
790     /**
791      * Remove all drag and drop hooks for this element
792      * @method unreg
793      */
794     unreg: function() {
795         Event.un(this.id, "mousedown",
796                 this.handleMouseDown);
797         this._domRef = null;
798         this.DDM._remove(this);
799     },
800
801     destroy : function(){
802         this.unreg();
803     },
804
805     /**
806      * Returns true if this instance is locked, or the drag drop mgr is locked
807      * (meaning that all drag/drop is disabled on the page.)
808      * @method isLocked
809      * @return {boolean} true if this obj or all drag/drop is locked, else
810      * false
811      */
812     isLocked: function() {
813         return (this.DDM.isLocked() || this.locked);
814     },
815
816     /**
817      * Fired when this object is clicked
818      * @method handleMouseDown
819      * @param {Event} e
820      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
821      * @private
822      */
823     handleMouseDown: function(e, oDD){
824         if (this.primaryButtonOnly && e.button != 0) {
825             return;
826         }
827
828         if (this.isLocked()) {
829             return;
830         }
831
832         this.DDM.refreshCache(this.groups);
833
834         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
835         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
836         } else {
837             if (this.clickValidator(e)) {
838
839                 // set the initial element position
840                 this.setStartPosition();
841
842
843                 this.b4MouseDown(e);
844                 this.onMouseDown(e);
845
846                 this.DDM.handleMouseDown(e, this);
847
848                 this.DDM.stopEvent(e);
849             } else {
850
851
852             }
853         }
854     },
855
856     clickValidator: function(e) {
857         var target = e.getTarget();
858         return ( this.isValidHandleChild(target) &&
859                     (this.id == this.handleElId ||
860                         this.DDM.handleWasClicked(target, this.id)) );
861     },
862
863     /**
864      * Allows you to specify a tag name that should not start a drag operation
865      * when clicked.  This is designed to facilitate embedding links within a
866      * drag handle that do something other than start the drag.
867      * @method addInvalidHandleType
868      * @param {string} tagName the type of element to exclude
869      */
870     addInvalidHandleType: function(tagName) {
871         var type = tagName.toUpperCase();
872         this.invalidHandleTypes[type] = type;
873     },
874
875     /**
876      * Lets you to specify an element id for a child of a drag handle
877      * that should not initiate a drag
878      * @method addInvalidHandleId
879      * @param {string} id the element id of the element you wish to ignore
880      */
881     addInvalidHandleId: function(id) {
882         if (typeof id !== "string") {
883             id = Roo.id(id);
884         }
885         this.invalidHandleIds[id] = id;
886     },
887
888     /**
889      * Lets you specify a css class of elements that will not initiate a drag
890      * @method addInvalidHandleClass
891      * @param {string} cssClass the class of the elements you wish to ignore
892      */
893     addInvalidHandleClass: function(cssClass) {
894         this.invalidHandleClasses.push(cssClass);
895     },
896
897     /**
898      * Unsets an excluded tag name set by addInvalidHandleType
899      * @method removeInvalidHandleType
900      * @param {string} tagName the type of element to unexclude
901      */
902     removeInvalidHandleType: function(tagName) {
903         var type = tagName.toUpperCase();
904         // this.invalidHandleTypes[type] = null;
905         delete this.invalidHandleTypes[type];
906     },
907
908     /**
909      * Unsets an invalid handle id
910      * @method removeInvalidHandleId
911      * @param {string} id the id of the element to re-enable
912      */
913     removeInvalidHandleId: function(id) {
914         if (typeof id !== "string") {
915             id = Roo.id(id);
916         }
917         delete this.invalidHandleIds[id];
918     },
919
920     /**
921      * Unsets an invalid css class
922      * @method removeInvalidHandleClass
923      * @param {string} cssClass the class of the element(s) you wish to
924      * re-enable
925      */
926     removeInvalidHandleClass: function(cssClass) {
927         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
928             if (this.invalidHandleClasses[i] == cssClass) {
929                 delete this.invalidHandleClasses[i];
930             }
931         }
932     },
933
934     /**
935      * Checks the tag exclusion list to see if this click should be ignored
936      * @method isValidHandleChild
937      * @param {HTMLElement} node the HTMLElement to evaluate
938      * @return {boolean} true if this is a valid tag type, false if not
939      */
940     isValidHandleChild: function(node) {
941
942         var valid = true;
943         // var n = (node.nodeName == "#text") ? node.parentNode : node;
944         var nodeName;
945         try {
946             nodeName = node.nodeName.toUpperCase();
947         } catch(e) {
948             nodeName = node.nodeName;
949         }
950         valid = valid && !this.invalidHandleTypes[nodeName];
951         valid = valid && !this.invalidHandleIds[node.id];
952
953         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
954             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
955         }
956
957
958         return valid;
959
960     },
961
962     /**
963      * Create the array of horizontal tick marks if an interval was specified
964      * in setXConstraint().
965      * @method setXTicks
966      * @private
967      */
968     setXTicks: function(iStartX, iTickSize) {
969         this.xTicks = [];
970         this.xTickSize = iTickSize;
971
972         var tickMap = {};
973
974         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
975             if (!tickMap[i]) {
976                 this.xTicks[this.xTicks.length] = i;
977                 tickMap[i] = true;
978             }
979         }
980
981         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
982             if (!tickMap[i]) {
983                 this.xTicks[this.xTicks.length] = i;
984                 tickMap[i] = true;
985             }
986         }
987
988         this.xTicks.sort(this.DDM.numericSort) ;
989     },
990
991     /**
992      * Create the array of vertical tick marks if an interval was specified in
993      * setYConstraint().
994      * @method setYTicks
995      * @private
996      */
997     setYTicks: function(iStartY, iTickSize) {
998         this.yTicks = [];
999         this.yTickSize = iTickSize;
1000
1001         var tickMap = {};
1002
1003         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
1004             if (!tickMap[i]) {
1005                 this.yTicks[this.yTicks.length] = i;
1006                 tickMap[i] = true;
1007             }
1008         }
1009
1010         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
1011             if (!tickMap[i]) {
1012                 this.yTicks[this.yTicks.length] = i;
1013                 tickMap[i] = true;
1014             }
1015         }
1016
1017         this.yTicks.sort(this.DDM.numericSort) ;
1018     },
1019
1020     /**
1021      * By default, the element can be dragged any place on the screen.  Use
1022      * this method to limit the horizontal travel of the element.  Pass in
1023      * 0,0 for the parameters if you want to lock the drag to the y axis.
1024      * @method setXConstraint
1025      * @param {int} iLeft the number of pixels the element can move to the left
1026      * @param {int} iRight the number of pixels the element can move to the
1027      * right
1028      * @param {int} iTickSize optional parameter for specifying that the
1029      * element
1030      * should move iTickSize pixels at a time.
1031      */
1032     setXConstraint: function(iLeft, iRight, iTickSize) {
1033         this.leftConstraint = iLeft;
1034         this.rightConstraint = iRight;
1035
1036         this.minX = this.initPageX - iLeft;
1037         this.maxX = this.initPageX + iRight;
1038         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
1039
1040         this.constrainX = true;
1041     },
1042
1043     /**
1044      * Clears any constraints applied to this instance.  Also clears ticks
1045      * since they can't exist independent of a constraint at this time.
1046      * @method clearConstraints
1047      */
1048     clearConstraints: function() {
1049         this.constrainX = false;
1050         this.constrainY = false;
1051         this.clearTicks();
1052     },
1053
1054     /**
1055      * Clears any tick interval defined for this instance
1056      * @method clearTicks
1057      */
1058     clearTicks: function() {
1059         this.xTicks = null;
1060         this.yTicks = null;
1061         this.xTickSize = 0;
1062         this.yTickSize = 0;
1063     },
1064
1065     /**
1066      * By default, the element can be dragged any place on the screen.  Set
1067      * this to limit the vertical travel of the element.  Pass in 0,0 for the
1068      * parameters if you want to lock the drag to the x axis.
1069      * @method setYConstraint
1070      * @param {int} iUp the number of pixels the element can move up
1071      * @param {int} iDown the number of pixels the element can move down
1072      * @param {int} iTickSize optional parameter for specifying that the
1073      * element should move iTickSize pixels at a time.
1074      */
1075     setYConstraint: function(iUp, iDown, iTickSize) {
1076         this.topConstraint = iUp;
1077         this.bottomConstraint = iDown;
1078
1079         this.minY = this.initPageY - iUp;
1080         this.maxY = this.initPageY + iDown;
1081         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
1082
1083         this.constrainY = true;
1084
1085     },
1086
1087     /**
1088      * resetConstraints must be called if you manually reposition a dd element.
1089      * @method resetConstraints
1090      * @param {boolean} maintainOffset
1091      */
1092     resetConstraints: function() {
1093
1094
1095         // Maintain offsets if necessary
1096         if (this.initPageX || this.initPageX === 0) {
1097             // figure out how much this thing has moved
1098             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
1099             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
1100
1101             this.setInitPosition(dx, dy);
1102
1103         // This is the first time we have detected the element's position
1104         } else {
1105             this.setInitPosition();
1106         }
1107
1108         if (this.constrainX) {
1109             this.setXConstraint( this.leftConstraint,
1110                                  this.rightConstraint,
1111                                  this.xTickSize        );
1112         }
1113
1114         if (this.constrainY) {
1115             this.setYConstraint( this.topConstraint,
1116                                  this.bottomConstraint,
1117                                  this.yTickSize         );
1118         }
1119     },
1120
1121     /**
1122      * Normally the drag element is moved pixel by pixel, but we can specify
1123      * that it move a number of pixels at a time.  This method resolves the
1124      * location when we have it set up like this.
1125      * @method getTick
1126      * @param {int} val where we want to place the object
1127      * @param {int[]} tickArray sorted array of valid points
1128      * @return {int} the closest tick
1129      * @private
1130      */
1131     getTick: function(val, tickArray) {
1132
1133         if (!tickArray) {
1134             // If tick interval is not defined, it is effectively 1 pixel,
1135             // so we return the value passed to us.
1136             return val;
1137         } else if (tickArray[0] >= val) {
1138             // The value is lower than the first tick, so we return the first
1139             // tick.
1140             return tickArray[0];
1141         } else {
1142             for (var i=0, len=tickArray.length; i<len; ++i) {
1143                 var next = i + 1;
1144                 if (tickArray[next] && tickArray[next] >= val) {
1145                     var diff1 = val - tickArray[i];
1146                     var diff2 = tickArray[next] - val;
1147                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
1148                 }
1149             }
1150
1151             // The value is larger than the last tick, so we return the last
1152             // tick.
1153             return tickArray[tickArray.length - 1];
1154         }
1155     },
1156
1157     /**
1158      * toString method
1159      * @method toString
1160      * @return {string} string representation of the dd obj
1161      */
1162     toString: function() {
1163         return ("DragDrop " + this.id);
1164     }
1165
1166 });
1167
1168 })();
1169 /*
1170  * Based on:
1171  * Ext JS Library 1.1.1
1172  * Copyright(c) 2006-2007, Ext JS, LLC.
1173  *
1174  * Originally Released Under LGPL - original licence link has changed is not relivant.
1175  *
1176  * Fork - LGPL
1177  * <script type="text/javascript">
1178  */
1179
1180
1181 /**
1182  * The drag and drop utility provides a framework for building drag and drop
1183  * applications.  In addition to enabling drag and drop for specific elements,
1184  * the drag and drop elements are tracked by the manager class, and the
1185  * interactions between the various elements are tracked during the drag and
1186  * the implementing code is notified about these important moments.
1187  */
1188
1189 // Only load the library once.  Rewriting the manager class would orphan
1190 // existing drag and drop instances.
1191 if (!Roo.dd.DragDropMgr) {
1192
1193 /**
1194  * @class Roo.dd.DragDropMgr
1195  * DragDropMgr is a singleton that tracks the element interaction for
1196  * all DragDrop items in the window.  Generally, you will not call
1197  * this class directly, but it does have helper methods that could
1198  * be useful in your DragDrop implementations.
1199  * @singleton
1200  */
1201 Roo.dd.DragDropMgr = function() {
1202
1203     var Event = Roo.EventManager;
1204
1205     return {
1206
1207         /**
1208          * Two dimensional Array of registered DragDrop objects.  The first
1209          * dimension is the DragDrop item group, the second the DragDrop
1210          * object.
1211          * @property ids
1212          * @type {string: string}
1213          * @private
1214          * @static
1215          */
1216         ids: {},
1217
1218         /**
1219          * Array of element ids defined as drag handles.  Used to determine
1220          * if the element that generated the mousedown event is actually the
1221          * handle and not the html element itself.
1222          * @property handleIds
1223          * @type {string: string}
1224          * @private
1225          * @static
1226          */
1227         handleIds: {},
1228
1229         /**
1230          * the DragDrop object that is currently being dragged
1231          * @property dragCurrent
1232          * @type DragDrop
1233          * @private
1234          * @static
1235          **/
1236         dragCurrent: null,
1237
1238         /**
1239          * the DragDrop object(s) that are being hovered over
1240          * @property dragOvers
1241          * @type Array
1242          * @private
1243          * @static
1244          */
1245         dragOvers: {},
1246
1247         /**
1248          * the X distance between the cursor and the object being dragged
1249          * @property deltaX
1250          * @type int
1251          * @private
1252          * @static
1253          */
1254         deltaX: 0,
1255
1256         /**
1257          * the Y distance between the cursor and the object being dragged
1258          * @property deltaY
1259          * @type int
1260          * @private
1261          * @static
1262          */
1263         deltaY: 0,
1264
1265         /**
1266          * Flag to determine if we should prevent the default behavior of the
1267          * events we define. By default this is true, but this can be set to
1268          * false if you need the default behavior (not recommended)
1269          * @property preventDefault
1270          * @type boolean
1271          * @static
1272          */
1273         preventDefault: true,
1274
1275         /**
1276          * Flag to determine if we should stop the propagation of the events
1277          * we generate. This is true by default but you may want to set it to
1278          * false if the html element contains other features that require the
1279          * mouse click.
1280          * @property stopPropagation
1281          * @type boolean
1282          * @static
1283          */
1284         stopPropagation: true,
1285
1286         /**
1287          * Internal flag that is set to true when drag and drop has been
1288          * intialized
1289          * @property initialized
1290          * @private
1291          * @static
1292          */
1293         initalized: false,
1294
1295         /**
1296          * All drag and drop can be disabled.
1297          * @property locked
1298          * @private
1299          * @static
1300          */
1301         locked: false,
1302
1303         /**
1304          * Called the first time an element is registered.
1305          * @method init
1306          * @private
1307          * @static
1308          */
1309         init: function() {
1310             this.initialized = true;
1311         },
1312
1313         /**
1314          * In point mode, drag and drop interaction is defined by the
1315          * location of the cursor during the drag/drop
1316          * @property POINT
1317          * @type int
1318          * @static
1319          */
1320         POINT: 0,
1321
1322         /**
1323          * In intersect mode, drag and drop interactio nis defined by the
1324          * overlap of two or more drag and drop objects.
1325          * @property INTERSECT
1326          * @type int
1327          * @static
1328          */
1329         INTERSECT: 1,
1330
1331         /**
1332          * The current drag and drop mode.  Default: POINT
1333          * @property mode
1334          * @type int
1335          * @static
1336          */
1337         mode: 0,
1338
1339         /**
1340          * Runs method on all drag and drop objects
1341          * @method _execOnAll
1342          * @private
1343          * @static
1344          */
1345         _execOnAll: function(sMethod, args) {
1346             for (var i in this.ids) {
1347                 for (var j in this.ids[i]) {
1348                     var oDD = this.ids[i][j];
1349                     if (! this.isTypeOfDD(oDD)) {
1350                         continue;
1351                     }
1352                     oDD[sMethod].apply(oDD, args);
1353                 }
1354             }
1355         },
1356
1357         /**
1358          * Drag and drop initialization.  Sets up the global event handlers
1359          * @method _onLoad
1360          * @private
1361          * @static
1362          */
1363         _onLoad: function() {
1364
1365             this.init();
1366
1367
1368             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
1369             Event.on(document, "mousemove", this.handleMouseMove, this, true);
1370             Event.on(window,   "unload",    this._onUnload, this, true);
1371             Event.on(window,   "resize",    this._onResize, this, true);
1372             // Event.on(window,   "mouseout",    this._test);
1373
1374         },
1375
1376         /**
1377          * Reset constraints on all drag and drop objs
1378          * @method _onResize
1379          * @private
1380          * @static
1381          */
1382         _onResize: function(e) {
1383             this._execOnAll("resetConstraints", []);
1384         },
1385
1386         /**
1387          * Lock all drag and drop functionality
1388          * @method lock
1389          * @static
1390          */
1391         lock: function() { this.locked = true; },
1392
1393         /**
1394          * Unlock all drag and drop functionality
1395          * @method unlock
1396          * @static
1397          */
1398         unlock: function() { this.locked = false; },
1399
1400         /**
1401          * Is drag and drop locked?
1402          * @method isLocked
1403          * @return {boolean} True if drag and drop is locked, false otherwise.
1404          * @static
1405          */
1406         isLocked: function() { return this.locked; },
1407
1408         /**
1409          * Location cache that is set for all drag drop objects when a drag is
1410          * initiated, cleared when the drag is finished.
1411          * @property locationCache
1412          * @private
1413          * @static
1414          */
1415         locationCache: {},
1416
1417         /**
1418          * Set useCache to false if you want to force object the lookup of each
1419          * drag and drop linked element constantly during a drag.
1420          * @property useCache
1421          * @type boolean
1422          * @static
1423          */
1424         useCache: true,
1425
1426         /**
1427          * The number of pixels that the mouse needs to move after the
1428          * mousedown before the drag is initiated.  Default=3;
1429          * @property clickPixelThresh
1430          * @type int
1431          * @static
1432          */
1433         clickPixelThresh: 3,
1434
1435         /**
1436          * The number of milliseconds after the mousedown event to initiate the
1437          * drag if we don't get a mouseup event. Default=1000
1438          * @property clickTimeThresh
1439          * @type int
1440          * @static
1441          */
1442         clickTimeThresh: 350,
1443
1444         /**
1445          * Flag that indicates that either the drag pixel threshold or the
1446          * mousdown time threshold has been met
1447          * @property dragThreshMet
1448          * @type boolean
1449          * @private
1450          * @static
1451          */
1452         dragThreshMet: false,
1453
1454         /**
1455          * Timeout used for the click time threshold
1456          * @property clickTimeout
1457          * @type Object
1458          * @private
1459          * @static
1460          */
1461         clickTimeout: null,
1462
1463         /**
1464          * The X position of the mousedown event stored for later use when a
1465          * drag threshold is met.
1466          * @property startX
1467          * @type int
1468          * @private
1469          * @static
1470          */
1471         startX: 0,
1472
1473         /**
1474          * The Y position of the mousedown event stored for later use when a
1475          * drag threshold is met.
1476          * @property startY
1477          * @type int
1478          * @private
1479          * @static
1480          */
1481         startY: 0,
1482
1483         /**
1484          * Each DragDrop instance must be registered with the DragDropMgr.
1485          * This is executed in DragDrop.init()
1486          * @method regDragDrop
1487          * @param {DragDrop} oDD the DragDrop object to register
1488          * @param {String} sGroup the name of the group this element belongs to
1489          * @static
1490          */
1491         regDragDrop: function(oDD, sGroup) {
1492             if (!this.initialized) { this.init(); }
1493
1494             if (!this.ids[sGroup]) {
1495                 this.ids[sGroup] = {};
1496             }
1497             this.ids[sGroup][oDD.id] = oDD;
1498         },
1499
1500         /**
1501          * Removes the supplied dd instance from the supplied group. Executed
1502          * by DragDrop.removeFromGroup, so don't call this function directly.
1503          * @method removeDDFromGroup
1504          * @private
1505          * @static
1506          */
1507         removeDDFromGroup: function(oDD, sGroup) {
1508             if (!this.ids[sGroup]) {
1509                 this.ids[sGroup] = {};
1510             }
1511
1512             var obj = this.ids[sGroup];
1513             if (obj && obj[oDD.id]) {
1514                 delete obj[oDD.id];
1515             }
1516         },
1517
1518         /**
1519          * Unregisters a drag and drop item.  This is executed in
1520          * DragDrop.unreg, use that method instead of calling this directly.
1521          * @method _remove
1522          * @private
1523          * @static
1524          */
1525         _remove: function(oDD) {
1526             for (var g in oDD.groups) {
1527                 if (g && this.ids[g][oDD.id]) {
1528                     delete this.ids[g][oDD.id];
1529                 }
1530             }
1531             delete this.handleIds[oDD.id];
1532         },
1533
1534         /**
1535          * Each DragDrop handle element must be registered.  This is done
1536          * automatically when executing DragDrop.setHandleElId()
1537          * @method regHandle
1538          * @param {String} sDDId the DragDrop id this element is a handle for
1539          * @param {String} sHandleId the id of the element that is the drag
1540          * handle
1541          * @static
1542          */
1543         regHandle: function(sDDId, sHandleId) {
1544             if (!this.handleIds[sDDId]) {
1545                 this.handleIds[sDDId] = {};
1546             }
1547             this.handleIds[sDDId][sHandleId] = sHandleId;
1548         },
1549
1550         /**
1551          * Utility function to determine if a given element has been
1552          * registered as a drag drop item.
1553          * @method isDragDrop
1554          * @param {String} id the element id to check
1555          * @return {boolean} true if this element is a DragDrop item,
1556          * false otherwise
1557          * @static
1558          */
1559         isDragDrop: function(id) {
1560             return ( this.getDDById(id) ) ? true : false;
1561         },
1562
1563         /**
1564          * Returns the drag and drop instances that are in all groups the
1565          * passed in instance belongs to.
1566          * @method getRelated
1567          * @param {DragDrop} p_oDD the obj to get related data for
1568          * @param {boolean} bTargetsOnly if true, only return targetable objs
1569          * @return {DragDrop[]} the related instances
1570          * @static
1571          */
1572         getRelated: function(p_oDD, bTargetsOnly) {
1573             var oDDs = [];
1574             for (var i in p_oDD.groups) {
1575                 for (j in this.ids[i]) {
1576                     var dd = this.ids[i][j];
1577                     if (! this.isTypeOfDD(dd)) {
1578                         continue;
1579                     }
1580                     if (!bTargetsOnly || dd.isTarget) {
1581                         oDDs[oDDs.length] = dd;
1582                     }
1583                 }
1584             }
1585
1586             return oDDs;
1587         },
1588
1589         /**
1590          * Returns true if the specified dd target is a legal target for
1591          * the specifice drag obj
1592          * @method isLegalTarget
1593          * @param {DragDrop} the drag obj
1594          * @param {DragDrop} the target
1595          * @return {boolean} true if the target is a legal target for the
1596          * dd obj
1597          * @static
1598          */
1599         isLegalTarget: function (oDD, oTargetDD) {
1600             var targets = this.getRelated(oDD, true);
1601             for (var i=0, len=targets.length;i<len;++i) {
1602                 if (targets[i].id == oTargetDD.id) {
1603                     return true;
1604                 }
1605             }
1606
1607             return false;
1608         },
1609
1610         /**
1611          * My goal is to be able to transparently determine if an object is
1612          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
1613          * returns "object", oDD.constructor.toString() always returns
1614          * "DragDrop" and not the name of the subclass.  So for now it just
1615          * evaluates a well-known variable in DragDrop.
1616          * @method isTypeOfDD
1617          * @param {Object} the object to evaluate
1618          * @return {boolean} true if typeof oDD = DragDrop
1619          * @static
1620          */
1621         isTypeOfDD: function (oDD) {
1622             return (oDD && oDD.__ygDragDrop);
1623         },
1624
1625         /**
1626          * Utility function to determine if a given element has been
1627          * registered as a drag drop handle for the given Drag Drop object.
1628          * @method isHandle
1629          * @param {String} id the element id to check
1630          * @return {boolean} true if this element is a DragDrop handle, false
1631          * otherwise
1632          * @static
1633          */
1634         isHandle: function(sDDId, sHandleId) {
1635             return ( this.handleIds[sDDId] &&
1636                             this.handleIds[sDDId][sHandleId] );
1637         },
1638
1639         /**
1640          * Returns the DragDrop instance for a given id
1641          * @method getDDById
1642          * @param {String} id the id of the DragDrop object
1643          * @return {DragDrop} the drag drop object, null if it is not found
1644          * @static
1645          */
1646         getDDById: function(id) {
1647             for (var i in this.ids) {
1648                 if (this.ids[i][id]) {
1649                     return this.ids[i][id];
1650                 }
1651             }
1652             return null;
1653         },
1654
1655         /**
1656          * Fired after a registered DragDrop object gets the mousedown event.
1657          * Sets up the events required to track the object being dragged
1658          * @method handleMouseDown
1659          * @param {Event} e the event
1660          * @param oDD the DragDrop object being dragged
1661          * @private
1662          * @static
1663          */
1664         handleMouseDown: function(e, oDD) {
1665             if(Roo.QuickTips){
1666                 Roo.QuickTips.disable();
1667             }
1668             this.currentTarget = e.getTarget();
1669
1670             this.dragCurrent = oDD;
1671
1672             var el = oDD.getEl();
1673
1674             // track start position
1675             this.startX = e.getPageX();
1676             this.startY = e.getPageY();
1677
1678             this.deltaX = this.startX - el.offsetLeft;
1679             this.deltaY = this.startY - el.offsetTop;
1680
1681             this.dragThreshMet = false;
1682
1683             this.clickTimeout = setTimeout(
1684                     function() {
1685                         var DDM = Roo.dd.DDM;
1686                         DDM.startDrag(DDM.startX, DDM.startY);
1687                     },
1688                     this.clickTimeThresh );
1689         },
1690
1691         /**
1692          * Fired when either the drag pixel threshol or the mousedown hold
1693          * time threshold has been met.
1694          * @method startDrag
1695          * @param x {int} the X position of the original mousedown
1696          * @param y {int} the Y position of the original mousedown
1697          * @static
1698          */
1699         startDrag: function(x, y) {
1700             clearTimeout(this.clickTimeout);
1701             if (this.dragCurrent) {
1702                 this.dragCurrent.b4StartDrag(x, y);
1703                 this.dragCurrent.startDrag(x, y);
1704             }
1705             this.dragThreshMet = true;
1706         },
1707
1708         /**
1709          * Internal function to handle the mouseup event.  Will be invoked
1710          * from the context of the document.
1711          * @method handleMouseUp
1712          * @param {Event} e the event
1713          * @private
1714          * @static
1715          */
1716         handleMouseUp: function(e) {
1717
1718             if(Roo.QuickTips){
1719                 Roo.QuickTips.enable();
1720             }
1721             if (! this.dragCurrent) {
1722                 return;
1723             }
1724
1725             clearTimeout(this.clickTimeout);
1726
1727             if (this.dragThreshMet) {
1728                 this.fireEvents(e, true);
1729             } else {
1730             }
1731
1732             this.stopDrag(e);
1733
1734             this.stopEvent(e);
1735         },
1736
1737         /**
1738          * Utility to stop event propagation and event default, if these
1739          * features are turned on.
1740          * @method stopEvent
1741          * @param {Event} e the event as returned by this.getEvent()
1742          * @static
1743          */
1744         stopEvent: function(e){
1745             if(this.stopPropagation) {
1746                 e.stopPropagation();
1747             }
1748
1749             if (this.preventDefault) {
1750                 e.preventDefault();
1751             }
1752         },
1753
1754         /**
1755          * Internal function to clean up event handlers after the drag
1756          * operation is complete
1757          * @method stopDrag
1758          * @param {Event} e the event
1759          * @private
1760          * @static
1761          */
1762         stopDrag: function(e) {
1763             // Fire the drag end event for the item that was dragged
1764             if (this.dragCurrent) {
1765                 if (this.dragThreshMet) {
1766                     this.dragCurrent.b4EndDrag(e);
1767                     this.dragCurrent.endDrag(e);
1768                 }
1769
1770                 this.dragCurrent.onMouseUp(e);
1771             }
1772
1773             this.dragCurrent = null;
1774             this.dragOvers = {};
1775         },
1776
1777         /**
1778          * Internal function to handle the mousemove event.  Will be invoked
1779          * from the context of the html element.
1780          *
1781          * @TODO figure out what we can do about mouse events lost when the
1782          * user drags objects beyond the window boundary.  Currently we can
1783          * detect this in internet explorer by verifying that the mouse is
1784          * down during the mousemove event.  Firefox doesn't give us the
1785          * button state on the mousemove event.
1786          * @method handleMouseMove
1787          * @param {Event} e the event
1788          * @private
1789          * @static
1790          */
1791         handleMouseMove: function(e) {
1792             if (! this.dragCurrent) {
1793                 return true;
1794             }
1795
1796             // var button = e.which || e.button;
1797
1798             // check for IE mouseup outside of page boundary
1799             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
1800                 this.stopEvent(e);
1801                 return this.handleMouseUp(e);
1802             }
1803
1804             if (!this.dragThreshMet) {
1805                 var diffX = Math.abs(this.startX - e.getPageX());
1806                 var diffY = Math.abs(this.startY - e.getPageY());
1807                 if (diffX > this.clickPixelThresh ||
1808                             diffY > this.clickPixelThresh) {
1809                     this.startDrag(this.startX, this.startY);
1810                 }
1811             }
1812
1813             if (this.dragThreshMet) {
1814                 this.dragCurrent.b4Drag(e);
1815                 this.dragCurrent.onDrag(e);
1816                 if(!this.dragCurrent.moveOnly){
1817                     this.fireEvents(e, false);
1818                 }
1819             }
1820
1821             this.stopEvent(e);
1822
1823             return true;
1824         },
1825
1826         /**
1827          * Iterates over all of the DragDrop elements to find ones we are
1828          * hovering over or dropping on
1829          * @method fireEvents
1830          * @param {Event} e the event
1831          * @param {boolean} isDrop is this a drop op or a mouseover op?
1832          * @private
1833          * @static
1834          */
1835         fireEvents: function(e, isDrop) {
1836             var dc = this.dragCurrent;
1837
1838             // If the user did the mouse up outside of the window, we could
1839             // get here even though we have ended the drag.
1840             if (!dc || dc.isLocked()) {
1841                 return;
1842             }
1843
1844             var pt = e.getPoint();
1845
1846             // cache the previous dragOver array
1847             var oldOvers = [];
1848
1849             var outEvts   = [];
1850             var overEvts  = [];
1851             var dropEvts  = [];
1852             var enterEvts = [];
1853
1854             // Check to see if the object(s) we were hovering over is no longer
1855             // being hovered over so we can fire the onDragOut event
1856             for (var i in this.dragOvers) {
1857
1858                 var ddo = this.dragOvers[i];
1859
1860                 if (! this.isTypeOfDD(ddo)) {
1861                     continue;
1862                 }
1863
1864                 if (! this.isOverTarget(pt, ddo, this.mode)) {
1865                     outEvts.push( ddo );
1866                 }
1867
1868                 oldOvers[i] = true;
1869                 delete this.dragOvers[i];
1870             }
1871
1872             for (var sGroup in dc.groups) {
1873
1874                 if ("string" != typeof sGroup) {
1875                     continue;
1876                 }
1877
1878                 for (i in this.ids[sGroup]) {
1879                     var oDD = this.ids[sGroup][i];
1880                     if (! this.isTypeOfDD(oDD)) {
1881                         continue;
1882                     }
1883
1884                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
1885                         if (this.isOverTarget(pt, oDD, this.mode)) {
1886                             // look for drop interactions
1887                             if (isDrop) {
1888                                 dropEvts.push( oDD );
1889                             // look for drag enter and drag over interactions
1890                             } else {
1891
1892                                 // initial drag over: dragEnter fires
1893                                 if (!oldOvers[oDD.id]) {
1894                                     enterEvts.push( oDD );
1895                                 // subsequent drag overs: dragOver fires
1896                                 } else {
1897                                     overEvts.push( oDD );
1898                                 }
1899
1900                                 this.dragOvers[oDD.id] = oDD;
1901                             }
1902                         }
1903                     }
1904                 }
1905             }
1906
1907             if (this.mode) {
1908                 if (outEvts.length) {
1909                     dc.b4DragOut(e, outEvts);
1910                     dc.onDragOut(e, outEvts);
1911                 }
1912
1913                 if (enterEvts.length) {
1914                     dc.onDragEnter(e, enterEvts);
1915                 }
1916
1917                 if (overEvts.length) {
1918                     dc.b4DragOver(e, overEvts);
1919                     dc.onDragOver(e, overEvts);
1920                 }
1921
1922                 if (dropEvts.length) {
1923                     dc.b4DragDrop(e, dropEvts);
1924                     dc.onDragDrop(e, dropEvts);
1925                 }
1926
1927             } else {
1928                 // fire dragout events
1929                 var len = 0;
1930                 for (i=0, len=outEvts.length; i<len; ++i) {
1931                     dc.b4DragOut(e, outEvts[i].id);
1932                     dc.onDragOut(e, outEvts[i].id);
1933                 }
1934
1935                 // fire enter events
1936                 for (i=0,len=enterEvts.length; i<len; ++i) {
1937                     // dc.b4DragEnter(e, oDD.id);
1938                     dc.onDragEnter(e, enterEvts[i].id);
1939                 }
1940
1941                 // fire over events
1942                 for (i=0,len=overEvts.length; i<len; ++i) {
1943                     dc.b4DragOver(e, overEvts[i].id);
1944                     dc.onDragOver(e, overEvts[i].id);
1945                 }
1946
1947                 // fire drop events
1948                 for (i=0, len=dropEvts.length; i<len; ++i) {
1949                     dc.b4DragDrop(e, dropEvts[i].id);
1950                     dc.onDragDrop(e, dropEvts[i].id);
1951                 }
1952
1953             }
1954
1955             // notify about a drop that did not find a target
1956             if (isDrop && !dropEvts.length) {
1957                 dc.onInvalidDrop(e);
1958             }
1959
1960         },
1961
1962         /**
1963          * Helper function for getting the best match from the list of drag
1964          * and drop objects returned by the drag and drop events when we are
1965          * in INTERSECT mode.  It returns either the first object that the
1966          * cursor is over, or the object that has the greatest overlap with
1967          * the dragged element.
1968          * @method getBestMatch
1969          * @param  {DragDrop[]} dds The array of drag and drop objects
1970          * targeted
1971          * @return {DragDrop}       The best single match
1972          * @static
1973          */
1974         getBestMatch: function(dds) {
1975             var winner = null;
1976             // Return null if the input is not what we expect
1977             //if (!dds || !dds.length || dds.length == 0) {
1978                // winner = null;
1979             // If there is only one item, it wins
1980             //} else if (dds.length == 1) {
1981
1982             var len = dds.length;
1983
1984             if (len == 1) {
1985                 winner = dds[0];
1986             } else {
1987                 // Loop through the targeted items
1988                 for (var i=0; i<len; ++i) {
1989                     var dd = dds[i];
1990                     // If the cursor is over the object, it wins.  If the
1991                     // cursor is over multiple matches, the first one we come
1992                     // to wins.
1993                     if (dd.cursorIsOver) {
1994                         winner = dd;
1995                         break;
1996                     // Otherwise the object with the most overlap wins
1997                     } else {
1998                         if (!winner ||
1999                             winner.overlap.getArea() < dd.overlap.getArea()) {
2000                             winner = dd;
2001                         }
2002                     }
2003                 }
2004             }
2005
2006             return winner;
2007         },
2008
2009         /**
2010          * Refreshes the cache of the top-left and bottom-right points of the
2011          * drag and drop objects in the specified group(s).  This is in the
2012          * format that is stored in the drag and drop instance, so typical
2013          * usage is:
2014          * <code>
2015          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
2016          * </code>
2017          * Alternatively:
2018          * <code>
2019          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
2020          * </code>
2021          * @TODO this really should be an indexed array.  Alternatively this
2022          * method could accept both.
2023          * @method refreshCache
2024          * @param {Object} groups an associative array of groups to refresh
2025          * @static
2026          */
2027         refreshCache: function(groups) {
2028             for (var sGroup in groups) {
2029                 if ("string" != typeof sGroup) {
2030                     continue;
2031                 }
2032                 for (var i in this.ids[sGroup]) {
2033                     var oDD = this.ids[sGroup][i];
2034
2035                     if (this.isTypeOfDD(oDD)) {
2036                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
2037                         var loc = this.getLocation(oDD);
2038                         if (loc) {
2039                             this.locationCache[oDD.id] = loc;
2040                         } else {
2041                             delete this.locationCache[oDD.id];
2042                             // this will unregister the drag and drop object if
2043                             // the element is not in a usable state
2044                             // oDD.unreg();
2045                         }
2046                     }
2047                 }
2048             }
2049         },
2050
2051         /**
2052          * This checks to make sure an element exists and is in the DOM.  The
2053          * main purpose is to handle cases where innerHTML is used to remove
2054          * drag and drop objects from the DOM.  IE provides an 'unspecified
2055          * error' when trying to access the offsetParent of such an element
2056          * @method verifyEl
2057          * @param {HTMLElement} el the element to check
2058          * @return {boolean} true if the element looks usable
2059          * @static
2060          */
2061         verifyEl: function(el) {
2062             if (el) {
2063                 var parent;
2064                 if(Roo.isIE){
2065                     try{
2066                         parent = el.offsetParent;
2067                     }catch(e){}
2068                 }else{
2069                     parent = el.offsetParent;
2070                 }
2071                 if (parent) {
2072                     return true;
2073                 }
2074             }
2075
2076             return false;
2077         },
2078
2079         /**
2080          * Returns a Region object containing the drag and drop element's position
2081          * and size, including the padding configured for it
2082          * @method getLocation
2083          * @param {DragDrop} oDD the drag and drop object to get the
2084          *                       location for
2085          * @return {Roo.lib.Region} a Region object representing the total area
2086          *                             the element occupies, including any padding
2087          *                             the instance is configured for.
2088          * @static
2089          */
2090         getLocation: function(oDD) {
2091             if (! this.isTypeOfDD(oDD)) {
2092                 return null;
2093             }
2094
2095             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
2096
2097             try {
2098                 pos= Roo.lib.Dom.getXY(el);
2099             } catch (e) { }
2100
2101             if (!pos) {
2102                 return null;
2103             }
2104
2105             x1 = pos[0];
2106             x2 = x1 + el.offsetWidth;
2107             y1 = pos[1];
2108             y2 = y1 + el.offsetHeight;
2109
2110             t = y1 - oDD.padding[0];
2111             r = x2 + oDD.padding[1];
2112             b = y2 + oDD.padding[2];
2113             l = x1 - oDD.padding[3];
2114
2115             return new Roo.lib.Region( t, r, b, l );
2116         },
2117
2118         /**
2119          * Checks the cursor location to see if it over the target
2120          * @method isOverTarget
2121          * @param {Roo.lib.Point} pt The point to evaluate
2122          * @param {DragDrop} oTarget the DragDrop object we are inspecting
2123          * @return {boolean} true if the mouse is over the target
2124          * @private
2125          * @static
2126          */
2127         isOverTarget: function(pt, oTarget, intersect) {
2128             // use cache if available
2129             var loc = this.locationCache[oTarget.id];
2130             if (!loc || !this.useCache) {
2131                 loc = this.getLocation(oTarget);
2132                 this.locationCache[oTarget.id] = loc;
2133
2134             }
2135
2136             if (!loc) {
2137                 return false;
2138             }
2139
2140             oTarget.cursorIsOver = loc.contains( pt );
2141
2142             // DragDrop is using this as a sanity check for the initial mousedown
2143             // in this case we are done.  In POINT mode, if the drag obj has no
2144             // contraints, we are also done. Otherwise we need to evaluate the
2145             // location of the target as related to the actual location of the
2146             // dragged element.
2147             var dc = this.dragCurrent;
2148             if (!dc || !dc.getTargetCoord ||
2149                     (!intersect && !dc.constrainX && !dc.constrainY)) {
2150                 return oTarget.cursorIsOver;
2151             }
2152
2153             oTarget.overlap = null;
2154
2155             // Get the current location of the drag element, this is the
2156             // location of the mouse event less the delta that represents
2157             // where the original mousedown happened on the element.  We
2158             // need to consider constraints and ticks as well.
2159             var pos = dc.getTargetCoord(pt.x, pt.y);
2160
2161             var el = dc.getDragEl();
2162             var curRegion = new Roo.lib.Region( pos.y,
2163                                                    pos.x + el.offsetWidth,
2164                                                    pos.y + el.offsetHeight,
2165                                                    pos.x );
2166
2167             var overlap = curRegion.intersect(loc);
2168
2169             if (overlap) {
2170                 oTarget.overlap = overlap;
2171                 return (intersect) ? true : oTarget.cursorIsOver;
2172             } else {
2173                 return false;
2174             }
2175         },
2176
2177         /**
2178          * unload event handler
2179          * @method _onUnload
2180          * @private
2181          * @static
2182          */
2183         _onUnload: function(e, me) {
2184             Roo.dd.DragDropMgr.unregAll();
2185         },
2186
2187         /**
2188          * Cleans up the drag and drop events and objects.
2189          * @method unregAll
2190          * @private
2191          * @static
2192          */
2193         unregAll: function() {
2194
2195             if (this.dragCurrent) {
2196                 this.stopDrag();
2197                 this.dragCurrent = null;
2198             }
2199
2200             this._execOnAll("unreg", []);
2201
2202             for (i in this.elementCache) {
2203                 delete this.elementCache[i];
2204             }
2205
2206             this.elementCache = {};
2207             this.ids = {};
2208         },
2209
2210         /**
2211          * A cache of DOM elements
2212          * @property elementCache
2213          * @private
2214          * @static
2215          */
2216         elementCache: {},
2217
2218         /**
2219          * Get the wrapper for the DOM element specified
2220          * @method getElWrapper
2221          * @param {String} id the id of the element to get
2222          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
2223          * @private
2224          * @deprecated This wrapper isn't that useful
2225          * @static
2226          */
2227         getElWrapper: function(id) {
2228             var oWrapper = this.elementCache[id];
2229             if (!oWrapper || !oWrapper.el) {
2230                 oWrapper = this.elementCache[id] =
2231                     new this.ElementWrapper(Roo.getDom(id));
2232             }
2233             return oWrapper;
2234         },
2235
2236         /**
2237          * Returns the actual DOM element
2238          * @method getElement
2239          * @param {String} id the id of the elment to get
2240          * @return {Object} The element
2241          * @deprecated use Roo.getDom instead
2242          * @static
2243          */
2244         getElement: function(id) {
2245             return Roo.getDom(id);
2246         },
2247
2248         /**
2249          * Returns the style property for the DOM element (i.e.,
2250          * document.getElById(id).style)
2251          * @method getCss
2252          * @param {String} id the id of the elment to get
2253          * @return {Object} The style property of the element
2254          * @deprecated use Roo.getDom instead
2255          * @static
2256          */
2257         getCss: function(id) {
2258             var el = Roo.getDom(id);
2259             return (el) ? el.style : null;
2260         },
2261
2262         /**
2263          * Inner class for cached elements
2264          * @class DragDropMgr.ElementWrapper
2265          * @for DragDropMgr
2266          * @private
2267          * @deprecated
2268          */
2269         ElementWrapper: function(el) {
2270                 /**
2271                  * The element
2272                  * @property el
2273                  */
2274                 this.el = el || null;
2275                 /**
2276                  * The element id
2277                  * @property id
2278                  */
2279                 this.id = this.el && el.id;
2280                 /**
2281                  * A reference to the style property
2282                  * @property css
2283                  */
2284                 this.css = this.el && el.style;
2285             },
2286
2287         /**
2288          * Returns the X position of an html element
2289          * @method getPosX
2290          * @param el the element for which to get the position
2291          * @return {int} the X coordinate
2292          * @for DragDropMgr
2293          * @deprecated use Roo.lib.Dom.getX instead
2294          * @static
2295          */
2296         getPosX: function(el) {
2297             return Roo.lib.Dom.getX(el);
2298         },
2299
2300         /**
2301          * Returns the Y position of an html element
2302          * @method getPosY
2303          * @param el the element for which to get the position
2304          * @return {int} the Y coordinate
2305          * @deprecated use Roo.lib.Dom.getY instead
2306          * @static
2307          */
2308         getPosY: function(el) {
2309             return Roo.lib.Dom.getY(el);
2310         },
2311
2312         /**
2313          * Swap two nodes.  In IE, we use the native method, for others we
2314          * emulate the IE behavior
2315          * @method swapNode
2316          * @param n1 the first node to swap
2317          * @param n2 the other node to swap
2318          * @static
2319          */
2320         swapNode: function(n1, n2) {
2321             if (n1.swapNode) {
2322                 n1.swapNode(n2);
2323             } else {
2324                 var p = n2.parentNode;
2325                 var s = n2.nextSibling;
2326
2327                 if (s == n1) {
2328                     p.insertBefore(n1, n2);
2329                 } else if (n2 == n1.nextSibling) {
2330                     p.insertBefore(n2, n1);
2331                 } else {
2332                     n1.parentNode.replaceChild(n2, n1);
2333                     p.insertBefore(n1, s);
2334                 }
2335             }
2336         },
2337
2338         /**
2339          * Returns the current scroll position
2340          * @method getScroll
2341          * @private
2342          * @static
2343          */
2344         getScroll: function () {
2345             var t, l, dde=document.documentElement, db=document.body;
2346             if (dde && (dde.scrollTop || dde.scrollLeft)) {
2347                 t = dde.scrollTop;
2348                 l = dde.scrollLeft;
2349             } else if (db) {
2350                 t = db.scrollTop;
2351                 l = db.scrollLeft;
2352             } else {
2353
2354             }
2355             return { top: t, left: l };
2356         },
2357
2358         /**
2359          * Returns the specified element style property
2360          * @method getStyle
2361          * @param {HTMLElement} el          the element
2362          * @param {string}      styleProp   the style property
2363          * @return {string} The value of the style property
2364          * @deprecated use Roo.lib.Dom.getStyle
2365          * @static
2366          */
2367         getStyle: function(el, styleProp) {
2368             return Roo.fly(el).getStyle(styleProp);
2369         },
2370
2371         /**
2372          * Gets the scrollTop
2373          * @method getScrollTop
2374          * @return {int} the document's scrollTop
2375          * @static
2376          */
2377         getScrollTop: function () { return this.getScroll().top; },
2378
2379         /**
2380          * Gets the scrollLeft
2381          * @method getScrollLeft
2382          * @return {int} the document's scrollTop
2383          * @static
2384          */
2385         getScrollLeft: function () { return this.getScroll().left; },
2386
2387         /**
2388          * Sets the x/y position of an element to the location of the
2389          * target element.
2390          * @method moveToEl
2391          * @param {HTMLElement} moveEl      The element to move
2392          * @param {HTMLElement} targetEl    The position reference element
2393          * @static
2394          */
2395         moveToEl: function (moveEl, targetEl) {
2396             var aCoord = Roo.lib.Dom.getXY(targetEl);
2397             Roo.lib.Dom.setXY(moveEl, aCoord);
2398         },
2399
2400         /**
2401          * Numeric array sort function
2402          * @method numericSort
2403          * @static
2404          */
2405         numericSort: function(a, b) { return (a - b); },
2406
2407         /**
2408          * Internal counter
2409          * @property _timeoutCount
2410          * @private
2411          * @static
2412          */
2413         _timeoutCount: 0,
2414
2415         /**
2416          * Trying to make the load order less important.  Without this we get
2417          * an error if this file is loaded before the Event Utility.
2418          * @method _addListeners
2419          * @private
2420          * @static
2421          */
2422         _addListeners: function() {
2423             var DDM = Roo.dd.DDM;
2424             if ( Roo.lib.Event && document ) {
2425                 DDM._onLoad();
2426             } else {
2427                 if (DDM._timeoutCount > 2000) {
2428                 } else {
2429                     setTimeout(DDM._addListeners, 10);
2430                     if (document && document.body) {
2431                         DDM._timeoutCount += 1;
2432                     }
2433                 }
2434             }
2435         },
2436
2437         /**
2438          * Recursively searches the immediate parent and all child nodes for
2439          * the handle element in order to determine wheter or not it was
2440          * clicked.
2441          * @method handleWasClicked
2442          * @param node the html element to inspect
2443          * @static
2444          */
2445         handleWasClicked: function(node, id) {
2446             if (this.isHandle(id, node.id)) {
2447                 return true;
2448             } else {
2449                 // check to see if this is a text node child of the one we want
2450                 var p = node.parentNode;
2451
2452                 while (p) {
2453                     if (this.isHandle(id, p.id)) {
2454                         return true;
2455                     } else {
2456                         p = p.parentNode;
2457                     }
2458                 }
2459             }
2460
2461             return false;
2462         }
2463
2464     };
2465
2466 }();
2467
2468 // shorter alias, save a few bytes
2469 Roo.dd.DDM = Roo.dd.DragDropMgr;
2470 Roo.dd.DDM._addListeners();
2471
2472 }/*
2473  * Based on:
2474  * Ext JS Library 1.1.1
2475  * Copyright(c) 2006-2007, Ext JS, LLC.
2476  *
2477  * Originally Released Under LGPL - original licence link has changed is not relivant.
2478  *
2479  * Fork - LGPL
2480  * <script type="text/javascript">
2481  */
2482
2483 /**
2484  * @class Roo.dd.DD
2485  * A DragDrop implementation where the linked element follows the
2486  * mouse cursor during a drag.
2487  * @extends Roo.dd.DragDrop
2488  * @constructor
2489  * @param {String} id the id of the linked element
2490  * @param {String} sGroup the group of related DragDrop items
2491  * @param {object} config an object containing configurable attributes
2492  *                Valid properties for DD:
2493  *                    scroll
2494  */
2495 Roo.dd.DD = function(id, sGroup, config) {
2496     if (id) {
2497         this.init(id, sGroup, config);
2498     }
2499 };
2500
2501 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
2502
2503     /**
2504      * When set to true, the utility automatically tries to scroll the browser
2505      * window wehn a drag and drop element is dragged near the viewport boundary.
2506      * Defaults to true.
2507      * @property scroll
2508      * @type boolean
2509      */
2510     scroll: true,
2511
2512     /**
2513      * Sets the pointer offset to the distance between the linked element's top
2514      * left corner and the location the element was clicked
2515      * @method autoOffset
2516      * @param {int} iPageX the X coordinate of the click
2517      * @param {int} iPageY the Y coordinate of the click
2518      */
2519     autoOffset: function(iPageX, iPageY) {
2520         var x = iPageX - this.startPageX;
2521         var y = iPageY - this.startPageY;
2522         this.setDelta(x, y);
2523     },
2524
2525     /**
2526      * Sets the pointer offset.  You can call this directly to force the
2527      * offset to be in a particular location (e.g., pass in 0,0 to set it
2528      * to the center of the object)
2529      * @method setDelta
2530      * @param {int} iDeltaX the distance from the left
2531      * @param {int} iDeltaY the distance from the top
2532      */
2533     setDelta: function(iDeltaX, iDeltaY) {
2534         this.deltaX = iDeltaX;
2535         this.deltaY = iDeltaY;
2536     },
2537
2538     /**
2539      * Sets the drag element to the location of the mousedown or click event,
2540      * maintaining the cursor location relative to the location on the element
2541      * that was clicked.  Override this if you want to place the element in a
2542      * location other than where the cursor is.
2543      * @method setDragElPos
2544      * @param {int} iPageX the X coordinate of the mousedown or drag event
2545      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2546      */
2547     setDragElPos: function(iPageX, iPageY) {
2548         // the first time we do this, we are going to check to make sure
2549         // the element has css positioning
2550
2551         var el = this.getDragEl();
2552         this.alignElWithMouse(el, iPageX, iPageY);
2553     },
2554
2555     /**
2556      * Sets the element to the location of the mousedown or click event,
2557      * maintaining the cursor location relative to the location on the element
2558      * that was clicked.  Override this if you want to place the element in a
2559      * location other than where the cursor is.
2560      * @method alignElWithMouse
2561      * @param {HTMLElement} el the element to move
2562      * @param {int} iPageX the X coordinate of the mousedown or drag event
2563      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2564      */
2565     alignElWithMouse: function(el, iPageX, iPageY) {
2566         var oCoord = this.getTargetCoord(iPageX, iPageY);
2567         var fly = el.dom ? el : Roo.fly(el);
2568         if (!this.deltaSetXY) {
2569             var aCoord = [oCoord.x, oCoord.y];
2570             fly.setXY(aCoord);
2571             var newLeft = fly.getLeft(true);
2572             var newTop  = fly.getTop(true);
2573             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
2574         } else {
2575             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
2576         }
2577
2578         this.cachePosition(oCoord.x, oCoord.y);
2579         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
2580         return oCoord;
2581     },
2582
2583     /**
2584      * Saves the most recent position so that we can reset the constraints and
2585      * tick marks on-demand.  We need to know this so that we can calculate the
2586      * number of pixels the element is offset from its original position.
2587      * @method cachePosition
2588      * @param iPageX the current x position (optional, this just makes it so we
2589      * don't have to look it up again)
2590      * @param iPageY the current y position (optional, this just makes it so we
2591      * don't have to look it up again)
2592      */
2593     cachePosition: function(iPageX, iPageY) {
2594         if (iPageX) {
2595             this.lastPageX = iPageX;
2596             this.lastPageY = iPageY;
2597         } else {
2598             var aCoord = Roo.lib.Dom.getXY(this.getEl());
2599             this.lastPageX = aCoord[0];
2600             this.lastPageY = aCoord[1];
2601         }
2602     },
2603
2604     /**
2605      * Auto-scroll the window if the dragged object has been moved beyond the
2606      * visible window boundary.
2607      * @method autoScroll
2608      * @param {int} x the drag element's x position
2609      * @param {int} y the drag element's y position
2610      * @param {int} h the height of the drag element
2611      * @param {int} w the width of the drag element
2612      * @private
2613      */
2614     autoScroll: function(x, y, h, w) {
2615
2616         if (this.scroll) {
2617             // The client height
2618             var clientH = Roo.lib.Dom.getViewWidth();
2619
2620             // The client width
2621             var clientW = Roo.lib.Dom.getViewHeight();
2622
2623             // The amt scrolled down
2624             var st = this.DDM.getScrollTop();
2625
2626             // The amt scrolled right
2627             var sl = this.DDM.getScrollLeft();
2628
2629             // Location of the bottom of the element
2630             var bot = h + y;
2631
2632             // Location of the right of the element
2633             var right = w + x;
2634
2635             // The distance from the cursor to the bottom of the visible area,
2636             // adjusted so that we don't scroll if the cursor is beyond the
2637             // element drag constraints
2638             var toBot = (clientH + st - y - this.deltaY);
2639
2640             // The distance from the cursor to the right of the visible area
2641             var toRight = (clientW + sl - x - this.deltaX);
2642
2643
2644             // How close to the edge the cursor must be before we scroll
2645             // var thresh = (document.all) ? 100 : 40;
2646             var thresh = 40;
2647
2648             // How many pixels to scroll per autoscroll op.  This helps to reduce
2649             // clunky scrolling. IE is more sensitive about this ... it needs this
2650             // value to be higher.
2651             var scrAmt = (document.all) ? 80 : 30;
2652
2653             // Scroll down if we are near the bottom of the visible page and the
2654             // obj extends below the crease
2655             if ( bot > clientH && toBot < thresh ) {
2656                 window.scrollTo(sl, st + scrAmt);
2657             }
2658
2659             // Scroll up if the window is scrolled down and the top of the object
2660             // goes above the top border
2661             if ( y < st && st > 0 && y - st < thresh ) {
2662                 window.scrollTo(sl, st - scrAmt);
2663             }
2664
2665             // Scroll right if the obj is beyond the right border and the cursor is
2666             // near the border.
2667             if ( right > clientW && toRight < thresh ) {
2668                 window.scrollTo(sl + scrAmt, st);
2669             }
2670
2671             // Scroll left if the window has been scrolled to the right and the obj
2672             // extends past the left border
2673             if ( x < sl && sl > 0 && x - sl < thresh ) {
2674                 window.scrollTo(sl - scrAmt, st);
2675             }
2676         }
2677     },
2678
2679     /**
2680      * Finds the location the element should be placed if we want to move
2681      * it to where the mouse location less the click offset would place us.
2682      * @method getTargetCoord
2683      * @param {int} iPageX the X coordinate of the click
2684      * @param {int} iPageY the Y coordinate of the click
2685      * @return an object that contains the coordinates (Object.x and Object.y)
2686      * @private
2687      */
2688     getTargetCoord: function(iPageX, iPageY) {
2689
2690
2691         var x = iPageX - this.deltaX;
2692         var y = iPageY - this.deltaY;
2693
2694         if (this.constrainX) {
2695             if (x < this.minX) { x = this.minX; }
2696             if (x > this.maxX) { x = this.maxX; }
2697         }
2698
2699         if (this.constrainY) {
2700             if (y < this.minY) { y = this.minY; }
2701             if (y > this.maxY) { y = this.maxY; }
2702         }
2703
2704         x = this.getTick(x, this.xTicks);
2705         y = this.getTick(y, this.yTicks);
2706
2707
2708         return {x:x, y:y};
2709     },
2710
2711     /*
2712      * Sets up config options specific to this class. Overrides
2713      * Roo.dd.DragDrop, but all versions of this method through the
2714      * inheritance chain are called
2715      */
2716     applyConfig: function() {
2717         Roo.dd.DD.superclass.applyConfig.call(this);
2718         this.scroll = (this.config.scroll !== false);
2719     },
2720
2721     /*
2722      * Event that fires prior to the onMouseDown event.  Overrides
2723      * Roo.dd.DragDrop.
2724      */
2725     b4MouseDown: function(e) {
2726         // this.resetConstraints();
2727         this.autoOffset(e.getPageX(),
2728                             e.getPageY());
2729     },
2730
2731     /*
2732      * Event that fires prior to the onDrag event.  Overrides
2733      * Roo.dd.DragDrop.
2734      */
2735     b4Drag: function(e) {
2736         this.setDragElPos(e.getPageX(),
2737                             e.getPageY());
2738     },
2739
2740     toString: function() {
2741         return ("DD " + this.id);
2742     }
2743
2744     //////////////////////////////////////////////////////////////////////////
2745     // Debugging ygDragDrop events that can be overridden
2746     //////////////////////////////////////////////////////////////////////////
2747     /*
2748     startDrag: function(x, y) {
2749     },
2750
2751     onDrag: function(e) {
2752     },
2753
2754     onDragEnter: function(e, id) {
2755     },
2756
2757     onDragOver: function(e, id) {
2758     },
2759
2760     onDragOut: function(e, id) {
2761     },
2762
2763     onDragDrop: function(e, id) {
2764     },
2765
2766     endDrag: function(e) {
2767     }
2768
2769     */
2770
2771 });/*
2772  * Based on:
2773  * Ext JS Library 1.1.1
2774  * Copyright(c) 2006-2007, Ext JS, LLC.
2775  *
2776  * Originally Released Under LGPL - original licence link has changed is not relivant.
2777  *
2778  * Fork - LGPL
2779  * <script type="text/javascript">
2780  */
2781
2782 /**
2783  * @class Roo.dd.DDProxy
2784  * A DragDrop implementation that inserts an empty, bordered div into
2785  * the document that follows the cursor during drag operations.  At the time of
2786  * the click, the frame div is resized to the dimensions of the linked html
2787  * element, and moved to the exact location of the linked element.
2788  *
2789  * References to the "frame" element refer to the single proxy element that
2790  * was created to be dragged in place of all DDProxy elements on the
2791  * page.
2792  *
2793  * @extends Roo.dd.DD
2794  * @constructor
2795  * @param {String} id the id of the linked html element
2796  * @param {String} sGroup the group of related DragDrop objects
2797  * @param {object} config an object containing configurable attributes
2798  *                Valid properties for DDProxy in addition to those in DragDrop:
2799  *                   resizeFrame, centerFrame, dragElId
2800  */
2801 Roo.dd.DDProxy = function(id, sGroup, config) {
2802     if (id) {
2803         this.init(id, sGroup, config);
2804         this.initFrame();
2805     }
2806 };
2807
2808 /**
2809  * The default drag frame div id
2810  * @property Roo.dd.DDProxy.dragElId
2811  * @type String
2812  * @static
2813  */
2814 Roo.dd.DDProxy.dragElId = "ygddfdiv";
2815
2816 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
2817
2818     /**
2819      * By default we resize the drag frame to be the same size as the element
2820      * we want to drag (this is to get the frame effect).  We can turn it off
2821      * if we want a different behavior.
2822      * @property resizeFrame
2823      * @type boolean
2824      */
2825     resizeFrame: true,
2826
2827     /**
2828      * By default the frame is positioned exactly where the drag element is, so
2829      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
2830      * you do not have constraints on the obj is to have the drag frame centered
2831      * around the cursor.  Set centerFrame to true for this effect.
2832      * @property centerFrame
2833      * @type boolean
2834      */
2835     centerFrame: false,
2836
2837     /**
2838      * Creates the proxy element if it does not yet exist
2839      * @method createFrame
2840      */
2841     createFrame: function() {
2842         var self = this;
2843         var body = document.body;
2844
2845         if (!body || !body.firstChild) {
2846             setTimeout( function() { self.createFrame(); }, 50 );
2847             return;
2848         }
2849
2850         var div = this.getDragEl();
2851
2852         if (!div) {
2853             div    = document.createElement("div");
2854             div.id = this.dragElId;
2855             var s  = div.style;
2856
2857             s.position   = "absolute";
2858             s.visibility = "hidden";
2859             s.cursor     = "move";
2860             s.border     = "2px solid #aaa";
2861             s.zIndex     = 999;
2862
2863             // appendChild can blow up IE if invoked prior to the window load event
2864             // while rendering a table.  It is possible there are other scenarios
2865             // that would cause this to happen as well.
2866             body.insertBefore(div, body.firstChild);
2867         }
2868     },
2869
2870     /**
2871      * Initialization for the drag frame element.  Must be called in the
2872      * constructor of all subclasses
2873      * @method initFrame
2874      */
2875     initFrame: function() {
2876         this.createFrame();
2877     },
2878
2879     applyConfig: function() {
2880         Roo.dd.DDProxy.superclass.applyConfig.call(this);
2881
2882         this.resizeFrame = (this.config.resizeFrame !== false);
2883         this.centerFrame = (this.config.centerFrame);
2884         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
2885     },
2886
2887     /**
2888      * Resizes the drag frame to the dimensions of the clicked object, positions
2889      * it over the object, and finally displays it
2890      * @method showFrame
2891      * @param {int} iPageX X click position
2892      * @param {int} iPageY Y click position
2893      * @private
2894      */
2895     showFrame: function(iPageX, iPageY) {
2896         var el = this.getEl();
2897         var dragEl = this.getDragEl();
2898         var s = dragEl.style;
2899
2900         this._resizeProxy();
2901
2902         if (this.centerFrame) {
2903             this.setDelta( Math.round(parseInt(s.width,  10)/2),
2904                            Math.round(parseInt(s.height, 10)/2) );
2905         }
2906
2907         this.setDragElPos(iPageX, iPageY);
2908
2909         Roo.fly(dragEl).show();
2910     },
2911
2912     /**
2913      * The proxy is automatically resized to the dimensions of the linked
2914      * element when a drag is initiated, unless resizeFrame is set to false
2915      * @method _resizeProxy
2916      * @private
2917      */
2918     _resizeProxy: function() {
2919         if (this.resizeFrame) {
2920             var el = this.getEl();
2921             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
2922         }
2923     },
2924
2925     // overrides Roo.dd.DragDrop
2926     b4MouseDown: function(e) {
2927         var x = e.getPageX();
2928         var y = e.getPageY();
2929         this.autoOffset(x, y);
2930         this.setDragElPos(x, y);
2931     },
2932
2933     // overrides Roo.dd.DragDrop
2934     b4StartDrag: function(x, y) {
2935         // show the drag frame
2936         this.showFrame(x, y);
2937     },
2938
2939     // overrides Roo.dd.DragDrop
2940     b4EndDrag: function(e) {
2941         Roo.fly(this.getDragEl()).hide();
2942     },
2943
2944     // overrides Roo.dd.DragDrop
2945     // By default we try to move the element to the last location of the frame.
2946     // This is so that the default behavior mirrors that of Roo.dd.DD.
2947     endDrag: function(e) {
2948
2949         var lel = this.getEl();
2950         var del = this.getDragEl();
2951
2952         // Show the drag frame briefly so we can get its position
2953         del.style.visibility = "";
2954
2955         this.beforeMove();
2956         // Hide the linked element before the move to get around a Safari
2957         // rendering bug.
2958         lel.style.visibility = "hidden";
2959         Roo.dd.DDM.moveToEl(lel, del);
2960         del.style.visibility = "hidden";
2961         lel.style.visibility = "";
2962
2963         this.afterDrag();
2964     },
2965
2966     beforeMove : function(){
2967
2968     },
2969
2970     afterDrag : function(){
2971
2972     },
2973
2974     toString: function() {
2975         return ("DDProxy " + this.id);
2976     }
2977
2978 });
2979 /*
2980  * Based on:
2981  * Ext JS Library 1.1.1
2982  * Copyright(c) 2006-2007, Ext JS, LLC.
2983  *
2984  * Originally Released Under LGPL - original licence link has changed is not relivant.
2985  *
2986  * Fork - LGPL
2987  * <script type="text/javascript">
2988  */
2989
2990  /**
2991  * @class Roo.dd.DDTarget
2992  * A DragDrop implementation that does not move, but can be a drop
2993  * target.  You would get the same result by simply omitting implementation
2994  * for the event callbacks, but this way we reduce the processing cost of the
2995  * event listener and the callbacks.
2996  * @extends Roo.dd.DragDrop
2997  * @constructor
2998  * @param {String} id the id of the element that is a drop target
2999  * @param {String} sGroup the group of related DragDrop objects
3000  * @param {object} config an object containing configurable attributes
3001  *                 Valid properties for DDTarget in addition to those in
3002  *                 DragDrop:
3003  *                    none
3004  */
3005 Roo.dd.DDTarget = function(id, sGroup, config) {
3006     if (id) {
3007         this.initTarget(id, sGroup, config);
3008     }
3009 };
3010
3011 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
3012 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
3013     toString: function() {
3014         return ("DDTarget " + this.id);
3015     }
3016 });
3017 /*
3018  * Based on:
3019  * Ext JS Library 1.1.1
3020  * Copyright(c) 2006-2007, Ext JS, LLC.
3021  *
3022  * Originally Released Under LGPL - original licence link has changed is not relivant.
3023  *
3024  * Fork - LGPL
3025  * <script type="text/javascript">
3026  */
3027  
3028
3029 /**
3030  * @class Roo.dd.ScrollManager
3031  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
3032  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
3033  * @singleton
3034  */
3035 Roo.dd.ScrollManager = function(){
3036     var ddm = Roo.dd.DragDropMgr;
3037     var els = {};
3038     var dragEl = null;
3039     var proc = {};
3040     
3041     var onStop = function(e){
3042         dragEl = null;
3043         clearProc();
3044     };
3045     
3046     var triggerRefresh = function(){
3047         if(ddm.dragCurrent){
3048              ddm.refreshCache(ddm.dragCurrent.groups);
3049         }
3050     };
3051     
3052     var doScroll = function(){
3053         if(ddm.dragCurrent){
3054             var dds = Roo.dd.ScrollManager;
3055             if(!dds.animate){
3056                 if(proc.el.scroll(proc.dir, dds.increment)){
3057                     triggerRefresh();
3058                 }
3059             }else{
3060                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
3061             }
3062         }
3063     };
3064     
3065     var clearProc = function(){
3066         if(proc.id){
3067             clearInterval(proc.id);
3068         }
3069         proc.id = 0;
3070         proc.el = null;
3071         proc.dir = "";
3072     };
3073     
3074     var startProc = function(el, dir){
3075         clearProc();
3076         proc.el = el;
3077         proc.dir = dir;
3078         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
3079     };
3080     
3081     var onFire = function(e, isDrop){
3082         if(isDrop || !ddm.dragCurrent){ return; }
3083         var dds = Roo.dd.ScrollManager;
3084         if(!dragEl || dragEl != ddm.dragCurrent){
3085             dragEl = ddm.dragCurrent;
3086             // refresh regions on drag start
3087             dds.refreshCache();
3088         }
3089         
3090         var xy = Roo.lib.Event.getXY(e);
3091         var pt = new Roo.lib.Point(xy[0], xy[1]);
3092         for(var id in els){
3093             var el = els[id], r = el._region;
3094             if(r && r.contains(pt) && el.isScrollable()){
3095                 if(r.bottom - pt.y <= dds.thresh){
3096                     if(proc.el != el){
3097                         startProc(el, "down");
3098                     }
3099                     return;
3100                 }else if(r.right - pt.x <= dds.thresh){
3101                     if(proc.el != el){
3102                         startProc(el, "left");
3103                     }
3104                     return;
3105                 }else if(pt.y - r.top <= dds.thresh){
3106                     if(proc.el != el){
3107                         startProc(el, "up");
3108                     }
3109                     return;
3110                 }else if(pt.x - r.left <= dds.thresh){
3111                     if(proc.el != el){
3112                         startProc(el, "right");
3113                     }
3114                     return;
3115                 }
3116             }
3117         }
3118         clearProc();
3119     };
3120     
3121     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
3122     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
3123     
3124     return {
3125         /**
3126          * Registers new overflow element(s) to auto scroll
3127          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
3128          */
3129         register : function(el){
3130             if(el instanceof Array){
3131                 for(var i = 0, len = el.length; i < len; i++) {
3132                         this.register(el[i]);
3133                 }
3134             }else{
3135                 el = Roo.get(el);
3136                 els[el.id] = el;
3137             }
3138         },
3139         
3140         /**
3141          * Unregisters overflow element(s) so they are no longer scrolled
3142          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
3143          */
3144         unregister : function(el){
3145             if(el instanceof Array){
3146                 for(var i = 0, len = el.length; i < len; i++) {
3147                         this.unregister(el[i]);
3148                 }
3149             }else{
3150                 el = Roo.get(el);
3151                 delete els[el.id];
3152             }
3153         },
3154         
3155         /**
3156          * The number of pixels from the edge of a container the pointer needs to be to 
3157          * trigger scrolling (defaults to 25)
3158          * @type Number
3159          */
3160         thresh : 25,
3161         
3162         /**
3163          * The number of pixels to scroll in each scroll increment (defaults to 50)
3164          * @type Number
3165          */
3166         increment : 100,
3167         
3168         /**
3169          * The frequency of scrolls in milliseconds (defaults to 500)
3170          * @type Number
3171          */
3172         frequency : 500,
3173         
3174         /**
3175          * True to animate the scroll (defaults to true)
3176          * @type Boolean
3177          */
3178         animate: true,
3179         
3180         /**
3181          * The animation duration in seconds - 
3182          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
3183          * @type Number
3184          */
3185         animDuration: .4,
3186         
3187         /**
3188          * Manually trigger a cache refresh.
3189          */
3190         refreshCache : function(){
3191             for(var id in els){
3192                 if(typeof els[id] == 'object'){ // for people extending the object prototype
3193                     els[id]._region = els[id].getRegion();
3194                 }
3195             }
3196         }
3197     };
3198 }();/*
3199  * Based on:
3200  * Ext JS Library 1.1.1
3201  * Copyright(c) 2006-2007, Ext JS, LLC.
3202  *
3203  * Originally Released Under LGPL - original licence link has changed is not relivant.
3204  *
3205  * Fork - LGPL
3206  * <script type="text/javascript">
3207  */
3208  
3209
3210 /**
3211  * @class Roo.dd.Registry
3212  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
3213  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
3214  * @singleton
3215  */
3216 Roo.dd.Registry = function(){
3217     var elements = {}; 
3218     var handles = {}; 
3219     var autoIdSeed = 0;
3220
3221     var getId = function(el, autogen){
3222         if(typeof el == "string"){
3223             return el;
3224         }
3225         var id = el.id;
3226         if(!id && autogen !== false){
3227             id = "roodd-" + (++autoIdSeed);
3228             el.id = id;
3229         }
3230         return id;
3231     };
3232     
3233     return {
3234     /**
3235      * Register a drag drop element
3236      * @param {String|HTMLElement} element The id or DOM node to register
3237      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
3238      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
3239      * knows how to interpret, plus there are some specific properties known to the Registry that should be
3240      * populated in the data object (if applicable):
3241      * <pre>
3242 Value      Description<br />
3243 ---------  ------------------------------------------<br />
3244 handles    Array of DOM nodes that trigger dragging<br />
3245            for the element being registered<br />
3246 isHandle   True if the element passed in triggers<br />
3247            dragging itself, else false
3248 </pre>
3249      */
3250         register : function(el, data){
3251             data = data || {};
3252             if(typeof el == "string"){
3253                 el = document.getElementById(el);
3254             }
3255             data.ddel = el;
3256             elements[getId(el)] = data;
3257             if(data.isHandle !== false){
3258                 handles[data.ddel.id] = data;
3259             }
3260             if(data.handles){
3261                 var hs = data.handles;
3262                 for(var i = 0, len = hs.length; i < len; i++){
3263                         handles[getId(hs[i])] = data;
3264                 }
3265             }
3266         },
3267
3268     /**
3269      * Unregister a drag drop element
3270      * @param {String|HTMLElement}  element The id or DOM node to unregister
3271      */
3272         unregister : function(el){
3273             var id = getId(el, false);
3274             var data = elements[id];
3275             if(data){
3276                 delete elements[id];
3277                 if(data.handles){
3278                     var hs = data.handles;
3279                     for(var i = 0, len = hs.length; i < len; i++){
3280                         delete handles[getId(hs[i], false)];
3281                     }
3282                 }
3283             }
3284         },
3285
3286     /**
3287      * Returns the handle registered for a DOM Node by id
3288      * @param {String|HTMLElement} id The DOM node or id to look up
3289      * @return {Object} handle The custom handle data
3290      */
3291         getHandle : function(id){
3292             if(typeof id != "string"){ // must be element?
3293                 id = id.id;
3294             }
3295             return handles[id];
3296         },
3297
3298     /**
3299      * Returns the handle that is registered for the DOM node that is the target of the event
3300      * @param {Event} e The event
3301      * @return {Object} handle The custom handle data
3302      */
3303         getHandleFromEvent : function(e){
3304             var t = Roo.lib.Event.getTarget(e);
3305             return t ? handles[t.id] : null;
3306         },
3307
3308     /**
3309      * Returns a custom data object that is registered for a DOM node by id
3310      * @param {String|HTMLElement} id The DOM node or id to look up
3311      * @return {Object} data The custom data
3312      */
3313         getTarget : function(id){
3314             if(typeof id != "string"){ // must be element?
3315                 id = id.id;
3316             }
3317             return elements[id];
3318         },
3319
3320     /**
3321      * Returns a custom data object that is registered for the DOM node that is the target of the event
3322      * @param {Event} e The event
3323      * @return {Object} data The custom data
3324      */
3325         getTargetFromEvent : function(e){
3326             var t = Roo.lib.Event.getTarget(e);
3327             return t ? elements[t.id] || handles[t.id] : null;
3328         }
3329     };
3330 }();/*
3331  * Based on:
3332  * Ext JS Library 1.1.1
3333  * Copyright(c) 2006-2007, Ext JS, LLC.
3334  *
3335  * Originally Released Under LGPL - original licence link has changed is not relivant.
3336  *
3337  * Fork - LGPL
3338  * <script type="text/javascript">
3339  */
3340  
3341
3342 /**
3343  * @class Roo.dd.StatusProxy
3344  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
3345  * default drag proxy used by all Roo.dd components.
3346  * @constructor
3347  * @param {Object} config
3348  */
3349 Roo.dd.StatusProxy = function(config){
3350     Roo.apply(this, config);
3351     this.id = this.id || Roo.id();
3352     this.el = new Roo.Layer({
3353         dh: {
3354             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
3355                 {tag: "div", cls: "x-dd-drop-icon"},
3356                 {tag: "div", cls: "x-dd-drag-ghost"}
3357             ]
3358         }, 
3359         shadow: !config || config.shadow !== false
3360     });
3361     this.ghost = Roo.get(this.el.dom.childNodes[1]);
3362     this.dropStatus = this.dropNotAllowed;
3363 };
3364
3365 Roo.dd.StatusProxy.prototype = {
3366     /**
3367      * @cfg {String} dropAllowed
3368      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
3369      */
3370     dropAllowed : "x-dd-drop-ok",
3371     /**
3372      * @cfg {String} dropNotAllowed
3373      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
3374      */
3375     dropNotAllowed : "x-dd-drop-nodrop",
3376
3377     /**
3378      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
3379      * over the current target element.
3380      * @param {String} cssClass The css class for the new drop status indicator image
3381      */
3382     setStatus : function(cssClass){
3383         cssClass = cssClass || this.dropNotAllowed;
3384         if(this.dropStatus != cssClass){
3385             this.el.replaceClass(this.dropStatus, cssClass);
3386             this.dropStatus = cssClass;
3387         }
3388     },
3389
3390     /**
3391      * Resets the status indicator to the default dropNotAllowed value
3392      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
3393      */
3394     reset : function(clearGhost){
3395         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
3396         this.dropStatus = this.dropNotAllowed;
3397         if(clearGhost){
3398             this.ghost.update("");
3399         }
3400     },
3401
3402     /**
3403      * Updates the contents of the ghost element
3404      * @param {String} html The html that will replace the current innerHTML of the ghost element
3405      */
3406     update : function(html){
3407         if(typeof html == "string"){
3408             this.ghost.update(html);
3409         }else{
3410             this.ghost.update("");
3411             html.style.margin = "0";
3412             this.ghost.dom.appendChild(html);
3413         }
3414         // ensure float = none set?? cant remember why though.
3415         var el = this.ghost.dom.firstChild;
3416                 if(el){
3417                         Roo.fly(el).setStyle('float', 'none');
3418                 }
3419     },
3420     
3421     /**
3422      * Returns the underlying proxy {@link Roo.Layer}
3423      * @return {Roo.Layer} el
3424     */
3425     getEl : function(){
3426         return this.el;
3427     },
3428
3429     /**
3430      * Returns the ghost element
3431      * @return {Roo.Element} el
3432      */
3433     getGhost : function(){
3434         return this.ghost;
3435     },
3436
3437     /**
3438      * Hides the proxy
3439      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
3440      */
3441     hide : function(clear){
3442         this.el.hide();
3443         if(clear){
3444             this.reset(true);
3445         }
3446     },
3447
3448     /**
3449      * Stops the repair animation if it's currently running
3450      */
3451     stop : function(){
3452         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
3453             this.anim.stop();
3454         }
3455     },
3456
3457     /**
3458      * Displays this proxy
3459      */
3460     show : function(){
3461         this.el.show();
3462     },
3463
3464     /**
3465      * Force the Layer to sync its shadow and shim positions to the element
3466      */
3467     sync : function(){
3468         this.el.sync();
3469     },
3470
3471     /**
3472      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
3473      * invalid drop operation by the item being dragged.
3474      * @param {Array} xy The XY position of the element ([x, y])
3475      * @param {Function} callback The function to call after the repair is complete
3476      * @param {Object} scope The scope in which to execute the callback
3477      */
3478     repair : function(xy, callback, scope){
3479         this.callback = callback;
3480         this.scope = scope;
3481         if(xy && this.animRepair !== false){
3482             this.el.addClass("x-dd-drag-repair");
3483             this.el.hideUnders(true);
3484             this.anim = this.el.shift({
3485                 duration: this.repairDuration || .5,
3486                 easing: 'easeOut',
3487                 xy: xy,
3488                 stopFx: true,
3489                 callback: this.afterRepair,
3490                 scope: this
3491             });
3492         }else{
3493             this.afterRepair();
3494         }
3495     },
3496
3497     // private
3498     afterRepair : function(){
3499         this.hide(true);
3500         if(typeof this.callback == "function"){
3501             this.callback.call(this.scope || this);
3502         }
3503         this.callback = null;
3504         this.scope = null;
3505     }
3506 };/*
3507  * Based on:
3508  * Ext JS Library 1.1.1
3509  * Copyright(c) 2006-2007, Ext JS, LLC.
3510  *
3511  * Originally Released Under LGPL - original licence link has changed is not relivant.
3512  *
3513  * Fork - LGPL
3514  * <script type="text/javascript">
3515  */
3516
3517 /**
3518  * @class Roo.dd.DragSource
3519  * @extends Roo.dd.DDProxy
3520  * A simple class that provides the basic implementation needed to make any element draggable.
3521  * @constructor
3522  * @param {String/HTMLElement/Element} el The container element
3523  * @param {Object} config
3524  */
3525 Roo.dd.DragSource = function(el, config){
3526     this.el = Roo.get(el);
3527     this.dragData = {};
3528     
3529     Roo.apply(this, config);
3530     
3531     if(!this.proxy){
3532         this.proxy = new Roo.dd.StatusProxy();
3533     }
3534
3535     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
3536           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
3537     
3538     this.dragging = false;
3539 };
3540
3541 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
3542     /**
3543      * @cfg {String} dropAllowed
3544      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3545      */
3546     dropAllowed : "x-dd-drop-ok",
3547     /**
3548      * @cfg {String} dropNotAllowed
3549      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3550      */
3551     dropNotAllowed : "x-dd-drop-nodrop",
3552
3553     /**
3554      * Returns the data object associated with this drag source
3555      * @return {Object} data An object containing arbitrary data
3556      */
3557     getDragData : function(e){
3558         return this.dragData;
3559     },
3560
3561     // private
3562     onDragEnter : function(e, id){
3563         var target = Roo.dd.DragDropMgr.getDDById(id);
3564         this.cachedTarget = target;
3565         if(this.beforeDragEnter(target, e, id) !== false){
3566             if(target.isNotifyTarget){
3567                 var status = target.notifyEnter(this, e, this.dragData);
3568                 this.proxy.setStatus(status);
3569             }else{
3570                 this.proxy.setStatus(this.dropAllowed);
3571             }
3572             
3573             if(this.afterDragEnter){
3574                 /**
3575                  * An empty function by default, but provided so that you can perform a custom action
3576                  * when the dragged item enters the drop target by providing an implementation.
3577                  * @param {Roo.dd.DragDrop} target The drop target
3578                  * @param {Event} e The event object
3579                  * @param {String} id The id of the dragged element
3580                  * @method afterDragEnter
3581                  */
3582                 this.afterDragEnter(target, e, id);
3583             }
3584         }
3585     },
3586
3587     /**
3588      * An empty function by default, but provided so that you can perform a custom action
3589      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
3590      * @param {Roo.dd.DragDrop} target The drop target
3591      * @param {Event} e The event object
3592      * @param {String} id The id of the dragged element
3593      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3594      */
3595     beforeDragEnter : function(target, e, id){
3596         return true;
3597     },
3598
3599     // private
3600     alignElWithMouse: function() {
3601         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
3602         this.proxy.sync();
3603     },
3604
3605     // private
3606     onDragOver : function(e, id){
3607         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3608         if(this.beforeDragOver(target, e, id) !== false){
3609             if(target.isNotifyTarget){
3610                 var status = target.notifyOver(this, e, this.dragData);
3611                 this.proxy.setStatus(status);
3612             }
3613
3614             if(this.afterDragOver){
3615                 /**
3616                  * An empty function by default, but provided so that you can perform a custom action
3617                  * while the dragged item is over the drop target by providing an implementation.
3618                  * @param {Roo.dd.DragDrop} target The drop target
3619                  * @param {Event} e The event object
3620                  * @param {String} id The id of the dragged element
3621                  * @method afterDragOver
3622                  */
3623                 this.afterDragOver(target, e, id);
3624             }
3625         }
3626     },
3627
3628     /**
3629      * An empty function by default, but provided so that you can perform a custom action
3630      * while the dragged item is over the drop target and optionally cancel the onDragOver.
3631      * @param {Roo.dd.DragDrop} target The drop target
3632      * @param {Event} e The event object
3633      * @param {String} id The id of the dragged element
3634      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3635      */
3636     beforeDragOver : function(target, e, id){
3637         return true;
3638     },
3639
3640     // private
3641     onDragOut : function(e, id){
3642         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3643         if(this.beforeDragOut(target, e, id) !== false){
3644             if(target.isNotifyTarget){
3645                 target.notifyOut(this, e, this.dragData);
3646             }
3647             this.proxy.reset();
3648             if(this.afterDragOut){
3649                 /**
3650                  * An empty function by default, but provided so that you can perform a custom action
3651                  * after the dragged item is dragged out of the target without dropping.
3652                  * @param {Roo.dd.DragDrop} target The drop target
3653                  * @param {Event} e The event object
3654                  * @param {String} id The id of the dragged element
3655                  * @method afterDragOut
3656                  */
3657                 this.afterDragOut(target, e, id);
3658             }
3659         }
3660         this.cachedTarget = null;
3661     },
3662
3663     /**
3664      * An empty function by default, but provided so that you can perform a custom action before the dragged
3665      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
3666      * @param {Roo.dd.DragDrop} target The drop target
3667      * @param {Event} e The event object
3668      * @param {String} id The id of the dragged element
3669      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3670      */
3671     beforeDragOut : function(target, e, id){
3672         return true;
3673     },
3674     
3675     // private
3676     onDragDrop : function(e, id){
3677         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3678         if(this.beforeDragDrop(target, e, id) !== false){
3679             if(target.isNotifyTarget){
3680                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
3681                     this.onValidDrop(target, e, id);
3682                 }else{
3683                     this.onInvalidDrop(target, e, id);
3684                 }
3685             }else{
3686                 this.onValidDrop(target, e, id);
3687             }
3688             
3689             if(this.afterDragDrop){
3690                 /**
3691                  * An empty function by default, but provided so that you can perform a custom action
3692                  * after a valid drag drop has occurred by providing an implementation.
3693                  * @param {Roo.dd.DragDrop} target The drop target
3694                  * @param {Event} e The event object
3695                  * @param {String} id The id of the dropped element
3696                  * @method afterDragDrop
3697                  */
3698                 this.afterDragDrop(target, e, id);
3699             }
3700         }
3701         delete this.cachedTarget;
3702     },
3703
3704     /**
3705      * An empty function by default, but provided so that you can perform a custom action before the dragged
3706      * item is dropped onto the target and optionally cancel the onDragDrop.
3707      * @param {Roo.dd.DragDrop} target The drop target
3708      * @param {Event} e The event object
3709      * @param {String} id The id of the dragged element
3710      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
3711      */
3712     beforeDragDrop : function(target, e, id){
3713         return true;
3714     },
3715
3716     // private
3717     onValidDrop : function(target, e, id){
3718         this.hideProxy();
3719         if(this.afterValidDrop){
3720             /**
3721              * An empty function by default, but provided so that you can perform a custom action
3722              * after a valid drop has occurred by providing an implementation.
3723              * @param {Object} target The target DD 
3724              * @param {Event} e The event object
3725              * @param {String} id The id of the dropped element
3726              * @method afterInvalidDrop
3727              */
3728             this.afterValidDrop(target, e, id);
3729         }
3730     },
3731
3732     // private
3733     getRepairXY : function(e, data){
3734         return this.el.getXY();  
3735     },
3736
3737     // private
3738     onInvalidDrop : function(target, e, id){
3739         this.beforeInvalidDrop(target, e, id);
3740         if(this.cachedTarget){
3741             if(this.cachedTarget.isNotifyTarget){
3742                 this.cachedTarget.notifyOut(this, e, this.dragData);
3743             }
3744             this.cacheTarget = null;
3745         }
3746         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
3747
3748         if(this.afterInvalidDrop){
3749             /**
3750              * An empty function by default, but provided so that you can perform a custom action
3751              * after an invalid drop has occurred by providing an implementation.
3752              * @param {Event} e The event object
3753              * @param {String} id The id of the dropped element
3754              * @method afterInvalidDrop
3755              */
3756             this.afterInvalidDrop(e, id);
3757         }
3758     },
3759
3760     // private
3761     afterRepair : function(){
3762         if(Roo.enableFx){
3763             this.el.highlight(this.hlColor || "c3daf9");
3764         }
3765         this.dragging = false;
3766     },
3767
3768     /**
3769      * An empty function by default, but provided so that you can perform a custom action after an invalid
3770      * drop has occurred.
3771      * @param {Roo.dd.DragDrop} target The drop target
3772      * @param {Event} e The event object
3773      * @param {String} id The id of the dragged element
3774      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
3775      */
3776     beforeInvalidDrop : function(target, e, id){
3777         return true;
3778     },
3779
3780     // private
3781     handleMouseDown : function(e){
3782         if(this.dragging) {
3783             return;
3784         }
3785         var data = this.getDragData(e);
3786         if(data && this.onBeforeDrag(data, e) !== false){
3787             this.dragData = data;
3788             this.proxy.stop();
3789             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
3790         } 
3791     },
3792
3793     /**
3794      * An empty function by default, but provided so that you can perform a custom action before the initial
3795      * drag event begins and optionally cancel it.
3796      * @param {Object} data An object containing arbitrary data to be shared with drop targets
3797      * @param {Event} e The event object
3798      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3799      */
3800     onBeforeDrag : function(data, e){
3801         return true;
3802     },
3803
3804     /**
3805      * An empty function by default, but provided so that you can perform a custom action once the initial
3806      * drag event has begun.  The drag cannot be canceled from this function.
3807      * @param {Number} x The x position of the click on the dragged object
3808      * @param {Number} y The y position of the click on the dragged object
3809      */
3810     onStartDrag : Roo.emptyFn,
3811
3812     // private - YUI override
3813     startDrag : function(x, y){
3814         this.proxy.reset();
3815         this.dragging = true;
3816         this.proxy.update("");
3817         this.onInitDrag(x, y);
3818         this.proxy.show();
3819     },
3820
3821     // private
3822     onInitDrag : function(x, y){
3823         var clone = this.el.dom.cloneNode(true);
3824         clone.id = Roo.id(); // prevent duplicate ids
3825         this.proxy.update(clone);
3826         this.onStartDrag(x, y);
3827         return true;
3828     },
3829
3830     /**
3831      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
3832      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
3833      */
3834     getProxy : function(){
3835         return this.proxy;  
3836     },
3837
3838     /**
3839      * Hides the drag source's {@link Roo.dd.StatusProxy}
3840      */
3841     hideProxy : function(){
3842         this.proxy.hide();  
3843         this.proxy.reset(true);
3844         this.dragging = false;
3845     },
3846
3847     // private
3848     triggerCacheRefresh : function(){
3849         Roo.dd.DDM.refreshCache(this.groups);
3850     },
3851
3852     // private - override to prevent hiding
3853     b4EndDrag: function(e) {
3854     },
3855
3856     // private - override to prevent moving
3857     endDrag : function(e){
3858         this.onEndDrag(this.dragData, e);
3859     },
3860
3861     // private
3862     onEndDrag : function(data, e){
3863     },
3864     
3865     // private - pin to cursor
3866     autoOffset : function(x, y) {
3867         this.setDelta(-12, -20);
3868     }    
3869 });/*
3870  * Based on:
3871  * Ext JS Library 1.1.1
3872  * Copyright(c) 2006-2007, Ext JS, LLC.
3873  *
3874  * Originally Released Under LGPL - original licence link has changed is not relivant.
3875  *
3876  * Fork - LGPL
3877  * <script type="text/javascript">
3878  */
3879
3880
3881 /**
3882  * @class Roo.dd.DropTarget
3883  * @extends Roo.dd.DDTarget
3884  * A simple class that provides the basic implementation needed to make any element a drop target that can have
3885  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
3886  * @constructor
3887  * @param {String/HTMLElement/Element} el The container element
3888  * @param {Object} config
3889  */
3890 Roo.dd.DropTarget = function(el, config){
3891     this.el = Roo.get(el);
3892     
3893     Roo.apply(this, config);
3894     
3895     if(this.containerScroll){
3896         Roo.dd.ScrollManager.register(this.el);
3897     }
3898     
3899      
3900     Roo.dd.DropTarget.superclass.constructor.call(  this, 
3901         this.el.dom, 
3902         this.ddGroup || this.group,
3903         {
3904             isTarget: true,
3905             events : {
3906                  /**
3907                  * @scope Roo.dd.DropTarget
3908                  */
3909                  
3910                  /**
3911                  * @event enter
3912                  * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
3913                  * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
3914                  * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
3915                  * 
3916                  * IMPORTANT : it should set this.overClass and this.dropAllowed
3917                  * 
3918                  * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3919                  * @param {Event} e The event
3920                  * @param {Object} data An object containing arbitrary data supplied by the drag source
3921                  */
3922                 "enter" : true,
3923                 
3924                  /**
3925                  * @event over
3926                  * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
3927                  * This method will be called on every mouse movement while the drag source is over the drop target.
3928                  * This default implementation simply returns the dropAllowed config value.
3929                  * 
3930                  * IMPORTANT : it should set this.dropAllowed
3931                  * 
3932                  * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3933                  * @param {Event} e The event
3934                  * @param {Object} data An object containing arbitrary data supplied by the drag source
3935                  
3936                  */
3937                 "over" : true,
3938                 /**
3939                  * @event out
3940                  * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
3941                  * out of the target without dropping.  This default implementation simply removes the CSS class specified by
3942                  * overClass (if any) from the drop element.
3943                  * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3944                  * @param {Event} e The event
3945                  * @param {Object} data An object containing arbitrary data supplied by the drag source
3946                  */
3947                  "out" : true,
3948                  
3949                 /**
3950                  * @event drop
3951                  * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
3952                  * been dropped on it.  This method has no default implementation and returns false, so you must provide an
3953                  * implementation that does something to process the drop event and returns true so that the drag source's
3954                  * repair action does not run.
3955                  * 
3956                  * IMPORTANT : it should set this.success
3957                  * 
3958                  * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3959                  * @param {Event} e The event
3960                  * @param {Object} data An object containing arbitrary data supplied by the drag source
3961                 */
3962                  "drop" : true
3963             }
3964                 
3965         
3966         }
3967     );
3968
3969 };
3970
3971 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
3972     /**
3973      * @cfg {String} overClass
3974      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
3975      */
3976      /**
3977      * @cfg {String} ddGroup
3978      * The drag drop group to handle drop events for
3979      */
3980      
3981     /**
3982      * @cfg {String} dropAllowed
3983      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3984      */
3985     dropAllowed : "x-dd-drop-ok",
3986     /**
3987      * @cfg {String} dropNotAllowed
3988      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3989      */
3990     dropNotAllowed : "x-dd-drop-nodrop",
3991     /**
3992      * @cfg {boolean} success
3993      * set this after drop listener.. 
3994      */
3995     success : false,
3996     /**
3997      * @cfg {boolean} valid
3998      * if the drop point is valid for over/enter..
3999      */
4000     valid : false,
4001     // private
4002     isTarget : true,
4003
4004     // private
4005     isNotifyTarget : true,
4006     
4007     /**
4008      * @hide
4009      */
4010     notifyEnter : function(dd, e, data){
4011         this.valid = true;
4012         this.fireEvent('enter', this, dd, e, data);
4013         if(this.overClass){
4014             this.el.addClass(this.overClass);
4015         }
4016         return this.valid ? this.dropAllowed : this.dropNotAllowed;
4017     },
4018
4019     /**
4020      * @hide
4021      */
4022     notifyOver : function(dd, e, data){
4023         this.valid = true;
4024         this.fireEvent('over', this, dd, e, data);
4025         return this.valid ? this.dropAllowed : this.dropNotAllowed;
4026     },
4027
4028     /**
4029      * @hide
4030      */
4031     notifyOut : function(dd, e, data){
4032         this.fireEvent('out', this, dd, e, data);
4033         if(this.overClass){
4034             this.el.removeClass(this.overClass);
4035         }
4036     },
4037
4038     /**
4039      * @hide
4040      */
4041     notifyDrop : function(dd, e, data){
4042         this.success = false;
4043         this.fireEvent('drop', this, dd, e, data);
4044         return this.success;
4045     }
4046 });/*
4047  * Based on:
4048  * Ext JS Library 1.1.1
4049  * Copyright(c) 2006-2007, Ext JS, LLC.
4050  *
4051  * Originally Released Under LGPL - original licence link has changed is not relivant.
4052  *
4053  * Fork - LGPL
4054  * <script type="text/javascript">
4055  */
4056
4057
4058 /**
4059  * @class Roo.dd.DragZone
4060  * @extends Roo.dd.DragSource
4061  * This class provides a container DD instance that proxies for multiple child node sources.<br />
4062  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
4063  * @constructor
4064  * @param {String/HTMLElement/Element} el The container element
4065  * @param {Object} config
4066  */
4067 Roo.dd.DragZone = function(el, config){
4068     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
4069     if(this.containerScroll){
4070         Roo.dd.ScrollManager.register(this.el);
4071     }
4072 };
4073
4074 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
4075     /**
4076      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
4077      * for auto scrolling during drag operations.
4078      */
4079     /**
4080      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
4081      * method after a failed drop (defaults to "c3daf9" - light blue)
4082      */
4083
4084     /**
4085      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
4086      * for a valid target to drag based on the mouse down. Override this method
4087      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
4088      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
4089      * @param {EventObject} e The mouse down event
4090      * @return {Object} The dragData
4091      */
4092     getDragData : function(e){
4093         return Roo.dd.Registry.getHandleFromEvent(e);
4094     },
4095     
4096     /**
4097      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
4098      * this.dragData.ddel
4099      * @param {Number} x The x position of the click on the dragged object
4100      * @param {Number} y The y position of the click on the dragged object
4101      * @return {Boolean} true to continue the drag, false to cancel
4102      */
4103     onInitDrag : function(x, y){
4104         this.proxy.update(this.dragData.ddel.cloneNode(true));
4105         this.onStartDrag(x, y);
4106         return true;
4107     },
4108     
4109     /**
4110      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
4111      */
4112     afterRepair : function(){
4113         if(Roo.enableFx){
4114             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
4115         }
4116         this.dragging = false;
4117     },
4118
4119     /**
4120      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
4121      * the XY of this.dragData.ddel
4122      * @param {EventObject} e The mouse up event
4123      * @return {Array} The xy location (e.g. [100, 200])
4124      */
4125     getRepairXY : function(e){
4126         return Roo.Element.fly(this.dragData.ddel).getXY();  
4127     }
4128 });/*
4129  * Based on:
4130  * Ext JS Library 1.1.1
4131  * Copyright(c) 2006-2007, Ext JS, LLC.
4132  *
4133  * Originally Released Under LGPL - original licence link has changed is not relivant.
4134  *
4135  * Fork - LGPL
4136  * <script type="text/javascript">
4137  */
4138 /**
4139  * @class Roo.dd.DropZone
4140  * @extends Roo.dd.DropTarget
4141  * This class provides a container DD instance that proxies for multiple child node targets.<br />
4142  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
4143  * @constructor
4144  * @param {String/HTMLElement/Element} el The container element
4145  * @param {Object} config
4146  */
4147 Roo.dd.DropZone = function(el, config){
4148     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
4149 };
4150
4151 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
4152     /**
4153      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
4154      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
4155      * provide your own custom lookup.
4156      * @param {Event} e The event
4157      * @return {Object} data The custom data
4158      */
4159     getTargetFromEvent : function(e){
4160         return Roo.dd.Registry.getTargetFromEvent(e);
4161     },
4162
4163     /**
4164      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
4165      * that it has registered.  This method has no default implementation and should be overridden to provide
4166      * node-specific processing if necessary.
4167      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
4168      * {@link #getTargetFromEvent} for this node)
4169      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4170      * @param {Event} e The event
4171      * @param {Object} data An object containing arbitrary data supplied by the drag source
4172      */
4173     onNodeEnter : function(n, dd, e, data){
4174         
4175     },
4176
4177     /**
4178      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
4179      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
4180      * overridden to provide the proper feedback.
4181      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4182      * {@link #getTargetFromEvent} for this node)
4183      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4184      * @param {Event} e The event
4185      * @param {Object} data An object containing arbitrary data supplied by the drag source
4186      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4187      * underlying {@link Roo.dd.StatusProxy} can be updated
4188      */
4189     onNodeOver : function(n, dd, e, data){
4190         return this.dropAllowed;
4191     },
4192
4193     /**
4194      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
4195      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
4196      * node-specific processing if necessary.
4197      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4198      * {@link #getTargetFromEvent} for this node)
4199      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4200      * @param {Event} e The event
4201      * @param {Object} data An object containing arbitrary data supplied by the drag source
4202      */
4203     onNodeOut : function(n, dd, e, data){
4204         
4205     },
4206
4207     /**
4208      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
4209      * the drop node.  The default implementation returns false, so it should be overridden to provide the
4210      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
4211      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4212      * {@link #getTargetFromEvent} for this node)
4213      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4214      * @param {Event} e The event
4215      * @param {Object} data An object containing arbitrary data supplied by the drag source
4216      * @return {Boolean} True if the drop was valid, else false
4217      */
4218     onNodeDrop : function(n, dd, e, data){
4219         return false;
4220     },
4221
4222     /**
4223      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
4224      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
4225      * it should be overridden to provide the proper feedback if necessary.
4226      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4227      * @param {Event} e The event
4228      * @param {Object} data An object containing arbitrary data supplied by the drag source
4229      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4230      * underlying {@link Roo.dd.StatusProxy} can be updated
4231      */
4232     onContainerOver : function(dd, e, data){
4233         return this.dropNotAllowed;
4234     },
4235
4236     /**
4237      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
4238      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
4239      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
4240      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
4241      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4242      * @param {Event} e The event
4243      * @param {Object} data An object containing arbitrary data supplied by the drag source
4244      * @return {Boolean} True if the drop was valid, else false
4245      */
4246     onContainerDrop : function(dd, e, data){
4247         return false;
4248     },
4249
4250     /**
4251      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
4252      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
4253      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
4254      * you should override this method and provide a custom implementation.
4255      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4256      * @param {Event} e The event
4257      * @param {Object} data An object containing arbitrary data supplied by the drag source
4258      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4259      * underlying {@link Roo.dd.StatusProxy} can be updated
4260      */
4261     notifyEnter : function(dd, e, data){
4262         return this.dropNotAllowed;
4263     },
4264
4265     /**
4266      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
4267      * This method will be called on every mouse movement while the drag source is over the drop zone.
4268      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
4269      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
4270      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
4271      * registered node, it will call {@link #onContainerOver}.
4272      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4273      * @param {Event} e The event
4274      * @param {Object} data An object containing arbitrary data supplied by the drag source
4275      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4276      * underlying {@link Roo.dd.StatusProxy} can be updated
4277      */
4278     notifyOver : function(dd, e, data){
4279         var n = this.getTargetFromEvent(e);
4280         if(!n){ // not over valid drop target
4281             if(this.lastOverNode){
4282                 this.onNodeOut(this.lastOverNode, dd, e, data);
4283                 this.lastOverNode = null;
4284             }
4285             return this.onContainerOver(dd, e, data);
4286         }
4287         if(this.lastOverNode != n){
4288             if(this.lastOverNode){
4289                 this.onNodeOut(this.lastOverNode, dd, e, data);
4290             }
4291             this.onNodeEnter(n, dd, e, data);
4292             this.lastOverNode = n;
4293         }
4294         return this.onNodeOver(n, dd, e, data);
4295     },
4296
4297     /**
4298      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
4299      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
4300      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
4301      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
4302      * @param {Event} e The event
4303      * @param {Object} data An object containing arbitrary data supplied by the drag zone
4304      */
4305     notifyOut : function(dd, e, data){
4306         if(this.lastOverNode){
4307             this.onNodeOut(this.lastOverNode, dd, e, data);
4308             this.lastOverNode = null;
4309         }
4310     },
4311
4312     /**
4313      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
4314      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
4315      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
4316      * otherwise it will call {@link #onContainerDrop}.
4317      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4318      * @param {Event} e The event
4319      * @param {Object} data An object containing arbitrary data supplied by the drag source
4320      * @return {Boolean} True if the drop was valid, else false
4321      */
4322     notifyDrop : function(dd, e, data){
4323         if(this.lastOverNode){
4324             this.onNodeOut(this.lastOverNode, dd, e, data);
4325             this.lastOverNode = null;
4326         }
4327         var n = this.getTargetFromEvent(e);
4328         return n ?
4329             this.onNodeDrop(n, dd, e, data) :
4330             this.onContainerDrop(dd, e, data);
4331     },
4332
4333     // private
4334     triggerCacheRefresh : function(){
4335         Roo.dd.DDM.refreshCache(this.groups);
4336     }  
4337 });/*
4338  * Based on:
4339  * Ext JS Library 1.1.1
4340  * Copyright(c) 2006-2007, Ext JS, LLC.
4341  *
4342  * Originally Released Under LGPL - original licence link has changed is not relivant.
4343  *
4344  * Fork - LGPL
4345  * <script type="text/javascript">
4346  */
4347
4348
4349 /**
4350  * @class Roo.data.SortTypes
4351  * @singleton
4352  * Defines the default sorting (casting?) comparison functions used when sorting data.
4353  */
4354 Roo.data.SortTypes = {
4355     /**
4356      * Default sort that does nothing
4357      * @param {Mixed} s The value being converted
4358      * @return {Mixed} The comparison value
4359      */
4360     none : function(s){
4361         return s;
4362     },
4363     
4364     /**
4365      * The regular expression used to strip tags
4366      * @type {RegExp}
4367      * @property
4368      */
4369     stripTagsRE : /<\/?[^>]+>/gi,
4370     
4371     /**
4372      * Strips all HTML tags to sort on text only
4373      * @param {Mixed} s The value being converted
4374      * @return {String} The comparison value
4375      */
4376     asText : function(s){
4377         return String(s).replace(this.stripTagsRE, "");
4378     },
4379     
4380     /**
4381      * Strips all HTML tags to sort on text only - Case insensitive
4382      * @param {Mixed} s The value being converted
4383      * @return {String} The comparison value
4384      */
4385     asUCText : function(s){
4386         return String(s).toUpperCase().replace(this.stripTagsRE, "");
4387     },
4388     
4389     /**
4390      * Case insensitive string
4391      * @param {Mixed} s The value being converted
4392      * @return {String} The comparison value
4393      */
4394     asUCString : function(s) {
4395         return String(s).toUpperCase();
4396     },
4397     
4398     /**
4399      * Date sorting
4400      * @param {Mixed} s The value being converted
4401      * @return {Number} The comparison value
4402      */
4403     asDate : function(s) {
4404         if(!s){
4405             return 0;
4406         }
4407         if(s instanceof Date){
4408             return s.getTime();
4409         }
4410         return Date.parse(String(s));
4411     },
4412     
4413     /**
4414      * Float sorting
4415      * @param {Mixed} s The value being converted
4416      * @return {Float} The comparison value
4417      */
4418     asFloat : function(s) {
4419         var val = parseFloat(String(s).replace(/,/g, ""));
4420         if(isNaN(val)) val = 0;
4421         return val;
4422     },
4423     
4424     /**
4425      * Integer sorting
4426      * @param {Mixed} s The value being converted
4427      * @return {Number} The comparison value
4428      */
4429     asInt : function(s) {
4430         var val = parseInt(String(s).replace(/,/g, ""));
4431         if(isNaN(val)) val = 0;
4432         return val;
4433     }
4434 };/*
4435  * Based on:
4436  * Ext JS Library 1.1.1
4437  * Copyright(c) 2006-2007, Ext JS, LLC.
4438  *
4439  * Originally Released Under LGPL - original licence link has changed is not relivant.
4440  *
4441  * Fork - LGPL
4442  * <script type="text/javascript">
4443  */
4444
4445 /**
4446 * @class Roo.data.Record
4447  * Instances of this class encapsulate both record <em>definition</em> information, and record
4448  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
4449  * to access Records cached in an {@link Roo.data.Store} object.<br>
4450  * <p>
4451  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
4452  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
4453  * objects.<br>
4454  * <p>
4455  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
4456  * @constructor
4457  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
4458  * {@link #create}. The parameters are the same.
4459  * @param {Array} data An associative Array of data values keyed by the field name.
4460  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
4461  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
4462  * not specified an integer id is generated.
4463  */
4464 Roo.data.Record = function(data, id){
4465     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
4466     this.data = data;
4467 };
4468
4469 /**
4470  * Generate a constructor for a specific record layout.
4471  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
4472  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
4473  * Each field definition object may contain the following properties: <ul>
4474  * <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,
4475  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
4476  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
4477  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
4478  * is being used, then this is a string containing the javascript expression to reference the data relative to 
4479  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
4480  * to the data item relative to the record element. If the mapping expression is the same as the field name,
4481  * this may be omitted.</p></li>
4482  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
4483  * <ul><li>auto (Default, implies no conversion)</li>
4484  * <li>string</li>
4485  * <li>int</li>
4486  * <li>float</li>
4487  * <li>boolean</li>
4488  * <li>date</li></ul></p></li>
4489  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
4490  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
4491  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
4492  * by the Reader into an object that will be stored in the Record. It is passed the
4493  * following parameters:<ul>
4494  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
4495  * </ul></p></li>
4496  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
4497  * </ul>
4498  * <br>usage:<br><pre><code>
4499 var TopicRecord = Roo.data.Record.create(
4500     {name: 'title', mapping: 'topic_title'},
4501     {name: 'author', mapping: 'username'},
4502     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
4503     {name: 'lastPost', mapping: 'post_time', type: 'date'},
4504     {name: 'lastPoster', mapping: 'user2'},
4505     {name: 'excerpt', mapping: 'post_text'}
4506 );
4507
4508 var myNewRecord = new TopicRecord({
4509     title: 'Do my job please',
4510     author: 'noobie',
4511     totalPosts: 1,
4512     lastPost: new Date(),
4513     lastPoster: 'Animal',
4514     excerpt: 'No way dude!'
4515 });
4516 myStore.add(myNewRecord);
4517 </code></pre>
4518  * @method create
4519  * @static
4520  */
4521 Roo.data.Record.create = function(o){
4522     var f = function(){
4523         f.superclass.constructor.apply(this, arguments);
4524     };
4525     Roo.extend(f, Roo.data.Record);
4526     var p = f.prototype;
4527     p.fields = new Roo.util.MixedCollection(false, function(field){
4528         return field.name;
4529     });
4530     for(var i = 0, len = o.length; i < len; i++){
4531         p.fields.add(new Roo.data.Field(o[i]));
4532     }
4533     f.getField = function(name){
4534         return p.fields.get(name);  
4535     };
4536     return f;
4537 };
4538
4539 Roo.data.Record.AUTO_ID = 1000;
4540 Roo.data.Record.EDIT = 'edit';
4541 Roo.data.Record.REJECT = 'reject';
4542 Roo.data.Record.COMMIT = 'commit';
4543
4544 Roo.data.Record.prototype = {
4545     /**
4546      * Readonly flag - true if this record has been modified.
4547      * @type Boolean
4548      */
4549     dirty : false,
4550     editing : false,
4551     error: null,
4552     modified: null,
4553
4554     // private
4555     join : function(store){
4556         this.store = store;
4557     },
4558
4559     /**
4560      * Set the named field to the specified value.
4561      * @param {String} name The name of the field to set.
4562      * @param {Object} value The value to set the field to.
4563      */
4564     set : function(name, value){
4565         if(this.data[name] == value){
4566             return;
4567         }
4568         this.dirty = true;
4569         if(!this.modified){
4570             this.modified = {};
4571         }
4572         if(typeof this.modified[name] == 'undefined'){
4573             this.modified[name] = this.data[name];
4574         }
4575         this.data[name] = value;
4576         if(!this.editing){
4577             this.store.afterEdit(this);
4578         }       
4579     },
4580
4581     /**
4582      * Get the value of the named field.
4583      * @param {String} name The name of the field to get the value of.
4584      * @return {Object} The value of the field.
4585      */
4586     get : function(name){
4587         return this.data[name]; 
4588     },
4589
4590     // private
4591     beginEdit : function(){
4592         this.editing = true;
4593         this.modified = {}; 
4594     },
4595
4596     // private
4597     cancelEdit : function(){
4598         this.editing = false;
4599         delete this.modified;
4600     },
4601
4602     // private
4603     endEdit : function(){
4604         this.editing = false;
4605         if(this.dirty && this.store){
4606             this.store.afterEdit(this);
4607         }
4608     },
4609
4610     /**
4611      * Usually called by the {@link Roo.data.Store} which owns the Record.
4612      * Rejects all changes made to the Record since either creation, or the last commit operation.
4613      * Modified fields are reverted to their original values.
4614      * <p>
4615      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4616      * of reject operations.
4617      */
4618     reject : function(){
4619         var m = this.modified;
4620         for(var n in m){
4621             if(typeof m[n] != "function"){
4622                 this.data[n] = m[n];
4623             }
4624         }
4625         this.dirty = false;
4626         delete this.modified;
4627         this.editing = false;
4628         if(this.store){
4629             this.store.afterReject(this);
4630         }
4631     },
4632
4633     /**
4634      * Usually called by the {@link Roo.data.Store} which owns the Record.
4635      * Commits all changes made to the Record since either creation, or the last commit operation.
4636      * <p>
4637      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4638      * of commit operations.
4639      */
4640     commit : function(){
4641         this.dirty = false;
4642         delete this.modified;
4643         this.editing = false;
4644         if(this.store){
4645             this.store.afterCommit(this);
4646         }
4647     },
4648
4649     // private
4650     hasError : function(){
4651         return this.error != null;
4652     },
4653
4654     // private
4655     clearError : function(){
4656         this.error = null;
4657     },
4658
4659     /**
4660      * Creates a copy of this record.
4661      * @param {String} id (optional) A new record id if you don't want to use this record's id
4662      * @return {Record}
4663      */
4664     copy : function(newId) {
4665         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
4666     }
4667 };/*
4668  * Based on:
4669  * Ext JS Library 1.1.1
4670  * Copyright(c) 2006-2007, Ext JS, LLC.
4671  *
4672  * Originally Released Under LGPL - original licence link has changed is not relivant.
4673  *
4674  * Fork - LGPL
4675  * <script type="text/javascript">
4676  */
4677
4678
4679
4680 /**
4681  * @class Roo.data.Store
4682  * @extends Roo.util.Observable
4683  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
4684  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
4685  * <p>
4686  * 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
4687  * has no knowledge of the format of the data returned by the Proxy.<br>
4688  * <p>
4689  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
4690  * instances from the data object. These records are cached and made available through accessor functions.
4691  * @constructor
4692  * Creates a new Store.
4693  * @param {Object} config A config object containing the objects needed for the Store to access data,
4694  * and read the data into Records.
4695  */
4696 Roo.data.Store = function(config){
4697     this.data = new Roo.util.MixedCollection(false);
4698     this.data.getKey = function(o){
4699         return o.id;
4700     };
4701     this.baseParams = {};
4702     // private
4703     this.paramNames = {
4704         "start" : "start",
4705         "limit" : "limit",
4706         "sort" : "sort",
4707         "dir" : "dir"
4708     };
4709
4710     if(config && config.data){
4711         this.inlineData = config.data;
4712         delete config.data;
4713     }
4714
4715     Roo.apply(this, config);
4716     
4717     if(this.reader){ // reader passed
4718         this.reader = Roo.factory(this.reader, Roo.data);
4719         this.reader.xmodule = this.xmodule || false;
4720         if(!this.recordType){
4721             this.recordType = this.reader.recordType;
4722         }
4723         if(this.reader.onMetaChange){
4724             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
4725         }
4726     }
4727
4728     if(this.recordType){
4729         this.fields = this.recordType.prototype.fields;
4730     }
4731     this.modified = [];
4732
4733     this.addEvents({
4734         /**
4735          * @event datachanged
4736          * Fires when the data cache has changed, and a widget which is using this Store
4737          * as a Record cache should refresh its view.
4738          * @param {Store} this
4739          */
4740         datachanged : true,
4741         /**
4742          * @event metachange
4743          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
4744          * @param {Store} this
4745          * @param {Object} meta The JSON metadata
4746          */
4747         metachange : true,
4748         /**
4749          * @event add
4750          * Fires when Records have been added to the Store
4751          * @param {Store} this
4752          * @param {Roo.data.Record[]} records The array of Records added
4753          * @param {Number} index The index at which the record(s) were added
4754          */
4755         add : true,
4756         /**
4757          * @event remove
4758          * Fires when a Record has been removed from the Store
4759          * @param {Store} this
4760          * @param {Roo.data.Record} record The Record that was removed
4761          * @param {Number} index The index at which the record was removed
4762          */
4763         remove : true,
4764         /**
4765          * @event update
4766          * Fires when a Record has been updated
4767          * @param {Store} this
4768          * @param {Roo.data.Record} record The Record that was updated
4769          * @param {String} operation The update operation being performed.  Value may be one of:
4770          * <pre><code>
4771  Roo.data.Record.EDIT
4772  Roo.data.Record.REJECT
4773  Roo.data.Record.COMMIT
4774          * </code></pre>
4775          */
4776         update : true,
4777         /**
4778          * @event clear
4779          * Fires when the data cache has been cleared.
4780          * @param {Store} this
4781          */
4782         clear : true,
4783         /**
4784          * @event beforeload
4785          * Fires before a request is made for a new data object.  If the beforeload handler returns false
4786          * the load action will be canceled.
4787          * @param {Store} this
4788          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4789          */
4790         beforeload : true,
4791         /**
4792          * @event load
4793          * Fires after a new set of Records has been loaded.
4794          * @param {Store} this
4795          * @param {Roo.data.Record[]} records The Records that were loaded
4796          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4797          */
4798         load : true,
4799         /**
4800          * @event loadexception
4801          * Fires if an exception occurs in the Proxy during loading.
4802          * Called with the signature of the Proxy's "loadexception" event.
4803          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
4804          * 
4805          * @param {Proxy} 
4806          * @param {Object} return from JsonData.reader() - success, totalRecords, records
4807          * @param {Object} load options 
4808          * @param {Object} jsonData from your request (normally this contains the Exception)
4809          */
4810         loadexception : true
4811     });
4812     
4813     if(this.proxy){
4814         this.proxy = Roo.factory(this.proxy, Roo.data);
4815         this.proxy.xmodule = this.xmodule || false;
4816         this.relayEvents(this.proxy,  ["loadexception"]);
4817     }
4818     this.sortToggle = {};
4819
4820     Roo.data.Store.superclass.constructor.call(this);
4821
4822     if(this.inlineData){
4823         this.loadData(this.inlineData);
4824         delete this.inlineData;
4825     }
4826 };
4827 Roo.extend(Roo.data.Store, Roo.util.Observable, {
4828      /**
4829     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
4830     * without a remote query - used by combo/forms at present.
4831     */
4832     
4833     /**
4834     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
4835     */
4836     /**
4837     * @cfg {Array} data Inline data to be loaded when the store is initialized.
4838     */
4839     /**
4840     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
4841     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
4842     */
4843     /**
4844     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
4845     * on any HTTP request
4846     */
4847     /**
4848     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
4849     */
4850     /**
4851     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
4852     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
4853     */
4854     remoteSort : false,
4855
4856     /**
4857     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
4858      * loaded or when a record is removed. (defaults to false).
4859     */
4860     pruneModifiedRecords : false,
4861
4862     // private
4863     lastOptions : null,
4864
4865     /**
4866      * Add Records to the Store and fires the add event.
4867      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4868      */
4869     add : function(records){
4870         records = [].concat(records);
4871         for(var i = 0, len = records.length; i < len; i++){
4872             records[i].join(this);
4873         }
4874         var index = this.data.length;
4875         this.data.addAll(records);
4876         this.fireEvent("add", this, records, index);
4877     },
4878
4879     /**
4880      * Remove a Record from the Store and fires the remove event.
4881      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
4882      */
4883     remove : function(record){
4884         var index = this.data.indexOf(record);
4885         this.data.removeAt(index);
4886         if(this.pruneModifiedRecords){
4887             this.modified.remove(record);
4888         }
4889         this.fireEvent("remove", this, record, index);
4890     },
4891
4892     /**
4893      * Remove all Records from the Store and fires the clear event.
4894      */
4895     removeAll : function(){
4896         this.data.clear();
4897         if(this.pruneModifiedRecords){
4898             this.modified = [];
4899         }
4900         this.fireEvent("clear", this);
4901     },
4902
4903     /**
4904      * Inserts Records to the Store at the given index and fires the add event.
4905      * @param {Number} index The start index at which to insert the passed Records.
4906      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4907      */
4908     insert : function(index, records){
4909         records = [].concat(records);
4910         for(var i = 0, len = records.length; i < len; i++){
4911             this.data.insert(index, records[i]);
4912             records[i].join(this);
4913         }
4914         this.fireEvent("add", this, records, index);
4915     },
4916
4917     /**
4918      * Get the index within the cache of the passed Record.
4919      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
4920      * @return {Number} The index of the passed Record. Returns -1 if not found.
4921      */
4922     indexOf : function(record){
4923         return this.data.indexOf(record);
4924     },
4925
4926     /**
4927      * Get the index within the cache of the Record with the passed id.
4928      * @param {String} id The id of the Record to find.
4929      * @return {Number} The index of the Record. Returns -1 if not found.
4930      */
4931     indexOfId : function(id){
4932         return this.data.indexOfKey(id);
4933     },
4934
4935     /**
4936      * Get the Record with the specified id.
4937      * @param {String} id The id of the Record to find.
4938      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
4939      */
4940     getById : function(id){
4941         return this.data.key(id);
4942     },
4943
4944     /**
4945      * Get the Record at the specified index.
4946      * @param {Number} index The index of the Record to find.
4947      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
4948      */
4949     getAt : function(index){
4950         return this.data.itemAt(index);
4951     },
4952
4953     /**
4954      * Returns a range of Records between specified indices.
4955      * @param {Number} startIndex (optional) The starting index (defaults to 0)
4956      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
4957      * @return {Roo.data.Record[]} An array of Records
4958      */
4959     getRange : function(start, end){
4960         return this.data.getRange(start, end);
4961     },
4962
4963     // private
4964     storeOptions : function(o){
4965         o = Roo.apply({}, o);
4966         delete o.callback;
4967         delete o.scope;
4968         this.lastOptions = o;
4969     },
4970
4971     /**
4972      * Loads the Record cache from the configured Proxy using the configured Reader.
4973      * <p>
4974      * If using remote paging, then the first load call must specify the <em>start</em>
4975      * and <em>limit</em> properties in the options.params property to establish the initial
4976      * position within the dataset, and the number of Records to cache on each read from the Proxy.
4977      * <p>
4978      * <strong>It is important to note that for remote data sources, loading is asynchronous,
4979      * and this call will return before the new data has been loaded. Perform any post-processing
4980      * in a callback function, or in a "load" event handler.</strong>
4981      * <p>
4982      * @param {Object} options An object containing properties which control loading options:<ul>
4983      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
4984      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
4985      * passed the following arguments:<ul>
4986      * <li>r : Roo.data.Record[]</li>
4987      * <li>options: Options object from the load call</li>
4988      * <li>success: Boolean success indicator</li></ul></li>
4989      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
4990      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
4991      * </ul>
4992      */
4993     load : function(options){
4994         options = options || {};
4995         if(this.fireEvent("beforeload", this, options) !== false){
4996             this.storeOptions(options);
4997             var p = Roo.apply(options.params || {}, this.baseParams);
4998             // if meta was not loaded from remote source.. try requesting it.
4999             if (!this.reader.metaFromRemote) {
5000                 p._requestMeta = 1;
5001             }
5002             if(this.sortInfo && this.remoteSort){
5003                 var pn = this.paramNames;
5004                 p[pn["sort"]] = this.sortInfo.field;
5005                 p[pn["dir"]] = this.sortInfo.direction;
5006             }
5007             this.proxy.load(p, this.reader, this.loadRecords, this, options);
5008         }
5009     },
5010
5011     /**
5012      * Reloads the Record cache from the configured Proxy using the configured Reader and
5013      * the options from the last load operation performed.
5014      * @param {Object} options (optional) An object containing properties which may override the options
5015      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
5016      * the most recently used options are reused).
5017      */
5018     reload : function(options){
5019         this.load(Roo.applyIf(options||{}, this.lastOptions));
5020     },
5021
5022     // private
5023     // Called as a callback by the Reader during a load operation.
5024     loadRecords : function(o, options, success){
5025         if(!o || success === false){
5026             if(success !== false){
5027                 this.fireEvent("load", this, [], options);
5028             }
5029             if(options.callback){
5030                 options.callback.call(options.scope || this, [], options, false);
5031             }
5032             return;
5033         }
5034         // if data returned failure - throw an exception.
5035         if (o.success === false) {
5036             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
5037             return;
5038         }
5039         var r = o.records, t = o.totalRecords || r.length;
5040         if(!options || options.add !== true){
5041             if(this.pruneModifiedRecords){
5042                 this.modified = [];
5043             }
5044             for(var i = 0, len = r.length; i < len; i++){
5045                 r[i].join(this);
5046             }
5047             if(this.snapshot){
5048                 this.data = this.snapshot;
5049                 delete this.snapshot;
5050             }
5051             this.data.clear();
5052             this.data.addAll(r);
5053             this.totalLength = t;
5054             this.applySort();
5055             this.fireEvent("datachanged", this);
5056         }else{
5057             this.totalLength = Math.max(t, this.data.length+r.length);
5058             this.add(r);
5059         }
5060         this.fireEvent("load", this, r, options);
5061         if(options.callback){
5062             options.callback.call(options.scope || this, r, options, true);
5063         }
5064     },
5065
5066     /**
5067      * Loads data from a passed data block. A Reader which understands the format of the data
5068      * must have been configured in the constructor.
5069      * @param {Object} data The data block from which to read the Records.  The format of the data expected
5070      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
5071      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
5072      */
5073     loadData : function(o, append){
5074         var r = this.reader.readRecords(o);
5075         this.loadRecords(r, {add: append}, true);
5076     },
5077
5078     /**
5079      * Gets the number of cached records.
5080      * <p>
5081      * <em>If using paging, this may not be the total size of the dataset. If the data object
5082      * used by the Reader contains the dataset size, then the getTotalCount() function returns
5083      * the data set size</em>
5084      */
5085     getCount : function(){
5086         return this.data.length || 0;
5087     },
5088
5089     /**
5090      * Gets the total number of records in the dataset as returned by the server.
5091      * <p>
5092      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
5093      * the dataset size</em>
5094      */
5095     getTotalCount : function(){
5096         return this.totalLength || 0;
5097     },
5098
5099     /**
5100      * Returns the sort state of the Store as an object with two properties:
5101      * <pre><code>
5102  field {String} The name of the field by which the Records are sorted
5103  direction {String} The sort order, "ASC" or "DESC"
5104      * </code></pre>
5105      */
5106     getSortState : function(){
5107         return this.sortInfo;
5108     },
5109
5110     // private
5111     applySort : function(){
5112         if(this.sortInfo && !this.remoteSort){
5113             var s = this.sortInfo, f = s.field;
5114             var st = this.fields.get(f).sortType;
5115             var fn = function(r1, r2){
5116                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
5117                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
5118             };
5119             this.data.sort(s.direction, fn);
5120             if(this.snapshot && this.snapshot != this.data){
5121                 this.snapshot.sort(s.direction, fn);
5122             }
5123         }
5124     },
5125
5126     /**
5127      * Sets the default sort column and order to be used by the next load operation.
5128      * @param {String} fieldName The name of the field to sort by.
5129      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5130      */
5131     setDefaultSort : function(field, dir){
5132         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
5133     },
5134
5135     /**
5136      * Sort the Records.
5137      * If remote sorting is used, the sort is performed on the server, and the cache is
5138      * reloaded. If local sorting is used, the cache is sorted internally.
5139      * @param {String} fieldName The name of the field to sort by.
5140      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5141      */
5142     sort : function(fieldName, dir){
5143         var f = this.fields.get(fieldName);
5144         if(!dir){
5145             if(this.sortInfo && this.sortInfo.field == f.name){ // toggle sort dir
5146                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
5147             }else{
5148                 dir = f.sortDir;
5149             }
5150         }
5151         this.sortToggle[f.name] = dir;
5152         this.sortInfo = {field: f.name, direction: dir};
5153         if(!this.remoteSort){
5154             this.applySort();
5155             this.fireEvent("datachanged", this);
5156         }else{
5157             this.load(this.lastOptions);
5158         }
5159     },
5160
5161     /**
5162      * Calls the specified function for each of the Records in the cache.
5163      * @param {Function} fn The function to call. The Record is passed as the first parameter.
5164      * Returning <em>false</em> aborts and exits the iteration.
5165      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
5166      */
5167     each : function(fn, scope){
5168         this.data.each(fn, scope);
5169     },
5170
5171     /**
5172      * Gets all records modified since the last commit.  Modified records are persisted across load operations
5173      * (e.g., during paging).
5174      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
5175      */
5176     getModifiedRecords : function(){
5177         return this.modified;
5178     },
5179
5180     // private
5181     createFilterFn : function(property, value, anyMatch){
5182         if(!value.exec){ // not a regex
5183             value = String(value);
5184             if(value.length == 0){
5185                 return false;
5186             }
5187             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
5188         }
5189         return function(r){
5190             return value.test(r.data[property]);
5191         };
5192     },
5193
5194     /**
5195      * Sums the value of <i>property</i> for each record between start and end and returns the result.
5196      * @param {String} property A field on your records
5197      * @param {Number} start The record index to start at (defaults to 0)
5198      * @param {Number} end The last record index to include (defaults to length - 1)
5199      * @return {Number} The sum
5200      */
5201     sum : function(property, start, end){
5202         var rs = this.data.items, v = 0;
5203         start = start || 0;
5204         end = (end || end === 0) ? end : rs.length-1;
5205
5206         for(var i = start; i <= end; i++){
5207             v += (rs[i].data[property] || 0);
5208         }
5209         return v;
5210     },
5211
5212     /**
5213      * Filter the records by a specified property.
5214      * @param {String} field A field on your records
5215      * @param {String/RegExp} value Either a string that the field
5216      * should start with or a RegExp to test against the field
5217      * @param {Boolean} anyMatch True to match any part not just the beginning
5218      */
5219     filter : function(property, value, anyMatch){
5220         var fn = this.createFilterFn(property, value, anyMatch);
5221         return fn ? this.filterBy(fn) : this.clearFilter();
5222     },
5223
5224     /**
5225      * Filter by a function. The specified function will be called with each
5226      * record in this data source. If the function returns true the record is included,
5227      * otherwise it is filtered.
5228      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5229      * @param {Object} scope (optional) The scope of the function (defaults to this)
5230      */
5231     filterBy : function(fn, scope){
5232         this.snapshot = this.snapshot || this.data;
5233         this.data = this.queryBy(fn, scope||this);
5234         this.fireEvent("datachanged", this);
5235     },
5236
5237     /**
5238      * Query the records by a specified property.
5239      * @param {String} field A field on your records
5240      * @param {String/RegExp} value Either a string that the field
5241      * should start with or a RegExp to test against the field
5242      * @param {Boolean} anyMatch True to match any part not just the beginning
5243      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5244      */
5245     query : function(property, value, anyMatch){
5246         var fn = this.createFilterFn(property, value, anyMatch);
5247         return fn ? this.queryBy(fn) : this.data.clone();
5248     },
5249
5250     /**
5251      * Query by a function. The specified function will be called with each
5252      * record in this data source. If the function returns true the record is included
5253      * in the results.
5254      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5255      * @param {Object} scope (optional) The scope of the function (defaults to this)
5256       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5257      **/
5258     queryBy : function(fn, scope){
5259         var data = this.snapshot || this.data;
5260         return data.filterBy(fn, scope||this);
5261     },
5262
5263     /**
5264      * Collects unique values for a particular dataIndex from this store.
5265      * @param {String} dataIndex The property to collect
5266      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
5267      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
5268      * @return {Array} An array of the unique values
5269      **/
5270     collect : function(dataIndex, allowNull, bypassFilter){
5271         var d = (bypassFilter === true && this.snapshot) ?
5272                 this.snapshot.items : this.data.items;
5273         var v, sv, r = [], l = {};
5274         for(var i = 0, len = d.length; i < len; i++){
5275             v = d[i].data[dataIndex];
5276             sv = String(v);
5277             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
5278                 l[sv] = true;
5279                 r[r.length] = v;
5280             }
5281         }
5282         return r;
5283     },
5284
5285     /**
5286      * Revert to a view of the Record cache with no filtering applied.
5287      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
5288      */
5289     clearFilter : function(suppressEvent){
5290         if(this.snapshot && this.snapshot != this.data){
5291             this.data = this.snapshot;
5292             delete this.snapshot;
5293             if(suppressEvent !== true){
5294                 this.fireEvent("datachanged", this);
5295             }
5296         }
5297     },
5298
5299     // private
5300     afterEdit : function(record){
5301         if(this.modified.indexOf(record) == -1){
5302             this.modified.push(record);
5303         }
5304         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
5305     },
5306
5307     // private
5308     afterReject : function(record){
5309         this.modified.remove(record);
5310         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
5311     },
5312
5313     // private
5314     afterCommit : function(record){
5315         this.modified.remove(record);
5316         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
5317     },
5318
5319     /**
5320      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
5321      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
5322      */
5323     commitChanges : function(){
5324         var m = this.modified.slice(0);
5325         this.modified = [];
5326         for(var i = 0, len = m.length; i < len; i++){
5327             m[i].commit();
5328         }
5329     },
5330
5331     /**
5332      * Cancel outstanding changes on all changed records.
5333      */
5334     rejectChanges : function(){
5335         var m = this.modified.slice(0);
5336         this.modified = [];
5337         for(var i = 0, len = m.length; i < len; i++){
5338             m[i].reject();
5339         }
5340     },
5341
5342     onMetaChange : function(meta, rtype, o){
5343         this.recordType = rtype;
5344         this.fields = rtype.prototype.fields;
5345         delete this.snapshot;
5346         this.sortInfo = meta.sortInfo || this.sortInfo;
5347         this.modified = [];
5348         this.fireEvent('metachange', this, this.reader.meta);
5349     }
5350 });/*
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  * @class Roo.data.SimpleStore
5363  * @extends Roo.data.Store
5364  * Small helper class to make creating Stores from Array data easier.
5365  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
5366  * @cfg {Array} fields An array of field definition objects, or field name strings.
5367  * @cfg {Array} data The multi-dimensional array of data
5368  * @constructor
5369  * @param {Object} config
5370  */
5371 Roo.data.SimpleStore = function(config){
5372     Roo.data.SimpleStore.superclass.constructor.call(this, {
5373         isLocal : true,
5374         reader: new Roo.data.ArrayReader({
5375                 id: config.id
5376             },
5377             Roo.data.Record.create(config.fields)
5378         ),
5379         proxy : new Roo.data.MemoryProxy(config.data)
5380     });
5381     this.load();
5382 };
5383 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
5384  * Based on:
5385  * Ext JS Library 1.1.1
5386  * Copyright(c) 2006-2007, Ext JS, LLC.
5387  *
5388  * Originally Released Under LGPL - original licence link has changed is not relivant.
5389  *
5390  * Fork - LGPL
5391  * <script type="text/javascript">
5392  */
5393
5394 /**
5395 /**
5396  * @extends Roo.data.Store
5397  * @class Roo.data.JsonStore
5398  * Small helper class to make creating Stores for JSON data easier. <br/>
5399 <pre><code>
5400 var store = new Roo.data.JsonStore({
5401     url: 'get-images.php',
5402     root: 'images',
5403     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
5404 });
5405 </code></pre>
5406  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
5407  * JsonReader and HttpProxy (unless inline data is provided).</b>
5408  * @cfg {Array} fields An array of field definition objects, or field name strings.
5409  * @constructor
5410  * @param {Object} config
5411  */
5412 Roo.data.JsonStore = function(c){
5413     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
5414         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
5415         reader: new Roo.data.JsonReader(c, c.fields)
5416     }));
5417 };
5418 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
5419  * Based on:
5420  * Ext JS Library 1.1.1
5421  * Copyright(c) 2006-2007, Ext JS, LLC.
5422  *
5423  * Originally Released Under LGPL - original licence link has changed is not relivant.
5424  *
5425  * Fork - LGPL
5426  * <script type="text/javascript">
5427  */
5428
5429  
5430 Roo.data.Field = function(config){
5431     if(typeof config == "string"){
5432         config = {name: config};
5433     }
5434     Roo.apply(this, config);
5435     
5436     if(!this.type){
5437         this.type = "auto";
5438     }
5439     
5440     var st = Roo.data.SortTypes;
5441     // named sortTypes are supported, here we look them up
5442     if(typeof this.sortType == "string"){
5443         this.sortType = st[this.sortType];
5444     }
5445     
5446     // set default sortType for strings and dates
5447     if(!this.sortType){
5448         switch(this.type){
5449             case "string":
5450                 this.sortType = st.asUCString;
5451                 break;
5452             case "date":
5453                 this.sortType = st.asDate;
5454                 break;
5455             default:
5456                 this.sortType = st.none;
5457         }
5458     }
5459
5460     // define once
5461     var stripRe = /[\$,%]/g;
5462
5463     // prebuilt conversion function for this field, instead of
5464     // switching every time we're reading a value
5465     if(!this.convert){
5466         var cv, dateFormat = this.dateFormat;
5467         switch(this.type){
5468             case "":
5469             case "auto":
5470             case undefined:
5471                 cv = function(v){ return v; };
5472                 break;
5473             case "string":
5474                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
5475                 break;
5476             case "int":
5477                 cv = function(v){
5478                     return v !== undefined && v !== null && v !== '' ?
5479                            parseInt(String(v).replace(stripRe, ""), 10) : '';
5480                     };
5481                 break;
5482             case "float":
5483                 cv = function(v){
5484                     return v !== undefined && v !== null && v !== '' ?
5485                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
5486                     };
5487                 break;
5488             case "bool":
5489             case "boolean":
5490                 cv = function(v){ return v === true || v === "true" || v == 1; };
5491                 break;
5492             case "date":
5493                 cv = function(v){
5494                     if(!v){
5495                         return '';
5496                     }
5497                     if(v instanceof Date){
5498                         return v;
5499                     }
5500                     if(dateFormat){
5501                         if(dateFormat == "timestamp"){
5502                             return new Date(v*1000);
5503                         }
5504                         return Date.parseDate(v, dateFormat);
5505                     }
5506                     var parsed = Date.parse(v);
5507                     return parsed ? new Date(parsed) : null;
5508                 };
5509              break;
5510             
5511         }
5512         this.convert = cv;
5513     }
5514 };
5515
5516 Roo.data.Field.prototype = {
5517     dateFormat: null,
5518     defaultValue: "",
5519     mapping: null,
5520     sortType : null,
5521     sortDir : "ASC"
5522 };/*
5523  * Based on:
5524  * Ext JS Library 1.1.1
5525  * Copyright(c) 2006-2007, Ext JS, LLC.
5526  *
5527  * Originally Released Under LGPL - original licence link has changed is not relivant.
5528  *
5529  * Fork - LGPL
5530  * <script type="text/javascript">
5531  */
5532  
5533 // Base class for reading structured data from a data source.  This class is intended to be
5534 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
5535
5536 /**
5537  * @class Roo.data.DataReader
5538  * Base class for reading structured data from a data source.  This class is intended to be
5539  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
5540  */
5541
5542 Roo.data.DataReader = function(meta, recordType){
5543     
5544     this.meta = meta;
5545     
5546     this.recordType = recordType instanceof Array ? 
5547         Roo.data.Record.create(recordType) : recordType;
5548 };
5549
5550 Roo.data.DataReader.prototype = {
5551      /**
5552      * Create an empty record
5553      * @param {Object} data (optional) - overlay some values
5554      * @return {Roo.data.Record} record created.
5555      */
5556     newRow :  function(d) {
5557         var da =  {};
5558         this.recordType.prototype.fields.each(function(c) {
5559             switch( c.type) {
5560                 case 'int' : da[c.name] = 0; break;
5561                 case 'date' : da[c.name] = new Date(); break;
5562                 case 'float' : da[c.name] = 0.0; break;
5563                 case 'boolean' : da[c.name] = false; break;
5564                 default : da[c.name] = ""; break;
5565             }
5566             
5567         });
5568         return new this.recordType(Roo.apply(da, d));
5569     }
5570     
5571 };/*
5572  * Based on:
5573  * Ext JS Library 1.1.1
5574  * Copyright(c) 2006-2007, Ext JS, LLC.
5575  *
5576  * Originally Released Under LGPL - original licence link has changed is not relivant.
5577  *
5578  * Fork - LGPL
5579  * <script type="text/javascript">
5580  */
5581
5582 /**
5583  * @class Roo.data.DataProxy
5584  * @extends Roo.data.Observable
5585  * This class is an abstract base class for implementations which provide retrieval of
5586  * unformatted data objects.<br>
5587  * <p>
5588  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
5589  * (of the appropriate type which knows how to parse the data object) to provide a block of
5590  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
5591  * <p>
5592  * Custom implementations must implement the load method as described in
5593  * {@link Roo.data.HttpProxy#load}.
5594  */
5595 Roo.data.DataProxy = function(){
5596     this.addEvents({
5597         /**
5598          * @event beforeload
5599          * Fires before a network request is made to retrieve a data object.
5600          * @param {Object} This DataProxy object.
5601          * @param {Object} params The params parameter to the load function.
5602          */
5603         beforeload : true,
5604         /**
5605          * @event load
5606          * Fires before the load method's callback is called.
5607          * @param {Object} This DataProxy object.
5608          * @param {Object} o The data object.
5609          * @param {Object} arg The callback argument object passed to the load function.
5610          */
5611         load : true,
5612         /**
5613          * @event loadexception
5614          * Fires if an Exception occurs during data retrieval.
5615          * @param {Object} This DataProxy object.
5616          * @param {Object} o The data object.
5617          * @param {Object} arg The callback argument object passed to the load function.
5618          * @param {Object} e The Exception.
5619          */
5620         loadexception : true
5621     });
5622     Roo.data.DataProxy.superclass.constructor.call(this);
5623 };
5624
5625 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
5626
5627     /**
5628      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
5629      */
5630 /*
5631  * Based on:
5632  * Ext JS Library 1.1.1
5633  * Copyright(c) 2006-2007, Ext JS, LLC.
5634  *
5635  * Originally Released Under LGPL - original licence link has changed is not relivant.
5636  *
5637  * Fork - LGPL
5638  * <script type="text/javascript">
5639  */
5640 /**
5641  * @class Roo.data.MemoryProxy
5642  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
5643  * to the Reader when its load method is called.
5644  * @constructor
5645  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
5646  */
5647 Roo.data.MemoryProxy = function(data){
5648     if (data.data) {
5649         data = data.data;
5650     }
5651     Roo.data.MemoryProxy.superclass.constructor.call(this);
5652     this.data = data;
5653 };
5654
5655 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
5656     /**
5657      * Load data from the requested source (in this case an in-memory
5658      * data object passed to the constructor), read the data object into
5659      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5660      * process that block using the passed callback.
5661      * @param {Object} params This parameter is not used by the MemoryProxy class.
5662      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5663      * object into a block of Roo.data.Records.
5664      * @param {Function} callback The function into which to pass the block of Roo.data.records.
5665      * The function must be passed <ul>
5666      * <li>The Record block object</li>
5667      * <li>The "arg" argument from the load function</li>
5668      * <li>A boolean success indicator</li>
5669      * </ul>
5670      * @param {Object} scope The scope in which to call the callback
5671      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5672      */
5673     load : function(params, reader, callback, scope, arg){
5674         params = params || {};
5675         var result;
5676         try {
5677             result = reader.readRecords(this.data);
5678         }catch(e){
5679             this.fireEvent("loadexception", this, arg, null, e);
5680             callback.call(scope, null, arg, false);
5681             return;
5682         }
5683         callback.call(scope, result, arg, true);
5684     },
5685     
5686     // private
5687     update : function(params, records){
5688         
5689     }
5690 });/*
5691  * Based on:
5692  * Ext JS Library 1.1.1
5693  * Copyright(c) 2006-2007, Ext JS, LLC.
5694  *
5695  * Originally Released Under LGPL - original licence link has changed is not relivant.
5696  *
5697  * Fork - LGPL
5698  * <script type="text/javascript">
5699  */
5700 /**
5701  * @class Roo.data.HttpProxy
5702  * @extends Roo.data.DataProxy
5703  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
5704  * configured to reference a certain URL.<br><br>
5705  * <p>
5706  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
5707  * from which the running page was served.<br><br>
5708  * <p>
5709  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
5710  * <p>
5711  * Be aware that to enable the browser to parse an XML document, the server must set
5712  * the Content-Type header in the HTTP response to "text/xml".
5713  * @constructor
5714  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
5715  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
5716  * will be used to make the request.
5717  */
5718 Roo.data.HttpProxy = function(conn){
5719     Roo.data.HttpProxy.superclass.constructor.call(this);
5720     // is conn a conn config or a real conn?
5721     this.conn = conn;
5722     this.useAjax = !conn || !conn.events;
5723   
5724 };
5725
5726 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
5727     // thse are take from connection...
5728     
5729     /**
5730      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
5731      */
5732     /**
5733      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
5734      * extra parameters to each request made by this object. (defaults to undefined)
5735      */
5736     /**
5737      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
5738      *  to each request made by this object. (defaults to undefined)
5739      */
5740     /**
5741      * @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)
5742      */
5743     /**
5744      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
5745      */
5746      /**
5747      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
5748      * @type Boolean
5749      */
5750   
5751
5752     /**
5753      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
5754      * @type Boolean
5755      */
5756     /**
5757      * Return the {@link Roo.data.Connection} object being used by this Proxy.
5758      * @return {Connection} The Connection object. This object may be used to subscribe to events on
5759      * a finer-grained basis than the DataProxy events.
5760      */
5761     getConnection : function(){
5762         return this.useAjax ? Roo.Ajax : this.conn;
5763     },
5764
5765     /**
5766      * Load data from the configured {@link Roo.data.Connection}, read the data object into
5767      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
5768      * process that block using the passed callback.
5769      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5770      * for the request to the remote server.
5771      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5772      * object into a block of Roo.data.Records.
5773      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5774      * The function must be passed <ul>
5775      * <li>The Record block object</li>
5776      * <li>The "arg" argument from the load function</li>
5777      * <li>A boolean success indicator</li>
5778      * </ul>
5779      * @param {Object} scope The scope in which to call the callback
5780      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5781      */
5782     load : function(params, reader, callback, scope, arg){
5783         if(this.fireEvent("beforeload", this, params) !== false){
5784             var  o = {
5785                 params : params || {},
5786                 request: {
5787                     callback : callback,
5788                     scope : scope,
5789                     arg : arg
5790                 },
5791                 reader: reader,
5792                 callback : this.loadResponse,
5793                 scope: this
5794             };
5795             if(this.useAjax){
5796                 Roo.applyIf(o, this.conn);
5797                 if(this.activeRequest){
5798                     Roo.Ajax.abort(this.activeRequest);
5799                 }
5800                 this.activeRequest = Roo.Ajax.request(o);
5801             }else{
5802                 this.conn.request(o);
5803             }
5804         }else{
5805             callback.call(scope||this, null, arg, false);
5806         }
5807     },
5808
5809     // private
5810     loadResponse : function(o, success, response){
5811         delete this.activeRequest;
5812         if(!success){
5813             this.fireEvent("loadexception", this, o, response);
5814             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5815             return;
5816         }
5817         var result;
5818         try {
5819             result = o.reader.read(response);
5820         }catch(e){
5821             this.fireEvent("loadexception", this, o, response, e);
5822             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5823             return;
5824         }
5825         
5826         this.fireEvent("load", this, o, o.request.arg);
5827         o.request.callback.call(o.request.scope, result, o.request.arg, true);
5828     },
5829
5830     // private
5831     update : function(dataSet){
5832
5833     },
5834
5835     // private
5836     updateResponse : function(dataSet){
5837
5838     }
5839 });/*
5840  * Based on:
5841  * Ext JS Library 1.1.1
5842  * Copyright(c) 2006-2007, Ext JS, LLC.
5843  *
5844  * Originally Released Under LGPL - original licence link has changed is not relivant.
5845  *
5846  * Fork - LGPL
5847  * <script type="text/javascript">
5848  */
5849
5850 /**
5851  * @class Roo.data.ScriptTagProxy
5852  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
5853  * other than the originating domain of the running page.<br><br>
5854  * <p>
5855  * <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
5856  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
5857  * <p>
5858  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
5859  * source code that is used as the source inside a &lt;script> tag.<br><br>
5860  * <p>
5861  * In order for the browser to process the returned data, the server must wrap the data object
5862  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
5863  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
5864  * depending on whether the callback name was passed:
5865  * <p>
5866  * <pre><code>
5867 boolean scriptTag = false;
5868 String cb = request.getParameter("callback");
5869 if (cb != null) {
5870     scriptTag = true;
5871     response.setContentType("text/javascript");
5872 } else {
5873     response.setContentType("application/x-json");
5874 }
5875 Writer out = response.getWriter();
5876 if (scriptTag) {
5877     out.write(cb + "(");
5878 }
5879 out.print(dataBlock.toJsonString());
5880 if (scriptTag) {
5881     out.write(");");
5882 }
5883 </pre></code>
5884  *
5885  * @constructor
5886  * @param {Object} config A configuration object.
5887  */
5888 Roo.data.ScriptTagProxy = function(config){
5889     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
5890     Roo.apply(this, config);
5891     this.head = document.getElementsByTagName("head")[0];
5892 };
5893
5894 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
5895
5896 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
5897     /**
5898      * @cfg {String} url The URL from which to request the data object.
5899      */
5900     /**
5901      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
5902      */
5903     timeout : 30000,
5904     /**
5905      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
5906      * the server the name of the callback function set up by the load call to process the returned data object.
5907      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
5908      * javascript output which calls this named function passing the data object as its only parameter.
5909      */
5910     callbackParam : "callback",
5911     /**
5912      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
5913      * name to the request.
5914      */
5915     nocache : true,
5916
5917     /**
5918      * Load data from the configured URL, read the data object into
5919      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5920      * process that block using the passed callback.
5921      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5922      * for the request to the remote server.
5923      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5924      * object into a block of Roo.data.Records.
5925      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5926      * The function must be passed <ul>
5927      * <li>The Record block object</li>
5928      * <li>The "arg" argument from the load function</li>
5929      * <li>A boolean success indicator</li>
5930      * </ul>
5931      * @param {Object} scope The scope in which to call the callback
5932      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5933      */
5934     load : function(params, reader, callback, scope, arg){
5935         if(this.fireEvent("beforeload", this, params) !== false){
5936
5937             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
5938
5939             var url = this.url;
5940             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
5941             if(this.nocache){
5942                 url += "&_dc=" + (new Date().getTime());
5943             }
5944             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
5945             var trans = {
5946                 id : transId,
5947                 cb : "stcCallback"+transId,
5948                 scriptId : "stcScript"+transId,
5949                 params : params,
5950                 arg : arg,
5951                 url : url,
5952                 callback : callback,
5953                 scope : scope,
5954                 reader : reader
5955             };
5956             var conn = this;
5957
5958             window[trans.cb] = function(o){
5959                 conn.handleResponse(o, trans);
5960             };
5961
5962             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
5963
5964             if(this.autoAbort !== false){
5965                 this.abort();
5966             }
5967
5968             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
5969
5970             var script = document.createElement("script");
5971             script.setAttribute("src", url);
5972             script.setAttribute("type", "text/javascript");
5973             script.setAttribute("id", trans.scriptId);
5974             this.head.appendChild(script);
5975
5976             this.trans = trans;
5977         }else{
5978             callback.call(scope||this, null, arg, false);
5979         }
5980     },
5981
5982     // private
5983     isLoading : function(){
5984         return this.trans ? true : false;
5985     },
5986
5987     /**
5988      * Abort the current server request.
5989      */
5990     abort : function(){
5991         if(this.isLoading()){
5992             this.destroyTrans(this.trans);
5993         }
5994     },
5995
5996     // private
5997     destroyTrans : function(trans, isLoaded){
5998         this.head.removeChild(document.getElementById(trans.scriptId));
5999         clearTimeout(trans.timeoutId);
6000         if(isLoaded){
6001             window[trans.cb] = undefined;
6002             try{
6003                 delete window[trans.cb];
6004             }catch(e){}
6005         }else{
6006             // if hasn't been loaded, wait for load to remove it to prevent script error
6007             window[trans.cb] = function(){
6008                 window[trans.cb] = undefined;
6009                 try{
6010                     delete window[trans.cb];
6011                 }catch(e){}
6012             };
6013         }
6014     },
6015
6016     // private
6017     handleResponse : function(o, trans){
6018         this.trans = false;
6019         this.destroyTrans(trans, true);
6020         var result;
6021         try {
6022             result = trans.reader.readRecords(o);
6023         }catch(e){
6024             this.fireEvent("loadexception", this, o, trans.arg, e);
6025             trans.callback.call(trans.scope||window, null, trans.arg, false);
6026             return;
6027         }
6028         this.fireEvent("load", this, o, trans.arg);
6029         trans.callback.call(trans.scope||window, result, trans.arg, true);
6030     },
6031
6032     // private
6033     handleFailure : function(trans){
6034         this.trans = false;
6035         this.destroyTrans(trans, false);
6036         this.fireEvent("loadexception", this, null, trans.arg);
6037         trans.callback.call(trans.scope||window, null, trans.arg, false);
6038     }
6039 });/*
6040  * Based on:
6041  * Ext JS Library 1.1.1
6042  * Copyright(c) 2006-2007, Ext JS, LLC.
6043  *
6044  * Originally Released Under LGPL - original licence link has changed is not relivant.
6045  *
6046  * Fork - LGPL
6047  * <script type="text/javascript">
6048  */
6049
6050 /**
6051  * @class Roo.data.JsonReader
6052  * @extends Roo.data.DataReader
6053  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
6054  * based on mappings in a provided Roo.data.Record constructor.
6055  * 
6056  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
6057  * in the reply previously. 
6058  * 
6059  * <p>
6060  * Example code:
6061  * <pre><code>
6062 var RecordDef = Roo.data.Record.create([
6063     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6064     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6065 ]);
6066 var myReader = new Roo.data.JsonReader({
6067     totalProperty: "results",    // The property which contains the total dataset size (optional)
6068     root: "rows",                // The property which contains an Array of row objects
6069     id: "id"                     // The property within each row object that provides an ID for the record (optional)
6070 }, RecordDef);
6071 </code></pre>
6072  * <p>
6073  * This would consume a JSON file like this:
6074  * <pre><code>
6075 { 'results': 2, 'rows': [
6076     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
6077     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
6078 }
6079 </code></pre>
6080  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
6081  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6082  * paged from the remote server.
6083  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
6084  * @cfg {String} root name of the property which contains the Array of row objects.
6085  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
6086  * @constructor
6087  * Create a new JsonReader
6088  * @param {Object} meta Metadata configuration options
6089  * @param {Object} recordType Either an Array of field definition objects,
6090  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
6091  */
6092 Roo.data.JsonReader = function(meta, recordType){
6093     
6094     meta = meta || {};
6095     // set some defaults:
6096     Roo.applyIf(meta, {
6097         totalProperty: 'total',
6098         successProperty : 'success',
6099         root : 'data',
6100         id : 'id'
6101     });
6102     
6103     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6104 };
6105 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
6106     
6107     /**
6108      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
6109      * Used by Store query builder to append _requestMeta to params.
6110      * 
6111      */
6112     metaFromRemote : false,
6113     /**
6114      * This method is only used by a DataProxy which has retrieved data from a remote server.
6115      * @param {Object} response The XHR object which contains the JSON data in its responseText.
6116      * @return {Object} data A data block which is used by an Roo.data.Store object as
6117      * a cache of Roo.data.Records.
6118      */
6119     read : function(response){
6120         var json = response.responseText;
6121        
6122         var o = /* eval:var:o */ eval("("+json+")");
6123         if(!o) {
6124             throw {message: "JsonReader.read: Json object not found"};
6125         }
6126         
6127         if(o.metaData){
6128             
6129             delete this.ef;
6130             this.metaFromRemote = true;
6131             this.meta = o.metaData;
6132             this.recordType = Roo.data.Record.create(o.metaData.fields);
6133             this.onMetaChange(this.meta, this.recordType, o);
6134         }
6135         return this.readRecords(o);
6136     },
6137
6138     // private function a store will implement
6139     onMetaChange : function(meta, recordType, o){
6140
6141     },
6142
6143     /**
6144          * @ignore
6145          */
6146     simpleAccess: function(obj, subsc) {
6147         return obj[subsc];
6148     },
6149
6150         /**
6151          * @ignore
6152          */
6153     getJsonAccessor: function(){
6154         var re = /[\[\.]/;
6155         return function(expr) {
6156             try {
6157                 return(re.test(expr))
6158                     ? new Function("obj", "return obj." + expr)
6159                     : function(obj){
6160                         return obj[expr];
6161                     };
6162             } catch(e){}
6163             return Roo.emptyFn;
6164         };
6165     }(),
6166
6167     /**
6168      * Create a data block containing Roo.data.Records from an XML document.
6169      * @param {Object} o An object which contains an Array of row objects in the property specified
6170      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
6171      * which contains the total size of the dataset.
6172      * @return {Object} data A data block which is used by an Roo.data.Store object as
6173      * a cache of Roo.data.Records.
6174      */
6175     readRecords : function(o){
6176         /**
6177          * After any data loads, the raw JSON data is available for further custom processing.
6178          * @type Object
6179          */
6180         this.jsonData = o;
6181         var s = this.meta, Record = this.recordType,
6182             f = Record.prototype.fields, fi = f.items, fl = f.length;
6183
6184 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
6185         if (!this.ef) {
6186             if(s.totalProperty) {
6187                     this.getTotal = this.getJsonAccessor(s.totalProperty);
6188                 }
6189                 if(s.successProperty) {
6190                     this.getSuccess = this.getJsonAccessor(s.successProperty);
6191                 }
6192                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
6193                 if (s.id) {
6194                         var g = this.getJsonAccessor(s.id);
6195                         this.getId = function(rec) {
6196                                 var r = g(rec);
6197                                 return (r === undefined || r === "") ? null : r;
6198                         };
6199                 } else {
6200                         this.getId = function(){return null;};
6201                 }
6202             this.ef = [];
6203             for(var jj = 0; jj < fl; jj++){
6204                 f = fi[jj];
6205                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
6206                 this.ef[jj] = this.getJsonAccessor(map);
6207             }
6208         }
6209
6210         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
6211         if(s.totalProperty){
6212             var vt = parseInt(this.getTotal(o), 10);
6213             if(!isNaN(vt)){
6214                 totalRecords = vt;
6215             }
6216         }
6217         if(s.successProperty){
6218             var vs = this.getSuccess(o);
6219             if(vs === false || vs === 'false'){
6220                 success = false;
6221             }
6222         }
6223         var records = [];
6224             for(var i = 0; i < c; i++){
6225                     var n = root[i];
6226                 var values = {};
6227                 var id = this.getId(n);
6228                 for(var j = 0; j < fl; j++){
6229                     f = fi[j];
6230                 var v = this.ef[j](n);
6231                 if (!f.convert) {
6232                     Roo.log('missing convert for ' + f.name);
6233                     Roo.log(f);
6234                     continue;
6235                 }
6236                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
6237                 }
6238                 var record = new Record(values, id);
6239                 record.json = n;
6240                 records[i] = record;
6241             }
6242             return {
6243                 success : success,
6244                 records : records,
6245                 totalRecords : totalRecords
6246             };
6247     }
6248 });/*
6249  * Based on:
6250  * Ext JS Library 1.1.1
6251  * Copyright(c) 2006-2007, Ext JS, LLC.
6252  *
6253  * Originally Released Under LGPL - original licence link has changed is not relivant.
6254  *
6255  * Fork - LGPL
6256  * <script type="text/javascript">
6257  */
6258
6259 /**
6260  * @class Roo.data.XmlReader
6261  * @extends Roo.data.DataReader
6262  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
6263  * based on mappings in a provided Roo.data.Record constructor.<br><br>
6264  * <p>
6265  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
6266  * header in the HTTP response must be set to "text/xml".</em>
6267  * <p>
6268  * Example code:
6269  * <pre><code>
6270 var RecordDef = Roo.data.Record.create([
6271    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6272    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6273 ]);
6274 var myReader = new Roo.data.XmlReader({
6275    totalRecords: "results", // The element which contains the total dataset size (optional)
6276    record: "row",           // The repeated element which contains row information
6277    id: "id"                 // The element within the row that provides an ID for the record (optional)
6278 }, RecordDef);
6279 </code></pre>
6280  * <p>
6281  * This would consume an XML file like this:
6282  * <pre><code>
6283 &lt;?xml?>
6284 &lt;dataset>
6285  &lt;results>2&lt;/results>
6286  &lt;row>
6287    &lt;id>1&lt;/id>
6288    &lt;name>Bill&lt;/name>
6289    &lt;occupation>Gardener&lt;/occupation>
6290  &lt;/row>
6291  &lt;row>
6292    &lt;id>2&lt;/id>
6293    &lt;name>Ben&lt;/name>
6294    &lt;occupation>Horticulturalist&lt;/occupation>
6295  &lt;/row>
6296 &lt;/dataset>
6297 </code></pre>
6298  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
6299  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6300  * paged from the remote server.
6301  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
6302  * @cfg {String} success The DomQuery path to the success attribute used by forms.
6303  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
6304  * a record identifier value.
6305  * @constructor
6306  * Create a new XmlReader
6307  * @param {Object} meta Metadata configuration options
6308  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
6309  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
6310  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
6311  */
6312 Roo.data.XmlReader = function(meta, recordType){
6313     meta = meta || {};
6314     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6315 };
6316 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
6317     /**
6318      * This method is only used by a DataProxy which has retrieved data from a remote server.
6319          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
6320          * to contain a method called 'responseXML' that returns an XML document object.
6321      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6322      * a cache of Roo.data.Records.
6323      */
6324     read : function(response){
6325         var doc = response.responseXML;
6326         if(!doc) {
6327             throw {message: "XmlReader.read: XML Document not available"};
6328         }
6329         return this.readRecords(doc);
6330     },
6331
6332     /**
6333      * Create a data block containing Roo.data.Records from an XML document.
6334          * @param {Object} doc A parsed XML document.
6335      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6336      * a cache of Roo.data.Records.
6337      */
6338     readRecords : function(doc){
6339         /**
6340          * After any data loads/reads, the raw XML Document is available for further custom processing.
6341          * @type XMLDocument
6342          */
6343         this.xmlData = doc;
6344         var root = doc.documentElement || doc;
6345         var q = Roo.DomQuery;
6346         var recordType = this.recordType, fields = recordType.prototype.fields;
6347         var sid = this.meta.id;
6348         var totalRecords = 0, success = true;
6349         if(this.meta.totalRecords){
6350             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
6351         }
6352         
6353         if(this.meta.success){
6354             var sv = q.selectValue(this.meta.success, root, true);
6355             success = sv !== false && sv !== 'false';
6356         }
6357         var records = [];
6358         var ns = q.select(this.meta.record, root);
6359         for(var i = 0, len = ns.length; i < len; i++) {
6360                 var n = ns[i];
6361                 var values = {};
6362                 var id = sid ? q.selectValue(sid, n) : undefined;
6363                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6364                     var f = fields.items[j];
6365                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
6366                     v = f.convert(v);
6367                     values[f.name] = v;
6368                 }
6369                 var record = new recordType(values, id);
6370                 record.node = n;
6371                 records[records.length] = record;
6372             }
6373
6374             return {
6375                 success : success,
6376                 records : records,
6377                 totalRecords : totalRecords || records.length
6378             };
6379     }
6380 });/*
6381  * Based on:
6382  * Ext JS Library 1.1.1
6383  * Copyright(c) 2006-2007, Ext JS, LLC.
6384  *
6385  * Originally Released Under LGPL - original licence link has changed is not relivant.
6386  *
6387  * Fork - LGPL
6388  * <script type="text/javascript">
6389  */
6390
6391 /**
6392  * @class Roo.data.ArrayReader
6393  * @extends Roo.data.DataReader
6394  * Data reader class to create an Array of Roo.data.Record objects from an Array.
6395  * Each element of that Array represents a row of data fields. The
6396  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
6397  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
6398  * <p>
6399  * Example code:.
6400  * <pre><code>
6401 var RecordDef = Roo.data.Record.create([
6402     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
6403     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
6404 ]);
6405 var myReader = new Roo.data.ArrayReader({
6406     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
6407 }, RecordDef);
6408 </code></pre>
6409  * <p>
6410  * This would consume an Array like this:
6411  * <pre><code>
6412 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
6413   </code></pre>
6414  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
6415  * @constructor
6416  * Create a new JsonReader
6417  * @param {Object} meta Metadata configuration options.
6418  * @param {Object} recordType Either an Array of field definition objects
6419  * as specified to {@link Roo.data.Record#create},
6420  * or an {@link Roo.data.Record} object
6421  * created using {@link Roo.data.Record#create}.
6422  */
6423 Roo.data.ArrayReader = function(meta, recordType){
6424     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
6425 };
6426
6427 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
6428     /**
6429      * Create a data block containing Roo.data.Records from an XML document.
6430      * @param {Object} o An Array of row objects which represents the dataset.
6431      * @return {Object} data A data block which is used by an Roo.data.Store object as
6432      * a cache of Roo.data.Records.
6433      */
6434     readRecords : function(o){
6435         var sid = this.meta ? this.meta.id : null;
6436         var recordType = this.recordType, fields = recordType.prototype.fields;
6437         var records = [];
6438         var root = o;
6439             for(var i = 0; i < root.length; i++){
6440                     var n = root[i];
6441                 var values = {};
6442                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
6443                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6444                 var f = fields.items[j];
6445                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
6446                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
6447                 v = f.convert(v);
6448                 values[f.name] = v;
6449             }
6450                 var record = new recordType(values, id);
6451                 record.json = n;
6452                 records[records.length] = record;
6453             }
6454             return {
6455                 records : records,
6456                 totalRecords : records.length
6457             };
6458     }
6459 });/*
6460  * Based on:
6461  * Ext JS Library 1.1.1
6462  * Copyright(c) 2006-2007, Ext JS, LLC.
6463  *
6464  * Originally Released Under LGPL - original licence link has changed is not relivant.
6465  *
6466  * Fork - LGPL
6467  * <script type="text/javascript">
6468  */
6469
6470
6471 /**
6472  * @class Roo.data.Tree
6473  * @extends Roo.util.Observable
6474  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
6475  * in the tree have most standard DOM functionality.
6476  * @constructor
6477  * @param {Node} root (optional) The root node
6478  */
6479 Roo.data.Tree = function(root){
6480    this.nodeHash = {};
6481    /**
6482     * The root node for this tree
6483     * @type Node
6484     */
6485    this.root = null;
6486    if(root){
6487        this.setRootNode(root);
6488    }
6489    this.addEvents({
6490        /**
6491         * @event append
6492         * Fires when a new child node is appended to a node in this tree.
6493         * @param {Tree} tree The owner tree
6494         * @param {Node} parent The parent node
6495         * @param {Node} node The newly appended node
6496         * @param {Number} index The index of the newly appended node
6497         */
6498        "append" : true,
6499        /**
6500         * @event remove
6501         * Fires when a child node is removed from a node in this tree.
6502         * @param {Tree} tree The owner tree
6503         * @param {Node} parent The parent node
6504         * @param {Node} node The child node removed
6505         */
6506        "remove" : true,
6507        /**
6508         * @event move
6509         * Fires when a node is moved to a new location in the tree
6510         * @param {Tree} tree The owner tree
6511         * @param {Node} node The node moved
6512         * @param {Node} oldParent The old parent of this node
6513         * @param {Node} newParent The new parent of this node
6514         * @param {Number} index The index it was moved to
6515         */
6516        "move" : true,
6517        /**
6518         * @event insert
6519         * Fires when a new child node is inserted in a node in this tree.
6520         * @param {Tree} tree The owner tree
6521         * @param {Node} parent The parent node
6522         * @param {Node} node The child node inserted
6523         * @param {Node} refNode The child node the node was inserted before
6524         */
6525        "insert" : true,
6526        /**
6527         * @event beforeappend
6528         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
6529         * @param {Tree} tree The owner tree
6530         * @param {Node} parent The parent node
6531         * @param {Node} node The child node to be appended
6532         */
6533        "beforeappend" : true,
6534        /**
6535         * @event beforeremove
6536         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
6537         * @param {Tree} tree The owner tree
6538         * @param {Node} parent The parent node
6539         * @param {Node} node The child node to be removed
6540         */
6541        "beforeremove" : true,
6542        /**
6543         * @event beforemove
6544         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
6545         * @param {Tree} tree The owner tree
6546         * @param {Node} node The node being moved
6547         * @param {Node} oldParent The parent of the node
6548         * @param {Node} newParent The new parent the node is moving to
6549         * @param {Number} index The index it is being moved to
6550         */
6551        "beforemove" : true,
6552        /**
6553         * @event beforeinsert
6554         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
6555         * @param {Tree} tree The owner tree
6556         * @param {Node} parent The parent node
6557         * @param {Node} node The child node to be inserted
6558         * @param {Node} refNode The child node the node is being inserted before
6559         */
6560        "beforeinsert" : true
6561    });
6562
6563     Roo.data.Tree.superclass.constructor.call(this);
6564 };
6565
6566 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
6567     pathSeparator: "/",
6568
6569     proxyNodeEvent : function(){
6570         return this.fireEvent.apply(this, arguments);
6571     },
6572
6573     /**
6574      * Returns the root node for this tree.
6575      * @return {Node}
6576      */
6577     getRootNode : function(){
6578         return this.root;
6579     },
6580
6581     /**
6582      * Sets the root node for this tree.
6583      * @param {Node} node
6584      * @return {Node}
6585      */
6586     setRootNode : function(node){
6587         this.root = node;
6588         node.ownerTree = this;
6589         node.isRoot = true;
6590         this.registerNode(node);
6591         return node;
6592     },
6593
6594     /**
6595      * Gets a node in this tree by its id.
6596      * @param {String} id
6597      * @return {Node}
6598      */
6599     getNodeById : function(id){
6600         return this.nodeHash[id];
6601     },
6602
6603     registerNode : function(node){
6604         this.nodeHash[node.id] = node;
6605     },
6606
6607     unregisterNode : function(node){
6608         delete this.nodeHash[node.id];
6609     },
6610
6611     toString : function(){
6612         return "[Tree"+(this.id?" "+this.id:"")+"]";
6613     }
6614 });
6615
6616 /**
6617  * @class Roo.data.Node
6618  * @extends Roo.util.Observable
6619  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
6620  * @cfg {String} id The id for this node. If one is not specified, one is generated.
6621  * @constructor
6622  * @param {Object} attributes The attributes/config for the node
6623  */
6624 Roo.data.Node = function(attributes){
6625     /**
6626      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
6627      * @type {Object}
6628      */
6629     this.attributes = attributes || {};
6630     this.leaf = this.attributes.leaf;
6631     /**
6632      * The node id. @type String
6633      */
6634     this.id = this.attributes.id;
6635     if(!this.id){
6636         this.id = Roo.id(null, "ynode-");
6637         this.attributes.id = this.id;
6638     }
6639     /**
6640      * All child nodes of this node. @type Array
6641      */
6642     this.childNodes = [];
6643     if(!this.childNodes.indexOf){ // indexOf is a must
6644         this.childNodes.indexOf = function(o){
6645             for(var i = 0, len = this.length; i < len; i++){
6646                 if(this[i] == o) {
6647                     return i;
6648                 }
6649             }
6650             return -1;
6651         };
6652     }
6653     /**
6654      * The parent node for this node. @type Node
6655      */
6656     this.parentNode = null;
6657     /**
6658      * The first direct child node of this node, or null if this node has no child nodes. @type Node
6659      */
6660     this.firstChild = null;
6661     /**
6662      * The last direct child node of this node, or null if this node has no child nodes. @type Node
6663      */
6664     this.lastChild = null;
6665     /**
6666      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
6667      */
6668     this.previousSibling = null;
6669     /**
6670      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
6671      */
6672     this.nextSibling = null;
6673
6674     this.addEvents({
6675        /**
6676         * @event append
6677         * Fires when a new child node is appended
6678         * @param {Tree} tree The owner tree
6679         * @param {Node} this This node
6680         * @param {Node} node The newly appended node
6681         * @param {Number} index The index of the newly appended node
6682         */
6683        "append" : true,
6684        /**
6685         * @event remove
6686         * Fires when a child node is removed
6687         * @param {Tree} tree The owner tree
6688         * @param {Node} this This node
6689         * @param {Node} node The removed node
6690         */
6691        "remove" : true,
6692        /**
6693         * @event move
6694         * Fires when this node is moved to a new location in the tree
6695         * @param {Tree} tree The owner tree
6696         * @param {Node} this This node
6697         * @param {Node} oldParent The old parent of this node
6698         * @param {Node} newParent The new parent of this node
6699         * @param {Number} index The index it was moved to
6700         */
6701        "move" : true,
6702        /**
6703         * @event insert
6704         * Fires when a new child node is inserted.
6705         * @param {Tree} tree The owner tree
6706         * @param {Node} this This node
6707         * @param {Node} node The child node inserted
6708         * @param {Node} refNode The child node the node was inserted before
6709         */
6710        "insert" : true,
6711        /**
6712         * @event beforeappend
6713         * Fires before a new child is appended, return false to cancel the append.
6714         * @param {Tree} tree The owner tree
6715         * @param {Node} this This node
6716         * @param {Node} node The child node to be appended
6717         */
6718        "beforeappend" : true,
6719        /**
6720         * @event beforeremove
6721         * Fires before a child is removed, return false to cancel the remove.
6722         * @param {Tree} tree The owner tree
6723         * @param {Node} this This node
6724         * @param {Node} node The child node to be removed
6725         */
6726        "beforeremove" : true,
6727        /**
6728         * @event beforemove
6729         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
6730         * @param {Tree} tree The owner tree
6731         * @param {Node} this This node
6732         * @param {Node} oldParent The parent of this node
6733         * @param {Node} newParent The new parent this node is moving to
6734         * @param {Number} index The index it is being moved to
6735         */
6736        "beforemove" : true,
6737        /**
6738         * @event beforeinsert
6739         * Fires before a new child is inserted, return false to cancel the insert.
6740         * @param {Tree} tree The owner tree
6741         * @param {Node} this This node
6742         * @param {Node} node The child node to be inserted
6743         * @param {Node} refNode The child node the node is being inserted before
6744         */
6745        "beforeinsert" : true
6746    });
6747     this.listeners = this.attributes.listeners;
6748     Roo.data.Node.superclass.constructor.call(this);
6749 };
6750
6751 Roo.extend(Roo.data.Node, Roo.util.Observable, {
6752     fireEvent : function(evtName){
6753         // first do standard event for this node
6754         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
6755             return false;
6756         }
6757         // then bubble it up to the tree if the event wasn't cancelled
6758         var ot = this.getOwnerTree();
6759         if(ot){
6760             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
6761                 return false;
6762             }
6763         }
6764         return true;
6765     },
6766
6767     /**
6768      * Returns true if this node is a leaf
6769      * @return {Boolean}
6770      */
6771     isLeaf : function(){
6772         return this.leaf === true;
6773     },
6774
6775     // private
6776     setFirstChild : function(node){
6777         this.firstChild = node;
6778     },
6779
6780     //private
6781     setLastChild : function(node){
6782         this.lastChild = node;
6783     },
6784
6785
6786     /**
6787      * Returns true if this node is the last child of its parent
6788      * @return {Boolean}
6789      */
6790     isLast : function(){
6791        return (!this.parentNode ? true : this.parentNode.lastChild == this);
6792     },
6793
6794     /**
6795      * Returns true if this node is the first child of its parent
6796      * @return {Boolean}
6797      */
6798     isFirst : function(){
6799        return (!this.parentNode ? true : this.parentNode.firstChild == this);
6800     },
6801
6802     hasChildNodes : function(){
6803         return !this.isLeaf() && this.childNodes.length > 0;
6804     },
6805
6806     /**
6807      * Insert node(s) as the last child node of this node.
6808      * @param {Node/Array} node The node or Array of nodes to append
6809      * @return {Node} The appended node if single append, or null if an array was passed
6810      */
6811     appendChild : function(node){
6812         var multi = false;
6813         if(node instanceof Array){
6814             multi = node;
6815         }else if(arguments.length > 1){
6816             multi = arguments;
6817         }
6818         // if passed an array or multiple args do them one by one
6819         if(multi){
6820             for(var i = 0, len = multi.length; i < len; i++) {
6821                 this.appendChild(multi[i]);
6822             }
6823         }else{
6824             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
6825                 return false;
6826             }
6827             var index = this.childNodes.length;
6828             var oldParent = node.parentNode;
6829             // it's a move, make sure we move it cleanly
6830             if(oldParent){
6831                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
6832                     return false;
6833                 }
6834                 oldParent.removeChild(node);
6835             }
6836             index = this.childNodes.length;
6837             if(index == 0){
6838                 this.setFirstChild(node);
6839             }
6840             this.childNodes.push(node);
6841             node.parentNode = this;
6842             var ps = this.childNodes[index-1];
6843             if(ps){
6844                 node.previousSibling = ps;
6845                 ps.nextSibling = node;
6846             }else{
6847                 node.previousSibling = null;
6848             }
6849             node.nextSibling = null;
6850             this.setLastChild(node);
6851             node.setOwnerTree(this.getOwnerTree());
6852             this.fireEvent("append", this.ownerTree, this, node, index);
6853             if(oldParent){
6854                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
6855             }
6856             return node;
6857         }
6858     },
6859
6860     /**
6861      * Removes a child node from this node.
6862      * @param {Node} node The node to remove
6863      * @return {Node} The removed node
6864      */
6865     removeChild : function(node){
6866         var index = this.childNodes.indexOf(node);
6867         if(index == -1){
6868             return false;
6869         }
6870         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
6871             return false;
6872         }
6873
6874         // remove it from childNodes collection
6875         this.childNodes.splice(index, 1);
6876
6877         // update siblings
6878         if(node.previousSibling){
6879             node.previousSibling.nextSibling = node.nextSibling;
6880         }
6881         if(node.nextSibling){
6882             node.nextSibling.previousSibling = node.previousSibling;
6883         }
6884
6885         // update child refs
6886         if(this.firstChild == node){
6887             this.setFirstChild(node.nextSibling);
6888         }
6889         if(this.lastChild == node){
6890             this.setLastChild(node.previousSibling);
6891         }
6892
6893         node.setOwnerTree(null);
6894         // clear any references from the node
6895         node.parentNode = null;
6896         node.previousSibling = null;
6897         node.nextSibling = null;
6898         this.fireEvent("remove", this.ownerTree, this, node);
6899         return node;
6900     },
6901
6902     /**
6903      * Inserts the first node before the second node in this nodes childNodes collection.
6904      * @param {Node} node The node to insert
6905      * @param {Node} refNode The node to insert before (if null the node is appended)
6906      * @return {Node} The inserted node
6907      */
6908     insertBefore : function(node, refNode){
6909         if(!refNode){ // like standard Dom, refNode can be null for append
6910             return this.appendChild(node);
6911         }
6912         // nothing to do
6913         if(node == refNode){
6914             return false;
6915         }
6916
6917         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
6918             return false;
6919         }
6920         var index = this.childNodes.indexOf(refNode);
6921         var oldParent = node.parentNode;
6922         var refIndex = index;
6923
6924         // when moving internally, indexes will change after remove
6925         if(oldParent == this && this.childNodes.indexOf(node) < index){
6926             refIndex--;
6927         }
6928
6929         // it's a move, make sure we move it cleanly
6930         if(oldParent){
6931             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
6932                 return false;
6933             }
6934             oldParent.removeChild(node);
6935         }
6936         if(refIndex == 0){
6937             this.setFirstChild(node);
6938         }
6939         this.childNodes.splice(refIndex, 0, node);
6940         node.parentNode = this;
6941         var ps = this.childNodes[refIndex-1];
6942         if(ps){
6943             node.previousSibling = ps;
6944             ps.nextSibling = node;
6945         }else{
6946             node.previousSibling = null;
6947         }
6948         node.nextSibling = refNode;
6949         refNode.previousSibling = node;
6950         node.setOwnerTree(this.getOwnerTree());
6951         this.fireEvent("insert", this.ownerTree, this, node, refNode);
6952         if(oldParent){
6953             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
6954         }
6955         return node;
6956     },
6957
6958     /**
6959      * Returns the child node at the specified index.
6960      * @param {Number} index
6961      * @return {Node}
6962      */
6963     item : function(index){
6964         return this.childNodes[index];
6965     },
6966
6967     /**
6968      * Replaces one child node in this node with another.
6969      * @param {Node} newChild The replacement node
6970      * @param {Node} oldChild The node to replace
6971      * @return {Node} The replaced node
6972      */
6973     replaceChild : function(newChild, oldChild){
6974         this.insertBefore(newChild, oldChild);
6975         this.removeChild(oldChild);
6976         return oldChild;
6977     },
6978
6979     /**
6980      * Returns the index of a child node
6981      * @param {Node} node
6982      * @return {Number} The index of the node or -1 if it was not found
6983      */
6984     indexOf : function(child){
6985         return this.childNodes.indexOf(child);
6986     },
6987
6988     /**
6989      * Returns the tree this node is in.
6990      * @return {Tree}
6991      */
6992     getOwnerTree : function(){
6993         // if it doesn't have one, look for one
6994         if(!this.ownerTree){
6995             var p = this;
6996             while(p){
6997                 if(p.ownerTree){
6998                     this.ownerTree = p.ownerTree;
6999                     break;
7000                 }
7001                 p = p.parentNode;
7002             }
7003         }
7004         return this.ownerTree;
7005     },
7006
7007     /**
7008      * Returns depth of this node (the root node has a depth of 0)
7009      * @return {Number}
7010      */
7011     getDepth : function(){
7012         var depth = 0;
7013         var p = this;
7014         while(p.parentNode){
7015             ++depth;
7016             p = p.parentNode;
7017         }
7018         return depth;
7019     },
7020
7021     // private
7022     setOwnerTree : function(tree){
7023         // if it's move, we need to update everyone
7024         if(tree != this.ownerTree){
7025             if(this.ownerTree){
7026                 this.ownerTree.unregisterNode(this);
7027             }
7028             this.ownerTree = tree;
7029             var cs = this.childNodes;
7030             for(var i = 0, len = cs.length; i < len; i++) {
7031                 cs[i].setOwnerTree(tree);
7032             }
7033             if(tree){
7034                 tree.registerNode(this);
7035             }
7036         }
7037     },
7038
7039     /**
7040      * Returns the path for this node. The path can be used to expand or select this node programmatically.
7041      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
7042      * @return {String} The path
7043      */
7044     getPath : function(attr){
7045         attr = attr || "id";
7046         var p = this.parentNode;
7047         var b = [this.attributes[attr]];
7048         while(p){
7049             b.unshift(p.attributes[attr]);
7050             p = p.parentNode;
7051         }
7052         var sep = this.getOwnerTree().pathSeparator;
7053         return sep + b.join(sep);
7054     },
7055
7056     /**
7057      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7058      * function call will be the scope provided or the current node. The arguments to the function
7059      * will be the args provided or the current node. If the function returns false at any point,
7060      * the bubble is stopped.
7061      * @param {Function} fn The function to call
7062      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7063      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7064      */
7065     bubble : function(fn, scope, args){
7066         var p = this;
7067         while(p){
7068             if(fn.call(scope || p, args || p) === false){
7069                 break;
7070             }
7071             p = p.parentNode;
7072         }
7073     },
7074
7075     /**
7076      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7077      * function call will be the scope provided or the current node. The arguments to the function
7078      * will be the args provided or the current node. If the function returns false at any point,
7079      * the cascade is stopped on that branch.
7080      * @param {Function} fn The function to call
7081      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7082      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7083      */
7084     cascade : function(fn, scope, args){
7085         if(fn.call(scope || this, args || this) !== false){
7086             var cs = this.childNodes;
7087             for(var i = 0, len = cs.length; i < len; i++) {
7088                 cs[i].cascade(fn, scope, args);
7089             }
7090         }
7091     },
7092
7093     /**
7094      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
7095      * function call will be the scope provided or the current node. The arguments to the function
7096      * will be the args provided or the current node. If the function returns false at any point,
7097      * the iteration stops.
7098      * @param {Function} fn The function to call
7099      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7100      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7101      */
7102     eachChild : function(fn, scope, args){
7103         var cs = this.childNodes;
7104         for(var i = 0, len = cs.length; i < len; i++) {
7105                 if(fn.call(scope || this, args || cs[i]) === false){
7106                     break;
7107                 }
7108         }
7109     },
7110
7111     /**
7112      * Finds the first child that has the attribute with the specified value.
7113      * @param {String} attribute The attribute name
7114      * @param {Mixed} value The value to search for
7115      * @return {Node} The found child or null if none was found
7116      */
7117     findChild : function(attribute, value){
7118         var cs = this.childNodes;
7119         for(var i = 0, len = cs.length; i < len; i++) {
7120                 if(cs[i].attributes[attribute] == value){
7121                     return cs[i];
7122                 }
7123         }
7124         return null;
7125     },
7126
7127     /**
7128      * Finds the first child by a custom function. The child matches if the function passed
7129      * returns true.
7130      * @param {Function} fn
7131      * @param {Object} scope (optional)
7132      * @return {Node} The found child or null if none was found
7133      */
7134     findChildBy : function(fn, scope){
7135         var cs = this.childNodes;
7136         for(var i = 0, len = cs.length; i < len; i++) {
7137                 if(fn.call(scope||cs[i], cs[i]) === true){
7138                     return cs[i];
7139                 }
7140         }
7141         return null;
7142     },
7143
7144     /**
7145      * Sorts this nodes children using the supplied sort function
7146      * @param {Function} fn
7147      * @param {Object} scope (optional)
7148      */
7149     sort : function(fn, scope){
7150         var cs = this.childNodes;
7151         var len = cs.length;
7152         if(len > 0){
7153             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
7154             cs.sort(sortFn);
7155             for(var i = 0; i < len; i++){
7156                 var n = cs[i];
7157                 n.previousSibling = cs[i-1];
7158                 n.nextSibling = cs[i+1];
7159                 if(i == 0){
7160                     this.setFirstChild(n);
7161                 }
7162                 if(i == len-1){
7163                     this.setLastChild(n);
7164                 }
7165             }
7166         }
7167     },
7168
7169     /**
7170      * Returns true if this node is an ancestor (at any point) of the passed node.
7171      * @param {Node} node
7172      * @return {Boolean}
7173      */
7174     contains : function(node){
7175         return node.isAncestor(this);
7176     },
7177
7178     /**
7179      * Returns true if the passed node is an ancestor (at any point) of this node.
7180      * @param {Node} node
7181      * @return {Boolean}
7182      */
7183     isAncestor : function(node){
7184         var p = this.parentNode;
7185         while(p){
7186             if(p == node){
7187                 return true;
7188             }
7189             p = p.parentNode;
7190         }
7191         return false;
7192     },
7193
7194     toString : function(){
7195         return "[Node"+(this.id?" "+this.id:"")+"]";
7196     }
7197 });/*
7198  * Based on:
7199  * Ext JS Library 1.1.1
7200  * Copyright(c) 2006-2007, Ext JS, LLC.
7201  *
7202  * Originally Released Under LGPL - original licence link has changed is not relivant.
7203  *
7204  * Fork - LGPL
7205  * <script type="text/javascript">
7206  */
7207  
7208
7209 /**
7210  * @class Roo.ComponentMgr
7211  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
7212  * @singleton
7213  */
7214 Roo.ComponentMgr = function(){
7215     var all = new Roo.util.MixedCollection();
7216
7217     return {
7218         /**
7219          * Registers a component.
7220          * @param {Roo.Component} c The component
7221          */
7222         register : function(c){
7223             all.add(c);
7224         },
7225
7226         /**
7227          * Unregisters a component.
7228          * @param {Roo.Component} c The component
7229          */
7230         unregister : function(c){
7231             all.remove(c);
7232         },
7233
7234         /**
7235          * Returns a component by id
7236          * @param {String} id The component id
7237          */
7238         get : function(id){
7239             return all.get(id);
7240         },
7241
7242         /**
7243          * Registers a function that will be called when a specified component is added to ComponentMgr
7244          * @param {String} id The component id
7245          * @param {Funtction} fn The callback function
7246          * @param {Object} scope The scope of the callback
7247          */
7248         onAvailable : function(id, fn, scope){
7249             all.on("add", function(index, o){
7250                 if(o.id == id){
7251                     fn.call(scope || o, o);
7252                     all.un("add", fn, scope);
7253                 }
7254             });
7255         }
7256     };
7257 }();/*
7258  * Based on:
7259  * Ext JS Library 1.1.1
7260  * Copyright(c) 2006-2007, Ext JS, LLC.
7261  *
7262  * Originally Released Under LGPL - original licence link has changed is not relivant.
7263  *
7264  * Fork - LGPL
7265  * <script type="text/javascript">
7266  */
7267  
7268 /**
7269  * @class Roo.Component
7270  * @extends Roo.util.Observable
7271  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
7272  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
7273  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
7274  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
7275  * All visual components (widgets) that require rendering into a layout should subclass Component.
7276  * @constructor
7277  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
7278  * 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
7279  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
7280  */
7281 Roo.Component = function(config){
7282     config = config || {};
7283     if(config.tagName || config.dom || typeof config == "string"){ // element object
7284         config = {el: config, id: config.id || config};
7285     }
7286     this.initialConfig = config;
7287
7288     Roo.apply(this, config);
7289     this.addEvents({
7290         /**
7291          * @event disable
7292          * Fires after the component is disabled.
7293              * @param {Roo.Component} this
7294              */
7295         disable : true,
7296         /**
7297          * @event enable
7298          * Fires after the component is enabled.
7299              * @param {Roo.Component} this
7300              */
7301         enable : true,
7302         /**
7303          * @event beforeshow
7304          * Fires before the component is shown.  Return false to stop the show.
7305              * @param {Roo.Component} this
7306              */
7307         beforeshow : true,
7308         /**
7309          * @event show
7310          * Fires after the component is shown.
7311              * @param {Roo.Component} this
7312              */
7313         show : true,
7314         /**
7315          * @event beforehide
7316          * Fires before the component is hidden. Return false to stop the hide.
7317              * @param {Roo.Component} this
7318              */
7319         beforehide : true,
7320         /**
7321          * @event hide
7322          * Fires after the component is hidden.
7323              * @param {Roo.Component} this
7324              */
7325         hide : true,
7326         /**
7327          * @event beforerender
7328          * Fires before the component is rendered. Return false to stop the render.
7329              * @param {Roo.Component} this
7330              */
7331         beforerender : true,
7332         /**
7333          * @event render
7334          * Fires after the component is rendered.
7335              * @param {Roo.Component} this
7336              */
7337         render : true,
7338         /**
7339          * @event beforedestroy
7340          * Fires before the component is destroyed. Return false to stop the destroy.
7341              * @param {Roo.Component} this
7342              */
7343         beforedestroy : true,
7344         /**
7345          * @event destroy
7346          * Fires after the component is destroyed.
7347              * @param {Roo.Component} this
7348              */
7349         destroy : true
7350     });
7351     if(!this.id){
7352         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
7353     }
7354     Roo.ComponentMgr.register(this);
7355     Roo.Component.superclass.constructor.call(this);
7356     this.initComponent();
7357     if(this.renderTo){ // not supported by all components yet. use at your own risk!
7358         this.render(this.renderTo);
7359         delete this.renderTo;
7360     }
7361 };
7362
7363 // private
7364 Roo.Component.AUTO_ID = 1000;
7365
7366 Roo.extend(Roo.Component, Roo.util.Observable, {
7367     /**
7368      * @property {Boolean} hidden
7369      * true if this component is hidden. Read-only.
7370      */
7371     hidden : false,
7372     /**
7373      * true if this component is disabled. Read-only.
7374      */
7375     disabled : false,
7376     /**
7377      * true if this component has been rendered. Read-only.
7378      */
7379     rendered : false,
7380     
7381     /** @cfg {String} disableClass
7382      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
7383      */
7384     disabledClass : "x-item-disabled",
7385         /** @cfg {Boolean} allowDomMove
7386          * Whether the component can move the Dom node when rendering (defaults to true).
7387          */
7388     allowDomMove : true,
7389     /** @cfg {String} hideMode
7390      * How this component should hidden. Supported values are
7391      * "visibility" (css visibility), "offsets" (negative offset position) and
7392      * "display" (css display) - defaults to "display".
7393      */
7394     hideMode: 'display',
7395
7396     // private
7397     ctype : "Roo.Component",
7398
7399     /** @cfg {String} actionMode 
7400      * which property holds the element that used for  hide() / show() / disable() / enable()
7401      * default is 'el' 
7402      */
7403     actionMode : "el",
7404
7405     // private
7406     getActionEl : function(){
7407         return this[this.actionMode];
7408     },
7409
7410     initComponent : Roo.emptyFn,
7411     /**
7412      * If this is a lazy rendering component, render it to its container element.
7413      * @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.
7414      */
7415     render : function(container, position){
7416         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
7417             if(!container && this.el){
7418                 this.el = Roo.get(this.el);
7419                 container = this.el.dom.parentNode;
7420                 this.allowDomMove = false;
7421             }
7422             this.container = Roo.get(container);
7423             this.rendered = true;
7424             if(position !== undefined){
7425                 if(typeof position == 'number'){
7426                     position = this.container.dom.childNodes[position];
7427                 }else{
7428                     position = Roo.getDom(position);
7429                 }
7430             }
7431             this.onRender(this.container, position || null);
7432             if(this.cls){
7433                 this.el.addClass(this.cls);
7434                 delete this.cls;
7435             }
7436             if(this.style){
7437                 this.el.applyStyles(this.style);
7438                 delete this.style;
7439             }
7440             this.fireEvent("render", this);
7441             this.afterRender(this.container);
7442             if(this.hidden){
7443                 this.hide();
7444             }
7445             if(this.disabled){
7446                 this.disable();
7447             }
7448         }
7449         return this;
7450     },
7451
7452     // private
7453     // default function is not really useful
7454     onRender : function(ct, position){
7455         if(this.el){
7456             this.el = Roo.get(this.el);
7457             if(this.allowDomMove !== false){
7458                 ct.dom.insertBefore(this.el.dom, position);
7459             }
7460         }
7461     },
7462
7463     // private
7464     getAutoCreate : function(){
7465         var cfg = typeof this.autoCreate == "object" ?
7466                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
7467         if(this.id && !cfg.id){
7468             cfg.id = this.id;
7469         }
7470         return cfg;
7471     },
7472
7473     // private
7474     afterRender : Roo.emptyFn,
7475
7476     /**
7477      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
7478      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
7479      */
7480     destroy : function(){
7481         if(this.fireEvent("beforedestroy", this) !== false){
7482             this.purgeListeners();
7483             this.beforeDestroy();
7484             if(this.rendered){
7485                 this.el.removeAllListeners();
7486                 this.el.remove();
7487                 if(this.actionMode == "container"){
7488                     this.container.remove();
7489                 }
7490             }
7491             this.onDestroy();
7492             Roo.ComponentMgr.unregister(this);
7493             this.fireEvent("destroy", this);
7494         }
7495     },
7496
7497         // private
7498     beforeDestroy : function(){
7499
7500     },
7501
7502         // private
7503         onDestroy : function(){
7504
7505     },
7506
7507     /**
7508      * Returns the underlying {@link Roo.Element}.
7509      * @return {Roo.Element} The element
7510      */
7511     getEl : function(){
7512         return this.el;
7513     },
7514
7515     /**
7516      * Returns the id of this component.
7517      * @return {String}
7518      */
7519     getId : function(){
7520         return this.id;
7521     },
7522
7523     /**
7524      * Try to focus this component.
7525      * @param {Boolean} selectText True to also select the text in this component (if applicable)
7526      * @return {Roo.Component} this
7527      */
7528     focus : function(selectText){
7529         if(this.rendered){
7530             this.el.focus();
7531             if(selectText === true){
7532                 this.el.dom.select();
7533             }
7534         }
7535         return this;
7536     },
7537
7538     // private
7539     blur : function(){
7540         if(this.rendered){
7541             this.el.blur();
7542         }
7543         return this;
7544     },
7545
7546     /**
7547      * Disable this component.
7548      * @return {Roo.Component} this
7549      */
7550     disable : function(){
7551         if(this.rendered){
7552             this.onDisable();
7553         }
7554         this.disabled = true;
7555         this.fireEvent("disable", this);
7556         return this;
7557     },
7558
7559         // private
7560     onDisable : function(){
7561         this.getActionEl().addClass(this.disabledClass);
7562         this.el.dom.disabled = true;
7563     },
7564
7565     /**
7566      * Enable this component.
7567      * @return {Roo.Component} this
7568      */
7569     enable : function(){
7570         if(this.rendered){
7571             this.onEnable();
7572         }
7573         this.disabled = false;
7574         this.fireEvent("enable", this);
7575         return this;
7576     },
7577
7578         // private
7579     onEnable : function(){
7580         this.getActionEl().removeClass(this.disabledClass);
7581         this.el.dom.disabled = false;
7582     },
7583
7584     /**
7585      * Convenience function for setting disabled/enabled by boolean.
7586      * @param {Boolean} disabled
7587      */
7588     setDisabled : function(disabled){
7589         this[disabled ? "disable" : "enable"]();
7590     },
7591
7592     /**
7593      * Show this component.
7594      * @return {Roo.Component} this
7595      */
7596     show: function(){
7597         if(this.fireEvent("beforeshow", this) !== false){
7598             this.hidden = false;
7599             if(this.rendered){
7600                 this.onShow();
7601             }
7602             this.fireEvent("show", this);
7603         }
7604         return this;
7605     },
7606
7607     // private
7608     onShow : function(){
7609         var ae = this.getActionEl();
7610         if(this.hideMode == 'visibility'){
7611             ae.dom.style.visibility = "visible";
7612         }else if(this.hideMode == 'offsets'){
7613             ae.removeClass('x-hidden');
7614         }else{
7615             ae.dom.style.display = "";
7616         }
7617     },
7618
7619     /**
7620      * Hide this component.
7621      * @return {Roo.Component} this
7622      */
7623     hide: function(){
7624         if(this.fireEvent("beforehide", this) !== false){
7625             this.hidden = true;
7626             if(this.rendered){
7627                 this.onHide();
7628             }
7629             this.fireEvent("hide", this);
7630         }
7631         return this;
7632     },
7633
7634     // private
7635     onHide : function(){
7636         var ae = this.getActionEl();
7637         if(this.hideMode == 'visibility'){
7638             ae.dom.style.visibility = "hidden";
7639         }else if(this.hideMode == 'offsets'){
7640             ae.addClass('x-hidden');
7641         }else{
7642             ae.dom.style.display = "none";
7643         }
7644     },
7645
7646     /**
7647      * Convenience function to hide or show this component by boolean.
7648      * @param {Boolean} visible True to show, false to hide
7649      * @return {Roo.Component} this
7650      */
7651     setVisible: function(visible){
7652         if(visible) {
7653             this.show();
7654         }else{
7655             this.hide();
7656         }
7657         return this;
7658     },
7659
7660     /**
7661      * Returns true if this component is visible.
7662      */
7663     isVisible : function(){
7664         return this.getActionEl().isVisible();
7665     },
7666
7667     cloneConfig : function(overrides){
7668         overrides = overrides || {};
7669         var id = overrides.id || Roo.id();
7670         var cfg = Roo.applyIf(overrides, this.initialConfig);
7671         cfg.id = id; // prevent dup id
7672         return new this.constructor(cfg);
7673     }
7674 });/*
7675  * Based on:
7676  * Ext JS Library 1.1.1
7677  * Copyright(c) 2006-2007, Ext JS, LLC.
7678  *
7679  * Originally Released Under LGPL - original licence link has changed is not relivant.
7680  *
7681  * Fork - LGPL
7682  * <script type="text/javascript">
7683  */
7684  (function(){ 
7685 /**
7686  * @class Roo.Layer
7687  * @extends Roo.Element
7688  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
7689  * automatic maintaining of shadow/shim positions.
7690  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
7691  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
7692  * you can pass a string with a CSS class name. False turns off the shadow.
7693  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
7694  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
7695  * @cfg {String} cls CSS class to add to the element
7696  * @cfg {Number} zindex Starting z-index (defaults to 11000)
7697  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
7698  * @constructor
7699  * @param {Object} config An object with config options.
7700  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
7701  */
7702
7703 Roo.Layer = function(config, existingEl){
7704     config = config || {};
7705     var dh = Roo.DomHelper;
7706     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
7707     if(existingEl){
7708         this.dom = Roo.getDom(existingEl);
7709     }
7710     if(!this.dom){
7711         var o = config.dh || {tag: "div", cls: "x-layer"};
7712         this.dom = dh.append(pel, o);
7713     }
7714     if(config.cls){
7715         this.addClass(config.cls);
7716     }
7717     this.constrain = config.constrain !== false;
7718     this.visibilityMode = Roo.Element.VISIBILITY;
7719     if(config.id){
7720         this.id = this.dom.id = config.id;
7721     }else{
7722         this.id = Roo.id(this.dom);
7723     }
7724     this.zindex = config.zindex || this.getZIndex();
7725     this.position("absolute", this.zindex);
7726     if(config.shadow){
7727         this.shadowOffset = config.shadowOffset || 4;
7728         this.shadow = new Roo.Shadow({
7729             offset : this.shadowOffset,
7730             mode : config.shadow
7731         });
7732     }else{
7733         this.shadowOffset = 0;
7734     }
7735     this.useShim = config.shim !== false && Roo.useShims;
7736     this.useDisplay = config.useDisplay;
7737     this.hide();
7738 };
7739
7740 var supr = Roo.Element.prototype;
7741
7742 // shims are shared among layer to keep from having 100 iframes
7743 var shims = [];
7744
7745 Roo.extend(Roo.Layer, Roo.Element, {
7746
7747     getZIndex : function(){
7748         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
7749     },
7750
7751     getShim : function(){
7752         if(!this.useShim){
7753             return null;
7754         }
7755         if(this.shim){
7756             return this.shim;
7757         }
7758         var shim = shims.shift();
7759         if(!shim){
7760             shim = this.createShim();
7761             shim.enableDisplayMode('block');
7762             shim.dom.style.display = 'none';
7763             shim.dom.style.visibility = 'visible';
7764         }
7765         var pn = this.dom.parentNode;
7766         if(shim.dom.parentNode != pn){
7767             pn.insertBefore(shim.dom, this.dom);
7768         }
7769         shim.setStyle('z-index', this.getZIndex()-2);
7770         this.shim = shim;
7771         return shim;
7772     },
7773
7774     hideShim : function(){
7775         if(this.shim){
7776             this.shim.setDisplayed(false);
7777             shims.push(this.shim);
7778             delete this.shim;
7779         }
7780     },
7781
7782     disableShadow : function(){
7783         if(this.shadow){
7784             this.shadowDisabled = true;
7785             this.shadow.hide();
7786             this.lastShadowOffset = this.shadowOffset;
7787             this.shadowOffset = 0;
7788         }
7789     },
7790
7791     enableShadow : function(show){
7792         if(this.shadow){
7793             this.shadowDisabled = false;
7794             this.shadowOffset = this.lastShadowOffset;
7795             delete this.lastShadowOffset;
7796             if(show){
7797                 this.sync(true);
7798             }
7799         }
7800     },
7801
7802     // private
7803     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
7804     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
7805     sync : function(doShow){
7806         var sw = this.shadow;
7807         if(!this.updating && this.isVisible() && (sw || this.useShim)){
7808             var sh = this.getShim();
7809
7810             var w = this.getWidth(),
7811                 h = this.getHeight();
7812
7813             var l = this.getLeft(true),
7814                 t = this.getTop(true);
7815
7816             if(sw && !this.shadowDisabled){
7817                 if(doShow && !sw.isVisible()){
7818                     sw.show(this);
7819                 }else{
7820                     sw.realign(l, t, w, h);
7821                 }
7822                 if(sh){
7823                     if(doShow){
7824                        sh.show();
7825                     }
7826                     // fit the shim behind the shadow, so it is shimmed too
7827                     var a = sw.adjusts, s = sh.dom.style;
7828                     s.left = (Math.min(l, l+a.l))+"px";
7829                     s.top = (Math.min(t, t+a.t))+"px";
7830                     s.width = (w+a.w)+"px";
7831                     s.height = (h+a.h)+"px";
7832                 }
7833             }else if(sh){
7834                 if(doShow){
7835                    sh.show();
7836                 }
7837                 sh.setSize(w, h);
7838                 sh.setLeftTop(l, t);
7839             }
7840             
7841         }
7842     },
7843
7844     // private
7845     destroy : function(){
7846         this.hideShim();
7847         if(this.shadow){
7848             this.shadow.hide();
7849         }
7850         this.removeAllListeners();
7851         var pn = this.dom.parentNode;
7852         if(pn){
7853             pn.removeChild(this.dom);
7854         }
7855         Roo.Element.uncache(this.id);
7856     },
7857
7858     remove : function(){
7859         this.destroy();
7860     },
7861
7862     // private
7863     beginUpdate : function(){
7864         this.updating = true;
7865     },
7866
7867     // private
7868     endUpdate : function(){
7869         this.updating = false;
7870         this.sync(true);
7871     },
7872
7873     // private
7874     hideUnders : function(negOffset){
7875         if(this.shadow){
7876             this.shadow.hide();
7877         }
7878         this.hideShim();
7879     },
7880
7881     // private
7882     constrainXY : function(){
7883         if(this.constrain){
7884             var vw = Roo.lib.Dom.getViewWidth(),
7885                 vh = Roo.lib.Dom.getViewHeight();
7886             var s = Roo.get(document).getScroll();
7887
7888             var xy = this.getXY();
7889             var x = xy[0], y = xy[1];   
7890             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
7891             // only move it if it needs it
7892             var moved = false;
7893             // first validate right/bottom
7894             if((x + w) > vw+s.left){
7895                 x = vw - w - this.shadowOffset;
7896                 moved = true;
7897             }
7898             if((y + h) > vh+s.top){
7899                 y = vh - h - this.shadowOffset;
7900                 moved = true;
7901             }
7902             // then make sure top/left isn't negative
7903             if(x < s.left){
7904                 x = s.left;
7905                 moved = true;
7906             }
7907             if(y < s.top){
7908                 y = s.top;
7909                 moved = true;
7910             }
7911             if(moved){
7912                 if(this.avoidY){
7913                     var ay = this.avoidY;
7914                     if(y <= ay && (y+h) >= ay){
7915                         y = ay-h-5;   
7916                     }
7917                 }
7918                 xy = [x, y];
7919                 this.storeXY(xy);
7920                 supr.setXY.call(this, xy);
7921                 this.sync();
7922             }
7923         }
7924     },
7925
7926     isVisible : function(){
7927         return this.visible;    
7928     },
7929
7930     // private
7931     showAction : function(){
7932         this.visible = true; // track visibility to prevent getStyle calls
7933         if(this.useDisplay === true){
7934             this.setDisplayed("");
7935         }else if(this.lastXY){
7936             supr.setXY.call(this, this.lastXY);
7937         }else if(this.lastLT){
7938             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
7939         }
7940     },
7941
7942     // private
7943     hideAction : function(){
7944         this.visible = false;
7945         if(this.useDisplay === true){
7946             this.setDisplayed(false);
7947         }else{
7948             this.setLeftTop(-10000,-10000);
7949         }
7950     },
7951
7952     // overridden Element method
7953     setVisible : function(v, a, d, c, e){
7954         if(v){
7955             this.showAction();
7956         }
7957         if(a && v){
7958             var cb = function(){
7959                 this.sync(true);
7960                 if(c){
7961                     c();
7962                 }
7963             }.createDelegate(this);
7964             supr.setVisible.call(this, true, true, d, cb, e);
7965         }else{
7966             if(!v){
7967                 this.hideUnders(true);
7968             }
7969             var cb = c;
7970             if(a){
7971                 cb = function(){
7972                     this.hideAction();
7973                     if(c){
7974                         c();
7975                     }
7976                 }.createDelegate(this);
7977             }
7978             supr.setVisible.call(this, v, a, d, cb, e);
7979             if(v){
7980                 this.sync(true);
7981             }else if(!a){
7982                 this.hideAction();
7983             }
7984         }
7985     },
7986
7987     storeXY : function(xy){
7988         delete this.lastLT;
7989         this.lastXY = xy;
7990     },
7991
7992     storeLeftTop : function(left, top){
7993         delete this.lastXY;
7994         this.lastLT = [left, top];
7995     },
7996
7997     // private
7998     beforeFx : function(){
7999         this.beforeAction();
8000         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
8001     },
8002
8003     // private
8004     afterFx : function(){
8005         Roo.Layer.superclass.afterFx.apply(this, arguments);
8006         this.sync(this.isVisible());
8007     },
8008
8009     // private
8010     beforeAction : function(){
8011         if(!this.updating && this.shadow){
8012             this.shadow.hide();
8013         }
8014     },
8015
8016     // overridden Element method
8017     setLeft : function(left){
8018         this.storeLeftTop(left, this.getTop(true));
8019         supr.setLeft.apply(this, arguments);
8020         this.sync();
8021     },
8022
8023     setTop : function(top){
8024         this.storeLeftTop(this.getLeft(true), top);
8025         supr.setTop.apply(this, arguments);
8026         this.sync();
8027     },
8028
8029     setLeftTop : function(left, top){
8030         this.storeLeftTop(left, top);
8031         supr.setLeftTop.apply(this, arguments);
8032         this.sync();
8033     },
8034
8035     setXY : function(xy, a, d, c, e){
8036         this.fixDisplay();
8037         this.beforeAction();
8038         this.storeXY(xy);
8039         var cb = this.createCB(c);
8040         supr.setXY.call(this, xy, a, d, cb, e);
8041         if(!a){
8042             cb();
8043         }
8044     },
8045
8046     // private
8047     createCB : function(c){
8048         var el = this;
8049         return function(){
8050             el.constrainXY();
8051             el.sync(true);
8052             if(c){
8053                 c();
8054             }
8055         };
8056     },
8057
8058     // overridden Element method
8059     setX : function(x, a, d, c, e){
8060         this.setXY([x, this.getY()], a, d, c, e);
8061     },
8062
8063     // overridden Element method
8064     setY : function(y, a, d, c, e){
8065         this.setXY([this.getX(), y], a, d, c, e);
8066     },
8067
8068     // overridden Element method
8069     setSize : function(w, h, a, d, c, e){
8070         this.beforeAction();
8071         var cb = this.createCB(c);
8072         supr.setSize.call(this, w, h, a, d, cb, e);
8073         if(!a){
8074             cb();
8075         }
8076     },
8077
8078     // overridden Element method
8079     setWidth : function(w, a, d, c, e){
8080         this.beforeAction();
8081         var cb = this.createCB(c);
8082         supr.setWidth.call(this, w, a, d, cb, e);
8083         if(!a){
8084             cb();
8085         }
8086     },
8087
8088     // overridden Element method
8089     setHeight : function(h, a, d, c, e){
8090         this.beforeAction();
8091         var cb = this.createCB(c);
8092         supr.setHeight.call(this, h, a, d, cb, e);
8093         if(!a){
8094             cb();
8095         }
8096     },
8097
8098     // overridden Element method
8099     setBounds : function(x, y, w, h, a, d, c, e){
8100         this.beforeAction();
8101         var cb = this.createCB(c);
8102         if(!a){
8103             this.storeXY([x, y]);
8104             supr.setXY.call(this, [x, y]);
8105             supr.setSize.call(this, w, h, a, d, cb, e);
8106             cb();
8107         }else{
8108             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
8109         }
8110         return this;
8111     },
8112     
8113     /**
8114      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
8115      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
8116      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
8117      * @param {Number} zindex The new z-index to set
8118      * @return {this} The Layer
8119      */
8120     setZIndex : function(zindex){
8121         this.zindex = zindex;
8122         this.setStyle("z-index", zindex + 2);
8123         if(this.shadow){
8124             this.shadow.setZIndex(zindex + 1);
8125         }
8126         if(this.shim){
8127             this.shim.setStyle("z-index", zindex);
8128         }
8129     }
8130 });
8131 })();/*
8132  * Based on:
8133  * Ext JS Library 1.1.1
8134  * Copyright(c) 2006-2007, Ext JS, LLC.
8135  *
8136  * Originally Released Under LGPL - original licence link has changed is not relivant.
8137  *
8138  * Fork - LGPL
8139  * <script type="text/javascript">
8140  */
8141
8142
8143 /**
8144  * @class Roo.Shadow
8145  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
8146  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
8147  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
8148  * @constructor
8149  * Create a new Shadow
8150  * @param {Object} config The config object
8151  */
8152 Roo.Shadow = function(config){
8153     Roo.apply(this, config);
8154     if(typeof this.mode != "string"){
8155         this.mode = this.defaultMode;
8156     }
8157     var o = this.offset, a = {h: 0};
8158     var rad = Math.floor(this.offset/2);
8159     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
8160         case "drop":
8161             a.w = 0;
8162             a.l = a.t = o;
8163             a.t -= 1;
8164             if(Roo.isIE){
8165                 a.l -= this.offset + rad;
8166                 a.t -= this.offset + rad;
8167                 a.w -= rad;
8168                 a.h -= rad;
8169                 a.t += 1;
8170             }
8171         break;
8172         case "sides":
8173             a.w = (o*2);
8174             a.l = -o;
8175             a.t = o-1;
8176             if(Roo.isIE){
8177                 a.l -= (this.offset - rad);
8178                 a.t -= this.offset + rad;
8179                 a.l += 1;
8180                 a.w -= (this.offset - rad)*2;
8181                 a.w -= rad + 1;
8182                 a.h -= 1;
8183             }
8184         break;
8185         case "frame":
8186             a.w = a.h = (o*2);
8187             a.l = a.t = -o;
8188             a.t += 1;
8189             a.h -= 2;
8190             if(Roo.isIE){
8191                 a.l -= (this.offset - rad);
8192                 a.t -= (this.offset - rad);
8193                 a.l += 1;
8194                 a.w -= (this.offset + rad + 1);
8195                 a.h -= (this.offset + rad);
8196                 a.h += 1;
8197             }
8198         break;
8199     };
8200
8201     this.adjusts = a;
8202 };
8203
8204 Roo.Shadow.prototype = {
8205     /**
8206      * @cfg {String} mode
8207      * The shadow display mode.  Supports the following options:<br />
8208      * sides: Shadow displays on both sides and bottom only<br />
8209      * frame: Shadow displays equally on all four sides<br />
8210      * drop: Traditional bottom-right drop shadow (default)
8211      */
8212     /**
8213      * @cfg {String} offset
8214      * The number of pixels to offset the shadow from the element (defaults to 4)
8215      */
8216     offset: 4,
8217
8218     // private
8219     defaultMode: "drop",
8220
8221     /**
8222      * Displays the shadow under the target element
8223      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
8224      */
8225     show : function(target){
8226         target = Roo.get(target);
8227         if(!this.el){
8228             this.el = Roo.Shadow.Pool.pull();
8229             if(this.el.dom.nextSibling != target.dom){
8230                 this.el.insertBefore(target);
8231             }
8232         }
8233         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
8234         if(Roo.isIE){
8235             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
8236         }
8237         this.realign(
8238             target.getLeft(true),
8239             target.getTop(true),
8240             target.getWidth(),
8241             target.getHeight()
8242         );
8243         this.el.dom.style.display = "block";
8244     },
8245
8246     /**
8247      * Returns true if the shadow is visible, else false
8248      */
8249     isVisible : function(){
8250         return this.el ? true : false;  
8251     },
8252
8253     /**
8254      * Direct alignment when values are already available. Show must be called at least once before
8255      * calling this method to ensure it is initialized.
8256      * @param {Number} left The target element left position
8257      * @param {Number} top The target element top position
8258      * @param {Number} width The target element width
8259      * @param {Number} height The target element height
8260      */
8261     realign : function(l, t, w, h){
8262         if(!this.el){
8263             return;
8264         }
8265         var a = this.adjusts, d = this.el.dom, s = d.style;
8266         var iea = 0;
8267         s.left = (l+a.l)+"px";
8268         s.top = (t+a.t)+"px";
8269         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
8270  
8271         if(s.width != sws || s.height != shs){
8272             s.width = sws;
8273             s.height = shs;
8274             if(!Roo.isIE){
8275                 var cn = d.childNodes;
8276                 var sww = Math.max(0, (sw-12))+"px";
8277                 cn[0].childNodes[1].style.width = sww;
8278                 cn[1].childNodes[1].style.width = sww;
8279                 cn[2].childNodes[1].style.width = sww;
8280                 cn[1].style.height = Math.max(0, (sh-12))+"px";
8281             }
8282         }
8283     },
8284
8285     /**
8286      * Hides this shadow
8287      */
8288     hide : function(){
8289         if(this.el){
8290             this.el.dom.style.display = "none";
8291             Roo.Shadow.Pool.push(this.el);
8292             delete this.el;
8293         }
8294     },
8295
8296     /**
8297      * Adjust the z-index of this shadow
8298      * @param {Number} zindex The new z-index
8299      */
8300     setZIndex : function(z){
8301         this.zIndex = z;
8302         if(this.el){
8303             this.el.setStyle("z-index", z);
8304         }
8305     }
8306 };
8307
8308 // Private utility class that manages the internal Shadow cache
8309 Roo.Shadow.Pool = function(){
8310     var p = [];
8311     var markup = Roo.isIE ?
8312                  '<div class="x-ie-shadow"></div>' :
8313                  '<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>';
8314     return {
8315         pull : function(){
8316             var sh = p.shift();
8317             if(!sh){
8318                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
8319                 sh.autoBoxAdjust = false;
8320             }
8321             return sh;
8322         },
8323
8324         push : function(sh){
8325             p.push(sh);
8326         }
8327     };
8328 }();/*
8329  * Based on:
8330  * Ext JS Library 1.1.1
8331  * Copyright(c) 2006-2007, Ext JS, LLC.
8332  *
8333  * Originally Released Under LGPL - original licence link has changed is not relivant.
8334  *
8335  * Fork - LGPL
8336  * <script type="text/javascript">
8337  */
8338
8339 /**
8340  * @class Roo.BoxComponent
8341  * @extends Roo.Component
8342  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
8343  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
8344  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
8345  * layout containers.
8346  * @constructor
8347  * @param {Roo.Element/String/Object} config The configuration options.
8348  */
8349 Roo.BoxComponent = function(config){
8350     Roo.Component.call(this, config);
8351     this.addEvents({
8352         /**
8353          * @event resize
8354          * Fires after the component is resized.
8355              * @param {Roo.Component} this
8356              * @param {Number} adjWidth The box-adjusted width that was set
8357              * @param {Number} adjHeight The box-adjusted height that was set
8358              * @param {Number} rawWidth The width that was originally specified
8359              * @param {Number} rawHeight The height that was originally specified
8360              */
8361         resize : true,
8362         /**
8363          * @event move
8364          * Fires after the component is moved.
8365              * @param {Roo.Component} this
8366              * @param {Number} x The new x position
8367              * @param {Number} y The new y position
8368              */
8369         move : true
8370     });
8371 };
8372
8373 Roo.extend(Roo.BoxComponent, Roo.Component, {
8374     // private, set in afterRender to signify that the component has been rendered
8375     boxReady : false,
8376     // private, used to defer height settings to subclasses
8377     deferHeight: false,
8378     /** @cfg {Number} width
8379      * width (optional) size of component
8380      */
8381      /** @cfg {Number} height
8382      * height (optional) size of component
8383      */
8384      
8385     /**
8386      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
8387      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
8388      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
8389      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
8390      * @return {Roo.BoxComponent} this
8391      */
8392     setSize : function(w, h){
8393         // support for standard size objects
8394         if(typeof w == 'object'){
8395             h = w.height;
8396             w = w.width;
8397         }
8398         // not rendered
8399         if(!this.boxReady){
8400             this.width = w;
8401             this.height = h;
8402             return this;
8403         }
8404
8405         // prevent recalcs when not needed
8406         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
8407             return this;
8408         }
8409         this.lastSize = {width: w, height: h};
8410
8411         var adj = this.adjustSize(w, h);
8412         var aw = adj.width, ah = adj.height;
8413         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
8414             var rz = this.getResizeEl();
8415             if(!this.deferHeight && aw !== undefined && ah !== undefined){
8416                 rz.setSize(aw, ah);
8417             }else if(!this.deferHeight && ah !== undefined){
8418                 rz.setHeight(ah);
8419             }else if(aw !== undefined){
8420                 rz.setWidth(aw);
8421             }
8422             this.onResize(aw, ah, w, h);
8423             this.fireEvent('resize', this, aw, ah, w, h);
8424         }
8425         return this;
8426     },
8427
8428     /**
8429      * Gets the current size of the component's underlying element.
8430      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8431      */
8432     getSize : function(){
8433         return this.el.getSize();
8434     },
8435
8436     /**
8437      * Gets the current XY position of the component's underlying element.
8438      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8439      * @return {Array} The XY position of the element (e.g., [100, 200])
8440      */
8441     getPosition : function(local){
8442         if(local === true){
8443             return [this.el.getLeft(true), this.el.getTop(true)];
8444         }
8445         return this.xy || this.el.getXY();
8446     },
8447
8448     /**
8449      * Gets the current box measurements of the component's underlying element.
8450      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8451      * @returns {Object} box An object in the format {x, y, width, height}
8452      */
8453     getBox : function(local){
8454         var s = this.el.getSize();
8455         if(local){
8456             s.x = this.el.getLeft(true);
8457             s.y = this.el.getTop(true);
8458         }else{
8459             var xy = this.xy || this.el.getXY();
8460             s.x = xy[0];
8461             s.y = xy[1];
8462         }
8463         return s;
8464     },
8465
8466     /**
8467      * Sets the current box measurements of the component's underlying element.
8468      * @param {Object} box An object in the format {x, y, width, height}
8469      * @returns {Roo.BoxComponent} this
8470      */
8471     updateBox : function(box){
8472         this.setSize(box.width, box.height);
8473         this.setPagePosition(box.x, box.y);
8474         return this;
8475     },
8476
8477     // protected
8478     getResizeEl : function(){
8479         return this.resizeEl || this.el;
8480     },
8481
8482     // protected
8483     getPositionEl : function(){
8484         return this.positionEl || this.el;
8485     },
8486
8487     /**
8488      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
8489      * This method fires the move event.
8490      * @param {Number} left The new left
8491      * @param {Number} top The new top
8492      * @returns {Roo.BoxComponent} this
8493      */
8494     setPosition : function(x, y){
8495         this.x = x;
8496         this.y = y;
8497         if(!this.boxReady){
8498             return this;
8499         }
8500         var adj = this.adjustPosition(x, y);
8501         var ax = adj.x, ay = adj.y;
8502
8503         var el = this.getPositionEl();
8504         if(ax !== undefined || ay !== undefined){
8505             if(ax !== undefined && ay !== undefined){
8506                 el.setLeftTop(ax, ay);
8507             }else if(ax !== undefined){
8508                 el.setLeft(ax);
8509             }else if(ay !== undefined){
8510                 el.setTop(ay);
8511             }
8512             this.onPosition(ax, ay);
8513             this.fireEvent('move', this, ax, ay);
8514         }
8515         return this;
8516     },
8517
8518     /**
8519      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
8520      * This method fires the move event.
8521      * @param {Number} x The new x position
8522      * @param {Number} y The new y position
8523      * @returns {Roo.BoxComponent} this
8524      */
8525     setPagePosition : function(x, y){
8526         this.pageX = x;
8527         this.pageY = y;
8528         if(!this.boxReady){
8529             return;
8530         }
8531         if(x === undefined || y === undefined){ // cannot translate undefined points
8532             return;
8533         }
8534         var p = this.el.translatePoints(x, y);
8535         this.setPosition(p.left, p.top);
8536         return this;
8537     },
8538
8539     // private
8540     onRender : function(ct, position){
8541         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
8542         if(this.resizeEl){
8543             this.resizeEl = Roo.get(this.resizeEl);
8544         }
8545         if(this.positionEl){
8546             this.positionEl = Roo.get(this.positionEl);
8547         }
8548     },
8549
8550     // private
8551     afterRender : function(){
8552         Roo.BoxComponent.superclass.afterRender.call(this);
8553         this.boxReady = true;
8554         this.setSize(this.width, this.height);
8555         if(this.x || this.y){
8556             this.setPosition(this.x, this.y);
8557         }
8558         if(this.pageX || this.pageY){
8559             this.setPagePosition(this.pageX, this.pageY);
8560         }
8561     },
8562
8563     /**
8564      * Force the component's size to recalculate based on the underlying element's current height and width.
8565      * @returns {Roo.BoxComponent} this
8566      */
8567     syncSize : function(){
8568         delete this.lastSize;
8569         this.setSize(this.el.getWidth(), this.el.getHeight());
8570         return this;
8571     },
8572
8573     /**
8574      * Called after the component is resized, this method is empty by default but can be implemented by any
8575      * subclass that needs to perform custom logic after a resize occurs.
8576      * @param {Number} adjWidth The box-adjusted width that was set
8577      * @param {Number} adjHeight The box-adjusted height that was set
8578      * @param {Number} rawWidth The width that was originally specified
8579      * @param {Number} rawHeight The height that was originally specified
8580      */
8581     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
8582
8583     },
8584
8585     /**
8586      * Called after the component is moved, this method is empty by default but can be implemented by any
8587      * subclass that needs to perform custom logic after a move occurs.
8588      * @param {Number} x The new x position
8589      * @param {Number} y The new y position
8590      */
8591     onPosition : function(x, y){
8592
8593     },
8594
8595     // private
8596     adjustSize : function(w, h){
8597         if(this.autoWidth){
8598             w = 'auto';
8599         }
8600         if(this.autoHeight){
8601             h = 'auto';
8602         }
8603         return {width : w, height: h};
8604     },
8605
8606     // private
8607     adjustPosition : function(x, y){
8608         return {x : x, y: y};
8609     }
8610 });/*
8611  * Based on:
8612  * Ext JS Library 1.1.1
8613  * Copyright(c) 2006-2007, Ext JS, LLC.
8614  *
8615  * Originally Released Under LGPL - original licence link has changed is not relivant.
8616  *
8617  * Fork - LGPL
8618  * <script type="text/javascript">
8619  */
8620
8621
8622 /**
8623  * @class Roo.SplitBar
8624  * @extends Roo.util.Observable
8625  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
8626  * <br><br>
8627  * Usage:
8628  * <pre><code>
8629 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
8630                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
8631 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
8632 split.minSize = 100;
8633 split.maxSize = 600;
8634 split.animate = true;
8635 split.on('moved', splitterMoved);
8636 </code></pre>
8637  * @constructor
8638  * Create a new SplitBar
8639  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
8640  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
8641  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8642  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
8643                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
8644                         position of the SplitBar).
8645  */
8646 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
8647     
8648     /** @private */
8649     this.el = Roo.get(dragElement, true);
8650     this.el.dom.unselectable = "on";
8651     /** @private */
8652     this.resizingEl = Roo.get(resizingElement, true);
8653
8654     /**
8655      * @private
8656      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8657      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
8658      * @type Number
8659      */
8660     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
8661     
8662     /**
8663      * The minimum size of the resizing element. (Defaults to 0)
8664      * @type Number
8665      */
8666     this.minSize = 0;
8667     
8668     /**
8669      * The maximum size of the resizing element. (Defaults to 2000)
8670      * @type Number
8671      */
8672     this.maxSize = 2000;
8673     
8674     /**
8675      * Whether to animate the transition to the new size
8676      * @type Boolean
8677      */
8678     this.animate = false;
8679     
8680     /**
8681      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
8682      * @type Boolean
8683      */
8684     this.useShim = false;
8685     
8686     /** @private */
8687     this.shim = null;
8688     
8689     if(!existingProxy){
8690         /** @private */
8691         this.proxy = Roo.SplitBar.createProxy(this.orientation);
8692     }else{
8693         this.proxy = Roo.get(existingProxy).dom;
8694     }
8695     /** @private */
8696     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
8697     
8698     /** @private */
8699     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
8700     
8701     /** @private */
8702     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
8703     
8704     /** @private */
8705     this.dragSpecs = {};
8706     
8707     /**
8708      * @private The adapter to use to positon and resize elements
8709      */
8710     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
8711     this.adapter.init(this);
8712     
8713     if(this.orientation == Roo.SplitBar.HORIZONTAL){
8714         /** @private */
8715         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
8716         this.el.addClass("x-splitbar-h");
8717     }else{
8718         /** @private */
8719         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
8720         this.el.addClass("x-splitbar-v");
8721     }
8722     
8723     this.addEvents({
8724         /**
8725          * @event resize
8726          * Fires when the splitter is moved (alias for {@link #event-moved})
8727          * @param {Roo.SplitBar} this
8728          * @param {Number} newSize the new width or height
8729          */
8730         "resize" : true,
8731         /**
8732          * @event moved
8733          * Fires when the splitter is moved
8734          * @param {Roo.SplitBar} this
8735          * @param {Number} newSize the new width or height
8736          */
8737         "moved" : true,
8738         /**
8739          * @event beforeresize
8740          * Fires before the splitter is dragged
8741          * @param {Roo.SplitBar} this
8742          */
8743         "beforeresize" : true,
8744
8745         "beforeapply" : true
8746     });
8747
8748     Roo.util.Observable.call(this);
8749 };
8750
8751 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
8752     onStartProxyDrag : function(x, y){
8753         this.fireEvent("beforeresize", this);
8754         if(!this.overlay){
8755             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
8756             o.unselectable();
8757             o.enableDisplayMode("block");
8758             // all splitbars share the same overlay
8759             Roo.SplitBar.prototype.overlay = o;
8760         }
8761         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8762         this.overlay.show();
8763         Roo.get(this.proxy).setDisplayed("block");
8764         var size = this.adapter.getElementSize(this);
8765         this.activeMinSize = this.getMinimumSize();;
8766         this.activeMaxSize = this.getMaximumSize();;
8767         var c1 = size - this.activeMinSize;
8768         var c2 = Math.max(this.activeMaxSize - size, 0);
8769         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8770             this.dd.resetConstraints();
8771             this.dd.setXConstraint(
8772                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
8773                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
8774             );
8775             this.dd.setYConstraint(0, 0);
8776         }else{
8777             this.dd.resetConstraints();
8778             this.dd.setXConstraint(0, 0);
8779             this.dd.setYConstraint(
8780                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
8781                 this.placement == Roo.SplitBar.TOP ? c2 : c1
8782             );
8783          }
8784         this.dragSpecs.startSize = size;
8785         this.dragSpecs.startPoint = [x, y];
8786         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
8787     },
8788     
8789     /** 
8790      * @private Called after the drag operation by the DDProxy
8791      */
8792     onEndProxyDrag : function(e){
8793         Roo.get(this.proxy).setDisplayed(false);
8794         var endPoint = Roo.lib.Event.getXY(e);
8795         if(this.overlay){
8796             this.overlay.hide();
8797         }
8798         var newSize;
8799         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8800             newSize = this.dragSpecs.startSize + 
8801                 (this.placement == Roo.SplitBar.LEFT ?
8802                     endPoint[0] - this.dragSpecs.startPoint[0] :
8803                     this.dragSpecs.startPoint[0] - endPoint[0]
8804                 );
8805         }else{
8806             newSize = this.dragSpecs.startSize + 
8807                 (this.placement == Roo.SplitBar.TOP ?
8808                     endPoint[1] - this.dragSpecs.startPoint[1] :
8809                     this.dragSpecs.startPoint[1] - endPoint[1]
8810                 );
8811         }
8812         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
8813         if(newSize != this.dragSpecs.startSize){
8814             if(this.fireEvent('beforeapply', this, newSize) !== false){
8815                 this.adapter.setElementSize(this, newSize);
8816                 this.fireEvent("moved", this, newSize);
8817                 this.fireEvent("resize", this, newSize);
8818             }
8819         }
8820     },
8821     
8822     /**
8823      * Get the adapter this SplitBar uses
8824      * @return The adapter object
8825      */
8826     getAdapter : function(){
8827         return this.adapter;
8828     },
8829     
8830     /**
8831      * Set the adapter this SplitBar uses
8832      * @param {Object} adapter A SplitBar adapter object
8833      */
8834     setAdapter : function(adapter){
8835         this.adapter = adapter;
8836         this.adapter.init(this);
8837     },
8838     
8839     /**
8840      * Gets the minimum size for the resizing element
8841      * @return {Number} The minimum size
8842      */
8843     getMinimumSize : function(){
8844         return this.minSize;
8845     },
8846     
8847     /**
8848      * Sets the minimum size for the resizing element
8849      * @param {Number} minSize The minimum size
8850      */
8851     setMinimumSize : function(minSize){
8852         this.minSize = minSize;
8853     },
8854     
8855     /**
8856      * Gets the maximum size for the resizing element
8857      * @return {Number} The maximum size
8858      */
8859     getMaximumSize : function(){
8860         return this.maxSize;
8861     },
8862     
8863     /**
8864      * Sets the maximum size for the resizing element
8865      * @param {Number} maxSize The maximum size
8866      */
8867     setMaximumSize : function(maxSize){
8868         this.maxSize = maxSize;
8869     },
8870     
8871     /**
8872      * Sets the initialize size for the resizing element
8873      * @param {Number} size The initial size
8874      */
8875     setCurrentSize : function(size){
8876         var oldAnimate = this.animate;
8877         this.animate = false;
8878         this.adapter.setElementSize(this, size);
8879         this.animate = oldAnimate;
8880     },
8881     
8882     /**
8883      * Destroy this splitbar. 
8884      * @param {Boolean} removeEl True to remove the element
8885      */
8886     destroy : function(removeEl){
8887         if(this.shim){
8888             this.shim.remove();
8889         }
8890         this.dd.unreg();
8891         this.proxy.parentNode.removeChild(this.proxy);
8892         if(removeEl){
8893             this.el.remove();
8894         }
8895     }
8896 });
8897
8898 /**
8899  * @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.
8900  */
8901 Roo.SplitBar.createProxy = function(dir){
8902     var proxy = new Roo.Element(document.createElement("div"));
8903     proxy.unselectable();
8904     var cls = 'x-splitbar-proxy';
8905     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
8906     document.body.appendChild(proxy.dom);
8907     return proxy.dom;
8908 };
8909
8910 /** 
8911  * @class Roo.SplitBar.BasicLayoutAdapter
8912  * Default Adapter. It assumes the splitter and resizing element are not positioned
8913  * elements and only gets/sets the width of the element. Generally used for table based layouts.
8914  */
8915 Roo.SplitBar.BasicLayoutAdapter = function(){
8916 };
8917
8918 Roo.SplitBar.BasicLayoutAdapter.prototype = {
8919     // do nothing for now
8920     init : function(s){
8921     
8922     },
8923     /**
8924      * Called before drag operations to get the current size of the resizing element. 
8925      * @param {Roo.SplitBar} s The SplitBar using this adapter
8926      */
8927      getElementSize : function(s){
8928         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8929             return s.resizingEl.getWidth();
8930         }else{
8931             return s.resizingEl.getHeight();
8932         }
8933     },
8934     
8935     /**
8936      * Called after drag operations to set the size of the resizing element.
8937      * @param {Roo.SplitBar} s The SplitBar using this adapter
8938      * @param {Number} newSize The new size to set
8939      * @param {Function} onComplete A function to be invoked when resizing is complete
8940      */
8941     setElementSize : function(s, newSize, onComplete){
8942         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8943             if(!s.animate){
8944                 s.resizingEl.setWidth(newSize);
8945                 if(onComplete){
8946                     onComplete(s, newSize);
8947                 }
8948             }else{
8949                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
8950             }
8951         }else{
8952             
8953             if(!s.animate){
8954                 s.resizingEl.setHeight(newSize);
8955                 if(onComplete){
8956                     onComplete(s, newSize);
8957                 }
8958             }else{
8959                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
8960             }
8961         }
8962     }
8963 };
8964
8965 /** 
8966  *@class Roo.SplitBar.AbsoluteLayoutAdapter
8967  * @extends Roo.SplitBar.BasicLayoutAdapter
8968  * Adapter that  moves the splitter element to align with the resized sizing element. 
8969  * Used with an absolute positioned SplitBar.
8970  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
8971  * document.body, make sure you assign an id to the body element.
8972  */
8973 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
8974     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
8975     this.container = Roo.get(container);
8976 };
8977
8978 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
8979     init : function(s){
8980         this.basic.init(s);
8981     },
8982     
8983     getElementSize : function(s){
8984         return this.basic.getElementSize(s);
8985     },
8986     
8987     setElementSize : function(s, newSize, onComplete){
8988         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
8989     },
8990     
8991     moveSplitter : function(s){
8992         var yes = Roo.SplitBar;
8993         switch(s.placement){
8994             case yes.LEFT:
8995                 s.el.setX(s.resizingEl.getRight());
8996                 break;
8997             case yes.RIGHT:
8998                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
8999                 break;
9000             case yes.TOP:
9001                 s.el.setY(s.resizingEl.getBottom());
9002                 break;
9003             case yes.BOTTOM:
9004                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
9005                 break;
9006         }
9007     }
9008 };
9009
9010 /**
9011  * Orientation constant - Create a vertical SplitBar
9012  * @static
9013  * @type Number
9014  */
9015 Roo.SplitBar.VERTICAL = 1;
9016
9017 /**
9018  * Orientation constant - Create a horizontal SplitBar
9019  * @static
9020  * @type Number
9021  */
9022 Roo.SplitBar.HORIZONTAL = 2;
9023
9024 /**
9025  * Placement constant - The resizing element is to the left of the splitter element
9026  * @static
9027  * @type Number
9028  */
9029 Roo.SplitBar.LEFT = 1;
9030
9031 /**
9032  * Placement constant - The resizing element is to the right of the splitter element
9033  * @static
9034  * @type Number
9035  */
9036 Roo.SplitBar.RIGHT = 2;
9037
9038 /**
9039  * Placement constant - The resizing element is positioned above the splitter element
9040  * @static
9041  * @type Number
9042  */
9043 Roo.SplitBar.TOP = 3;
9044
9045 /**
9046  * Placement constant - The resizing element is positioned under splitter element
9047  * @static
9048  * @type Number
9049  */
9050 Roo.SplitBar.BOTTOM = 4;
9051 /*
9052  * Based on:
9053  * Ext JS Library 1.1.1
9054  * Copyright(c) 2006-2007, Ext JS, LLC.
9055  *
9056  * Originally Released Under LGPL - original licence link has changed is not relivant.
9057  *
9058  * Fork - LGPL
9059  * <script type="text/javascript">
9060  */
9061
9062 /**
9063  * @class Roo.View
9064  * @extends Roo.util.Observable
9065  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
9066  * This class also supports single and multi selection modes. <br>
9067  * Create a data model bound view:
9068  <pre><code>
9069  var store = new Roo.data.Store(...);
9070
9071  var view = new Roo.View({
9072     el : "my-element",
9073     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
9074  
9075     singleSelect: true,
9076     selectedClass: "ydataview-selected",
9077     store: store
9078  });
9079
9080  // listen for node click?
9081  view.on("click", function(vw, index, node, e){
9082  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9083  });
9084
9085  // load XML data
9086  dataModel.load("foobar.xml");
9087  </code></pre>
9088  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9089  * <br><br>
9090  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9091  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9092  * 
9093  * Note: old style constructor is still suported (container, template, config)
9094  * 
9095  * @constructor
9096  * Create a new View
9097  * @param {Object} config The config object
9098  * 
9099  */
9100 Roo.View = function(config, depreciated_tpl, depreciated_config){
9101     
9102     if (typeof(depreciated_tpl) == 'undefined') {
9103         // new way.. - universal constructor.
9104         Roo.apply(this, config);
9105         this.el  = Roo.get(this.el);
9106     } else {
9107         // old format..
9108         this.el  = Roo.get(config);
9109         this.tpl = depreciated_tpl;
9110         Roo.apply(this, depreciated_config);
9111     }
9112      
9113     
9114     if(typeof(this.tpl) == "string"){
9115         this.tpl = new Roo.Template(this.tpl);
9116     } else {
9117         // support xtype ctors..
9118         this.tpl = new Roo.factory(this.tpl, Roo);
9119     }
9120     
9121     
9122     this.tpl.compile();
9123    
9124
9125      
9126     /** @private */
9127     this.addEvents({
9128     /**
9129      * @event beforeclick
9130      * Fires before a click is processed. Returns false to cancel the default action.
9131      * @param {Roo.View} this
9132      * @param {Number} index The index of the target node
9133      * @param {HTMLElement} node The target node
9134      * @param {Roo.EventObject} e The raw event object
9135      */
9136         "beforeclick" : true,
9137     /**
9138      * @event click
9139      * Fires when a template node is clicked.
9140      * @param {Roo.View} this
9141      * @param {Number} index The index of the target node
9142      * @param {HTMLElement} node The target node
9143      * @param {Roo.EventObject} e The raw event object
9144      */
9145         "click" : true,
9146     /**
9147      * @event dblclick
9148      * Fires when a template node is double clicked.
9149      * @param {Roo.View} this
9150      * @param {Number} index The index of the target node
9151      * @param {HTMLElement} node The target node
9152      * @param {Roo.EventObject} e The raw event object
9153      */
9154         "dblclick" : true,
9155     /**
9156      * @event contextmenu
9157      * Fires when a template node is right clicked.
9158      * @param {Roo.View} this
9159      * @param {Number} index The index of the target node
9160      * @param {HTMLElement} node The target node
9161      * @param {Roo.EventObject} e The raw event object
9162      */
9163         "contextmenu" : true,
9164     /**
9165      * @event selectionchange
9166      * Fires when the selected nodes change.
9167      * @param {Roo.View} this
9168      * @param {Array} selections Array of the selected nodes
9169      */
9170         "selectionchange" : true,
9171
9172     /**
9173      * @event beforeselect
9174      * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9175      * @param {Roo.View} this
9176      * @param {HTMLElement} node The node to be selected
9177      * @param {Array} selections Array of currently selected nodes
9178      */
9179         "beforeselect" : true
9180     });
9181
9182     this.el.on({
9183         "click": this.onClick,
9184         "dblclick": this.onDblClick,
9185         "contextmenu": this.onContextMenu,
9186         scope:this
9187     });
9188
9189     this.selections = [];
9190     this.nodes = [];
9191     this.cmp = new Roo.CompositeElementLite([]);
9192     if(this.store){
9193         this.store = Roo.factory(this.store, Roo.data);
9194         this.setStore(this.store, true);
9195     }
9196     Roo.View.superclass.constructor.call(this);
9197 };
9198
9199 Roo.extend(Roo.View, Roo.util.Observable, {
9200     
9201      /**
9202      * @cfg {Roo.data.Store} store Data store to load data from.
9203      */
9204     store : false,
9205     
9206     /**
9207      * @cfg {String|Roo.Element} el The container element.
9208      */
9209     el : '',
9210     
9211     /**
9212      * @cfg {String|Roo.Template} tpl The template used by this View 
9213      */
9214     tpl : false,
9215     
9216     /**
9217      * @cfg {String} selectedClass The css class to add to selected nodes
9218      */
9219     selectedClass : "x-view-selected",
9220      /**
9221      * @cfg {String} emptyText The empty text to show when nothing is loaded.
9222      */
9223     emptyText : "",
9224     /**
9225      * @cfg {Boolean} multiSelect Allow multiple selection
9226      */
9227     
9228     multiSelect : false,
9229     /**
9230      * @cfg {Boolean} singleSelect Allow single selection
9231      */
9232     singleSelect:  false,
9233     
9234     /**
9235      * Returns the element this view is bound to.
9236      * @return {Roo.Element}
9237      */
9238     getEl : function(){
9239         return this.el;
9240     },
9241
9242     /**
9243      * Refreshes the view.
9244      */
9245     refresh : function(){
9246         var t = this.tpl;
9247         this.clearSelections();
9248         this.el.update("");
9249         var html = [];
9250         var records = this.store.getRange();
9251         if(records.length < 1){
9252             this.el.update(this.emptyText);
9253             return;
9254         }
9255         for(var i = 0, len = records.length; i < len; i++){
9256             var data = this.prepareData(records[i].data, i, records[i]);
9257             html[html.length] = t.apply(data);
9258         }
9259         this.el.update(html.join(""));
9260         this.nodes = this.el.dom.childNodes;
9261         this.updateIndexes(0);
9262     },
9263
9264     /**
9265      * Function to override to reformat the data that is sent to
9266      * the template for each node.
9267      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9268      * a JSON object for an UpdateManager bound view).
9269      */
9270     prepareData : function(data){
9271         return data;
9272     },
9273
9274     onUpdate : function(ds, record){
9275         this.clearSelections();
9276         var index = this.store.indexOf(record);
9277         var n = this.nodes[index];
9278         this.tpl.insertBefore(n, this.prepareData(record.data));
9279         n.parentNode.removeChild(n);
9280         this.updateIndexes(index, index);
9281     },
9282
9283     onAdd : function(ds, records, index){
9284         this.clearSelections();
9285         if(this.nodes.length == 0){
9286             this.refresh();
9287             return;
9288         }
9289         var n = this.nodes[index];
9290         for(var i = 0, len = records.length; i < len; i++){
9291             var d = this.prepareData(records[i].data);
9292             if(n){
9293                 this.tpl.insertBefore(n, d);
9294             }else{
9295                 this.tpl.append(this.el, d);
9296             }
9297         }
9298         this.updateIndexes(index);
9299     },
9300
9301     onRemove : function(ds, record, index){
9302         this.clearSelections();
9303         this.el.dom.removeChild(this.nodes[index]);
9304         this.updateIndexes(index);
9305     },
9306
9307     /**
9308      * Refresh an individual node.
9309      * @param {Number} index
9310      */
9311     refreshNode : function(index){
9312         this.onUpdate(this.store, this.store.getAt(index));
9313     },
9314
9315     updateIndexes : function(startIndex, endIndex){
9316         var ns = this.nodes;
9317         startIndex = startIndex || 0;
9318         endIndex = endIndex || ns.length - 1;
9319         for(var i = startIndex; i <= endIndex; i++){
9320             ns[i].nodeIndex = i;
9321         }
9322     },
9323
9324     /**
9325      * Changes the data store this view uses and refresh the view.
9326      * @param {Store} store
9327      */
9328     setStore : function(store, initial){
9329         if(!initial && this.store){
9330             this.store.un("datachanged", this.refresh);
9331             this.store.un("add", this.onAdd);
9332             this.store.un("remove", this.onRemove);
9333             this.store.un("update", this.onUpdate);
9334             this.store.un("clear", this.refresh);
9335         }
9336         if(store){
9337           
9338             store.on("datachanged", this.refresh, this);
9339             store.on("add", this.onAdd, this);
9340             store.on("remove", this.onRemove, this);
9341             store.on("update", this.onUpdate, this);
9342             store.on("clear", this.refresh, this);
9343         }
9344         
9345         if(store){
9346             this.refresh();
9347         }
9348     },
9349
9350     /**
9351      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9352      * @param {HTMLElement} node
9353      * @return {HTMLElement} The template node
9354      */
9355     findItemFromChild : function(node){
9356         var el = this.el.dom;
9357         if(!node || node.parentNode == el){
9358                     return node;
9359             }
9360             var p = node.parentNode;
9361             while(p && p != el){
9362             if(p.parentNode == el){
9363                 return p;
9364             }
9365             p = p.parentNode;
9366         }
9367             return null;
9368     },
9369
9370     /** @ignore */
9371     onClick : function(e){
9372         var item = this.findItemFromChild(e.getTarget());
9373         if(item){
9374             var index = this.indexOf(item);
9375             if(this.onItemClick(item, index, e) !== false){
9376                 this.fireEvent("click", this, index, item, e);
9377             }
9378         }else{
9379             this.clearSelections();
9380         }
9381     },
9382
9383     /** @ignore */
9384     onContextMenu : function(e){
9385         var item = this.findItemFromChild(e.getTarget());
9386         if(item){
9387             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9388         }
9389     },
9390
9391     /** @ignore */
9392     onDblClick : function(e){
9393         var item = this.findItemFromChild(e.getTarget());
9394         if(item){
9395             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9396         }
9397     },
9398
9399     onItemClick : function(item, index, e){
9400         if(this.fireEvent("beforeclick", this, index, item, e) === false){
9401             return false;
9402         }
9403         if(this.multiSelect || this.singleSelect){
9404             if(this.multiSelect && e.shiftKey && this.lastSelection){
9405                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9406             }else{
9407                 this.select(item, this.multiSelect && e.ctrlKey);
9408                 this.lastSelection = item;
9409             }
9410             e.preventDefault();
9411         }
9412         return true;
9413     },
9414
9415     /**
9416      * Get the number of selected nodes.
9417      * @return {Number}
9418      */
9419     getSelectionCount : function(){
9420         return this.selections.length;
9421     },
9422
9423     /**
9424      * Get the currently selected nodes.
9425      * @return {Array} An array of HTMLElements
9426      */
9427     getSelectedNodes : function(){
9428         return this.selections;
9429     },
9430
9431     /**
9432      * Get the indexes of the selected nodes.
9433      * @return {Array}
9434      */
9435     getSelectedIndexes : function(){
9436         var indexes = [], s = this.selections;
9437         for(var i = 0, len = s.length; i < len; i++){
9438             indexes.push(s[i].nodeIndex);
9439         }
9440         return indexes;
9441     },
9442
9443     /**
9444      * Clear all selections
9445      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9446      */
9447     clearSelections : function(suppressEvent){
9448         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9449             this.cmp.elements = this.selections;
9450             this.cmp.removeClass(this.selectedClass);
9451             this.selections = [];
9452             if(!suppressEvent){
9453                 this.fireEvent("selectionchange", this, this.selections);
9454             }
9455         }
9456     },
9457
9458     /**
9459      * Returns true if the passed node is selected
9460      * @param {HTMLElement/Number} node The node or node index
9461      * @return {Boolean}
9462      */
9463     isSelected : function(node){
9464         var s = this.selections;
9465         if(s.length < 1){
9466             return false;
9467         }
9468         node = this.getNode(node);
9469         return s.indexOf(node) !== -1;
9470     },
9471
9472     /**
9473      * Selects nodes.
9474      * @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
9475      * @param {Boolean} keepExisting (optional) true to keep existing selections
9476      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9477      */
9478     select : function(nodeInfo, keepExisting, suppressEvent){
9479         if(nodeInfo instanceof Array){
9480             if(!keepExisting){
9481                 this.clearSelections(true);
9482             }
9483             for(var i = 0, len = nodeInfo.length; i < len; i++){
9484                 this.select(nodeInfo[i], true, true);
9485             }
9486         } else{
9487             var node = this.getNode(nodeInfo);
9488             if(node && !this.isSelected(node)){
9489                 if(!keepExisting){
9490                     this.clearSelections(true);
9491                 }
9492                 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9493                     Roo.fly(node).addClass(this.selectedClass);
9494                     this.selections.push(node);
9495                     if(!suppressEvent){
9496                         this.fireEvent("selectionchange", this, this.selections);
9497                     }
9498                 }
9499             }
9500         }
9501     },
9502
9503     /**
9504      * Gets a template node.
9505      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9506      * @return {HTMLElement} The node or null if it wasn't found
9507      */
9508     getNode : function(nodeInfo){
9509         if(typeof nodeInfo == "string"){
9510             return document.getElementById(nodeInfo);
9511         }else if(typeof nodeInfo == "number"){
9512             return this.nodes[nodeInfo];
9513         }
9514         return nodeInfo;
9515     },
9516
9517     /**
9518      * Gets a range template nodes.
9519      * @param {Number} startIndex
9520      * @param {Number} endIndex
9521      * @return {Array} An array of nodes
9522      */
9523     getNodes : function(start, end){
9524         var ns = this.nodes;
9525         start = start || 0;
9526         end = typeof end == "undefined" ? ns.length - 1 : end;
9527         var nodes = [];
9528         if(start <= end){
9529             for(var i = start; i <= end; i++){
9530                 nodes.push(ns[i]);
9531             }
9532         } else{
9533             for(var i = start; i >= end; i--){
9534                 nodes.push(ns[i]);
9535             }
9536         }
9537         return nodes;
9538     },
9539
9540     /**
9541      * Finds the index of the passed node
9542      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9543      * @return {Number} The index of the node or -1
9544      */
9545     indexOf : function(node){
9546         node = this.getNode(node);
9547         if(typeof node.nodeIndex == "number"){
9548             return node.nodeIndex;
9549         }
9550         var ns = this.nodes;
9551         for(var i = 0, len = ns.length; i < len; i++){
9552             if(ns[i] == node){
9553                 return i;
9554             }
9555         }
9556         return -1;
9557     }
9558 });
9559 /*
9560  * Based on:
9561  * Ext JS Library 1.1.1
9562  * Copyright(c) 2006-2007, Ext JS, LLC.
9563  *
9564  * Originally Released Under LGPL - original licence link has changed is not relivant.
9565  *
9566  * Fork - LGPL
9567  * <script type="text/javascript">
9568  */
9569
9570 /**
9571  * @class Roo.JsonView
9572  * @extends Roo.View
9573  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
9574 <pre><code>
9575 var view = new Roo.JsonView({
9576     container: "my-element",
9577     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
9578     multiSelect: true, 
9579     jsonRoot: "data" 
9580 });
9581
9582 // listen for node click?
9583 view.on("click", function(vw, index, node, e){
9584     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9585 });
9586
9587 // direct load of JSON data
9588 view.load("foobar.php");
9589
9590 // Example from my blog list
9591 var tpl = new Roo.Template(
9592     '&lt;div class="entry"&gt;' +
9593     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
9594     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
9595     "&lt;/div&gt;&lt;hr /&gt;"
9596 );
9597
9598 var moreView = new Roo.JsonView({
9599     container :  "entry-list", 
9600     template : tpl,
9601     jsonRoot: "posts"
9602 });
9603 moreView.on("beforerender", this.sortEntries, this);
9604 moreView.load({
9605     url: "/blog/get-posts.php",
9606     params: "allposts=true",
9607     text: "Loading Blog Entries..."
9608 });
9609 </code></pre>
9610
9611 * Note: old code is supported with arguments : (container, template, config)
9612
9613
9614  * @constructor
9615  * Create a new JsonView
9616  * 
9617  * @param {Object} config The config object
9618  * 
9619  */
9620 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
9621     
9622     
9623     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
9624
9625     var um = this.el.getUpdateManager();
9626     um.setRenderer(this);
9627     um.on("update", this.onLoad, this);
9628     um.on("failure", this.onLoadException, this);
9629
9630     /**
9631      * @event beforerender
9632      * Fires before rendering of the downloaded JSON data.
9633      * @param {Roo.JsonView} this
9634      * @param {Object} data The JSON data loaded
9635      */
9636     /**
9637      * @event load
9638      * Fires when data is loaded.
9639      * @param {Roo.JsonView} this
9640      * @param {Object} data The JSON data loaded
9641      * @param {Object} response The raw Connect response object
9642      */
9643     /**
9644      * @event loadexception
9645      * Fires when loading fails.
9646      * @param {Roo.JsonView} this
9647      * @param {Object} response The raw Connect response object
9648      */
9649     this.addEvents({
9650         'beforerender' : true,
9651         'load' : true,
9652         'loadexception' : true
9653     });
9654 };
9655 Roo.extend(Roo.JsonView, Roo.View, {
9656     /**
9657      * @type {String} The root property in the loaded JSON object that contains the data
9658      */
9659     jsonRoot : "",
9660
9661     /**
9662      * Refreshes the view.
9663      */
9664     refresh : function(){
9665         this.clearSelections();
9666         this.el.update("");
9667         var html = [];
9668         var o = this.jsonData;
9669         if(o && o.length > 0){
9670             for(var i = 0, len = o.length; i < len; i++){
9671                 var data = this.prepareData(o[i], i, o);
9672                 html[html.length] = this.tpl.apply(data);
9673             }
9674         }else{
9675             html.push(this.emptyText);
9676         }
9677         this.el.update(html.join(""));
9678         this.nodes = this.el.dom.childNodes;
9679         this.updateIndexes(0);
9680     },
9681
9682     /**
9683      * 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.
9684      * @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:
9685      <pre><code>
9686      view.load({
9687          url: "your-url.php",
9688          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
9689          callback: yourFunction,
9690          scope: yourObject, //(optional scope)
9691          discardUrl: false,
9692          nocache: false,
9693          text: "Loading...",
9694          timeout: 30,
9695          scripts: false
9696      });
9697      </code></pre>
9698      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
9699      * 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.
9700      * @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}
9701      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9702      * @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.
9703      */
9704     load : function(){
9705         var um = this.el.getUpdateManager();
9706         um.update.apply(um, arguments);
9707     },
9708
9709     render : function(el, response){
9710         this.clearSelections();
9711         this.el.update("");
9712         var o;
9713         try{
9714             o = Roo.util.JSON.decode(response.responseText);
9715             if(this.jsonRoot){
9716                 
9717                 o = o[this.jsonRoot];
9718             }
9719         } catch(e){
9720         }
9721         /**
9722          * The current JSON data or null
9723          */
9724         this.jsonData = o;
9725         this.beforeRender();
9726         this.refresh();
9727     },
9728
9729 /**
9730  * Get the number of records in the current JSON dataset
9731  * @return {Number}
9732  */
9733     getCount : function(){
9734         return this.jsonData ? this.jsonData.length : 0;
9735     },
9736
9737 /**
9738  * Returns the JSON object for the specified node(s)
9739  * @param {HTMLElement/Array} node The node or an array of nodes
9740  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
9741  * you get the JSON object for the node
9742  */
9743     getNodeData : function(node){
9744         if(node instanceof Array){
9745             var data = [];
9746             for(var i = 0, len = node.length; i < len; i++){
9747                 data.push(this.getNodeData(node[i]));
9748             }
9749             return data;
9750         }
9751         return this.jsonData[this.indexOf(node)] || null;
9752     },
9753
9754     beforeRender : function(){
9755         this.snapshot = this.jsonData;
9756         if(this.sortInfo){
9757             this.sort.apply(this, this.sortInfo);
9758         }
9759         this.fireEvent("beforerender", this, this.jsonData);
9760     },
9761
9762     onLoad : function(el, o){
9763         this.fireEvent("load", this, this.jsonData, o);
9764     },
9765
9766     onLoadException : function(el, o){
9767         this.fireEvent("loadexception", this, o);
9768     },
9769
9770 /**
9771  * Filter the data by a specific property.
9772  * @param {String} property A property on your JSON objects
9773  * @param {String/RegExp} value Either string that the property values
9774  * should start with, or a RegExp to test against the property
9775  */
9776     filter : function(property, value){
9777         if(this.jsonData){
9778             var data = [];
9779             var ss = this.snapshot;
9780             if(typeof value == "string"){
9781                 var vlen = value.length;
9782                 if(vlen == 0){
9783                     this.clearFilter();
9784                     return;
9785                 }
9786                 value = value.toLowerCase();
9787                 for(var i = 0, len = ss.length; i < len; i++){
9788                     var o = ss[i];
9789                     if(o[property].substr(0, vlen).toLowerCase() == value){
9790                         data.push(o);
9791                     }
9792                 }
9793             } else if(value.exec){ // regex?
9794                 for(var i = 0, len = ss.length; i < len; i++){
9795                     var o = ss[i];
9796                     if(value.test(o[property])){
9797                         data.push(o);
9798                     }
9799                 }
9800             } else{
9801                 return;
9802             }
9803             this.jsonData = data;
9804             this.refresh();
9805         }
9806     },
9807
9808 /**
9809  * Filter by a function. The passed function will be called with each
9810  * object in the current dataset. If the function returns true the value is kept,
9811  * otherwise it is filtered.
9812  * @param {Function} fn
9813  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
9814  */
9815     filterBy : function(fn, scope){
9816         if(this.jsonData){
9817             var data = [];
9818             var ss = this.snapshot;
9819             for(var i = 0, len = ss.length; i < len; i++){
9820                 var o = ss[i];
9821                 if(fn.call(scope || this, o)){
9822                     data.push(o);
9823                 }
9824             }
9825             this.jsonData = data;
9826             this.refresh();
9827         }
9828     },
9829
9830 /**
9831  * Clears the current filter.
9832  */
9833     clearFilter : function(){
9834         if(this.snapshot && this.jsonData != this.snapshot){
9835             this.jsonData = this.snapshot;
9836             this.refresh();
9837         }
9838     },
9839
9840
9841 /**
9842  * Sorts the data for this view and refreshes it.
9843  * @param {String} property A property on your JSON objects to sort on
9844  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
9845  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
9846  */
9847     sort : function(property, dir, sortType){
9848         this.sortInfo = Array.prototype.slice.call(arguments, 0);
9849         if(this.jsonData){
9850             var p = property;
9851             var dsc = dir && dir.toLowerCase() == "desc";
9852             var f = function(o1, o2){
9853                 var v1 = sortType ? sortType(o1[p]) : o1[p];
9854                 var v2 = sortType ? sortType(o2[p]) : o2[p];
9855                 ;
9856                 if(v1 < v2){
9857                     return dsc ? +1 : -1;
9858                 } else if(v1 > v2){
9859                     return dsc ? -1 : +1;
9860                 } else{
9861                     return 0;
9862                 }
9863             };
9864             this.jsonData.sort(f);
9865             this.refresh();
9866             if(this.jsonData != this.snapshot){
9867                 this.snapshot.sort(f);
9868             }
9869         }
9870     }
9871 });/*
9872  * Based on:
9873  * Ext JS Library 1.1.1
9874  * Copyright(c) 2006-2007, Ext JS, LLC.
9875  *
9876  * Originally Released Under LGPL - original licence link has changed is not relivant.
9877  *
9878  * Fork - LGPL
9879  * <script type="text/javascript">
9880  */
9881  
9882
9883 /**
9884  * @class Roo.ColorPalette
9885  * @extends Roo.Component
9886  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
9887  * Here's an example of typical usage:
9888  * <pre><code>
9889 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
9890 cp.render('my-div');
9891
9892 cp.on('select', function(palette, selColor){
9893     // do something with selColor
9894 });
9895 </code></pre>
9896  * @constructor
9897  * Create a new ColorPalette
9898  * @param {Object} config The config object
9899  */
9900 Roo.ColorPalette = function(config){
9901     Roo.ColorPalette.superclass.constructor.call(this, config);
9902     this.addEvents({
9903         /**
9904              * @event select
9905              * Fires when a color is selected
9906              * @param {ColorPalette} this
9907              * @param {String} color The 6-digit color hex code (without the # symbol)
9908              */
9909         select: true
9910     });
9911
9912     if(this.handler){
9913         this.on("select", this.handler, this.scope, true);
9914     }
9915 };
9916 Roo.extend(Roo.ColorPalette, Roo.Component, {
9917     /**
9918      * @cfg {String} itemCls
9919      * The CSS class to apply to the containing element (defaults to "x-color-palette")
9920      */
9921     itemCls : "x-color-palette",
9922     /**
9923      * @cfg {String} value
9924      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
9925      * the hex codes are case-sensitive.
9926      */
9927     value : null,
9928     clickEvent:'click',
9929     // private
9930     ctype: "Roo.ColorPalette",
9931
9932     /**
9933      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
9934      */
9935     allowReselect : false,
9936
9937     /**
9938      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
9939      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
9940      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
9941      * of colors with the width setting until the box is symmetrical.</p>
9942      * <p>You can override individual colors if needed:</p>
9943      * <pre><code>
9944 var cp = new Roo.ColorPalette();
9945 cp.colors[0] = "FF0000";  // change the first box to red
9946 </code></pre>
9947
9948 Or you can provide a custom array of your own for complete control:
9949 <pre><code>
9950 var cp = new Roo.ColorPalette();
9951 cp.colors = ["000000", "993300", "333300"];
9952 </code></pre>
9953      * @type Array
9954      */
9955     colors : [
9956         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
9957         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
9958         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
9959         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
9960         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
9961     ],
9962
9963     // private
9964     onRender : function(container, position){
9965         var t = new Roo.MasterTemplate(
9966             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
9967         );
9968         var c = this.colors;
9969         for(var i = 0, len = c.length; i < len; i++){
9970             t.add([c[i]]);
9971         }
9972         var el = document.createElement("div");
9973         el.className = this.itemCls;
9974         t.overwrite(el);
9975         container.dom.insertBefore(el, position);
9976         this.el = Roo.get(el);
9977         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
9978         if(this.clickEvent != 'click'){
9979             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
9980         }
9981     },
9982
9983     // private
9984     afterRender : function(){
9985         Roo.ColorPalette.superclass.afterRender.call(this);
9986         if(this.value){
9987             var s = this.value;
9988             this.value = null;
9989             this.select(s);
9990         }
9991     },
9992
9993     // private
9994     handleClick : function(e, t){
9995         e.preventDefault();
9996         if(!this.disabled){
9997             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
9998             this.select(c.toUpperCase());
9999         }
10000     },
10001
10002     /**
10003      * Selects the specified color in the palette (fires the select event)
10004      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
10005      */
10006     select : function(color){
10007         color = color.replace("#", "");
10008         if(color != this.value || this.allowReselect){
10009             var el = this.el;
10010             if(this.value){
10011                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
10012             }
10013             el.child("a.color-"+color).addClass("x-color-palette-sel");
10014             this.value = color;
10015             this.fireEvent("select", this, color);
10016         }
10017     }
10018 });/*
10019  * Based on:
10020  * Ext JS Library 1.1.1
10021  * Copyright(c) 2006-2007, Ext JS, LLC.
10022  *
10023  * Originally Released Under LGPL - original licence link has changed is not relivant.
10024  *
10025  * Fork - LGPL
10026  * <script type="text/javascript">
10027  */
10028  
10029 /**
10030  * @class Roo.DatePicker
10031  * @extends Roo.Component
10032  * Simple date picker class.
10033  * @constructor
10034  * Create a new DatePicker
10035  * @param {Object} config The config object
10036  */
10037 Roo.DatePicker = function(config){
10038     Roo.DatePicker.superclass.constructor.call(this, config);
10039
10040     this.value = config && config.value ?
10041                  config.value.clearTime() : new Date().clearTime();
10042
10043     this.addEvents({
10044         /**
10045              * @event select
10046              * Fires when a date is selected
10047              * @param {DatePicker} this
10048              * @param {Date} date The selected date
10049              */
10050         select: true
10051     });
10052
10053     if(this.handler){
10054         this.on("select", this.handler,  this.scope || this);
10055     }
10056     // build the disabledDatesRE
10057     if(!this.disabledDatesRE && this.disabledDates){
10058         var dd = this.disabledDates;
10059         var re = "(?:";
10060         for(var i = 0; i < dd.length; i++){
10061             re += dd[i];
10062             if(i != dd.length-1) re += "|";
10063         }
10064         this.disabledDatesRE = new RegExp(re + ")");
10065     }
10066 };
10067
10068 Roo.extend(Roo.DatePicker, Roo.Component, {
10069     /**
10070      * @cfg {String} todayText
10071      * The text to display on the button that selects the current date (defaults to "Today")
10072      */
10073     todayText : "Today",
10074     /**
10075      * @cfg {String} okText
10076      * The text to display on the ok button
10077      */
10078     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
10079     /**
10080      * @cfg {String} cancelText
10081      * The text to display on the cancel button
10082      */
10083     cancelText : "Cancel",
10084     /**
10085      * @cfg {String} todayTip
10086      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
10087      */
10088     todayTip : "{0} (Spacebar)",
10089     /**
10090      * @cfg {Date} minDate
10091      * Minimum allowable date (JavaScript date object, defaults to null)
10092      */
10093     minDate : null,
10094     /**
10095      * @cfg {Date} maxDate
10096      * Maximum allowable date (JavaScript date object, defaults to null)
10097      */
10098     maxDate : null,
10099     /**
10100      * @cfg {String} minText
10101      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
10102      */
10103     minText : "This date is before the minimum date",
10104     /**
10105      * @cfg {String} maxText
10106      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
10107      */
10108     maxText : "This date is after the maximum date",
10109     /**
10110      * @cfg {String} format
10111      * The default date format string which can be overriden for localization support.  The format must be
10112      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
10113      */
10114     format : "m/d/y",
10115     /**
10116      * @cfg {Array} disabledDays
10117      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
10118      */
10119     disabledDays : null,
10120     /**
10121      * @cfg {String} disabledDaysText
10122      * The tooltip to display when the date falls on a disabled day (defaults to "")
10123      */
10124     disabledDaysText : "",
10125     /**
10126      * @cfg {RegExp} disabledDatesRE
10127      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
10128      */
10129     disabledDatesRE : null,
10130     /**
10131      * @cfg {String} disabledDatesText
10132      * The tooltip text to display when the date falls on a disabled date (defaults to "")
10133      */
10134     disabledDatesText : "",
10135     /**
10136      * @cfg {Boolean} constrainToViewport
10137      * True to constrain the date picker to the viewport (defaults to true)
10138      */
10139     constrainToViewport : true,
10140     /**
10141      * @cfg {Array} monthNames
10142      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
10143      */
10144     monthNames : Date.monthNames,
10145     /**
10146      * @cfg {Array} dayNames
10147      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
10148      */
10149     dayNames : Date.dayNames,
10150     /**
10151      * @cfg {String} nextText
10152      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
10153      */
10154     nextText: 'Next Month (Control+Right)',
10155     /**
10156      * @cfg {String} prevText
10157      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
10158      */
10159     prevText: 'Previous Month (Control+Left)',
10160     /**
10161      * @cfg {String} monthYearText
10162      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
10163      */
10164     monthYearText: 'Choose a month (Control+Up/Down to move years)',
10165     /**
10166      * @cfg {Number} startDay
10167      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
10168      */
10169     startDay : 0,
10170     /**
10171      * @cfg {Bool} showClear
10172      * Show a clear button (usefull for date form elements that can be blank.)
10173      */
10174     
10175     showClear: false,
10176     
10177     /**
10178      * Sets the value of the date field
10179      * @param {Date} value The date to set
10180      */
10181     setValue : function(value){
10182         var old = this.value;
10183         this.value = value.clearTime(true);
10184         if(this.el){
10185             this.update(this.value);
10186         }
10187     },
10188
10189     /**
10190      * Gets the current selected value of the date field
10191      * @return {Date} The selected date
10192      */
10193     getValue : function(){
10194         return this.value;
10195     },
10196
10197     // private
10198     focus : function(){
10199         if(this.el){
10200             this.update(this.activeDate);
10201         }
10202     },
10203
10204     // private
10205     onRender : function(container, position){
10206         var m = [
10207              '<table cellspacing="0">',
10208                 '<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>',
10209                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
10210         var dn = this.dayNames;
10211         for(var i = 0; i < 7; i++){
10212             var d = this.startDay+i;
10213             if(d > 6){
10214                 d = d-7;
10215             }
10216             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
10217         }
10218         m[m.length] = "</tr></thead><tbody><tr>";
10219         for(var i = 0; i < 42; i++) {
10220             if(i % 7 == 0 && i != 0){
10221                 m[m.length] = "</tr><tr>";
10222             }
10223             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
10224         }
10225         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
10226             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
10227
10228         var el = document.createElement("div");
10229         el.className = "x-date-picker";
10230         el.innerHTML = m.join("");
10231
10232         container.dom.insertBefore(el, position);
10233
10234         this.el = Roo.get(el);
10235         this.eventEl = Roo.get(el.firstChild);
10236
10237         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
10238             handler: this.showPrevMonth,
10239             scope: this,
10240             preventDefault:true,
10241             stopDefault:true
10242         });
10243
10244         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
10245             handler: this.showNextMonth,
10246             scope: this,
10247             preventDefault:true,
10248             stopDefault:true
10249         });
10250
10251         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
10252
10253         this.monthPicker = this.el.down('div.x-date-mp');
10254         this.monthPicker.enableDisplayMode('block');
10255         
10256         var kn = new Roo.KeyNav(this.eventEl, {
10257             "left" : function(e){
10258                 e.ctrlKey ?
10259                     this.showPrevMonth() :
10260                     this.update(this.activeDate.add("d", -1));
10261             },
10262
10263             "right" : function(e){
10264                 e.ctrlKey ?
10265                     this.showNextMonth() :
10266                     this.update(this.activeDate.add("d", 1));
10267             },
10268
10269             "up" : function(e){
10270                 e.ctrlKey ?
10271                     this.showNextYear() :
10272                     this.update(this.activeDate.add("d", -7));
10273             },
10274
10275             "down" : function(e){
10276                 e.ctrlKey ?
10277                     this.showPrevYear() :
10278                     this.update(this.activeDate.add("d", 7));
10279             },
10280
10281             "pageUp" : function(e){
10282                 this.showNextMonth();
10283             },
10284
10285             "pageDown" : function(e){
10286                 this.showPrevMonth();
10287             },
10288
10289             "enter" : function(e){
10290                 e.stopPropagation();
10291                 return true;
10292             },
10293
10294             scope : this
10295         });
10296
10297         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
10298
10299         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
10300
10301         this.el.unselectable();
10302         
10303         this.cells = this.el.select("table.x-date-inner tbody td");
10304         this.textNodes = this.el.query("table.x-date-inner tbody span");
10305
10306         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
10307             text: "&#160;",
10308             tooltip: this.monthYearText
10309         });
10310
10311         this.mbtn.on('click', this.showMonthPicker, this);
10312         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
10313
10314
10315         var today = (new Date()).dateFormat(this.format);
10316         
10317         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
10318         if (this.showClear) {
10319             baseTb.add( new Roo.Toolbar.Fill());
10320         }
10321         baseTb.add({
10322             text: String.format(this.todayText, today),
10323             tooltip: String.format(this.todayTip, today),
10324             handler: this.selectToday,
10325             scope: this
10326         });
10327         
10328         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
10329             
10330         //});
10331         if (this.showClear) {
10332             
10333             baseTb.add( new Roo.Toolbar.Fill());
10334             baseTb.add({
10335                 text: '&#160;',
10336                 cls: 'x-btn-icon x-btn-clear',
10337                 handler: function() {
10338                     //this.value = '';
10339                     this.fireEvent("select", this, '');
10340                 },
10341                 scope: this
10342             });
10343         }
10344         
10345         
10346         if(Roo.isIE){
10347             this.el.repaint();
10348         }
10349         this.update(this.value);
10350     },
10351
10352     createMonthPicker : function(){
10353         if(!this.monthPicker.dom.firstChild){
10354             var buf = ['<table border="0" cellspacing="0">'];
10355             for(var i = 0; i < 6; i++){
10356                 buf.push(
10357                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
10358                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
10359                     i == 0 ?
10360                     '<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>' :
10361                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
10362                 );
10363             }
10364             buf.push(
10365                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
10366                     this.okText,
10367                     '</button><button type="button" class="x-date-mp-cancel">',
10368                     this.cancelText,
10369                     '</button></td></tr>',
10370                 '</table>'
10371             );
10372             this.monthPicker.update(buf.join(''));
10373             this.monthPicker.on('click', this.onMonthClick, this);
10374             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
10375
10376             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
10377             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
10378
10379             this.mpMonths.each(function(m, a, i){
10380                 i += 1;
10381                 if((i%2) == 0){
10382                     m.dom.xmonth = 5 + Math.round(i * .5);
10383                 }else{
10384                     m.dom.xmonth = Math.round((i-1) * .5);
10385                 }
10386             });
10387         }
10388     },
10389
10390     showMonthPicker : function(){
10391         this.createMonthPicker();
10392         var size = this.el.getSize();
10393         this.monthPicker.setSize(size);
10394         this.monthPicker.child('table').setSize(size);
10395
10396         this.mpSelMonth = (this.activeDate || this.value).getMonth();
10397         this.updateMPMonth(this.mpSelMonth);
10398         this.mpSelYear = (this.activeDate || this.value).getFullYear();
10399         this.updateMPYear(this.mpSelYear);
10400
10401         this.monthPicker.slideIn('t', {duration:.2});
10402     },
10403
10404     updateMPYear : function(y){
10405         this.mpyear = y;
10406         var ys = this.mpYears.elements;
10407         for(var i = 1; i <= 10; i++){
10408             var td = ys[i-1], y2;
10409             if((i%2) == 0){
10410                 y2 = y + Math.round(i * .5);
10411                 td.firstChild.innerHTML = y2;
10412                 td.xyear = y2;
10413             }else{
10414                 y2 = y - (5-Math.round(i * .5));
10415                 td.firstChild.innerHTML = y2;
10416                 td.xyear = y2;
10417             }
10418             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
10419         }
10420     },
10421
10422     updateMPMonth : function(sm){
10423         this.mpMonths.each(function(m, a, i){
10424             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
10425         });
10426     },
10427
10428     selectMPMonth: function(m){
10429         
10430     },
10431
10432     onMonthClick : function(e, t){
10433         e.stopEvent();
10434         var el = new Roo.Element(t), pn;
10435         if(el.is('button.x-date-mp-cancel')){
10436             this.hideMonthPicker();
10437         }
10438         else if(el.is('button.x-date-mp-ok')){
10439             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10440             this.hideMonthPicker();
10441         }
10442         else if(pn = el.up('td.x-date-mp-month', 2)){
10443             this.mpMonths.removeClass('x-date-mp-sel');
10444             pn.addClass('x-date-mp-sel');
10445             this.mpSelMonth = pn.dom.xmonth;
10446         }
10447         else if(pn = el.up('td.x-date-mp-year', 2)){
10448             this.mpYears.removeClass('x-date-mp-sel');
10449             pn.addClass('x-date-mp-sel');
10450             this.mpSelYear = pn.dom.xyear;
10451         }
10452         else if(el.is('a.x-date-mp-prev')){
10453             this.updateMPYear(this.mpyear-10);
10454         }
10455         else if(el.is('a.x-date-mp-next')){
10456             this.updateMPYear(this.mpyear+10);
10457         }
10458     },
10459
10460     onMonthDblClick : function(e, t){
10461         e.stopEvent();
10462         var el = new Roo.Element(t), pn;
10463         if(pn = el.up('td.x-date-mp-month', 2)){
10464             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
10465             this.hideMonthPicker();
10466         }
10467         else if(pn = el.up('td.x-date-mp-year', 2)){
10468             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10469             this.hideMonthPicker();
10470         }
10471     },
10472
10473     hideMonthPicker : function(disableAnim){
10474         if(this.monthPicker){
10475             if(disableAnim === true){
10476                 this.monthPicker.hide();
10477             }else{
10478                 this.monthPicker.slideOut('t', {duration:.2});
10479             }
10480         }
10481     },
10482
10483     // private
10484     showPrevMonth : function(e){
10485         this.update(this.activeDate.add("mo", -1));
10486     },
10487
10488     // private
10489     showNextMonth : function(e){
10490         this.update(this.activeDate.add("mo", 1));
10491     },
10492
10493     // private
10494     showPrevYear : function(){
10495         this.update(this.activeDate.add("y", -1));
10496     },
10497
10498     // private
10499     showNextYear : function(){
10500         this.update(this.activeDate.add("y", 1));
10501     },
10502
10503     // private
10504     handleMouseWheel : function(e){
10505         var delta = e.getWheelDelta();
10506         if(delta > 0){
10507             this.showPrevMonth();
10508             e.stopEvent();
10509         } else if(delta < 0){
10510             this.showNextMonth();
10511             e.stopEvent();
10512         }
10513     },
10514
10515     // private
10516     handleDateClick : function(e, t){
10517         e.stopEvent();
10518         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
10519             this.setValue(new Date(t.dateValue));
10520             this.fireEvent("select", this, this.value);
10521         }
10522     },
10523
10524     // private
10525     selectToday : function(){
10526         this.setValue(new Date().clearTime());
10527         this.fireEvent("select", this, this.value);
10528     },
10529
10530     // private
10531     update : function(date){
10532         var vd = this.activeDate;
10533         this.activeDate = date;
10534         if(vd && this.el){
10535             var t = date.getTime();
10536             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10537                 this.cells.removeClass("x-date-selected");
10538                 this.cells.each(function(c){
10539                    if(c.dom.firstChild.dateValue == t){
10540                        c.addClass("x-date-selected");
10541                        setTimeout(function(){
10542                             try{c.dom.firstChild.focus();}catch(e){}
10543                        }, 50);
10544                        return false;
10545                    }
10546                 });
10547                 return;
10548             }
10549         }
10550         var days = date.getDaysInMonth();
10551         var firstOfMonth = date.getFirstDateOfMonth();
10552         var startingPos = firstOfMonth.getDay()-this.startDay;
10553
10554         if(startingPos <= this.startDay){
10555             startingPos += 7;
10556         }
10557
10558         var pm = date.add("mo", -1);
10559         var prevStart = pm.getDaysInMonth()-startingPos;
10560
10561         var cells = this.cells.elements;
10562         var textEls = this.textNodes;
10563         days += startingPos;
10564
10565         // convert everything to numbers so it's fast
10566         var day = 86400000;
10567         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10568         var today = new Date().clearTime().getTime();
10569         var sel = date.clearTime().getTime();
10570         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10571         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10572         var ddMatch = this.disabledDatesRE;
10573         var ddText = this.disabledDatesText;
10574         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10575         var ddaysText = this.disabledDaysText;
10576         var format = this.format;
10577
10578         var setCellClass = function(cal, cell){
10579             cell.title = "";
10580             var t = d.getTime();
10581             cell.firstChild.dateValue = t;
10582             if(t == today){
10583                 cell.className += " x-date-today";
10584                 cell.title = cal.todayText;
10585             }
10586             if(t == sel){
10587                 cell.className += " x-date-selected";
10588                 setTimeout(function(){
10589                     try{cell.firstChild.focus();}catch(e){}
10590                 }, 50);
10591             }
10592             // disabling
10593             if(t < min) {
10594                 cell.className = " x-date-disabled";
10595                 cell.title = cal.minText;
10596                 return;
10597             }
10598             if(t > max) {
10599                 cell.className = " x-date-disabled";
10600                 cell.title = cal.maxText;
10601                 return;
10602             }
10603             if(ddays){
10604                 if(ddays.indexOf(d.getDay()) != -1){
10605                     cell.title = ddaysText;
10606                     cell.className = " x-date-disabled";
10607                 }
10608             }
10609             if(ddMatch && format){
10610                 var fvalue = d.dateFormat(format);
10611                 if(ddMatch.test(fvalue)){
10612                     cell.title = ddText.replace("%0", fvalue);
10613                     cell.className = " x-date-disabled";
10614                 }
10615             }
10616         };
10617
10618         var i = 0;
10619         for(; i < startingPos; i++) {
10620             textEls[i].innerHTML = (++prevStart);
10621             d.setDate(d.getDate()+1);
10622             cells[i].className = "x-date-prevday";
10623             setCellClass(this, cells[i]);
10624         }
10625         for(; i < days; i++){
10626             intDay = i - startingPos + 1;
10627             textEls[i].innerHTML = (intDay);
10628             d.setDate(d.getDate()+1);
10629             cells[i].className = "x-date-active";
10630             setCellClass(this, cells[i]);
10631         }
10632         var extraDays = 0;
10633         for(; i < 42; i++) {
10634              textEls[i].innerHTML = (++extraDays);
10635              d.setDate(d.getDate()+1);
10636              cells[i].className = "x-date-nextday";
10637              setCellClass(this, cells[i]);
10638         }
10639
10640         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
10641
10642         if(!this.internalRender){
10643             var main = this.el.dom.firstChild;
10644             var w = main.offsetWidth;
10645             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10646             Roo.fly(main).setWidth(w);
10647             this.internalRender = true;
10648             // opera does not respect the auto grow header center column
10649             // then, after it gets a width opera refuses to recalculate
10650             // without a second pass
10651             if(Roo.isOpera && !this.secondPass){
10652                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10653                 this.secondPass = true;
10654                 this.update.defer(10, this, [date]);
10655             }
10656         }
10657     }
10658 });/*
10659  * Based on:
10660  * Ext JS Library 1.1.1
10661  * Copyright(c) 2006-2007, Ext JS, LLC.
10662  *
10663  * Originally Released Under LGPL - original licence link has changed is not relivant.
10664  *
10665  * Fork - LGPL
10666  * <script type="text/javascript">
10667  */
10668 /**
10669  * @class Roo.TabPanel
10670  * @extends Roo.util.Observable
10671  * A lightweight tab container.
10672  * <br><br>
10673  * Usage:
10674  * <pre><code>
10675 // basic tabs 1, built from existing content
10676 var tabs = new Roo.TabPanel("tabs1");
10677 tabs.addTab("script", "View Script");
10678 tabs.addTab("markup", "View Markup");
10679 tabs.activate("script");
10680
10681 // more advanced tabs, built from javascript
10682 var jtabs = new Roo.TabPanel("jtabs");
10683 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
10684
10685 // set up the UpdateManager
10686 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
10687 var updater = tab2.getUpdateManager();
10688 updater.setDefaultUrl("ajax1.htm");
10689 tab2.on('activate', updater.refresh, updater, true);
10690
10691 // Use setUrl for Ajax loading
10692 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
10693 tab3.setUrl("ajax2.htm", null, true);
10694
10695 // Disabled tab
10696 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
10697 tab4.disable();
10698
10699 jtabs.activate("jtabs-1");
10700  * </code></pre>
10701  * @constructor
10702  * Create a new TabPanel.
10703  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
10704  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
10705  */
10706 Roo.TabPanel = function(container, config){
10707     /**
10708     * The container element for this TabPanel.
10709     * @type Roo.Element
10710     */
10711     this.el = Roo.get(container, true);
10712     if(config){
10713         if(typeof config == "boolean"){
10714             this.tabPosition = config ? "bottom" : "top";
10715         }else{
10716             Roo.apply(this, config);
10717         }
10718     }
10719     if(this.tabPosition == "bottom"){
10720         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10721         this.el.addClass("x-tabs-bottom");
10722     }
10723     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
10724     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
10725     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
10726     if(Roo.isIE){
10727         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
10728     }
10729     if(this.tabPosition != "bottom"){
10730     /** The body element that contains {@link Roo.TabPanelItem} bodies.
10731      * @type Roo.Element
10732      */
10733       this.bodyEl = Roo.get(this.createBody(this.el.dom));
10734       this.el.addClass("x-tabs-top");
10735     }
10736     this.items = [];
10737
10738     this.bodyEl.setStyle("position", "relative");
10739
10740     this.active = null;
10741     this.activateDelegate = this.activate.createDelegate(this);
10742
10743     this.addEvents({
10744         /**
10745          * @event tabchange
10746          * Fires when the active tab changes
10747          * @param {Roo.TabPanel} this
10748          * @param {Roo.TabPanelItem} activePanel The new active tab
10749          */
10750         "tabchange": true,
10751         /**
10752          * @event beforetabchange
10753          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
10754          * @param {Roo.TabPanel} this
10755          * @param {Object} e Set cancel to true on this object to cancel the tab change
10756          * @param {Roo.TabPanelItem} tab The tab being changed to
10757          */
10758         "beforetabchange" : true
10759     });
10760
10761     Roo.EventManager.onWindowResize(this.onResize, this);
10762     this.cpad = this.el.getPadding("lr");
10763     this.hiddenCount = 0;
10764
10765     Roo.TabPanel.superclass.constructor.call(this);
10766 };
10767
10768 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
10769         /*
10770          *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
10771          */
10772     tabPosition : "top",
10773         /*
10774          *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
10775          */
10776     currentTabWidth : 0,
10777         /*
10778          *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
10779          */
10780     minTabWidth : 40,
10781         /*
10782          *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
10783          */
10784     maxTabWidth : 250,
10785         /*
10786          *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
10787          */
10788     preferredTabWidth : 175,
10789         /*
10790          *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
10791          */
10792     resizeTabs : false,
10793         /*
10794          *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
10795          */
10796     monitorResize : true,
10797
10798     /**
10799      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
10800      * @param {String} id The id of the div to use <b>or create</b>
10801      * @param {String} text The text for the tab
10802      * @param {String} content (optional) Content to put in the TabPanelItem body
10803      * @param {Boolean} closable (optional) True to create a close icon on the tab
10804      * @return {Roo.TabPanelItem} The created TabPanelItem
10805      */
10806     addTab : function(id, text, content, closable){
10807         var item = new Roo.TabPanelItem(this, id, text, closable);
10808         this.addTabItem(item);
10809         if(content){
10810             item.setContent(content);
10811         }
10812         return item;
10813     },
10814
10815     /**
10816      * Returns the {@link Roo.TabPanelItem} with the specified id/index
10817      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
10818      * @return {Roo.TabPanelItem}
10819      */
10820     getTab : function(id){
10821         return this.items[id];
10822     },
10823
10824     /**
10825      * Hides the {@link Roo.TabPanelItem} with the specified id/index
10826      * @param {String/Number} id The id or index of the TabPanelItem to hide.
10827      */
10828     hideTab : function(id){
10829         var t = this.items[id];
10830         if(!t.isHidden()){
10831            t.setHidden(true);
10832            this.hiddenCount++;
10833            this.autoSizeTabs();
10834         }
10835     },
10836
10837     /**
10838      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
10839      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
10840      */
10841     unhideTab : function(id){
10842         var t = this.items[id];
10843         if(t.isHidden()){
10844            t.setHidden(false);
10845            this.hiddenCount--;
10846            this.autoSizeTabs();
10847         }
10848     },
10849
10850     /**
10851      * Adds an existing {@link Roo.TabPanelItem}.
10852      * @param {Roo.TabPanelItem} item The TabPanelItem to add
10853      */
10854     addTabItem : function(item){
10855         this.items[item.id] = item;
10856         this.items.push(item);
10857         if(this.resizeTabs){
10858            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
10859            this.autoSizeTabs();
10860         }else{
10861             item.autoSize();
10862         }
10863     },
10864
10865     /**
10866      * Removes a {@link Roo.TabPanelItem}.
10867      * @param {String/Number} id The id or index of the TabPanelItem to remove.
10868      */
10869     removeTab : function(id){
10870         var items = this.items;
10871         var tab = items[id];
10872         if(!tab) { return; }
10873         var index = items.indexOf(tab);
10874         if(this.active == tab && items.length > 1){
10875             var newTab = this.getNextAvailable(index);
10876             if(newTab) {
10877                 newTab.activate();
10878             }
10879         }
10880         this.stripEl.dom.removeChild(tab.pnode.dom);
10881         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
10882             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
10883         }
10884         items.splice(index, 1);
10885         delete this.items[tab.id];
10886         tab.fireEvent("close", tab);
10887         tab.purgeListeners();
10888         this.autoSizeTabs();
10889     },
10890
10891     getNextAvailable : function(start){
10892         var items = this.items;
10893         var index = start;
10894         // look for a next tab that will slide over to
10895         // replace the one being removed
10896         while(index < items.length){
10897             var item = items[++index];
10898             if(item && !item.isHidden()){
10899                 return item;
10900             }
10901         }
10902         // if one isn't found select the previous tab (on the left)
10903         index = start;
10904         while(index >= 0){
10905             var item = items[--index];
10906             if(item && !item.isHidden()){
10907                 return item;
10908             }
10909         }
10910         return null;
10911     },
10912
10913     /**
10914      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
10915      * @param {String/Number} id The id or index of the TabPanelItem to disable.
10916      */
10917     disableTab : function(id){
10918         var tab = this.items[id];
10919         if(tab && this.active != tab){
10920             tab.disable();
10921         }
10922     },
10923
10924     /**
10925      * Enables a {@link Roo.TabPanelItem} that is disabled.
10926      * @param {String/Number} id The id or index of the TabPanelItem to enable.
10927      */
10928     enableTab : function(id){
10929         var tab = this.items[id];
10930         tab.enable();
10931     },
10932
10933     /**
10934      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
10935      * @param {String/Number} id The id or index of the TabPanelItem to activate.
10936      * @return {Roo.TabPanelItem} The TabPanelItem.
10937      */
10938     activate : function(id){
10939         var tab = this.items[id];
10940         if(!tab){
10941             return null;
10942         }
10943         if(tab == this.active || tab.disabled){
10944             return tab;
10945         }
10946         var e = {};
10947         this.fireEvent("beforetabchange", this, e, tab);
10948         if(e.cancel !== true && !tab.disabled){
10949             if(this.active){
10950                 this.active.hide();
10951             }
10952             this.active = this.items[id];
10953             this.active.show();
10954             this.fireEvent("tabchange", this, this.active);
10955         }
10956         return tab;
10957     },
10958
10959     /**
10960      * Gets the active {@link Roo.TabPanelItem}.
10961      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
10962      */
10963     getActiveTab : function(){
10964         return this.active;
10965     },
10966
10967     /**
10968      * Updates the tab body element to fit the height of the container element
10969      * for overflow scrolling
10970      * @param {Number} targetHeight (optional) Override the starting height from the elements height
10971      */
10972     syncHeight : function(targetHeight){
10973         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
10974         var bm = this.bodyEl.getMargins();
10975         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
10976         this.bodyEl.setHeight(newHeight);
10977         return newHeight;
10978     },
10979
10980     onResize : function(){
10981         if(this.monitorResize){
10982             this.autoSizeTabs();
10983         }
10984     },
10985
10986     /**
10987      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
10988      */
10989     beginUpdate : function(){
10990         this.updating = true;
10991     },
10992
10993     /**
10994      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
10995      */
10996     endUpdate : function(){
10997         this.updating = false;
10998         this.autoSizeTabs();
10999     },
11000
11001     /**
11002      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
11003      */
11004     autoSizeTabs : function(){
11005         var count = this.items.length;
11006         var vcount = count - this.hiddenCount;
11007         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
11008         var w = Math.max(this.el.getWidth() - this.cpad, 10);
11009         var availWidth = Math.floor(w / vcount);
11010         var b = this.stripBody;
11011         if(b.getWidth() > w){
11012             var tabs = this.items;
11013             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
11014             if(availWidth < this.minTabWidth){
11015                 /*if(!this.sleft){    // incomplete scrolling code
11016                     this.createScrollButtons();
11017                 }
11018                 this.showScroll();
11019                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
11020             }
11021         }else{
11022             if(this.currentTabWidth < this.preferredTabWidth){
11023                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
11024             }
11025         }
11026     },
11027
11028     /**
11029      * Returns the number of tabs in this TabPanel.
11030      * @return {Number}
11031      */
11032      getCount : function(){
11033          return this.items.length;
11034      },
11035
11036     /**
11037      * Resizes all the tabs to the passed width
11038      * @param {Number} The new width
11039      */
11040     setTabWidth : function(width){
11041         this.currentTabWidth = width;
11042         for(var i = 0, len = this.items.length; i < len; i++) {
11043                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
11044         }
11045     },
11046
11047     /**
11048      * Destroys this TabPanel
11049      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
11050      */
11051     destroy : function(removeEl){
11052         Roo.EventManager.removeResizeListener(this.onResize, this);
11053         for(var i = 0, len = this.items.length; i < len; i++){
11054             this.items[i].purgeListeners();
11055         }
11056         if(removeEl === true){
11057             this.el.update("");
11058             this.el.remove();
11059         }
11060     }
11061 });
11062
11063 /**
11064  * @class Roo.TabPanelItem
11065  * @extends Roo.util.Observable
11066  * Represents an individual item (tab plus body) in a TabPanel.
11067  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
11068  * @param {String} id The id of this TabPanelItem
11069  * @param {String} text The text for the tab of this TabPanelItem
11070  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
11071  */
11072 Roo.TabPanelItem = function(tabPanel, id, text, closable){
11073     /**
11074      * The {@link Roo.TabPanel} this TabPanelItem belongs to
11075      * @type Roo.TabPanel
11076      */
11077     this.tabPanel = tabPanel;
11078     /**
11079      * The id for this TabPanelItem
11080      * @type String
11081      */
11082     this.id = id;
11083     /** @private */
11084     this.disabled = false;
11085     /** @private */
11086     this.text = text;
11087     /** @private */
11088     this.loaded = false;
11089     this.closable = closable;
11090
11091     /**
11092      * The body element for this TabPanelItem.
11093      * @type Roo.Element
11094      */
11095     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
11096     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
11097     this.bodyEl.setStyle("display", "block");
11098     this.bodyEl.setStyle("zoom", "1");
11099     this.hideAction();
11100
11101     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
11102     /** @private */
11103     this.el = Roo.get(els.el, true);
11104     this.inner = Roo.get(els.inner, true);
11105     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
11106     this.pnode = Roo.get(els.el.parentNode, true);
11107     this.el.on("mousedown", this.onTabMouseDown, this);
11108     this.el.on("click", this.onTabClick, this);
11109     /** @private */
11110     if(closable){
11111         var c = Roo.get(els.close, true);
11112         c.dom.title = this.closeText;
11113         c.addClassOnOver("close-over");
11114         c.on("click", this.closeClick, this);
11115      }
11116
11117     this.addEvents({
11118          /**
11119          * @event activate
11120          * Fires when this tab becomes the active tab.
11121          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11122          * @param {Roo.TabPanelItem} this
11123          */
11124         "activate": true,
11125         /**
11126          * @event beforeclose
11127          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
11128          * @param {Roo.TabPanelItem} this
11129          * @param {Object} e Set cancel to true on this object to cancel the close.
11130          */
11131         "beforeclose": true,
11132         /**
11133          * @event close
11134          * Fires when this tab is closed.
11135          * @param {Roo.TabPanelItem} this
11136          */
11137          "close": true,
11138         /**
11139          * @event deactivate
11140          * Fires when this tab is no longer the active tab.
11141          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11142          * @param {Roo.TabPanelItem} this
11143          */
11144          "deactivate" : true
11145     });
11146     this.hidden = false;
11147
11148     Roo.TabPanelItem.superclass.constructor.call(this);
11149 };
11150
11151 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
11152     purgeListeners : function(){
11153        Roo.util.Observable.prototype.purgeListeners.call(this);
11154        this.el.removeAllListeners();
11155     },
11156     /**
11157      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
11158      */
11159     show : function(){
11160         this.pnode.addClass("on");
11161         this.showAction();
11162         if(Roo.isOpera){
11163             this.tabPanel.stripWrap.repaint();
11164         }
11165         this.fireEvent("activate", this.tabPanel, this);
11166     },
11167
11168     /**
11169      * Returns true if this tab is the active tab.
11170      * @return {Boolean}
11171      */
11172     isActive : function(){
11173         return this.tabPanel.getActiveTab() == this;
11174     },
11175
11176     /**
11177      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
11178      */
11179     hide : function(){
11180         this.pnode.removeClass("on");
11181         this.hideAction();
11182         this.fireEvent("deactivate", this.tabPanel, this);
11183     },
11184
11185     hideAction : function(){
11186         this.bodyEl.hide();
11187         this.bodyEl.setStyle("position", "absolute");
11188         this.bodyEl.setLeft("-20000px");
11189         this.bodyEl.setTop("-20000px");
11190     },
11191
11192     showAction : function(){
11193         this.bodyEl.setStyle("position", "relative");
11194         this.bodyEl.setTop("");
11195         this.bodyEl.setLeft("");
11196         this.bodyEl.show();
11197     },
11198
11199     /**
11200      * Set the tooltip for the tab.
11201      * @param {String} tooltip The tab's tooltip
11202      */
11203     setTooltip : function(text){
11204         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
11205             this.textEl.dom.qtip = text;
11206             this.textEl.dom.removeAttribute('title');
11207         }else{
11208             this.textEl.dom.title = text;
11209         }
11210     },
11211
11212     onTabClick : function(e){
11213         e.preventDefault();
11214         this.tabPanel.activate(this.id);
11215     },
11216
11217     onTabMouseDown : function(e){
11218         e.preventDefault();
11219         this.tabPanel.activate(this.id);
11220     },
11221
11222     getWidth : function(){
11223         return this.inner.getWidth();
11224     },
11225
11226     setWidth : function(width){
11227         var iwidth = width - this.pnode.getPadding("lr");
11228         this.inner.setWidth(iwidth);
11229         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
11230         this.pnode.setWidth(width);
11231     },
11232
11233     /**
11234      * Show or hide the tab
11235      * @param {Boolean} hidden True to hide or false to show.
11236      */
11237     setHidden : function(hidden){
11238         this.hidden = hidden;
11239         this.pnode.setStyle("display", hidden ? "none" : "");
11240     },
11241
11242     /**
11243      * Returns true if this tab is "hidden"
11244      * @return {Boolean}
11245      */
11246     isHidden : function(){
11247         return this.hidden;
11248     },
11249
11250     /**
11251      * Returns the text for this tab
11252      * @return {String}
11253      */
11254     getText : function(){
11255         return this.text;
11256     },
11257
11258     autoSize : function(){
11259         //this.el.beginMeasure();
11260         this.textEl.setWidth(1);
11261         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
11262         //this.el.endMeasure();
11263     },
11264
11265     /**
11266      * Sets the text for the tab (Note: this also sets the tooltip text)
11267      * @param {String} text The tab's text and tooltip
11268      */
11269     setText : function(text){
11270         this.text = text;
11271         this.textEl.update(text);
11272         this.setTooltip(text);
11273         if(!this.tabPanel.resizeTabs){
11274             this.autoSize();
11275         }
11276     },
11277     /**
11278      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
11279      */
11280     activate : function(){
11281         this.tabPanel.activate(this.id);
11282     },
11283
11284     /**
11285      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
11286      */
11287     disable : function(){
11288         if(this.tabPanel.active != this){
11289             this.disabled = true;
11290             this.pnode.addClass("disabled");
11291         }
11292     },
11293
11294     /**
11295      * Enables this TabPanelItem if it was previously disabled.
11296      */
11297     enable : function(){
11298         this.disabled = false;
11299         this.pnode.removeClass("disabled");
11300     },
11301
11302     /**
11303      * Sets the content for this TabPanelItem.
11304      * @param {String} content The content
11305      * @param {Boolean} loadScripts true to look for and load scripts
11306      */
11307     setContent : function(content, loadScripts){
11308         this.bodyEl.update(content, loadScripts);
11309     },
11310
11311     /**
11312      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
11313      * @return {Roo.UpdateManager} The UpdateManager
11314      */
11315     getUpdateManager : function(){
11316         return this.bodyEl.getUpdateManager();
11317     },
11318
11319     /**
11320      * Set a URL to be used to load the content for this TabPanelItem.
11321      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
11322      * @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)
11323      * @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)
11324      * @return {Roo.UpdateManager} The UpdateManager
11325      */
11326     setUrl : function(url, params, loadOnce){
11327         if(this.refreshDelegate){
11328             this.un('activate', this.refreshDelegate);
11329         }
11330         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
11331         this.on("activate", this.refreshDelegate);
11332         return this.bodyEl.getUpdateManager();
11333     },
11334
11335     /** @private */
11336     _handleRefresh : function(url, params, loadOnce){
11337         if(!loadOnce || !this.loaded){
11338             var updater = this.bodyEl.getUpdateManager();
11339             updater.update(url, params, this._setLoaded.createDelegate(this));
11340         }
11341     },
11342
11343     /**
11344      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
11345      *   Will fail silently if the setUrl method has not been called.
11346      *   This does not activate the panel, just updates its content.
11347      */
11348     refresh : function(){
11349         if(this.refreshDelegate){
11350            this.loaded = false;
11351            this.refreshDelegate();
11352         }
11353     },
11354
11355     /** @private */
11356     _setLoaded : function(){
11357         this.loaded = true;
11358     },
11359
11360     /** @private */
11361     closeClick : function(e){
11362         var o = {};
11363         e.stopEvent();
11364         this.fireEvent("beforeclose", this, o);
11365         if(o.cancel !== true){
11366             this.tabPanel.removeTab(this.id);
11367         }
11368     },
11369     /**
11370      * The text displayed in the tooltip for the close icon.
11371      * @type String
11372      */
11373     closeText : "Close this tab"
11374 });
11375
11376 /** @private */
11377 Roo.TabPanel.prototype.createStrip = function(container){
11378     var strip = document.createElement("div");
11379     strip.className = "x-tabs-wrap";
11380     container.appendChild(strip);
11381     return strip;
11382 };
11383 /** @private */
11384 Roo.TabPanel.prototype.createStripList = function(strip){
11385     // div wrapper for retard IE
11386     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>';
11387     return strip.firstChild.firstChild.firstChild.firstChild;
11388 };
11389 /** @private */
11390 Roo.TabPanel.prototype.createBody = function(container){
11391     var body = document.createElement("div");
11392     Roo.id(body, "tab-body");
11393     Roo.fly(body).addClass("x-tabs-body");
11394     container.appendChild(body);
11395     return body;
11396 };
11397 /** @private */
11398 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
11399     var body = Roo.getDom(id);
11400     if(!body){
11401         body = document.createElement("div");
11402         body.id = id;
11403     }
11404     Roo.fly(body).addClass("x-tabs-item-body");
11405     bodyEl.insertBefore(body, bodyEl.firstChild);
11406     return body;
11407 };
11408 /** @private */
11409 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
11410     var td = document.createElement("td");
11411     stripEl.appendChild(td);
11412     if(closable){
11413         td.className = "x-tabs-closable";
11414         if(!this.closeTpl){
11415             this.closeTpl = new Roo.Template(
11416                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11417                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
11418                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
11419             );
11420         }
11421         var el = this.closeTpl.overwrite(td, {"text": text});
11422         var close = el.getElementsByTagName("div")[0];
11423         var inner = el.getElementsByTagName("em")[0];
11424         return {"el": el, "close": close, "inner": inner};
11425     } else {
11426         if(!this.tabTpl){
11427             this.tabTpl = new Roo.Template(
11428                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11429                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
11430             );
11431         }
11432         var el = this.tabTpl.overwrite(td, {"text": text});
11433         var inner = el.getElementsByTagName("em")[0];
11434         return {"el": el, "inner": inner};
11435     }
11436 };/*
11437  * Based on:
11438  * Ext JS Library 1.1.1
11439  * Copyright(c) 2006-2007, Ext JS, LLC.
11440  *
11441  * Originally Released Under LGPL - original licence link has changed is not relivant.
11442  *
11443  * Fork - LGPL
11444  * <script type="text/javascript">
11445  */
11446
11447 /**
11448  * @class Roo.Button
11449  * @extends Roo.util.Observable
11450  * Simple Button class
11451  * @cfg {String} text The button text
11452  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
11453  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
11454  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
11455  * @cfg {Object} scope The scope of the handler
11456  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
11457  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
11458  * @cfg {Boolean} hidden True to start hidden (defaults to false)
11459  * @cfg {Boolean} disabled True to start disabled (defaults to false)
11460  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
11461  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
11462    applies if enableToggle = true)
11463  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
11464  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
11465   an {@link Roo.util.ClickRepeater} config object (defaults to false).
11466  * @constructor
11467  * Create a new button
11468  * @param {Object} config The config object
11469  */
11470 Roo.Button = function(renderTo, config)
11471 {
11472     if (!config) {
11473         config = renderTo;
11474         renderTo = config.renderTo || false;
11475     }
11476     
11477     Roo.apply(this, config);
11478     this.addEvents({
11479         /**
11480              * @event click
11481              * Fires when this button is clicked
11482              * @param {Button} this
11483              * @param {EventObject} e The click event
11484              */
11485             "click" : true,
11486         /**
11487              * @event toggle
11488              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
11489              * @param {Button} this
11490              * @param {Boolean} pressed
11491              */
11492             "toggle" : true,
11493         /**
11494              * @event mouseover
11495              * Fires when the mouse hovers over the button
11496              * @param {Button} this
11497              * @param {Event} e The event object
11498              */
11499         'mouseover' : true,
11500         /**
11501              * @event mouseout
11502              * Fires when the mouse exits the button
11503              * @param {Button} this
11504              * @param {Event} e The event object
11505              */
11506         'mouseout': true,
11507          /**
11508              * @event render
11509              * Fires when the button is rendered
11510              * @param {Button} this
11511              */
11512         'render': true
11513     });
11514     if(this.menu){
11515         this.menu = Roo.menu.MenuMgr.get(this.menu);
11516     }
11517     // register listeners first!!  - so render can be captured..
11518     Roo.util.Observable.call(this);
11519     if(renderTo){
11520         this.render(renderTo);
11521     }
11522     
11523   
11524 };
11525
11526 Roo.extend(Roo.Button, Roo.util.Observable, {
11527     /**
11528      * 
11529      */
11530     
11531     /**
11532      * Read-only. True if this button is hidden
11533      * @type Boolean
11534      */
11535     hidden : false,
11536     /**
11537      * Read-only. True if this button is disabled
11538      * @type Boolean
11539      */
11540     disabled : false,
11541     /**
11542      * Read-only. True if this button is pressed (only if enableToggle = true)
11543      * @type Boolean
11544      */
11545     pressed : false,
11546
11547     /**
11548      * @cfg {Number} tabIndex 
11549      * The DOM tabIndex for this button (defaults to undefined)
11550      */
11551     tabIndex : undefined,
11552
11553     /**
11554      * @cfg {Boolean} enableToggle
11555      * True to enable pressed/not pressed toggling (defaults to false)
11556      */
11557     enableToggle: false,
11558     /**
11559      * @cfg {Mixed} menu
11560      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
11561      */
11562     menu : undefined,
11563     /**
11564      * @cfg {String} menuAlign
11565      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
11566      */
11567     menuAlign : "tl-bl?",
11568
11569     /**
11570      * @cfg {String} iconCls
11571      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
11572      */
11573     iconCls : undefined,
11574     /**
11575      * @cfg {String} type
11576      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
11577      */
11578     type : 'button',
11579
11580     // private
11581     menuClassTarget: 'tr',
11582
11583     /**
11584      * @cfg {String} clickEvent
11585      * The type of event to map to the button's event handler (defaults to 'click')
11586      */
11587     clickEvent : 'click',
11588
11589     /**
11590      * @cfg {Boolean} handleMouseEvents
11591      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
11592      */
11593     handleMouseEvents : true,
11594
11595     /**
11596      * @cfg {String} tooltipType
11597      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
11598      */
11599     tooltipType : 'qtip',
11600
11601     /**
11602      * @cfg {String} cls
11603      * A CSS class to apply to the button's main element.
11604      */
11605     
11606     /**
11607      * @cfg {Roo.Template} template (Optional)
11608      * An {@link Roo.Template} with which to create the Button's main element. This Template must
11609      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
11610      * require code modifications if required elements (e.g. a button) aren't present.
11611      */
11612
11613     // private
11614     render : function(renderTo){
11615         var btn;
11616         if(this.hideParent){
11617             this.parentEl = Roo.get(renderTo);
11618         }
11619         if(!this.dhconfig){
11620             if(!this.template){
11621                 if(!Roo.Button.buttonTemplate){
11622                     // hideous table template
11623                     Roo.Button.buttonTemplate = new Roo.Template(
11624                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
11625                         '<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>',
11626                         "</tr></tbody></table>");
11627                 }
11628                 this.template = Roo.Button.buttonTemplate;
11629             }
11630             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
11631             var btnEl = btn.child("button:first");
11632             btnEl.on('focus', this.onFocus, this);
11633             btnEl.on('blur', this.onBlur, this);
11634             if(this.cls){
11635                 btn.addClass(this.cls);
11636             }
11637             if(this.icon){
11638                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
11639             }
11640             if(this.iconCls){
11641                 btnEl.addClass(this.iconCls);
11642                 if(!this.cls){
11643                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11644                 }
11645             }
11646             if(this.tabIndex !== undefined){
11647                 btnEl.dom.tabIndex = this.tabIndex;
11648             }
11649             if(this.tooltip){
11650                 if(typeof this.tooltip == 'object'){
11651                     Roo.QuickTips.tips(Roo.apply({
11652                           target: btnEl.id
11653                     }, this.tooltip));
11654                 } else {
11655                     btnEl.dom[this.tooltipType] = this.tooltip;
11656                 }
11657             }
11658         }else{
11659             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
11660         }
11661         this.el = btn;
11662         if(this.id){
11663             this.el.dom.id = this.el.id = this.id;
11664         }
11665         if(this.menu){
11666             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
11667             this.menu.on("show", this.onMenuShow, this);
11668             this.menu.on("hide", this.onMenuHide, this);
11669         }
11670         btn.addClass("x-btn");
11671         if(Roo.isIE && !Roo.isIE7){
11672             this.autoWidth.defer(1, this);
11673         }else{
11674             this.autoWidth();
11675         }
11676         if(this.handleMouseEvents){
11677             btn.on("mouseover", this.onMouseOver, this);
11678             btn.on("mouseout", this.onMouseOut, this);
11679             btn.on("mousedown", this.onMouseDown, this);
11680         }
11681         btn.on(this.clickEvent, this.onClick, this);
11682         //btn.on("mouseup", this.onMouseUp, this);
11683         if(this.hidden){
11684             this.hide();
11685         }
11686         if(this.disabled){
11687             this.disable();
11688         }
11689         Roo.ButtonToggleMgr.register(this);
11690         if(this.pressed){
11691             this.el.addClass("x-btn-pressed");
11692         }
11693         if(this.repeat){
11694             var repeater = new Roo.util.ClickRepeater(btn,
11695                 typeof this.repeat == "object" ? this.repeat : {}
11696             );
11697             repeater.on("click", this.onClick,  this);
11698         }
11699         
11700         this.fireEvent('render', this);
11701         
11702     },
11703     /**
11704      * Returns the button's underlying element
11705      * @return {Roo.Element} The element
11706      */
11707     getEl : function(){
11708         return this.el;  
11709     },
11710     
11711     /**
11712      * Destroys this Button and removes any listeners.
11713      */
11714     destroy : function(){
11715         Roo.ButtonToggleMgr.unregister(this);
11716         this.el.removeAllListeners();
11717         this.purgeListeners();
11718         this.el.remove();
11719     },
11720
11721     // private
11722     autoWidth : function(){
11723         if(this.el){
11724             this.el.setWidth("auto");
11725             if(Roo.isIE7 && Roo.isStrict){
11726                 var ib = this.el.child('button');
11727                 if(ib && ib.getWidth() > 20){
11728                     ib.clip();
11729                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11730                 }
11731             }
11732             if(this.minWidth){
11733                 if(this.hidden){
11734                     this.el.beginMeasure();
11735                 }
11736                 if(this.el.getWidth() < this.minWidth){
11737                     this.el.setWidth(this.minWidth);
11738                 }
11739                 if(this.hidden){
11740                     this.el.endMeasure();
11741                 }
11742             }
11743         }
11744     },
11745
11746     /**
11747      * Assigns this button's click handler
11748      * @param {Function} handler The function to call when the button is clicked
11749      * @param {Object} scope (optional) Scope for the function passed in
11750      */
11751     setHandler : function(handler, scope){
11752         this.handler = handler;
11753         this.scope = scope;  
11754     },
11755     
11756     /**
11757      * Sets this button's text
11758      * @param {String} text The button text
11759      */
11760     setText : function(text){
11761         this.text = text;
11762         if(this.el){
11763             this.el.child("td.x-btn-center button.x-btn-text").update(text);
11764         }
11765         this.autoWidth();
11766     },
11767     
11768     /**
11769      * Gets the text for this button
11770      * @return {String} The button text
11771      */
11772     getText : function(){
11773         return this.text;  
11774     },
11775     
11776     /**
11777      * Show this button
11778      */
11779     show: function(){
11780         this.hidden = false;
11781         if(this.el){
11782             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
11783         }
11784     },
11785     
11786     /**
11787      * Hide this button
11788      */
11789     hide: function(){
11790         this.hidden = true;
11791         if(this.el){
11792             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
11793         }
11794     },
11795     
11796     /**
11797      * Convenience function for boolean show/hide
11798      * @param {Boolean} visible True to show, false to hide
11799      */
11800     setVisible: function(visible){
11801         if(visible) {
11802             this.show();
11803         }else{
11804             this.hide();
11805         }
11806     },
11807     
11808     /**
11809      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
11810      * @param {Boolean} state (optional) Force a particular state
11811      */
11812     toggle : function(state){
11813         state = state === undefined ? !this.pressed : state;
11814         if(state != this.pressed){
11815             if(state){
11816                 this.el.addClass("x-btn-pressed");
11817                 this.pressed = true;
11818                 this.fireEvent("toggle", this, true);
11819             }else{
11820                 this.el.removeClass("x-btn-pressed");
11821                 this.pressed = false;
11822                 this.fireEvent("toggle", this, false);
11823             }
11824             if(this.toggleHandler){
11825                 this.toggleHandler.call(this.scope || this, this, state);
11826             }
11827         }
11828     },
11829     
11830     /**
11831      * Focus the button
11832      */
11833     focus : function(){
11834         this.el.child('button:first').focus();
11835     },
11836     
11837     /**
11838      * Disable this button
11839      */
11840     disable : function(){
11841         if(this.el){
11842             this.el.addClass("x-btn-disabled");
11843         }
11844         this.disabled = true;
11845     },
11846     
11847     /**
11848      * Enable this button
11849      */
11850     enable : function(){
11851         if(this.el){
11852             this.el.removeClass("x-btn-disabled");
11853         }
11854         this.disabled = false;
11855     },
11856
11857     /**
11858      * Convenience function for boolean enable/disable
11859      * @param {Boolean} enabled True to enable, false to disable
11860      */
11861     setDisabled : function(v){
11862         this[v !== true ? "enable" : "disable"]();
11863     },
11864
11865     // private
11866     onClick : function(e){
11867         if(e){
11868             e.preventDefault();
11869         }
11870         if(e.button != 0){
11871             return;
11872         }
11873         if(!this.disabled){
11874             if(this.enableToggle){
11875                 this.toggle();
11876             }
11877             if(this.menu && !this.menu.isVisible()){
11878                 this.menu.show(this.el, this.menuAlign);
11879             }
11880             this.fireEvent("click", this, e);
11881             if(this.handler){
11882                 this.el.removeClass("x-btn-over");
11883                 this.handler.call(this.scope || this, this, e);
11884             }
11885         }
11886     },
11887     // private
11888     onMouseOver : function(e){
11889         if(!this.disabled){
11890             this.el.addClass("x-btn-over");
11891             this.fireEvent('mouseover', this, e);
11892         }
11893     },
11894     // private
11895     onMouseOut : function(e){
11896         if(!e.within(this.el,  true)){
11897             this.el.removeClass("x-btn-over");
11898             this.fireEvent('mouseout', this, e);
11899         }
11900     },
11901     // private
11902     onFocus : function(e){
11903         if(!this.disabled){
11904             this.el.addClass("x-btn-focus");
11905         }
11906     },
11907     // private
11908     onBlur : function(e){
11909         this.el.removeClass("x-btn-focus");
11910     },
11911     // private
11912     onMouseDown : function(e){
11913         if(!this.disabled && e.button == 0){
11914             this.el.addClass("x-btn-click");
11915             Roo.get(document).on('mouseup', this.onMouseUp, this);
11916         }
11917     },
11918     // private
11919     onMouseUp : function(e){
11920         if(e.button == 0){
11921             this.el.removeClass("x-btn-click");
11922             Roo.get(document).un('mouseup', this.onMouseUp, this);
11923         }
11924     },
11925     // private
11926     onMenuShow : function(e){
11927         this.el.addClass("x-btn-menu-active");
11928     },
11929     // private
11930     onMenuHide : function(e){
11931         this.el.removeClass("x-btn-menu-active");
11932     }   
11933 });
11934
11935 // Private utility class used by Button
11936 Roo.ButtonToggleMgr = function(){
11937    var groups = {};
11938    
11939    function toggleGroup(btn, state){
11940        if(state){
11941            var g = groups[btn.toggleGroup];
11942            for(var i = 0, l = g.length; i < l; i++){
11943                if(g[i] != btn){
11944                    g[i].toggle(false);
11945                }
11946            }
11947        }
11948    }
11949    
11950    return {
11951        register : function(btn){
11952            if(!btn.toggleGroup){
11953                return;
11954            }
11955            var g = groups[btn.toggleGroup];
11956            if(!g){
11957                g = groups[btn.toggleGroup] = [];
11958            }
11959            g.push(btn);
11960            btn.on("toggle", toggleGroup);
11961        },
11962        
11963        unregister : function(btn){
11964            if(!btn.toggleGroup){
11965                return;
11966            }
11967            var g = groups[btn.toggleGroup];
11968            if(g){
11969                g.remove(btn);
11970                btn.un("toggle", toggleGroup);
11971            }
11972        }
11973    };
11974 }();/*
11975  * Based on:
11976  * Ext JS Library 1.1.1
11977  * Copyright(c) 2006-2007, Ext JS, LLC.
11978  *
11979  * Originally Released Under LGPL - original licence link has changed is not relivant.
11980  *
11981  * Fork - LGPL
11982  * <script type="text/javascript">
11983  */
11984  
11985 /**
11986  * @class Roo.SplitButton
11987  * @extends Roo.Button
11988  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
11989  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
11990  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
11991  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
11992  * @cfg {String} arrowTooltip The title attribute of the arrow
11993  * @constructor
11994  * Create a new menu button
11995  * @param {String/HTMLElement/Element} renderTo The element to append the button to
11996  * @param {Object} config The config object
11997  */
11998 Roo.SplitButton = function(renderTo, config){
11999     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
12000     /**
12001      * @event arrowclick
12002      * Fires when this button's arrow is clicked
12003      * @param {SplitButton} this
12004      * @param {EventObject} e The click event
12005      */
12006     this.addEvents({"arrowclick":true});
12007 };
12008
12009 Roo.extend(Roo.SplitButton, Roo.Button, {
12010     render : function(renderTo){
12011         // this is one sweet looking template!
12012         var tpl = new Roo.Template(
12013             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
12014             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
12015             '<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>',
12016             "</tbody></table></td><td>",
12017             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
12018             '<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>',
12019             "</tbody></table></td></tr></table>"
12020         );
12021         var btn = tpl.append(renderTo, [this.text, this.type], true);
12022         var btnEl = btn.child("button");
12023         if(this.cls){
12024             btn.addClass(this.cls);
12025         }
12026         if(this.icon){
12027             btnEl.setStyle('background-image', 'url(' +this.icon +')');
12028         }
12029         if(this.iconCls){
12030             btnEl.addClass(this.iconCls);
12031             if(!this.cls){
12032                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
12033             }
12034         }
12035         this.el = btn;
12036         if(this.handleMouseEvents){
12037             btn.on("mouseover", this.onMouseOver, this);
12038             btn.on("mouseout", this.onMouseOut, this);
12039             btn.on("mousedown", this.onMouseDown, this);
12040             btn.on("mouseup", this.onMouseUp, this);
12041         }
12042         btn.on(this.clickEvent, this.onClick, this);
12043         if(this.tooltip){
12044             if(typeof this.tooltip == 'object'){
12045                 Roo.QuickTips.tips(Roo.apply({
12046                       target: btnEl.id
12047                 }, this.tooltip));
12048             } else {
12049                 btnEl.dom[this.tooltipType] = this.tooltip;
12050             }
12051         }
12052         if(this.arrowTooltip){
12053             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
12054         }
12055         if(this.hidden){
12056             this.hide();
12057         }
12058         if(this.disabled){
12059             this.disable();
12060         }
12061         if(this.pressed){
12062             this.el.addClass("x-btn-pressed");
12063         }
12064         if(Roo.isIE && !Roo.isIE7){
12065             this.autoWidth.defer(1, this);
12066         }else{
12067             this.autoWidth();
12068         }
12069         if(this.menu){
12070             this.menu.on("show", this.onMenuShow, this);
12071             this.menu.on("hide", this.onMenuHide, this);
12072         }
12073         this.fireEvent('render', this);
12074     },
12075
12076     // private
12077     autoWidth : function(){
12078         if(this.el){
12079             var tbl = this.el.child("table:first");
12080             var tbl2 = this.el.child("table:last");
12081             this.el.setWidth("auto");
12082             tbl.setWidth("auto");
12083             if(Roo.isIE7 && Roo.isStrict){
12084                 var ib = this.el.child('button:first');
12085                 if(ib && ib.getWidth() > 20){
12086                     ib.clip();
12087                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
12088                 }
12089             }
12090             if(this.minWidth){
12091                 if(this.hidden){
12092                     this.el.beginMeasure();
12093                 }
12094                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
12095                     tbl.setWidth(this.minWidth-tbl2.getWidth());
12096                 }
12097                 if(this.hidden){
12098                     this.el.endMeasure();
12099                 }
12100             }
12101             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
12102         } 
12103     },
12104     /**
12105      * Sets this button's click handler
12106      * @param {Function} handler The function to call when the button is clicked
12107      * @param {Object} scope (optional) Scope for the function passed above
12108      */
12109     setHandler : function(handler, scope){
12110         this.handler = handler;
12111         this.scope = scope;  
12112     },
12113     
12114     /**
12115      * Sets this button's arrow click handler
12116      * @param {Function} handler The function to call when the arrow is clicked
12117      * @param {Object} scope (optional) Scope for the function passed above
12118      */
12119     setArrowHandler : function(handler, scope){
12120         this.arrowHandler = handler;
12121         this.scope = scope;  
12122     },
12123     
12124     /**
12125      * Focus the button
12126      */
12127     focus : function(){
12128         if(this.el){
12129             this.el.child("button:first").focus();
12130         }
12131     },
12132
12133     // private
12134     onClick : function(e){
12135         e.preventDefault();
12136         if(!this.disabled){
12137             if(e.getTarget(".x-btn-menu-arrow-wrap")){
12138                 if(this.menu && !this.menu.isVisible()){
12139                     this.menu.show(this.el, this.menuAlign);
12140                 }
12141                 this.fireEvent("arrowclick", this, e);
12142                 if(this.arrowHandler){
12143                     this.arrowHandler.call(this.scope || this, this, e);
12144                 }
12145             }else{
12146                 this.fireEvent("click", this, e);
12147                 if(this.handler){
12148                     this.handler.call(this.scope || this, this, e);
12149                 }
12150             }
12151         }
12152     },
12153     // private
12154     onMouseDown : function(e){
12155         if(!this.disabled){
12156             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
12157         }
12158     },
12159     // private
12160     onMouseUp : function(e){
12161         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
12162     }   
12163 });
12164
12165
12166 // backwards compat
12167 Roo.MenuButton = Roo.SplitButton;/*
12168  * Based on:
12169  * Ext JS Library 1.1.1
12170  * Copyright(c) 2006-2007, Ext JS, LLC.
12171  *
12172  * Originally Released Under LGPL - original licence link has changed is not relivant.
12173  *
12174  * Fork - LGPL
12175  * <script type="text/javascript">
12176  */
12177
12178 /**
12179  * @class Roo.Toolbar
12180  * Basic Toolbar class.
12181  * @constructor
12182  * Creates a new Toolbar
12183  * @param {Object} config The config object
12184  */ 
12185 Roo.Toolbar = function(container, buttons, config)
12186 {
12187     /// old consturctor format still supported..
12188     if(container instanceof Array){ // omit the container for later rendering
12189         buttons = container;
12190         config = buttons;
12191         container = null;
12192     }
12193     if (typeof(container) == 'object' && container.xtype) {
12194         config = container;
12195         container = config.container;
12196         buttons = config.buttons; // not really - use items!!
12197     }
12198     var xitems = [];
12199     if (config && config.items) {
12200         xitems = config.items;
12201         delete config.items;
12202     }
12203     Roo.apply(this, config);
12204     this.buttons = buttons;
12205     
12206     if(container){
12207         this.render(container);
12208     }
12209     Roo.each(xitems, function(b) {
12210         this.add(b);
12211     }, this);
12212     
12213 };
12214
12215 Roo.Toolbar.prototype = {
12216     /**
12217      * @cfg {Roo.data.Store} items
12218      * array of button configs or elements to add
12219      */
12220     
12221     /**
12222      * @cfg {String/HTMLElement/Element} container
12223      * The id or element that will contain the toolbar
12224      */
12225     // private
12226     render : function(ct){
12227         this.el = Roo.get(ct);
12228         if(this.cls){
12229             this.el.addClass(this.cls);
12230         }
12231         // using a table allows for vertical alignment
12232         // 100% width is needed by Safari...
12233         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
12234         this.tr = this.el.child("tr", true);
12235         var autoId = 0;
12236         this.items = new Roo.util.MixedCollection(false, function(o){
12237             return o.id || ("item" + (++autoId));
12238         });
12239         if(this.buttons){
12240             this.add.apply(this, this.buttons);
12241             delete this.buttons;
12242         }
12243     },
12244
12245     /**
12246      * Adds element(s) to the toolbar -- this function takes a variable number of 
12247      * arguments of mixed type and adds them to the toolbar.
12248      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
12249      * <ul>
12250      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
12251      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
12252      * <li>Field: Any form field (equivalent to {@link #addField})</li>
12253      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
12254      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
12255      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
12256      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
12257      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
12258      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
12259      * </ul>
12260      * @param {Mixed} arg2
12261      * @param {Mixed} etc.
12262      */
12263     add : function(){
12264         var a = arguments, l = a.length;
12265         for(var i = 0; i < l; i++){
12266             this._add(a[i]);
12267         }
12268     },
12269     // private..
12270     _add : function(el) {
12271         
12272         if (el.xtype) {
12273             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
12274         }
12275         
12276         if (el.applyTo){ // some kind of form field
12277             return this.addField(el);
12278         } 
12279         if (el.render){ // some kind of Toolbar.Item
12280             return this.addItem(el);
12281         }
12282         if (typeof el == "string"){ // string
12283             if(el == "separator" || el == "-"){
12284                 return this.addSeparator();
12285             }
12286             if (el == " "){
12287                 return this.addSpacer();
12288             }
12289             if(el == "->"){
12290                 return this.addFill();
12291             }
12292             return this.addText(el);
12293             
12294         }
12295         if(el.tagName){ // element
12296             return this.addElement(el);
12297         }
12298         if(typeof el == "object"){ // must be button config?
12299             return this.addButton(el);
12300         }
12301         // and now what?!?!
12302         return false;
12303         
12304     },
12305     
12306     /**
12307      * Add an Xtype element
12308      * @param {Object} xtype Xtype Object
12309      * @return {Object} created Object
12310      */
12311     addxtype : function(e){
12312         return this.add(e);  
12313     },
12314     
12315     /**
12316      * Returns the Element for this toolbar.
12317      * @return {Roo.Element}
12318      */
12319     getEl : function(){
12320         return this.el;  
12321     },
12322     
12323     /**
12324      * Adds a separator
12325      * @return {Roo.Toolbar.Item} The separator item
12326      */
12327     addSeparator : function(){
12328         return this.addItem(new Roo.Toolbar.Separator());
12329     },
12330
12331     /**
12332      * Adds a spacer element
12333      * @return {Roo.Toolbar.Spacer} The spacer item
12334      */
12335     addSpacer : function(){
12336         return this.addItem(new Roo.Toolbar.Spacer());
12337     },
12338
12339     /**
12340      * Adds a fill element that forces subsequent additions to the right side of the toolbar
12341      * @return {Roo.Toolbar.Fill} The fill item
12342      */
12343     addFill : function(){
12344         return this.addItem(new Roo.Toolbar.Fill());
12345     },
12346
12347     /**
12348      * Adds any standard HTML element to the toolbar
12349      * @param {String/HTMLElement/Element} el The element or id of the element to add
12350      * @return {Roo.Toolbar.Item} The element's item
12351      */
12352     addElement : function(el){
12353         return this.addItem(new Roo.Toolbar.Item(el));
12354     },
12355     /**
12356      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
12357      * @type Roo.util.MixedCollection  
12358      */
12359     items : false,
12360      
12361     /**
12362      * Adds any Toolbar.Item or subclass
12363      * @param {Roo.Toolbar.Item} item
12364      * @return {Roo.Toolbar.Item} The item
12365      */
12366     addItem : function(item){
12367         var td = this.nextBlock();
12368         item.render(td);
12369         this.items.add(item);
12370         return item;
12371     },
12372     
12373     /**
12374      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
12375      * @param {Object/Array} config A button config or array of configs
12376      * @return {Roo.Toolbar.Button/Array}
12377      */
12378     addButton : function(config){
12379         if(config instanceof Array){
12380             var buttons = [];
12381             for(var i = 0, len = config.length; i < len; i++) {
12382                 buttons.push(this.addButton(config[i]));
12383             }
12384             return buttons;
12385         }
12386         var b = config;
12387         if(!(config instanceof Roo.Toolbar.Button)){
12388             b = config.split ?
12389                 new Roo.Toolbar.SplitButton(config) :
12390                 new Roo.Toolbar.Button(config);
12391         }
12392         var td = this.nextBlock();
12393         b.render(td);
12394         this.items.add(b);
12395         return b;
12396     },
12397     
12398     /**
12399      * Adds text to the toolbar
12400      * @param {String} text The text to add
12401      * @return {Roo.Toolbar.Item} The element's item
12402      */
12403     addText : function(text){
12404         return this.addItem(new Roo.Toolbar.TextItem(text));
12405     },
12406     
12407     /**
12408      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
12409      * @param {Number} index The index where the item is to be inserted
12410      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
12411      * @return {Roo.Toolbar.Button/Item}
12412      */
12413     insertButton : function(index, item){
12414         if(item instanceof Array){
12415             var buttons = [];
12416             for(var i = 0, len = item.length; i < len; i++) {
12417                buttons.push(this.insertButton(index + i, item[i]));
12418             }
12419             return buttons;
12420         }
12421         if (!(item instanceof Roo.Toolbar.Button)){
12422            item = new Roo.Toolbar.Button(item);
12423         }
12424         var td = document.createElement("td");
12425         this.tr.insertBefore(td, this.tr.childNodes[index]);
12426         item.render(td);
12427         this.items.insert(index, item);
12428         return item;
12429     },
12430     
12431     /**
12432      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
12433      * @param {Object} config
12434      * @return {Roo.Toolbar.Item} The element's item
12435      */
12436     addDom : function(config, returnEl){
12437         var td = this.nextBlock();
12438         Roo.DomHelper.overwrite(td, config);
12439         var ti = new Roo.Toolbar.Item(td.firstChild);
12440         ti.render(td);
12441         this.items.add(ti);
12442         return ti;
12443     },
12444
12445     /**
12446      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
12447      * @type Roo.util.MixedCollection  
12448      */
12449     fields : false,
12450     
12451     /**
12452      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc). Note: the field should not have
12453      * been rendered yet. For a field that has already been rendered, use {@link #addElement}.
12454      * @param {Roo.form.Field} field
12455      * @return {Roo.ToolbarItem}
12456      */
12457      
12458       
12459     addField : function(field) {
12460         if (!this.fields) {
12461             var autoId = 0;
12462             this.fields = new Roo.util.MixedCollection(false, function(o){
12463                 return o.id || ("item" + (++autoId));
12464             });
12465
12466         }
12467         
12468         var td = this.nextBlock();
12469         field.render(td);
12470         var ti = new Roo.Toolbar.Item(td.firstChild);
12471         ti.render(td);
12472         this.items.add(ti);
12473         this.fields.add(field);
12474         return ti;
12475     },
12476     /**
12477      * Hide the toolbar
12478      * @method hide
12479      */
12480      
12481       
12482     hide : function()
12483     {
12484         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
12485         this.el.child('div').hide();
12486     },
12487     /**
12488      * Show the toolbar
12489      * @method show
12490      */
12491     show : function()
12492     {
12493         this.el.child('div').show();
12494     },
12495       
12496     // private
12497     nextBlock : function(){
12498         var td = document.createElement("td");
12499         this.tr.appendChild(td);
12500         return td;
12501     },
12502
12503     // private
12504     destroy : function(){
12505         if(this.items){ // rendered?
12506             Roo.destroy.apply(Roo, this.items.items);
12507         }
12508         if(this.fields){ // rendered?
12509             Roo.destroy.apply(Roo, this.fields.items);
12510         }
12511         Roo.Element.uncache(this.el, this.tr);
12512     }
12513 };
12514
12515 /**
12516  * @class Roo.Toolbar.Item
12517  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
12518  * @constructor
12519  * Creates a new Item
12520  * @param {HTMLElement} el 
12521  */
12522 Roo.Toolbar.Item = function(el){
12523     this.el = Roo.getDom(el);
12524     this.id = Roo.id(this.el);
12525     this.hidden = false;
12526 };
12527
12528 Roo.Toolbar.Item.prototype = {
12529     
12530     /**
12531      * Get this item's HTML Element
12532      * @return {HTMLElement}
12533      */
12534     getEl : function(){
12535        return this.el;  
12536     },
12537
12538     // private
12539     render : function(td){
12540         this.td = td;
12541         td.appendChild(this.el);
12542     },
12543     
12544     /**
12545      * Removes and destroys this item.
12546      */
12547     destroy : function(){
12548         this.td.parentNode.removeChild(this.td);
12549     },
12550     
12551     /**
12552      * Shows this item.
12553      */
12554     show: function(){
12555         this.hidden = false;
12556         this.td.style.display = "";
12557     },
12558     
12559     /**
12560      * Hides this item.
12561      */
12562     hide: function(){
12563         this.hidden = true;
12564         this.td.style.display = "none";
12565     },
12566     
12567     /**
12568      * Convenience function for boolean show/hide.
12569      * @param {Boolean} visible true to show/false to hide
12570      */
12571     setVisible: function(visible){
12572         if(visible) {
12573             this.show();
12574         }else{
12575             this.hide();
12576         }
12577     },
12578     
12579     /**
12580      * Try to focus this item.
12581      */
12582     focus : function(){
12583         Roo.fly(this.el).focus();
12584     },
12585     
12586     /**
12587      * Disables this item.
12588      */
12589     disable : function(){
12590         Roo.fly(this.td).addClass("x-item-disabled");
12591         this.disabled = true;
12592         this.el.disabled = true;
12593     },
12594     
12595     /**
12596      * Enables this item.
12597      */
12598     enable : function(){
12599         Roo.fly(this.td).removeClass("x-item-disabled");
12600         this.disabled = false;
12601         this.el.disabled = false;
12602     }
12603 };
12604
12605
12606 /**
12607  * @class Roo.Toolbar.Separator
12608  * @extends Roo.Toolbar.Item
12609  * A simple toolbar separator class
12610  * @constructor
12611  * Creates a new Separator
12612  */
12613 Roo.Toolbar.Separator = function(){
12614     var s = document.createElement("span");
12615     s.className = "ytb-sep";
12616     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
12617 };
12618 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
12619     enable:Roo.emptyFn,
12620     disable:Roo.emptyFn,
12621     focus:Roo.emptyFn
12622 });
12623
12624 /**
12625  * @class Roo.Toolbar.Spacer
12626  * @extends Roo.Toolbar.Item
12627  * A simple element that adds extra horizontal space to a toolbar.
12628  * @constructor
12629  * Creates a new Spacer
12630  */
12631 Roo.Toolbar.Spacer = function(){
12632     var s = document.createElement("div");
12633     s.className = "ytb-spacer";
12634     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
12635 };
12636 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
12637     enable:Roo.emptyFn,
12638     disable:Roo.emptyFn,
12639     focus:Roo.emptyFn
12640 });
12641
12642 /**
12643  * @class Roo.Toolbar.Fill
12644  * @extends Roo.Toolbar.Spacer
12645  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
12646  * @constructor
12647  * Creates a new Spacer
12648  */
12649 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
12650     // private
12651     render : function(td){
12652         td.style.width = '100%';
12653         Roo.Toolbar.Fill.superclass.render.call(this, td);
12654     }
12655 });
12656
12657 /**
12658  * @class Roo.Toolbar.TextItem
12659  * @extends Roo.Toolbar.Item
12660  * A simple class that renders text directly into a toolbar.
12661  * @constructor
12662  * Creates a new TextItem
12663  * @param {String} text
12664  */
12665 Roo.Toolbar.TextItem = function(text){
12666     if (typeof(text) == 'object') {
12667         text = text.text;
12668     }
12669     var s = document.createElement("span");
12670     s.className = "ytb-text";
12671     s.innerHTML = text;
12672     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
12673 };
12674 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
12675     enable:Roo.emptyFn,
12676     disable:Roo.emptyFn,
12677     focus:Roo.emptyFn
12678 });
12679
12680 /**
12681  * @class Roo.Toolbar.Button
12682  * @extends Roo.Button
12683  * A button that renders into a toolbar.
12684  * @constructor
12685  * Creates a new Button
12686  * @param {Object} config A standard {@link Roo.Button} config object
12687  */
12688 Roo.Toolbar.Button = function(config){
12689     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
12690 };
12691 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
12692     render : function(td){
12693         this.td = td;
12694         Roo.Toolbar.Button.superclass.render.call(this, td);
12695     },
12696     
12697     /**
12698      * Removes and destroys this button
12699      */
12700     destroy : function(){
12701         Roo.Toolbar.Button.superclass.destroy.call(this);
12702         this.td.parentNode.removeChild(this.td);
12703     },
12704     
12705     /**
12706      * Shows this button
12707      */
12708     show: function(){
12709         this.hidden = false;
12710         this.td.style.display = "";
12711     },
12712     
12713     /**
12714      * Hides this button
12715      */
12716     hide: function(){
12717         this.hidden = true;
12718         this.td.style.display = "none";
12719     },
12720
12721     /**
12722      * Disables this item
12723      */
12724     disable : function(){
12725         Roo.fly(this.td).addClass("x-item-disabled");
12726         this.disabled = true;
12727     },
12728
12729     /**
12730      * Enables this item
12731      */
12732     enable : function(){
12733         Roo.fly(this.td).removeClass("x-item-disabled");
12734         this.disabled = false;
12735     }
12736 });
12737 // backwards compat
12738 Roo.ToolbarButton = Roo.Toolbar.Button;
12739
12740 /**
12741  * @class Roo.Toolbar.SplitButton
12742  * @extends Roo.SplitButton
12743  * A menu button that renders into a toolbar.
12744  * @constructor
12745  * Creates a new SplitButton
12746  * @param {Object} config A standard {@link Roo.SplitButton} config object
12747  */
12748 Roo.Toolbar.SplitButton = function(config){
12749     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
12750 };
12751 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
12752     render : function(td){
12753         this.td = td;
12754         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
12755     },
12756     
12757     /**
12758      * Removes and destroys this button
12759      */
12760     destroy : function(){
12761         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
12762         this.td.parentNode.removeChild(this.td);
12763     },
12764     
12765     /**
12766      * Shows this button
12767      */
12768     show: function(){
12769         this.hidden = false;
12770         this.td.style.display = "";
12771     },
12772     
12773     /**
12774      * Hides this button
12775      */
12776     hide: function(){
12777         this.hidden = true;
12778         this.td.style.display = "none";
12779     }
12780 });
12781
12782 // backwards compat
12783 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
12784  * Based on:
12785  * Ext JS Library 1.1.1
12786  * Copyright(c) 2006-2007, Ext JS, LLC.
12787  *
12788  * Originally Released Under LGPL - original licence link has changed is not relivant.
12789  *
12790  * Fork - LGPL
12791  * <script type="text/javascript">
12792  */
12793  
12794 /**
12795  * @class Roo.PagingToolbar
12796  * @extends Roo.Toolbar
12797  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
12798  * @constructor
12799  * Create a new PagingToolbar
12800  * @param {Object} config The config object
12801  */
12802 Roo.PagingToolbar = function(el, ds, config)
12803 {
12804     // old args format still supported... - xtype is prefered..
12805     if (typeof(el) == 'object' && el.xtype) {
12806         // created from xtype...
12807         config = el;
12808         ds = el.dataSource;
12809         el = config.container;
12810     }
12811     var items = [];
12812     if (config.items) {
12813         items = config.items;
12814         config.items = [];
12815     }
12816     
12817     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
12818     this.ds = ds;
12819     this.cursor = 0;
12820     this.renderButtons(this.el);
12821     this.bind(ds);
12822     
12823     // supprot items array.
12824    
12825     Roo.each(items, function(e) {
12826         this.add(Roo.factory(e));
12827     },this);
12828     
12829 };
12830
12831 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
12832     /**
12833      * @cfg {Roo.data.Store} dataSource
12834      * The underlying data store providing the paged data
12835      */
12836     /**
12837      * @cfg {String/HTMLElement/Element} container
12838      * container The id or element that will contain the toolbar
12839      */
12840     /**
12841      * @cfg {Boolean} displayInfo
12842      * True to display the displayMsg (defaults to false)
12843      */
12844     /**
12845      * @cfg {Number} pageSize
12846      * The number of records to display per page (defaults to 20)
12847      */
12848     pageSize: 20,
12849     /**
12850      * @cfg {String} displayMsg
12851      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
12852      */
12853     displayMsg : 'Displaying {0} - {1} of {2}',
12854     /**
12855      * @cfg {String} emptyMsg
12856      * The message to display when no records are found (defaults to "No data to display")
12857      */
12858     emptyMsg : 'No data to display',
12859     /**
12860      * Customizable piece of the default paging text (defaults to "Page")
12861      * @type String
12862      */
12863     beforePageText : "Page",
12864     /**
12865      * Customizable piece of the default paging text (defaults to "of %0")
12866      * @type String
12867      */
12868     afterPageText : "of {0}",
12869     /**
12870      * Customizable piece of the default paging text (defaults to "First Page")
12871      * @type String
12872      */
12873     firstText : "First Page",
12874     /**
12875      * Customizable piece of the default paging text (defaults to "Previous Page")
12876      * @type String
12877      */
12878     prevText : "Previous Page",
12879     /**
12880      * Customizable piece of the default paging text (defaults to "Next Page")
12881      * @type String
12882      */
12883     nextText : "Next Page",
12884     /**
12885      * Customizable piece of the default paging text (defaults to "Last Page")
12886      * @type String
12887      */
12888     lastText : "Last Page",
12889     /**
12890      * Customizable piece of the default paging text (defaults to "Refresh")
12891      * @type String
12892      */
12893     refreshText : "Refresh",
12894
12895     // private
12896     renderButtons : function(el){
12897         Roo.PagingToolbar.superclass.render.call(this, el);
12898         this.first = this.addButton({
12899             tooltip: this.firstText,
12900             cls: "x-btn-icon x-grid-page-first",
12901             disabled: true,
12902             handler: this.onClick.createDelegate(this, ["first"])
12903         });
12904         this.prev = this.addButton({
12905             tooltip: this.prevText,
12906             cls: "x-btn-icon x-grid-page-prev",
12907             disabled: true,
12908             handler: this.onClick.createDelegate(this, ["prev"])
12909         });
12910         //this.addSeparator();
12911         this.add(this.beforePageText);
12912         this.field = Roo.get(this.addDom({
12913            tag: "input",
12914            type: "text",
12915            size: "3",
12916            value: "1",
12917            cls: "x-grid-page-number"
12918         }).el);
12919         this.field.on("keydown", this.onPagingKeydown, this);
12920         this.field.on("focus", function(){this.dom.select();});
12921         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
12922         this.field.setHeight(18);
12923         //this.addSeparator();
12924         this.next = this.addButton({
12925             tooltip: this.nextText,
12926             cls: "x-btn-icon x-grid-page-next",
12927             disabled: true,
12928             handler: this.onClick.createDelegate(this, ["next"])
12929         });
12930         this.last = this.addButton({
12931             tooltip: this.lastText,
12932             cls: "x-btn-icon x-grid-page-last",
12933             disabled: true,
12934             handler: this.onClick.createDelegate(this, ["last"])
12935         });
12936         //this.addSeparator();
12937         this.loading = this.addButton({
12938             tooltip: this.refreshText,
12939             cls: "x-btn-icon x-grid-loading",
12940             handler: this.onClick.createDelegate(this, ["refresh"])
12941         });
12942
12943         if(this.displayInfo){
12944             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
12945         }
12946     },
12947
12948     // private
12949     updateInfo : function(){
12950         if(this.displayEl){
12951             var count = this.ds.getCount();
12952             var msg = count == 0 ?
12953                 this.emptyMsg :
12954                 String.format(
12955                     this.displayMsg,
12956                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
12957                 );
12958             this.displayEl.update(msg);
12959         }
12960     },
12961
12962     // private
12963     onLoad : function(ds, r, o){
12964        this.cursor = o.params ? o.params.start : 0;
12965        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
12966
12967        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
12968        this.field.dom.value = ap;
12969        this.first.setDisabled(ap == 1);
12970        this.prev.setDisabled(ap == 1);
12971        this.next.setDisabled(ap == ps);
12972        this.last.setDisabled(ap == ps);
12973        this.loading.enable();
12974        this.updateInfo();
12975     },
12976
12977     // private
12978     getPageData : function(){
12979         var total = this.ds.getTotalCount();
12980         return {
12981             total : total,
12982             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
12983             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
12984         };
12985     },
12986
12987     // private
12988     onLoadError : function(){
12989         this.loading.enable();
12990     },
12991
12992     // private
12993     onPagingKeydown : function(e){
12994         var k = e.getKey();
12995         var d = this.getPageData();
12996         if(k == e.RETURN){
12997             var v = this.field.dom.value, pageNum;
12998             if(!v || isNaN(pageNum = parseInt(v, 10))){
12999                 this.field.dom.value = d.activePage;
13000                 return;
13001             }
13002             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
13003             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13004             e.stopEvent();
13005         }
13006         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))
13007         {
13008           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
13009           this.field.dom.value = pageNum;
13010           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
13011           e.stopEvent();
13012         }
13013         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13014         {
13015           var v = this.field.dom.value, pageNum; 
13016           var increment = (e.shiftKey) ? 10 : 1;
13017           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13018             increment *= -1;
13019           if(!v || isNaN(pageNum = parseInt(v, 10))) {
13020             this.field.dom.value = d.activePage;
13021             return;
13022           }
13023           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
13024           {
13025             this.field.dom.value = parseInt(v, 10) + increment;
13026             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
13027             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13028           }
13029           e.stopEvent();
13030         }
13031     },
13032
13033     // private
13034     beforeLoad : function(){
13035         if(this.loading){
13036             this.loading.disable();
13037         }
13038     },
13039
13040     // private
13041     onClick : function(which){
13042         var ds = this.ds;
13043         switch(which){
13044             case "first":
13045                 ds.load({params:{start: 0, limit: this.pageSize}});
13046             break;
13047             case "prev":
13048                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
13049             break;
13050             case "next":
13051                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
13052             break;
13053             case "last":
13054                 var total = ds.getTotalCount();
13055                 var extra = total % this.pageSize;
13056                 var lastStart = extra ? (total - extra) : total-this.pageSize;
13057                 ds.load({params:{start: lastStart, limit: this.pageSize}});
13058             break;
13059             case "refresh":
13060                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
13061             break;
13062         }
13063     },
13064
13065     /**
13066      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
13067      * @param {Roo.data.Store} store The data store to unbind
13068      */
13069     unbind : function(ds){
13070         ds.un("beforeload", this.beforeLoad, this);
13071         ds.un("load", this.onLoad, this);
13072         ds.un("loadexception", this.onLoadError, this);
13073         ds.un("remove", this.updateInfo, this);
13074         ds.un("add", this.updateInfo, this);
13075         this.ds = undefined;
13076     },
13077
13078     /**
13079      * Binds the paging toolbar to the specified {@link Roo.data.Store}
13080      * @param {Roo.data.Store} store The data store to bind
13081      */
13082     bind : function(ds){
13083         ds.on("beforeload", this.beforeLoad, this);
13084         ds.on("load", this.onLoad, this);
13085         ds.on("loadexception", this.onLoadError, this);
13086         ds.on("remove", this.updateInfo, this);
13087         ds.on("add", this.updateInfo, this);
13088         this.ds = ds;
13089     }
13090 });/*
13091  * Based on:
13092  * Ext JS Library 1.1.1
13093  * Copyright(c) 2006-2007, Ext JS, LLC.
13094  *
13095  * Originally Released Under LGPL - original licence link has changed is not relivant.
13096  *
13097  * Fork - LGPL
13098  * <script type="text/javascript">
13099  */
13100
13101 /**
13102  * @class Roo.Resizable
13103  * @extends Roo.util.Observable
13104  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
13105  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
13106  * 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
13107  * the element will be wrapped for you automatically.</p>
13108  * <p>Here is the list of valid resize handles:</p>
13109  * <pre>
13110 Value   Description
13111 ------  -------------------
13112  'n'     north
13113  's'     south
13114  'e'     east
13115  'w'     west
13116  'nw'    northwest
13117  'sw'    southwest
13118  'se'    southeast
13119  'ne'    northeast
13120  'hd'    horizontal drag
13121  'all'   all
13122 </pre>
13123  * <p>Here's an example showing the creation of a typical Resizable:</p>
13124  * <pre><code>
13125 var resizer = new Roo.Resizable("element-id", {
13126     handles: 'all',
13127     minWidth: 200,
13128     minHeight: 100,
13129     maxWidth: 500,
13130     maxHeight: 400,
13131     pinned: true
13132 });
13133 resizer.on("resize", myHandler);
13134 </code></pre>
13135  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
13136  * resizer.east.setDisplayed(false);</p>
13137  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
13138  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
13139  * resize operation's new size (defaults to [0, 0])
13140  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
13141  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
13142  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
13143  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
13144  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
13145  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
13146  * @cfg {Number} width The width of the element in pixels (defaults to null)
13147  * @cfg {Number} height The height of the element in pixels (defaults to null)
13148  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
13149  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
13150  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
13151  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
13152  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
13153  * in favor of the handles config option (defaults to false)
13154  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
13155  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
13156  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
13157  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
13158  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
13159  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
13160  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
13161  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
13162  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
13163  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
13164  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
13165  * @constructor
13166  * Create a new resizable component
13167  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
13168  * @param {Object} config configuration options
13169   */
13170 Roo.Resizable = function(el, config)
13171 {
13172     this.el = Roo.get(el);
13173
13174     if(config && config.wrap){
13175         config.resizeChild = this.el;
13176         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
13177         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
13178         this.el.setStyle("overflow", "hidden");
13179         this.el.setPositioning(config.resizeChild.getPositioning());
13180         config.resizeChild.clearPositioning();
13181         if(!config.width || !config.height){
13182             var csize = config.resizeChild.getSize();
13183             this.el.setSize(csize.width, csize.height);
13184         }
13185         if(config.pinned && !config.adjustments){
13186             config.adjustments = "auto";
13187         }
13188     }
13189
13190     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
13191     this.proxy.unselectable();
13192     this.proxy.enableDisplayMode('block');
13193
13194     Roo.apply(this, config);
13195
13196     if(this.pinned){
13197         this.disableTrackOver = true;
13198         this.el.addClass("x-resizable-pinned");
13199     }
13200     // if the element isn't positioned, make it relative
13201     var position = this.el.getStyle("position");
13202     if(position != "absolute" && position != "fixed"){
13203         this.el.setStyle("position", "relative");
13204     }
13205     if(!this.handles){ // no handles passed, must be legacy style
13206         this.handles = 's,e,se';
13207         if(this.multiDirectional){
13208             this.handles += ',n,w';
13209         }
13210     }
13211     if(this.handles == "all"){
13212         this.handles = "n s e w ne nw se sw";
13213     }
13214     var hs = this.handles.split(/\s*?[,;]\s*?| /);
13215     var ps = Roo.Resizable.positions;
13216     for(var i = 0, len = hs.length; i < len; i++){
13217         if(hs[i] && ps[hs[i]]){
13218             var pos = ps[hs[i]];
13219             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
13220         }
13221     }
13222     // legacy
13223     this.corner = this.southeast;
13224     
13225     // updateBox = the box can move..
13226     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
13227         this.updateBox = true;
13228     }
13229
13230     this.activeHandle = null;
13231
13232     if(this.resizeChild){
13233         if(typeof this.resizeChild == "boolean"){
13234             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
13235         }else{
13236             this.resizeChild = Roo.get(this.resizeChild, true);
13237         }
13238     }
13239     
13240     if(this.adjustments == "auto"){
13241         var rc = this.resizeChild;
13242         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
13243         if(rc && (hw || hn)){
13244             rc.position("relative");
13245             rc.setLeft(hw ? hw.el.getWidth() : 0);
13246             rc.setTop(hn ? hn.el.getHeight() : 0);
13247         }
13248         this.adjustments = [
13249             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
13250             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
13251         ];
13252     }
13253
13254     if(this.draggable){
13255         this.dd = this.dynamic ?
13256             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
13257         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
13258     }
13259
13260     // public events
13261     this.addEvents({
13262         /**
13263          * @event beforeresize
13264          * Fired before resize is allowed. Set enabled to false to cancel resize.
13265          * @param {Roo.Resizable} this
13266          * @param {Roo.EventObject} e The mousedown event
13267          */
13268         "beforeresize" : true,
13269         /**
13270          * @event resize
13271          * Fired after a resize.
13272          * @param {Roo.Resizable} this
13273          * @param {Number} width The new width
13274          * @param {Number} height The new height
13275          * @param {Roo.EventObject} e The mouseup event
13276          */
13277         "resize" : true
13278     });
13279
13280     if(this.width !== null && this.height !== null){
13281         this.resizeTo(this.width, this.height);
13282     }else{
13283         this.updateChildSize();
13284     }
13285     if(Roo.isIE){
13286         this.el.dom.style.zoom = 1;
13287     }
13288     Roo.Resizable.superclass.constructor.call(this);
13289 };
13290
13291 Roo.extend(Roo.Resizable, Roo.util.Observable, {
13292         resizeChild : false,
13293         adjustments : [0, 0],
13294         minWidth : 5,
13295         minHeight : 5,
13296         maxWidth : 10000,
13297         maxHeight : 10000,
13298         enabled : true,
13299         animate : false,
13300         duration : .35,
13301         dynamic : false,
13302         handles : false,
13303         multiDirectional : false,
13304         disableTrackOver : false,
13305         easing : 'easeOutStrong',
13306         widthIncrement : 0,
13307         heightIncrement : 0,
13308         pinned : false,
13309         width : null,
13310         height : null,
13311         preserveRatio : false,
13312         transparent: false,
13313         minX: 0,
13314         minY: 0,
13315         draggable: false,
13316
13317         /**
13318          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
13319          */
13320         constrainTo: undefined,
13321         /**
13322          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
13323          */
13324         resizeRegion: undefined,
13325
13326
13327     /**
13328      * Perform a manual resize
13329      * @param {Number} width
13330      * @param {Number} height
13331      */
13332     resizeTo : function(width, height){
13333         this.el.setSize(width, height);
13334         this.updateChildSize();
13335         this.fireEvent("resize", this, width, height, null);
13336     },
13337
13338     // private
13339     startSizing : function(e, handle){
13340         this.fireEvent("beforeresize", this, e);
13341         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
13342
13343             if(!this.overlay){
13344                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
13345                 this.overlay.unselectable();
13346                 this.overlay.enableDisplayMode("block");
13347                 this.overlay.on("mousemove", this.onMouseMove, this);
13348                 this.overlay.on("mouseup", this.onMouseUp, this);
13349             }
13350             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
13351
13352             this.resizing = true;
13353             this.startBox = this.el.getBox();
13354             this.startPoint = e.getXY();
13355             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
13356                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
13357
13358             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13359             this.overlay.show();
13360
13361             if(this.constrainTo) {
13362                 var ct = Roo.get(this.constrainTo);
13363                 this.resizeRegion = ct.getRegion().adjust(
13364                     ct.getFrameWidth('t'),
13365                     ct.getFrameWidth('l'),
13366                     -ct.getFrameWidth('b'),
13367                     -ct.getFrameWidth('r')
13368                 );
13369             }
13370
13371             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
13372             this.proxy.show();
13373             this.proxy.setBox(this.startBox);
13374             if(!this.dynamic){
13375                 this.proxy.setStyle('visibility', 'visible');
13376             }
13377         }
13378     },
13379
13380     // private
13381     onMouseDown : function(handle, e){
13382         if(this.enabled){
13383             e.stopEvent();
13384             this.activeHandle = handle;
13385             this.startSizing(e, handle);
13386         }
13387     },
13388
13389     // private
13390     onMouseUp : function(e){
13391         var size = this.resizeElement();
13392         this.resizing = false;
13393         this.handleOut();
13394         this.overlay.hide();
13395         this.proxy.hide();
13396         this.fireEvent("resize", this, size.width, size.height, e);
13397     },
13398
13399     // private
13400     updateChildSize : function(){
13401         if(this.resizeChild){
13402             var el = this.el;
13403             var child = this.resizeChild;
13404             var adj = this.adjustments;
13405             if(el.dom.offsetWidth){
13406                 var b = el.getSize(true);
13407                 child.setSize(b.width+adj[0], b.height+adj[1]);
13408             }
13409             // Second call here for IE
13410             // The first call enables instant resizing and
13411             // the second call corrects scroll bars if they
13412             // exist
13413             if(Roo.isIE){
13414                 setTimeout(function(){
13415                     if(el.dom.offsetWidth){
13416                         var b = el.getSize(true);
13417                         child.setSize(b.width+adj[0], b.height+adj[1]);
13418                     }
13419                 }, 10);
13420             }
13421         }
13422     },
13423
13424     // private
13425     snap : function(value, inc, min){
13426         if(!inc || !value) return value;
13427         var newValue = value;
13428         var m = value % inc;
13429         if(m > 0){
13430             if(m > (inc/2)){
13431                 newValue = value + (inc-m);
13432             }else{
13433                 newValue = value - m;
13434             }
13435         }
13436         return Math.max(min, newValue);
13437     },
13438
13439     // private
13440     resizeElement : function(){
13441         var box = this.proxy.getBox();
13442         if(this.updateBox){
13443             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
13444         }else{
13445             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
13446         }
13447         this.updateChildSize();
13448         if(!this.dynamic){
13449             this.proxy.hide();
13450         }
13451         return box;
13452     },
13453
13454     // private
13455     constrain : function(v, diff, m, mx){
13456         if(v - diff < m){
13457             diff = v - m;
13458         }else if(v - diff > mx){
13459             diff = mx - v;
13460         }
13461         return diff;
13462     },
13463
13464     // private
13465     onMouseMove : function(e){
13466         if(this.enabled){
13467             try{// try catch so if something goes wrong the user doesn't get hung
13468
13469             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
13470                 return;
13471             }
13472
13473             //var curXY = this.startPoint;
13474             var curSize = this.curSize || this.startBox;
13475             var x = this.startBox.x, y = this.startBox.y;
13476             var ox = x, oy = y;
13477             var w = curSize.width, h = curSize.height;
13478             var ow = w, oh = h;
13479             var mw = this.minWidth, mh = this.minHeight;
13480             var mxw = this.maxWidth, mxh = this.maxHeight;
13481             var wi = this.widthIncrement;
13482             var hi = this.heightIncrement;
13483
13484             var eventXY = e.getXY();
13485             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
13486             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
13487
13488             var pos = this.activeHandle.position;
13489
13490             switch(pos){
13491                 case "east":
13492                     w += diffX;
13493                     w = Math.min(Math.max(mw, w), mxw);
13494                     break;
13495              
13496                 case "south":
13497                     h += diffY;
13498                     h = Math.min(Math.max(mh, h), mxh);
13499                     break;
13500                 case "southeast":
13501                     w += diffX;
13502                     h += diffY;
13503                     w = Math.min(Math.max(mw, w), mxw);
13504                     h = Math.min(Math.max(mh, h), mxh);
13505                     break;
13506                 case "north":
13507                     diffY = this.constrain(h, diffY, mh, mxh);
13508                     y += diffY;
13509                     h -= diffY;
13510                     break;
13511                 case "hdrag":
13512                     
13513                     if (wi) {
13514                         var adiffX = Math.abs(diffX);
13515                         var sub = (adiffX % wi); // how much 
13516                         if (sub > (wi/2)) { // far enough to snap
13517                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
13518                         } else {
13519                             // remove difference.. 
13520                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
13521                         }
13522                     }
13523                     x += diffX;
13524                     x = Math.max(this.minX, x);
13525                     break;
13526                 case "west":
13527                     diffX = this.constrain(w, diffX, mw, mxw);
13528                     x += diffX;
13529                     w -= diffX;
13530                     break;
13531                 case "northeast":
13532                     w += diffX;
13533                     w = Math.min(Math.max(mw, w), mxw);
13534                     diffY = this.constrain(h, diffY, mh, mxh);
13535                     y += diffY;
13536                     h -= diffY;
13537                     break;
13538                 case "northwest":
13539                     diffX = this.constrain(w, diffX, mw, mxw);
13540                     diffY = this.constrain(h, diffY, mh, mxh);
13541                     y += diffY;
13542                     h -= diffY;
13543                     x += diffX;
13544                     w -= diffX;
13545                     break;
13546                case "southwest":
13547                     diffX = this.constrain(w, diffX, mw, mxw);
13548                     h += diffY;
13549                     h = Math.min(Math.max(mh, h), mxh);
13550                     x += diffX;
13551                     w -= diffX;
13552                     break;
13553             }
13554
13555             var sw = this.snap(w, wi, mw);
13556             var sh = this.snap(h, hi, mh);
13557             if(sw != w || sh != h){
13558                 switch(pos){
13559                     case "northeast":
13560                         y -= sh - h;
13561                     break;
13562                     case "north":
13563                         y -= sh - h;
13564                         break;
13565                     case "southwest":
13566                         x -= sw - w;
13567                     break;
13568                     case "west":
13569                         x -= sw - w;
13570                         break;
13571                     case "northwest":
13572                         x -= sw - w;
13573                         y -= sh - h;
13574                     break;
13575                 }
13576                 w = sw;
13577                 h = sh;
13578             }
13579
13580             if(this.preserveRatio){
13581                 switch(pos){
13582                     case "southeast":
13583                     case "east":
13584                         h = oh * (w/ow);
13585                         h = Math.min(Math.max(mh, h), mxh);
13586                         w = ow * (h/oh);
13587                        break;
13588                     case "south":
13589                         w = ow * (h/oh);
13590                         w = Math.min(Math.max(mw, w), mxw);
13591                         h = oh * (w/ow);
13592                         break;
13593                     case "northeast":
13594                         w = ow * (h/oh);
13595                         w = Math.min(Math.max(mw, w), mxw);
13596                         h = oh * (w/ow);
13597                     break;
13598                     case "north":
13599                         var tw = w;
13600                         w = ow * (h/oh);
13601                         w = Math.min(Math.max(mw, w), mxw);
13602                         h = oh * (w/ow);
13603                         x += (tw - w) / 2;
13604                         break;
13605                     case "southwest":
13606                         h = oh * (w/ow);
13607                         h = Math.min(Math.max(mh, h), mxh);
13608                         var tw = w;
13609                         w = ow * (h/oh);
13610                         x += tw - w;
13611                         break;
13612                     case "west":
13613                         var th = h;
13614                         h = oh * (w/ow);
13615                         h = Math.min(Math.max(mh, h), mxh);
13616                         y += (th - h) / 2;
13617                         var tw = w;
13618                         w = ow * (h/oh);
13619                         x += tw - w;
13620                        break;
13621                     case "northwest":
13622                         var tw = w;
13623                         var th = h;
13624                         h = oh * (w/ow);
13625                         h = Math.min(Math.max(mh, h), mxh);
13626                         w = ow * (h/oh);
13627                         y += th - h;
13628                         x += tw - w;
13629                        break;
13630
13631                 }
13632             }
13633             if (pos == 'hdrag') {
13634                 w = ow;
13635             }
13636             this.proxy.setBounds(x, y, w, h);
13637             if(this.dynamic){
13638                 this.resizeElement();
13639             }
13640             }catch(e){}
13641         }
13642     },
13643
13644     // private
13645     handleOver : function(){
13646         if(this.enabled){
13647             this.el.addClass("x-resizable-over");
13648         }
13649     },
13650
13651     // private
13652     handleOut : function(){
13653         if(!this.resizing){
13654             this.el.removeClass("x-resizable-over");
13655         }
13656     },
13657
13658     /**
13659      * Returns the element this component is bound to.
13660      * @return {Roo.Element}
13661      */
13662     getEl : function(){
13663         return this.el;
13664     },
13665
13666     /**
13667      * Returns the resizeChild element (or null).
13668      * @return {Roo.Element}
13669      */
13670     getResizeChild : function(){
13671         return this.resizeChild;
13672     },
13673
13674     /**
13675      * Destroys this resizable. If the element was wrapped and
13676      * removeEl is not true then the element remains.
13677      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
13678      */
13679     destroy : function(removeEl){
13680         this.proxy.remove();
13681         if(this.overlay){
13682             this.overlay.removeAllListeners();
13683             this.overlay.remove();
13684         }
13685         var ps = Roo.Resizable.positions;
13686         for(var k in ps){
13687             if(typeof ps[k] != "function" && this[ps[k]]){
13688                 var h = this[ps[k]];
13689                 h.el.removeAllListeners();
13690                 h.el.remove();
13691             }
13692         }
13693         if(removeEl){
13694             this.el.update("");
13695             this.el.remove();
13696         }
13697     }
13698 });
13699
13700 // private
13701 // hash to map config positions to true positions
13702 Roo.Resizable.positions = {
13703     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
13704     hd: "hdrag"
13705 };
13706
13707 // private
13708 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
13709     if(!this.tpl){
13710         // only initialize the template if resizable is used
13711         var tpl = Roo.DomHelper.createTemplate(
13712             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
13713         );
13714         tpl.compile();
13715         Roo.Resizable.Handle.prototype.tpl = tpl;
13716     }
13717     this.position = pos;
13718     this.rz = rz;
13719     // show north drag fro topdra
13720     var handlepos = pos == 'hdrag' ? 'north' : pos;
13721     
13722     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
13723     if (pos == 'hdrag') {
13724         this.el.setStyle('cursor', 'pointer');
13725     }
13726     this.el.unselectable();
13727     if(transparent){
13728         this.el.setOpacity(0);
13729     }
13730     this.el.on("mousedown", this.onMouseDown, this);
13731     if(!disableTrackOver){
13732         this.el.on("mouseover", this.onMouseOver, this);
13733         this.el.on("mouseout", this.onMouseOut, this);
13734     }
13735 };
13736
13737 // private
13738 Roo.Resizable.Handle.prototype = {
13739     afterResize : function(rz){
13740         // do nothing
13741     },
13742     // private
13743     onMouseDown : function(e){
13744         this.rz.onMouseDown(this, e);
13745     },
13746     // private
13747     onMouseOver : function(e){
13748         this.rz.handleOver(this, e);
13749     },
13750     // private
13751     onMouseOut : function(e){
13752         this.rz.handleOut(this, e);
13753     }
13754 };/*
13755  * Based on:
13756  * Ext JS Library 1.1.1
13757  * Copyright(c) 2006-2007, Ext JS, LLC.
13758  *
13759  * Originally Released Under LGPL - original licence link has changed is not relivant.
13760  *
13761  * Fork - LGPL
13762  * <script type="text/javascript">
13763  */
13764
13765 /**
13766  * @class Roo.Editor
13767  * @extends Roo.Component
13768  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
13769  * @constructor
13770  * Create a new Editor
13771  * @param {Roo.form.Field} field The Field object (or descendant)
13772  * @param {Object} config The config object
13773  */
13774 Roo.Editor = function(field, config){
13775     Roo.Editor.superclass.constructor.call(this, config);
13776     this.field = field;
13777     this.addEvents({
13778         /**
13779              * @event beforestartedit
13780              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
13781              * false from the handler of this event.
13782              * @param {Editor} this
13783              * @param {Roo.Element} boundEl The underlying element bound to this editor
13784              * @param {Mixed} value The field value being set
13785              */
13786         "beforestartedit" : true,
13787         /**
13788              * @event startedit
13789              * Fires when this editor is displayed
13790              * @param {Roo.Element} boundEl The underlying element bound to this editor
13791              * @param {Mixed} value The starting field value
13792              */
13793         "startedit" : true,
13794         /**
13795              * @event beforecomplete
13796              * Fires after a change has been made to the field, but before the change is reflected in the underlying
13797              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
13798              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
13799              * event will not fire since no edit actually occurred.
13800              * @param {Editor} this
13801              * @param {Mixed} value The current field value
13802              * @param {Mixed} startValue The original field value
13803              */
13804         "beforecomplete" : true,
13805         /**
13806              * @event complete
13807              * Fires after editing is complete and any changed value has been written to the underlying field.
13808              * @param {Editor} this
13809              * @param {Mixed} value The current field value
13810              * @param {Mixed} startValue The original field value
13811              */
13812         "complete" : true,
13813         /**
13814          * @event specialkey
13815          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
13816          * {@link Roo.EventObject#getKey} to determine which key was pressed.
13817          * @param {Roo.form.Field} this
13818          * @param {Roo.EventObject} e The event object
13819          */
13820         "specialkey" : true
13821     });
13822 };
13823
13824 Roo.extend(Roo.Editor, Roo.Component, {
13825     /**
13826      * @cfg {Boolean/String} autosize
13827      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
13828      * or "height" to adopt the height only (defaults to false)
13829      */
13830     /**
13831      * @cfg {Boolean} revertInvalid
13832      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
13833      * validation fails (defaults to true)
13834      */
13835     /**
13836      * @cfg {Boolean} ignoreNoChange
13837      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
13838      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
13839      * will never be ignored.
13840      */
13841     /**
13842      * @cfg {Boolean} hideEl
13843      * False to keep the bound element visible while the editor is displayed (defaults to true)
13844      */
13845     /**
13846      * @cfg {Mixed} value
13847      * The data value of the underlying field (defaults to "")
13848      */
13849     value : "",
13850     /**
13851      * @cfg {String} alignment
13852      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
13853      */
13854     alignment: "c-c?",
13855     /**
13856      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
13857      * for bottom-right shadow (defaults to "frame")
13858      */
13859     shadow : "frame",
13860     /**
13861      * @cfg {Boolean} constrain True to constrain the editor to the viewport
13862      */
13863     constrain : false,
13864     /**
13865      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
13866      */
13867     completeOnEnter : false,
13868     /**
13869      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
13870      */
13871     cancelOnEsc : false,
13872     /**
13873      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
13874      */
13875     updateEl : false,
13876
13877     // private
13878     onRender : function(ct, position){
13879         this.el = new Roo.Layer({
13880             shadow: this.shadow,
13881             cls: "x-editor",
13882             parentEl : ct,
13883             shim : this.shim,
13884             shadowOffset:4,
13885             id: this.id,
13886             constrain: this.constrain
13887         });
13888         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
13889         if(this.field.msgTarget != 'title'){
13890             this.field.msgTarget = 'qtip';
13891         }
13892         this.field.render(this.el);
13893         if(Roo.isGecko){
13894             this.field.el.dom.setAttribute('autocomplete', 'off');
13895         }
13896         this.field.on("specialkey", this.onSpecialKey, this);
13897         if(this.swallowKeys){
13898             this.field.el.swallowEvent(['keydown','keypress']);
13899         }
13900         this.field.show();
13901         this.field.on("blur", this.onBlur, this);
13902         if(this.field.grow){
13903             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
13904         }
13905     },
13906
13907     onSpecialKey : function(field, e){
13908         //Roo.log('editor onSpecialKey');
13909         if(this.completeOnEnter && e.getKey() == e.ENTER){
13910             e.stopEvent();
13911             this.completeEdit();
13912         }else if(this.cancelOnEsc && e.getKey() == e.ESC){
13913             this.cancelEdit();
13914         }else{
13915             this.fireEvent('specialkey', field, e);
13916         }
13917     },
13918
13919     /**
13920      * Starts the editing process and shows the editor.
13921      * @param {String/HTMLElement/Element} el The element to edit
13922      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
13923       * to the innerHTML of el.
13924      */
13925     startEdit : function(el, value){
13926         if(this.editing){
13927             this.completeEdit();
13928         }
13929         this.boundEl = Roo.get(el);
13930         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
13931         if(!this.rendered){
13932             this.render(this.parentEl || document.body);
13933         }
13934         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
13935             return;
13936         }
13937         this.startValue = v;
13938         this.field.setValue(v);
13939         if(this.autoSize){
13940             var sz = this.boundEl.getSize();
13941             switch(this.autoSize){
13942                 case "width":
13943                 this.setSize(sz.width,  "");
13944                 break;
13945                 case "height":
13946                 this.setSize("",  sz.height);
13947                 break;
13948                 default:
13949                 this.setSize(sz.width,  sz.height);
13950             }
13951         }
13952         this.el.alignTo(this.boundEl, this.alignment);
13953         this.editing = true;
13954         if(Roo.QuickTips){
13955             Roo.QuickTips.disable();
13956         }
13957         this.show();
13958     },
13959
13960     /**
13961      * Sets the height and width of this editor.
13962      * @param {Number} width The new width
13963      * @param {Number} height The new height
13964      */
13965     setSize : function(w, h){
13966         this.field.setSize(w, h);
13967         if(this.el){
13968             this.el.sync();
13969         }
13970     },
13971
13972     /**
13973      * Realigns the editor to the bound field based on the current alignment config value.
13974      */
13975     realign : function(){
13976         this.el.alignTo(this.boundEl, this.alignment);
13977     },
13978
13979     /**
13980      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
13981      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
13982      */
13983     completeEdit : function(remainVisible){
13984         if(!this.editing){
13985             return;
13986         }
13987         var v = this.getValue();
13988         if(this.revertInvalid !== false && !this.field.isValid()){
13989             v = this.startValue;
13990             this.cancelEdit(true);
13991         }
13992         if(String(v) === String(this.startValue) && this.ignoreNoChange){
13993             this.editing = false;
13994             this.hide();
13995             return;
13996         }
13997         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
13998             this.editing = false;
13999             if(this.updateEl && this.boundEl){
14000                 this.boundEl.update(v);
14001             }
14002             if(remainVisible !== true){
14003                 this.hide();
14004             }
14005             this.fireEvent("complete", this, v, this.startValue);
14006         }
14007     },
14008
14009     // private
14010     onShow : function(){
14011         this.el.show();
14012         if(this.hideEl !== false){
14013             this.boundEl.hide();
14014         }
14015         this.field.show();
14016         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
14017             this.fixIEFocus = true;
14018             this.deferredFocus.defer(50, this);
14019         }else{
14020             this.field.focus();
14021         }
14022         this.fireEvent("startedit", this.boundEl, this.startValue);
14023     },
14024
14025     deferredFocus : function(){
14026         if(this.editing){
14027             this.field.focus();
14028         }
14029     },
14030
14031     /**
14032      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
14033      * reverted to the original starting value.
14034      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
14035      * cancel (defaults to false)
14036      */
14037     cancelEdit : function(remainVisible){
14038         if(this.editing){
14039             this.setValue(this.startValue);
14040             if(remainVisible !== true){
14041                 this.hide();
14042             }
14043         }
14044     },
14045
14046     // private
14047     onBlur : function(){
14048         if(this.allowBlur !== true && this.editing){
14049             this.completeEdit();
14050         }
14051     },
14052
14053     // private
14054     onHide : function(){
14055         if(this.editing){
14056             this.completeEdit();
14057             return;
14058         }
14059         this.field.blur();
14060         if(this.field.collapse){
14061             this.field.collapse();
14062         }
14063         this.el.hide();
14064         if(this.hideEl !== false){
14065             this.boundEl.show();
14066         }
14067         if(Roo.QuickTips){
14068             Roo.QuickTips.enable();
14069         }
14070     },
14071
14072     /**
14073      * Sets the data value of the editor
14074      * @param {Mixed} value Any valid value supported by the underlying field
14075      */
14076     setValue : function(v){
14077         this.field.setValue(v);
14078     },
14079
14080     /**
14081      * Gets the data value of the editor
14082      * @return {Mixed} The data value
14083      */
14084     getValue : function(){
14085         return this.field.getValue();
14086     }
14087 });/*
14088  * Based on:
14089  * Ext JS Library 1.1.1
14090  * Copyright(c) 2006-2007, Ext JS, LLC.
14091  *
14092  * Originally Released Under LGPL - original licence link has changed is not relivant.
14093  *
14094  * Fork - LGPL
14095  * <script type="text/javascript">
14096  */
14097  
14098 /**
14099  * @class Roo.BasicDialog
14100  * @extends Roo.util.Observable
14101  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
14102  * <pre><code>
14103 var dlg = new Roo.BasicDialog("my-dlg", {
14104     height: 200,
14105     width: 300,
14106     minHeight: 100,
14107     minWidth: 150,
14108     modal: true,
14109     proxyDrag: true,
14110     shadow: true
14111 });
14112 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
14113 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
14114 dlg.addButton('Cancel', dlg.hide, dlg);
14115 dlg.show();
14116 </code></pre>
14117   <b>A Dialog should always be a direct child of the body element.</b>
14118  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
14119  * @cfg {String} title Default text to display in the title bar (defaults to null)
14120  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14121  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14122  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
14123  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
14124  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
14125  * (defaults to null with no animation)
14126  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
14127  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
14128  * property for valid values (defaults to 'all')
14129  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
14130  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
14131  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
14132  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
14133  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
14134  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
14135  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
14136  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
14137  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
14138  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
14139  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
14140  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
14141  * draggable = true (defaults to false)
14142  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
14143  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14144  * shadow (defaults to false)
14145  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
14146  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
14147  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
14148  * @cfg {Array} buttons Array of buttons
14149  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
14150  * @constructor
14151  * Create a new BasicDialog.
14152  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
14153  * @param {Object} config Configuration options
14154  */
14155 Roo.BasicDialog = function(el, config){
14156     this.el = Roo.get(el);
14157     var dh = Roo.DomHelper;
14158     if(!this.el && config && config.autoCreate){
14159         if(typeof config.autoCreate == "object"){
14160             if(!config.autoCreate.id){
14161                 config.autoCreate.id = el;
14162             }
14163             this.el = dh.append(document.body,
14164                         config.autoCreate, true);
14165         }else{
14166             this.el = dh.append(document.body,
14167                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
14168         }
14169     }
14170     el = this.el;
14171     el.setDisplayed(true);
14172     el.hide = this.hideAction;
14173     this.id = el.id;
14174     el.addClass("x-dlg");
14175
14176     Roo.apply(this, config);
14177
14178     this.proxy = el.createProxy("x-dlg-proxy");
14179     this.proxy.hide = this.hideAction;
14180     this.proxy.setOpacity(.5);
14181     this.proxy.hide();
14182
14183     if(config.width){
14184         el.setWidth(config.width);
14185     }
14186     if(config.height){
14187         el.setHeight(config.height);
14188     }
14189     this.size = el.getSize();
14190     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
14191         this.xy = [config.x,config.y];
14192     }else{
14193         this.xy = el.getCenterXY(true);
14194     }
14195     /** The header element @type Roo.Element */
14196     this.header = el.child("> .x-dlg-hd");
14197     /** The body element @type Roo.Element */
14198     this.body = el.child("> .x-dlg-bd");
14199     /** The footer element @type Roo.Element */
14200     this.footer = el.child("> .x-dlg-ft");
14201
14202     if(!this.header){
14203         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
14204     }
14205     if(!this.body){
14206         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
14207     }
14208
14209     this.header.unselectable();
14210     if(this.title){
14211         this.header.update(this.title);
14212     }
14213     // this element allows the dialog to be focused for keyboard event
14214     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
14215     this.focusEl.swallowEvent("click", true);
14216
14217     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
14218
14219     // wrap the body and footer for special rendering
14220     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
14221     if(this.footer){
14222         this.bwrap.dom.appendChild(this.footer.dom);
14223     }
14224
14225     this.bg = this.el.createChild({
14226         tag: "div", cls:"x-dlg-bg",
14227         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
14228     });
14229     this.centerBg = this.bg.child("div.x-dlg-bg-center");
14230
14231
14232     if(this.autoScroll !== false && !this.autoTabs){
14233         this.body.setStyle("overflow", "auto");
14234     }
14235
14236     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
14237
14238     if(this.closable !== false){
14239         this.el.addClass("x-dlg-closable");
14240         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
14241         this.close.on("click", this.closeClick, this);
14242         this.close.addClassOnOver("x-dlg-close-over");
14243     }
14244     if(this.collapsible !== false){
14245         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
14246         this.collapseBtn.on("click", this.collapseClick, this);
14247         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
14248         this.header.on("dblclick", this.collapseClick, this);
14249     }
14250     if(this.resizable !== false){
14251         this.el.addClass("x-dlg-resizable");
14252         this.resizer = new Roo.Resizable(el, {
14253             minWidth: this.minWidth || 80,
14254             minHeight:this.minHeight || 80,
14255             handles: this.resizeHandles || "all",
14256             pinned: true
14257         });
14258         this.resizer.on("beforeresize", this.beforeResize, this);
14259         this.resizer.on("resize", this.onResize, this);
14260     }
14261     if(this.draggable !== false){
14262         el.addClass("x-dlg-draggable");
14263         if (!this.proxyDrag) {
14264             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
14265         }
14266         else {
14267             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
14268         }
14269         dd.setHandleElId(this.header.id);
14270         dd.endDrag = this.endMove.createDelegate(this);
14271         dd.startDrag = this.startMove.createDelegate(this);
14272         dd.onDrag = this.onDrag.createDelegate(this);
14273         dd.scroll = false;
14274         this.dd = dd;
14275     }
14276     if(this.modal){
14277         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
14278         this.mask.enableDisplayMode("block");
14279         this.mask.hide();
14280         this.el.addClass("x-dlg-modal");
14281     }
14282     if(this.shadow){
14283         this.shadow = new Roo.Shadow({
14284             mode : typeof this.shadow == "string" ? this.shadow : "sides",
14285             offset : this.shadowOffset
14286         });
14287     }else{
14288         this.shadowOffset = 0;
14289     }
14290     if(Roo.useShims && this.shim !== false){
14291         this.shim = this.el.createShim();
14292         this.shim.hide = this.hideAction;
14293         this.shim.hide();
14294     }else{
14295         this.shim = false;
14296     }
14297     if(this.autoTabs){
14298         this.initTabs();
14299     }
14300     if (this.buttons) { 
14301         var bts= this.buttons;
14302         this.buttons = [];
14303         Roo.each(bts, function(b) {
14304             this.addButton(b);
14305         }, this);
14306     }
14307     
14308     
14309     this.addEvents({
14310         /**
14311          * @event keydown
14312          * Fires when a key is pressed
14313          * @param {Roo.BasicDialog} this
14314          * @param {Roo.EventObject} e
14315          */
14316         "keydown" : true,
14317         /**
14318          * @event move
14319          * Fires when this dialog is moved by the user.
14320          * @param {Roo.BasicDialog} this
14321          * @param {Number} x The new page X
14322          * @param {Number} y The new page Y
14323          */
14324         "move" : true,
14325         /**
14326          * @event resize
14327          * Fires when this dialog is resized by the user.
14328          * @param {Roo.BasicDialog} this
14329          * @param {Number} width The new width
14330          * @param {Number} height The new height
14331          */
14332         "resize" : true,
14333         /**
14334          * @event beforehide
14335          * Fires before this dialog is hidden.
14336          * @param {Roo.BasicDialog} this
14337          */
14338         "beforehide" : true,
14339         /**
14340          * @event hide
14341          * Fires when this dialog is hidden.
14342          * @param {Roo.BasicDialog} this
14343          */
14344         "hide" : true,
14345         /**
14346          * @event beforeshow
14347          * Fires before this dialog is shown.
14348          * @param {Roo.BasicDialog} this
14349          */
14350         "beforeshow" : true,
14351         /**
14352          * @event show
14353          * Fires when this dialog is shown.
14354          * @param {Roo.BasicDialog} this
14355          */
14356         "show" : true
14357     });
14358     el.on("keydown", this.onKeyDown, this);
14359     el.on("mousedown", this.toFront, this);
14360     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
14361     this.el.hide();
14362     Roo.DialogManager.register(this);
14363     Roo.BasicDialog.superclass.constructor.call(this);
14364 };
14365
14366 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
14367     shadowOffset: Roo.isIE ? 6 : 5,
14368     minHeight: 80,
14369     minWidth: 200,
14370     minButtonWidth: 75,
14371     defaultButton: null,
14372     buttonAlign: "right",
14373     tabTag: 'div',
14374     firstShow: true,
14375
14376     /**
14377      * Sets the dialog title text
14378      * @param {String} text The title text to display
14379      * @return {Roo.BasicDialog} this
14380      */
14381     setTitle : function(text){
14382         this.header.update(text);
14383         return this;
14384     },
14385
14386     // private
14387     closeClick : function(){
14388         this.hide();
14389     },
14390
14391     // private
14392     collapseClick : function(){
14393         this[this.collapsed ? "expand" : "collapse"]();
14394     },
14395
14396     /**
14397      * Collapses the dialog to its minimized state (only the title bar is visible).
14398      * Equivalent to the user clicking the collapse dialog button.
14399      */
14400     collapse : function(){
14401         if(!this.collapsed){
14402             this.collapsed = true;
14403             this.el.addClass("x-dlg-collapsed");
14404             this.restoreHeight = this.el.getHeight();
14405             this.resizeTo(this.el.getWidth(), this.header.getHeight());
14406         }
14407     },
14408
14409     /**
14410      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
14411      * clicking the expand dialog button.
14412      */
14413     expand : function(){
14414         if(this.collapsed){
14415             this.collapsed = false;
14416             this.el.removeClass("x-dlg-collapsed");
14417             this.resizeTo(this.el.getWidth(), this.restoreHeight);
14418         }
14419     },
14420
14421     /**
14422      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
14423      * @return {Roo.TabPanel} The tabs component
14424      */
14425     initTabs : function(){
14426         var tabs = this.getTabs();
14427         while(tabs.getTab(0)){
14428             tabs.removeTab(0);
14429         }
14430         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
14431             var dom = el.dom;
14432             tabs.addTab(Roo.id(dom), dom.title);
14433             dom.title = "";
14434         });
14435         tabs.activate(0);
14436         return tabs;
14437     },
14438
14439     // private
14440     beforeResize : function(){
14441         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
14442     },
14443
14444     // private
14445     onResize : function(){
14446         this.refreshSize();
14447         this.syncBodyHeight();
14448         this.adjustAssets();
14449         this.focus();
14450         this.fireEvent("resize", this, this.size.width, this.size.height);
14451     },
14452
14453     // private
14454     onKeyDown : function(e){
14455         if(this.isVisible()){
14456             this.fireEvent("keydown", this, e);
14457         }
14458     },
14459
14460     /**
14461      * Resizes the dialog.
14462      * @param {Number} width
14463      * @param {Number} height
14464      * @return {Roo.BasicDialog} this
14465      */
14466     resizeTo : function(width, height){
14467         this.el.setSize(width, height);
14468         this.size = {width: width, height: height};
14469         this.syncBodyHeight();
14470         if(this.fixedcenter){
14471             this.center();
14472         }
14473         if(this.isVisible()){
14474             this.constrainXY();
14475             this.adjustAssets();
14476         }
14477         this.fireEvent("resize", this, width, height);
14478         return this;
14479     },
14480
14481
14482     /**
14483      * Resizes the dialog to fit the specified content size.
14484      * @param {Number} width
14485      * @param {Number} height
14486      * @return {Roo.BasicDialog} this
14487      */
14488     setContentSize : function(w, h){
14489         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
14490         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
14491         //if(!this.el.isBorderBox()){
14492             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
14493             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
14494         //}
14495         if(this.tabs){
14496             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
14497             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
14498         }
14499         this.resizeTo(w, h);
14500         return this;
14501     },
14502
14503     /**
14504      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
14505      * executed in response to a particular key being pressed while the dialog is active.
14506      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
14507      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14508      * @param {Function} fn The function to call
14509      * @param {Object} scope (optional) The scope of the function
14510      * @return {Roo.BasicDialog} this
14511      */
14512     addKeyListener : function(key, fn, scope){
14513         var keyCode, shift, ctrl, alt;
14514         if(typeof key == "object" && !(key instanceof Array)){
14515             keyCode = key["key"];
14516             shift = key["shift"];
14517             ctrl = key["ctrl"];
14518             alt = key["alt"];
14519         }else{
14520             keyCode = key;
14521         }
14522         var handler = function(dlg, e){
14523             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14524                 var k = e.getKey();
14525                 if(keyCode instanceof Array){
14526                     for(var i = 0, len = keyCode.length; i < len; i++){
14527                         if(keyCode[i] == k){
14528                           fn.call(scope || window, dlg, k, e);
14529                           return;
14530                         }
14531                     }
14532                 }else{
14533                     if(k == keyCode){
14534                         fn.call(scope || window, dlg, k, e);
14535                     }
14536                 }
14537             }
14538         };
14539         this.on("keydown", handler);
14540         return this;
14541     },
14542
14543     /**
14544      * Returns the TabPanel component (creates it if it doesn't exist).
14545      * Note: If you wish to simply check for the existence of tabs without creating them,
14546      * check for a null 'tabs' property.
14547      * @return {Roo.TabPanel} The tabs component
14548      */
14549     getTabs : function(){
14550         if(!this.tabs){
14551             this.el.addClass("x-dlg-auto-tabs");
14552             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
14553             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
14554         }
14555         return this.tabs;
14556     },
14557
14558     /**
14559      * Adds a button to the footer section of the dialog.
14560      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
14561      * object or a valid Roo.DomHelper element config
14562      * @param {Function} handler The function called when the button is clicked
14563      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
14564      * @return {Roo.Button} The new button
14565      */
14566     addButton : function(config, handler, scope){
14567         var dh = Roo.DomHelper;
14568         if(!this.footer){
14569             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
14570         }
14571         if(!this.btnContainer){
14572             var tb = this.footer.createChild({
14573
14574                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
14575                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
14576             }, null, true);
14577             this.btnContainer = tb.firstChild.firstChild.firstChild;
14578         }
14579         var bconfig = {
14580             handler: handler,
14581             scope: scope,
14582             minWidth: this.minButtonWidth,
14583             hideParent:true
14584         };
14585         if(typeof config == "string"){
14586             bconfig.text = config;
14587         }else{
14588             if(config.tag){
14589                 bconfig.dhconfig = config;
14590             }else{
14591                 Roo.apply(bconfig, config);
14592             }
14593         }
14594         var fc = false;
14595         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
14596             bconfig.position = Math.max(0, bconfig.position);
14597             fc = this.btnContainer.childNodes[bconfig.position];
14598         }
14599          
14600         var btn = new Roo.Button(
14601             fc ? 
14602                 this.btnContainer.insertBefore(document.createElement("td"),fc)
14603                 : this.btnContainer.appendChild(document.createElement("td")),
14604             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
14605             bconfig
14606         );
14607         this.syncBodyHeight();
14608         if(!this.buttons){
14609             /**
14610              * Array of all the buttons that have been added to this dialog via addButton
14611              * @type Array
14612              */
14613             this.buttons = [];
14614         }
14615         this.buttons.push(btn);
14616         return btn;
14617     },
14618
14619     /**
14620      * Sets the default button to be focused when the dialog is displayed.
14621      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
14622      * @return {Roo.BasicDialog} this
14623      */
14624     setDefaultButton : function(btn){
14625         this.defaultButton = btn;
14626         return this;
14627     },
14628
14629     // private
14630     getHeaderFooterHeight : function(safe){
14631         var height = 0;
14632         if(this.header){
14633            height += this.header.getHeight();
14634         }
14635         if(this.footer){
14636            var fm = this.footer.getMargins();
14637             height += (this.footer.getHeight()+fm.top+fm.bottom);
14638         }
14639         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
14640         height += this.centerBg.getPadding("tb");
14641         return height;
14642     },
14643
14644     // private
14645     syncBodyHeight : function(){
14646         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
14647         var height = this.size.height - this.getHeaderFooterHeight(false);
14648         bd.setHeight(height-bd.getMargins("tb"));
14649         var hh = this.header.getHeight();
14650         var h = this.size.height-hh;
14651         cb.setHeight(h);
14652         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
14653         bw.setHeight(h-cb.getPadding("tb"));
14654         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
14655         bd.setWidth(bw.getWidth(true));
14656         if(this.tabs){
14657             this.tabs.syncHeight();
14658             if(Roo.isIE){
14659                 this.tabs.el.repaint();
14660             }
14661         }
14662     },
14663
14664     /**
14665      * Restores the previous state of the dialog if Roo.state is configured.
14666      * @return {Roo.BasicDialog} this
14667      */
14668     restoreState : function(){
14669         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
14670         if(box && box.width){
14671             this.xy = [box.x, box.y];
14672             this.resizeTo(box.width, box.height);
14673         }
14674         return this;
14675     },
14676
14677     // private
14678     beforeShow : function(){
14679         this.expand();
14680         if(this.fixedcenter){
14681             this.xy = this.el.getCenterXY(true);
14682         }
14683         if(this.modal){
14684             Roo.get(document.body).addClass("x-body-masked");
14685             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14686             this.mask.show();
14687         }
14688         this.constrainXY();
14689     },
14690
14691     // private
14692     animShow : function(){
14693         var b = Roo.get(this.animateTarget).getBox();
14694         this.proxy.setSize(b.width, b.height);
14695         this.proxy.setLocation(b.x, b.y);
14696         this.proxy.show();
14697         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
14698                     true, .35, this.showEl.createDelegate(this));
14699     },
14700
14701     /**
14702      * Shows the dialog.
14703      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
14704      * @return {Roo.BasicDialog} this
14705      */
14706     show : function(animateTarget){
14707         if (this.fireEvent("beforeshow", this) === false){
14708             return;
14709         }
14710         if(this.syncHeightBeforeShow){
14711             this.syncBodyHeight();
14712         }else if(this.firstShow){
14713             this.firstShow = false;
14714             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
14715         }
14716         this.animateTarget = animateTarget || this.animateTarget;
14717         if(!this.el.isVisible()){
14718             this.beforeShow();
14719             if(this.animateTarget && Roo.get(this.animateTarget)){
14720                 this.animShow();
14721             }else{
14722                 this.showEl();
14723             }
14724         }
14725         return this;
14726     },
14727
14728     // private
14729     showEl : function(){
14730         this.proxy.hide();
14731         this.el.setXY(this.xy);
14732         this.el.show();
14733         this.adjustAssets(true);
14734         this.toFront();
14735         this.focus();
14736         // IE peekaboo bug - fix found by Dave Fenwick
14737         if(Roo.isIE){
14738             this.el.repaint();
14739         }
14740         this.fireEvent("show", this);
14741     },
14742
14743     /**
14744      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
14745      * dialog itself will receive focus.
14746      */
14747     focus : function(){
14748         if(this.defaultButton){
14749             this.defaultButton.focus();
14750         }else{
14751             this.focusEl.focus();
14752         }
14753     },
14754
14755     // private
14756     constrainXY : function(){
14757         if(this.constraintoviewport !== false){
14758             if(!this.viewSize){
14759                 if(this.container){
14760                     var s = this.container.getSize();
14761                     this.viewSize = [s.width, s.height];
14762                 }else{
14763                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
14764                 }
14765             }
14766             var s = Roo.get(this.container||document).getScroll();
14767
14768             var x = this.xy[0], y = this.xy[1];
14769             var w = this.size.width, h = this.size.height;
14770             var vw = this.viewSize[0], vh = this.viewSize[1];
14771             // only move it if it needs it
14772             var moved = false;
14773             // first validate right/bottom
14774             if(x + w > vw+s.left){
14775                 x = vw - w;
14776                 moved = true;
14777             }
14778             if(y + h > vh+s.top){
14779                 y = vh - h;
14780                 moved = true;
14781             }
14782             // then make sure top/left isn't negative
14783             if(x < s.left){
14784                 x = s.left;
14785                 moved = true;
14786             }
14787             if(y < s.top){
14788                 y = s.top;
14789                 moved = true;
14790             }
14791             if(moved){
14792                 // cache xy
14793                 this.xy = [x, y];
14794                 if(this.isVisible()){
14795                     this.el.setLocation(x, y);
14796                     this.adjustAssets();
14797                 }
14798             }
14799         }
14800     },
14801
14802     // private
14803     onDrag : function(){
14804         if(!this.proxyDrag){
14805             this.xy = this.el.getXY();
14806             this.adjustAssets();
14807         }
14808     },
14809
14810     // private
14811     adjustAssets : function(doShow){
14812         var x = this.xy[0], y = this.xy[1];
14813         var w = this.size.width, h = this.size.height;
14814         if(doShow === true){
14815             if(this.shadow){
14816                 this.shadow.show(this.el);
14817             }
14818             if(this.shim){
14819                 this.shim.show();
14820             }
14821         }
14822         if(this.shadow && this.shadow.isVisible()){
14823             this.shadow.show(this.el);
14824         }
14825         if(this.shim && this.shim.isVisible()){
14826             this.shim.setBounds(x, y, w, h);
14827         }
14828     },
14829
14830     // private
14831     adjustViewport : function(w, h){
14832         if(!w || !h){
14833             w = Roo.lib.Dom.getViewWidth();
14834             h = Roo.lib.Dom.getViewHeight();
14835         }
14836         // cache the size
14837         this.viewSize = [w, h];
14838         if(this.modal && this.mask.isVisible()){
14839             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
14840             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14841         }
14842         if(this.isVisible()){
14843             this.constrainXY();
14844         }
14845     },
14846
14847     /**
14848      * Destroys this dialog and all its supporting elements (including any tabs, shim,
14849      * shadow, proxy, mask, etc.)  Also removes all event listeners.
14850      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
14851      */
14852     destroy : function(removeEl){
14853         if(this.isVisible()){
14854             this.animateTarget = null;
14855             this.hide();
14856         }
14857         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
14858         if(this.tabs){
14859             this.tabs.destroy(removeEl);
14860         }
14861         Roo.destroy(
14862              this.shim,
14863              this.proxy,
14864              this.resizer,
14865              this.close,
14866              this.mask
14867         );
14868         if(this.dd){
14869             this.dd.unreg();
14870         }
14871         if(this.buttons){
14872            for(var i = 0, len = this.buttons.length; i < len; i++){
14873                this.buttons[i].destroy();
14874            }
14875         }
14876         this.el.removeAllListeners();
14877         if(removeEl === true){
14878             this.el.update("");
14879             this.el.remove();
14880         }
14881         Roo.DialogManager.unregister(this);
14882     },
14883
14884     // private
14885     startMove : function(){
14886         if(this.proxyDrag){
14887             this.proxy.show();
14888         }
14889         if(this.constraintoviewport !== false){
14890             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
14891         }
14892     },
14893
14894     // private
14895     endMove : function(){
14896         if(!this.proxyDrag){
14897             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
14898         }else{
14899             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
14900             this.proxy.hide();
14901         }
14902         this.refreshSize();
14903         this.adjustAssets();
14904         this.focus();
14905         this.fireEvent("move", this, this.xy[0], this.xy[1]);
14906     },
14907
14908     /**
14909      * Brings this dialog to the front of any other visible dialogs
14910      * @return {Roo.BasicDialog} this
14911      */
14912     toFront : function(){
14913         Roo.DialogManager.bringToFront(this);
14914         return this;
14915     },
14916
14917     /**
14918      * Sends this dialog to the back (under) of any other visible dialogs
14919      * @return {Roo.BasicDialog} this
14920      */
14921     toBack : function(){
14922         Roo.DialogManager.sendToBack(this);
14923         return this;
14924     },
14925
14926     /**
14927      * Centers this dialog in the viewport
14928      * @return {Roo.BasicDialog} this
14929      */
14930     center : function(){
14931         var xy = this.el.getCenterXY(true);
14932         this.moveTo(xy[0], xy[1]);
14933         return this;
14934     },
14935
14936     /**
14937      * Moves the dialog's top-left corner to the specified point
14938      * @param {Number} x
14939      * @param {Number} y
14940      * @return {Roo.BasicDialog} this
14941      */
14942     moveTo : function(x, y){
14943         this.xy = [x,y];
14944         if(this.isVisible()){
14945             this.el.setXY(this.xy);
14946             this.adjustAssets();
14947         }
14948         return this;
14949     },
14950
14951     /**
14952      * Aligns the dialog to the specified element
14953      * @param {String/HTMLElement/Roo.Element} element The element to align to.
14954      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
14955      * @param {Array} offsets (optional) Offset the positioning by [x, y]
14956      * @return {Roo.BasicDialog} this
14957      */
14958     alignTo : function(element, position, offsets){
14959         this.xy = this.el.getAlignToXY(element, position, offsets);
14960         if(this.isVisible()){
14961             this.el.setXY(this.xy);
14962             this.adjustAssets();
14963         }
14964         return this;
14965     },
14966
14967     /**
14968      * Anchors an element to another element and realigns it when the window is resized.
14969      * @param {String/HTMLElement/Roo.Element} element The element to align to.
14970      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
14971      * @param {Array} offsets (optional) Offset the positioning by [x, y]
14972      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
14973      * is a number, it is used as the buffer delay (defaults to 50ms).
14974      * @return {Roo.BasicDialog} this
14975      */
14976     anchorTo : function(el, alignment, offsets, monitorScroll){
14977         var action = function(){
14978             this.alignTo(el, alignment, offsets);
14979         };
14980         Roo.EventManager.onWindowResize(action, this);
14981         var tm = typeof monitorScroll;
14982         if(tm != 'undefined'){
14983             Roo.EventManager.on(window, 'scroll', action, this,
14984                 {buffer: tm == 'number' ? monitorScroll : 50});
14985         }
14986         action.call(this);
14987         return this;
14988     },
14989
14990     /**
14991      * Returns true if the dialog is visible
14992      * @return {Boolean}
14993      */
14994     isVisible : function(){
14995         return this.el.isVisible();
14996     },
14997
14998     // private
14999     animHide : function(callback){
15000         var b = Roo.get(this.animateTarget).getBox();
15001         this.proxy.show();
15002         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
15003         this.el.hide();
15004         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
15005                     this.hideEl.createDelegate(this, [callback]));
15006     },
15007
15008     /**
15009      * Hides the dialog.
15010      * @param {Function} callback (optional) Function to call when the dialog is hidden
15011      * @return {Roo.BasicDialog} this
15012      */
15013     hide : function(callback){
15014         if (this.fireEvent("beforehide", this) === false){
15015             return;
15016         }
15017         if(this.shadow){
15018             this.shadow.hide();
15019         }
15020         if(this.shim) {
15021           this.shim.hide();
15022         }
15023         // sometimes animateTarget seems to get set.. causing problems...
15024         // this just double checks..
15025         if(this.animateTarget && Roo.get(this.animateTarget)) {
15026            this.animHide(callback);
15027         }else{
15028             this.el.hide();
15029             this.hideEl(callback);
15030         }
15031         return this;
15032     },
15033
15034     // private
15035     hideEl : function(callback){
15036         this.proxy.hide();
15037         if(this.modal){
15038             this.mask.hide();
15039             Roo.get(document.body).removeClass("x-body-masked");
15040         }
15041         this.fireEvent("hide", this);
15042         if(typeof callback == "function"){
15043             callback();
15044         }
15045     },
15046
15047     // private
15048     hideAction : function(){
15049         this.setLeft("-10000px");
15050         this.setTop("-10000px");
15051         this.setStyle("visibility", "hidden");
15052     },
15053
15054     // private
15055     refreshSize : function(){
15056         this.size = this.el.getSize();
15057         this.xy = this.el.getXY();
15058         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
15059     },
15060
15061     // private
15062     // z-index is managed by the DialogManager and may be overwritten at any time
15063     setZIndex : function(index){
15064         if(this.modal){
15065             this.mask.setStyle("z-index", index);
15066         }
15067         if(this.shim){
15068             this.shim.setStyle("z-index", ++index);
15069         }
15070         if(this.shadow){
15071             this.shadow.setZIndex(++index);
15072         }
15073         this.el.setStyle("z-index", ++index);
15074         if(this.proxy){
15075             this.proxy.setStyle("z-index", ++index);
15076         }
15077         if(this.resizer){
15078             this.resizer.proxy.setStyle("z-index", ++index);
15079         }
15080
15081         this.lastZIndex = index;
15082     },
15083
15084     /**
15085      * Returns the element for this dialog
15086      * @return {Roo.Element} The underlying dialog Element
15087      */
15088     getEl : function(){
15089         return this.el;
15090     }
15091 });
15092
15093 /**
15094  * @class Roo.DialogManager
15095  * Provides global access to BasicDialogs that have been created and
15096  * support for z-indexing (layering) multiple open dialogs.
15097  */
15098 Roo.DialogManager = function(){
15099     var list = {};
15100     var accessList = [];
15101     var front = null;
15102
15103     // private
15104     var sortDialogs = function(d1, d2){
15105         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
15106     };
15107
15108     // private
15109     var orderDialogs = function(){
15110         accessList.sort(sortDialogs);
15111         var seed = Roo.DialogManager.zseed;
15112         for(var i = 0, len = accessList.length; i < len; i++){
15113             var dlg = accessList[i];
15114             if(dlg){
15115                 dlg.setZIndex(seed + (i*10));
15116             }
15117         }
15118     };
15119
15120     return {
15121         /**
15122          * The starting z-index for BasicDialogs (defaults to 9000)
15123          * @type Number The z-index value
15124          */
15125         zseed : 9000,
15126
15127         // private
15128         register : function(dlg){
15129             list[dlg.id] = dlg;
15130             accessList.push(dlg);
15131         },
15132
15133         // private
15134         unregister : function(dlg){
15135             delete list[dlg.id];
15136             var i=0;
15137             var len=0;
15138             if(!accessList.indexOf){
15139                 for(  i = 0, len = accessList.length; i < len; i++){
15140                     if(accessList[i] == dlg){
15141                         accessList.splice(i, 1);
15142                         return;
15143                     }
15144                 }
15145             }else{
15146                  i = accessList.indexOf(dlg);
15147                 if(i != -1){
15148                     accessList.splice(i, 1);
15149                 }
15150             }
15151         },
15152
15153         /**
15154          * Gets a registered dialog by id
15155          * @param {String/Object} id The id of the dialog or a dialog
15156          * @return {Roo.BasicDialog} this
15157          */
15158         get : function(id){
15159             return typeof id == "object" ? id : list[id];
15160         },
15161
15162         /**
15163          * Brings the specified dialog to the front
15164          * @param {String/Object} dlg The id of the dialog or a dialog
15165          * @return {Roo.BasicDialog} this
15166          */
15167         bringToFront : function(dlg){
15168             dlg = this.get(dlg);
15169             if(dlg != front){
15170                 front = dlg;
15171                 dlg._lastAccess = new Date().getTime();
15172                 orderDialogs();
15173             }
15174             return dlg;
15175         },
15176
15177         /**
15178          * Sends the specified dialog to the back
15179          * @param {String/Object} dlg The id of the dialog or a dialog
15180          * @return {Roo.BasicDialog} this
15181          */
15182         sendToBack : function(dlg){
15183             dlg = this.get(dlg);
15184             dlg._lastAccess = -(new Date().getTime());
15185             orderDialogs();
15186             return dlg;
15187         },
15188
15189         /**
15190          * Hides all dialogs
15191          */
15192         hideAll : function(){
15193             for(var id in list){
15194                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
15195                     list[id].hide();
15196                 }
15197             }
15198         }
15199     };
15200 }();
15201
15202 /**
15203  * @class Roo.LayoutDialog
15204  * @extends Roo.BasicDialog
15205  * Dialog which provides adjustments for working with a layout in a Dialog.
15206  * Add your necessary layout config options to the dialog's config.<br>
15207  * Example usage (including a nested layout):
15208  * <pre><code>
15209 if(!dialog){
15210     dialog = new Roo.LayoutDialog("download-dlg", {
15211         modal: true,
15212         width:600,
15213         height:450,
15214         shadow:true,
15215         minWidth:500,
15216         minHeight:350,
15217         autoTabs:true,
15218         proxyDrag:true,
15219         // layout config merges with the dialog config
15220         center:{
15221             tabPosition: "top",
15222             alwaysShowTabs: true
15223         }
15224     });
15225     dialog.addKeyListener(27, dialog.hide, dialog);
15226     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
15227     dialog.addButton("Build It!", this.getDownload, this);
15228
15229     // we can even add nested layouts
15230     var innerLayout = new Roo.BorderLayout("dl-inner", {
15231         east: {
15232             initialSize: 200,
15233             autoScroll:true,
15234             split:true
15235         },
15236         center: {
15237             autoScroll:true
15238         }
15239     });
15240     innerLayout.beginUpdate();
15241     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
15242     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
15243     innerLayout.endUpdate(true);
15244
15245     var layout = dialog.getLayout();
15246     layout.beginUpdate();
15247     layout.add("center", new Roo.ContentPanel("standard-panel",
15248                         {title: "Download the Source", fitToFrame:true}));
15249     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
15250                {title: "Build your own roo.js"}));
15251     layout.getRegion("center").showPanel(sp);
15252     layout.endUpdate();
15253 }
15254 </code></pre>
15255     * @constructor
15256     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
15257     * @param {Object} config configuration options
15258   */
15259 Roo.LayoutDialog = function(el, cfg){
15260     
15261     var config=  cfg;
15262     if (typeof(cfg) == 'undefined') {
15263         config = Roo.apply({}, el);
15264         // not sure why we use documentElement here.. - it should always be body.
15265         // IE7 borks horribly if we use documentElement.
15266         el = Roo.get( Roo.isIE ? (document.body || document.documentElement) : (document.documentElement || document.body) ).createChild();
15267         //config.autoCreate = true;
15268     }
15269     
15270     
15271     config.autoTabs = false;
15272     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
15273     this.body.setStyle({overflow:"hidden", position:"relative"});
15274     this.layout = new Roo.BorderLayout(this.body.dom, config);
15275     this.layout.monitorWindowResize = false;
15276     this.el.addClass("x-dlg-auto-layout");
15277     // fix case when center region overwrites center function
15278     this.center = Roo.BasicDialog.prototype.center;
15279     this.on("show", this.layout.layout, this.layout, true);
15280     if (config.items) {
15281         var xitems = config.items;
15282         delete config.items;
15283         Roo.each(xitems, this.addxtype, this);
15284     }
15285     
15286     
15287 };
15288 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
15289     /**
15290      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
15291      * @deprecated
15292      */
15293     endUpdate : function(){
15294         this.layout.endUpdate();
15295     },
15296
15297     /**
15298      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
15299      *  @deprecated
15300      */
15301     beginUpdate : function(){
15302         this.layout.beginUpdate();
15303     },
15304
15305     /**
15306      * Get the BorderLayout for this dialog
15307      * @return {Roo.BorderLayout}
15308      */
15309     getLayout : function(){
15310         return this.layout;
15311     },
15312
15313     showEl : function(){
15314         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
15315         if(Roo.isIE7){
15316             this.layout.layout();
15317         }
15318     },
15319
15320     // private
15321     // Use the syncHeightBeforeShow config option to control this automatically
15322     syncBodyHeight : function(){
15323         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
15324         if(this.layout){this.layout.layout();}
15325     },
15326     
15327       /**
15328      * Add an xtype element (actually adds to the layout.)
15329      * @return {Object} xdata xtype object data.
15330      */
15331     
15332     addxtype : function(c) {
15333         return this.layout.addxtype(c);
15334     }
15335 });/*
15336  * Based on:
15337  * Ext JS Library 1.1.1
15338  * Copyright(c) 2006-2007, Ext JS, LLC.
15339  *
15340  * Originally Released Under LGPL - original licence link has changed is not relivant.
15341  *
15342  * Fork - LGPL
15343  * <script type="text/javascript">
15344  */
15345  
15346 /**
15347  * @class Roo.MessageBox
15348  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
15349  * Example usage:
15350  *<pre><code>
15351 // Basic alert:
15352 Roo.Msg.alert('Status', 'Changes saved successfully.');
15353
15354 // Prompt for user data:
15355 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
15356     if (btn == 'ok'){
15357         // process text value...
15358     }
15359 });
15360
15361 // Show a dialog using config options:
15362 Roo.Msg.show({
15363    title:'Save Changes?',
15364    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
15365    buttons: Roo.Msg.YESNOCANCEL,
15366    fn: processResult,
15367    animEl: 'elId'
15368 });
15369 </code></pre>
15370  * @singleton
15371  */
15372 Roo.MessageBox = function(){
15373     var dlg, opt, mask, waitTimer;
15374     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
15375     var buttons, activeTextEl, bwidth;
15376
15377     // private
15378     var handleButton = function(button){
15379         dlg.hide();
15380         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
15381     };
15382
15383     // private
15384     var handleHide = function(){
15385         if(opt && opt.cls){
15386             dlg.el.removeClass(opt.cls);
15387         }
15388         if(waitTimer){
15389             Roo.TaskMgr.stop(waitTimer);
15390             waitTimer = null;
15391         }
15392     };
15393
15394     // private
15395     var updateButtons = function(b){
15396         var width = 0;
15397         if(!b){
15398             buttons["ok"].hide();
15399             buttons["cancel"].hide();
15400             buttons["yes"].hide();
15401             buttons["no"].hide();
15402             dlg.footer.dom.style.display = 'none';
15403             return width;
15404         }
15405         dlg.footer.dom.style.display = '';
15406         for(var k in buttons){
15407             if(typeof buttons[k] != "function"){
15408                 if(b[k]){
15409                     buttons[k].show();
15410                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
15411                     width += buttons[k].el.getWidth()+15;
15412                 }else{
15413                     buttons[k].hide();
15414                 }
15415             }
15416         }
15417         return width;
15418     };
15419
15420     // private
15421     var handleEsc = function(d, k, e){
15422         if(opt && opt.closable !== false){
15423             dlg.hide();
15424         }
15425         if(e){
15426             e.stopEvent();
15427         }
15428     };
15429
15430     return {
15431         /**
15432          * Returns a reference to the underlying {@link Roo.BasicDialog} element
15433          * @return {Roo.BasicDialog} The BasicDialog element
15434          */
15435         getDialog : function(){
15436            if(!dlg){
15437                 dlg = new Roo.BasicDialog("x-msg-box", {
15438                     autoCreate : true,
15439                     shadow: true,
15440                     draggable: true,
15441                     resizable:false,
15442                     constraintoviewport:false,
15443                     fixedcenter:true,
15444                     collapsible : false,
15445                     shim:true,
15446                     modal: true,
15447                     width:400, height:100,
15448                     buttonAlign:"center",
15449                     closeClick : function(){
15450                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
15451                             handleButton("no");
15452                         }else{
15453                             handleButton("cancel");
15454                         }
15455                     }
15456                 });
15457                 dlg.on("hide", handleHide);
15458                 mask = dlg.mask;
15459                 dlg.addKeyListener(27, handleEsc);
15460                 buttons = {};
15461                 var bt = this.buttonText;
15462                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
15463                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
15464                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
15465                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
15466                 bodyEl = dlg.body.createChild({
15467
15468                     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>'
15469                 });
15470                 msgEl = bodyEl.dom.firstChild;
15471                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
15472                 textboxEl.enableDisplayMode();
15473                 textboxEl.addKeyListener([10,13], function(){
15474                     if(dlg.isVisible() && opt && opt.buttons){
15475                         if(opt.buttons.ok){
15476                             handleButton("ok");
15477                         }else if(opt.buttons.yes){
15478                             handleButton("yes");
15479                         }
15480                     }
15481                 });
15482                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
15483                 textareaEl.enableDisplayMode();
15484                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
15485                 progressEl.enableDisplayMode();
15486                 var pf = progressEl.dom.firstChild;
15487                 if (pf) {
15488                     pp = Roo.get(pf.firstChild);
15489                     pp.setHeight(pf.offsetHeight);
15490                 }
15491                 
15492             }
15493             return dlg;
15494         },
15495
15496         /**
15497          * Updates the message box body text
15498          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
15499          * the XHTML-compliant non-breaking space character '&amp;#160;')
15500          * @return {Roo.MessageBox} This message box
15501          */
15502         updateText : function(text){
15503             if(!dlg.isVisible() && !opt.width){
15504                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
15505             }
15506             msgEl.innerHTML = text || '&#160;';
15507             var w = Math.max(Math.min(opt.width || msgEl.offsetWidth, this.maxWidth), 
15508                         Math.max(opt.minWidth || this.minWidth, bwidth));
15509             if(opt.prompt){
15510                 activeTextEl.setWidth(w);
15511             }
15512             if(dlg.isVisible()){
15513                 dlg.fixedcenter = false;
15514             }
15515             dlg.setContentSize(w, bodyEl.getHeight());
15516             if(dlg.isVisible()){
15517                 dlg.fixedcenter = true;
15518             }
15519             return this;
15520         },
15521
15522         /**
15523          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
15524          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
15525          * @param {Number} value Any number between 0 and 1 (e.g., .5)
15526          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
15527          * @return {Roo.MessageBox} This message box
15528          */
15529         updateProgress : function(value, text){
15530             if(text){
15531                 this.updateText(text);
15532             }
15533             if (pp) { // weird bug on my firefox - for some reason this is not defined
15534                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
15535             }
15536             return this;
15537         },        
15538
15539         /**
15540          * Returns true if the message box is currently displayed
15541          * @return {Boolean} True if the message box is visible, else false
15542          */
15543         isVisible : function(){
15544             return dlg && dlg.isVisible();  
15545         },
15546
15547         /**
15548          * Hides the message box if it is displayed
15549          */
15550         hide : function(){
15551             if(this.isVisible()){
15552                 dlg.hide();
15553             }  
15554         },
15555
15556         /**
15557          * Displays a new message box, or reinitializes an existing message box, based on the config options
15558          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
15559          * The following config object properties are supported:
15560          * <pre>
15561 Property    Type             Description
15562 ----------  ---------------  ------------------------------------------------------------------------------------
15563 animEl            String/Element   An id or Element from which the message box should animate as it opens and
15564                                    closes (defaults to undefined)
15565 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
15566                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
15567 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
15568                                    progress and wait dialogs will ignore this property and always hide the
15569                                    close button as they can only be closed programmatically.
15570 cls               String           A custom CSS class to apply to the message box element
15571 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
15572                                    displayed (defaults to 75)
15573 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
15574                                    function will be btn (the name of the button that was clicked, if applicable,
15575                                    e.g. "ok"), and text (the value of the active text field, if applicable).
15576                                    Progress and wait dialogs will ignore this option since they do not respond to
15577                                    user actions and can only be closed programmatically, so any required function
15578                                    should be called by the same code after it closes the dialog.
15579 icon              String           A CSS class that provides a background image to be used as an icon for
15580                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
15581 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
15582 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
15583 modal             Boolean          False to allow user interaction with the page while the message box is
15584                                    displayed (defaults to true)
15585 msg               String           A string that will replace the existing message box body text (defaults
15586                                    to the XHTML-compliant non-breaking space character '&#160;')
15587 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
15588 progress          Boolean          True to display a progress bar (defaults to false)
15589 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
15590 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
15591 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
15592 title             String           The title text
15593 value             String           The string value to set into the active textbox element if displayed
15594 wait              Boolean          True to display a progress bar (defaults to false)
15595 width             Number           The width of the dialog in pixels
15596 </pre>
15597          *
15598          * Example usage:
15599          * <pre><code>
15600 Roo.Msg.show({
15601    title: 'Address',
15602    msg: 'Please enter your address:',
15603    width: 300,
15604    buttons: Roo.MessageBox.OKCANCEL,
15605    multiline: true,
15606    fn: saveAddress,
15607    animEl: 'addAddressBtn'
15608 });
15609 </code></pre>
15610          * @param {Object} config Configuration options
15611          * @return {Roo.MessageBox} This message box
15612          */
15613         show : function(options){
15614             if(this.isVisible()){
15615                 this.hide();
15616             }
15617             var d = this.getDialog();
15618             opt = options;
15619             d.setTitle(opt.title || "&#160;");
15620             d.close.setDisplayed(opt.closable !== false);
15621             activeTextEl = textboxEl;
15622             opt.prompt = opt.prompt || (opt.multiline ? true : false);
15623             if(opt.prompt){
15624                 if(opt.multiline){
15625                     textboxEl.hide();
15626                     textareaEl.show();
15627                     textareaEl.setHeight(typeof opt.multiline == "number" ?
15628                         opt.multiline : this.defaultTextHeight);
15629                     activeTextEl = textareaEl;
15630                 }else{
15631                     textboxEl.show();
15632                     textareaEl.hide();
15633                 }
15634             }else{
15635                 textboxEl.hide();
15636                 textareaEl.hide();
15637             }
15638             progressEl.setDisplayed(opt.progress === true);
15639             this.updateProgress(0);
15640             activeTextEl.dom.value = opt.value || "";
15641             if(opt.prompt){
15642                 dlg.setDefaultButton(activeTextEl);
15643             }else{
15644                 var bs = opt.buttons;
15645                 var db = null;
15646                 if(bs && bs.ok){
15647                     db = buttons["ok"];
15648                 }else if(bs && bs.yes){
15649                     db = buttons["yes"];
15650                 }
15651                 dlg.setDefaultButton(db);
15652             }
15653             bwidth = updateButtons(opt.buttons);
15654             this.updateText(opt.msg);
15655             if(opt.cls){
15656                 d.el.addClass(opt.cls);
15657             }
15658             d.proxyDrag = opt.proxyDrag === true;
15659             d.modal = opt.modal !== false;
15660             d.mask = opt.modal !== false ? mask : false;
15661             if(!d.isVisible()){
15662                 // force it to the end of the z-index stack so it gets a cursor in FF
15663                 document.body.appendChild(dlg.el.dom);
15664                 d.animateTarget = null;
15665                 d.show(options.animEl);
15666             }
15667             return this;
15668         },
15669
15670         /**
15671          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
15672          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
15673          * and closing the message box when the process is complete.
15674          * @param {String} title The title bar text
15675          * @param {String} msg The message box body text
15676          * @return {Roo.MessageBox} This message box
15677          */
15678         progress : function(title, msg){
15679             this.show({
15680                 title : title,
15681                 msg : msg,
15682                 buttons: false,
15683                 progress:true,
15684                 closable:false,
15685                 minWidth: this.minProgressWidth,
15686                 modal : true
15687             });
15688             return this;
15689         },
15690
15691         /**
15692          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
15693          * If a callback function is passed it will be called after the user clicks the button, and the
15694          * id of the button that was clicked will be passed as the only parameter to the callback
15695          * (could also be the top-right close button).
15696          * @param {String} title The title bar text
15697          * @param {String} msg The message box body text
15698          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15699          * @param {Object} scope (optional) The scope of the callback function
15700          * @return {Roo.MessageBox} This message box
15701          */
15702         alert : function(title, msg, fn, scope){
15703             this.show({
15704                 title : title,
15705                 msg : msg,
15706                 buttons: this.OK,
15707                 fn: fn,
15708                 scope : scope,
15709                 modal : true
15710             });
15711             return this;
15712         },
15713
15714         /**
15715          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
15716          * interaction while waiting for a long-running process to complete that does not have defined intervals.
15717          * You are responsible for closing the message box when the process is complete.
15718          * @param {String} msg The message box body text
15719          * @param {String} title (optional) The title bar text
15720          * @return {Roo.MessageBox} This message box
15721          */
15722         wait : function(msg, title){
15723             this.show({
15724                 title : title,
15725                 msg : msg,
15726                 buttons: false,
15727                 closable:false,
15728                 progress:true,
15729                 modal:true,
15730                 width:300,
15731                 wait:true
15732             });
15733             waitTimer = Roo.TaskMgr.start({
15734                 run: function(i){
15735                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
15736                 },
15737                 interval: 1000
15738             });
15739             return this;
15740         },
15741
15742         /**
15743          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
15744          * If a callback function is passed it will be called after the user clicks either button, and the id of the
15745          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
15746          * @param {String} title The title bar text
15747          * @param {String} msg The message box body text
15748          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15749          * @param {Object} scope (optional) The scope of the callback function
15750          * @return {Roo.MessageBox} This message box
15751          */
15752         confirm : function(title, msg, fn, scope){
15753             this.show({
15754                 title : title,
15755                 msg : msg,
15756                 buttons: this.YESNO,
15757                 fn: fn,
15758                 scope : scope,
15759                 modal : true
15760             });
15761             return this;
15762         },
15763
15764         /**
15765          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
15766          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
15767          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
15768          * (could also be the top-right close button) and the text that was entered will be passed as the two
15769          * parameters to the callback.
15770          * @param {String} title The title bar text
15771          * @param {String} msg The message box body text
15772          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15773          * @param {Object} scope (optional) The scope of the callback function
15774          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
15775          * property, or the height in pixels to create the textbox (defaults to false / single-line)
15776          * @return {Roo.MessageBox} This message box
15777          */
15778         prompt : function(title, msg, fn, scope, multiline){
15779             this.show({
15780                 title : title,
15781                 msg : msg,
15782                 buttons: this.OKCANCEL,
15783                 fn: fn,
15784                 minWidth:250,
15785                 scope : scope,
15786                 prompt:true,
15787                 multiline: multiline,
15788                 modal : true
15789             });
15790             return this;
15791         },
15792
15793         /**
15794          * Button config that displays a single OK button
15795          * @type Object
15796          */
15797         OK : {ok:true},
15798         /**
15799          * Button config that displays Yes and No buttons
15800          * @type Object
15801          */
15802         YESNO : {yes:true, no:true},
15803         /**
15804          * Button config that displays OK and Cancel buttons
15805          * @type Object
15806          */
15807         OKCANCEL : {ok:true, cancel:true},
15808         /**
15809          * Button config that displays Yes, No and Cancel buttons
15810          * @type Object
15811          */
15812         YESNOCANCEL : {yes:true, no:true, cancel:true},
15813
15814         /**
15815          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
15816          * @type Number
15817          */
15818         defaultTextHeight : 75,
15819         /**
15820          * The maximum width in pixels of the message box (defaults to 600)
15821          * @type Number
15822          */
15823         maxWidth : 600,
15824         /**
15825          * The minimum width in pixels of the message box (defaults to 100)
15826          * @type Number
15827          */
15828         minWidth : 100,
15829         /**
15830          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
15831          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
15832          * @type Number
15833          */
15834         minProgressWidth : 250,
15835         /**
15836          * An object containing the default button text strings that can be overriden for localized language support.
15837          * Supported properties are: ok, cancel, yes and no.
15838          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
15839          * @type Object
15840          */
15841         buttonText : {
15842             ok : "OK",
15843             cancel : "Cancel",
15844             yes : "Yes",
15845             no : "No"
15846         }
15847     };
15848 }();
15849
15850 /**
15851  * Shorthand for {@link Roo.MessageBox}
15852  */
15853 Roo.Msg = Roo.MessageBox;/*
15854  * Based on:
15855  * Ext JS Library 1.1.1
15856  * Copyright(c) 2006-2007, Ext JS, LLC.
15857  *
15858  * Originally Released Under LGPL - original licence link has changed is not relivant.
15859  *
15860  * Fork - LGPL
15861  * <script type="text/javascript">
15862  */
15863 /**
15864  * @class Roo.QuickTips
15865  * Provides attractive and customizable tooltips for any element.
15866  * @singleton
15867  */
15868 Roo.QuickTips = function(){
15869     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
15870     var ce, bd, xy, dd;
15871     var visible = false, disabled = true, inited = false;
15872     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
15873     
15874     var onOver = function(e){
15875         if(disabled){
15876             return;
15877         }
15878         var t = e.getTarget();
15879         if(!t || t.nodeType !== 1 || t == document || t == document.body){
15880             return;
15881         }
15882         if(ce && t == ce.el){
15883             clearTimeout(hideProc);
15884             return;
15885         }
15886         if(t && tagEls[t.id]){
15887             tagEls[t.id].el = t;
15888             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
15889             return;
15890         }
15891         var ttp, et = Roo.fly(t);
15892         var ns = cfg.namespace;
15893         if(tm.interceptTitles && t.title){
15894             ttp = t.title;
15895             t.qtip = ttp;
15896             t.removeAttribute("title");
15897             e.preventDefault();
15898         }else{
15899             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
15900         }
15901         if(ttp){
15902             showProc = show.defer(tm.showDelay, tm, [{
15903                 el: t, 
15904                 text: ttp, 
15905                 width: et.getAttributeNS(ns, cfg.width),
15906                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
15907                 title: et.getAttributeNS(ns, cfg.title),
15908                     cls: et.getAttributeNS(ns, cfg.cls)
15909             }]);
15910         }
15911     };
15912     
15913     var onOut = function(e){
15914         clearTimeout(showProc);
15915         var t = e.getTarget();
15916         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
15917             hideProc = setTimeout(hide, tm.hideDelay);
15918         }
15919     };
15920     
15921     var onMove = function(e){
15922         if(disabled){
15923             return;
15924         }
15925         xy = e.getXY();
15926         xy[1] += 18;
15927         if(tm.trackMouse && ce){
15928             el.setXY(xy);
15929         }
15930     };
15931     
15932     var onDown = function(e){
15933         clearTimeout(showProc);
15934         clearTimeout(hideProc);
15935         if(!e.within(el)){
15936             if(tm.hideOnClick){
15937                 hide();
15938                 tm.disable();
15939                 tm.enable.defer(100, tm);
15940             }
15941         }
15942     };
15943     
15944     var getPad = function(){
15945         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
15946     };
15947
15948     var show = function(o){
15949         if(disabled){
15950             return;
15951         }
15952         clearTimeout(dismissProc);
15953         ce = o;
15954         if(removeCls){ // in case manually hidden
15955             el.removeClass(removeCls);
15956             removeCls = null;
15957         }
15958         if(ce.cls){
15959             el.addClass(ce.cls);
15960             removeCls = ce.cls;
15961         }
15962         if(ce.title){
15963             tipTitle.update(ce.title);
15964             tipTitle.show();
15965         }else{
15966             tipTitle.update('');
15967             tipTitle.hide();
15968         }
15969         el.dom.style.width  = tm.maxWidth+'px';
15970         //tipBody.dom.style.width = '';
15971         tipBodyText.update(o.text);
15972         var p = getPad(), w = ce.width;
15973         if(!w){
15974             var td = tipBodyText.dom;
15975             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
15976             if(aw > tm.maxWidth){
15977                 w = tm.maxWidth;
15978             }else if(aw < tm.minWidth){
15979                 w = tm.minWidth;
15980             }else{
15981                 w = aw;
15982             }
15983         }
15984         //tipBody.setWidth(w);
15985         el.setWidth(parseInt(w, 10) + p);
15986         if(ce.autoHide === false){
15987             close.setDisplayed(true);
15988             if(dd){
15989                 dd.unlock();
15990             }
15991         }else{
15992             close.setDisplayed(false);
15993             if(dd){
15994                 dd.lock();
15995             }
15996         }
15997         if(xy){
15998             el.avoidY = xy[1]-18;
15999             el.setXY(xy);
16000         }
16001         if(tm.animate){
16002             el.setOpacity(.1);
16003             el.setStyle("visibility", "visible");
16004             el.fadeIn({callback: afterShow});
16005         }else{
16006             afterShow();
16007         }
16008     };
16009     
16010     var afterShow = function(){
16011         if(ce){
16012             el.show();
16013             esc.enable();
16014             if(tm.autoDismiss && ce.autoHide !== false){
16015                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
16016             }
16017         }
16018     };
16019     
16020     var hide = function(noanim){
16021         clearTimeout(dismissProc);
16022         clearTimeout(hideProc);
16023         ce = null;
16024         if(el.isVisible()){
16025             esc.disable();
16026             if(noanim !== true && tm.animate){
16027                 el.fadeOut({callback: afterHide});
16028             }else{
16029                 afterHide();
16030             } 
16031         }
16032     };
16033     
16034     var afterHide = function(){
16035         el.hide();
16036         if(removeCls){
16037             el.removeClass(removeCls);
16038             removeCls = null;
16039         }
16040     };
16041     
16042     return {
16043         /**
16044         * @cfg {Number} minWidth
16045         * The minimum width of the quick tip (defaults to 40)
16046         */
16047        minWidth : 40,
16048         /**
16049         * @cfg {Number} maxWidth
16050         * The maximum width of the quick tip (defaults to 300)
16051         */
16052        maxWidth : 300,
16053         /**
16054         * @cfg {Boolean} interceptTitles
16055         * True to automatically use the element's DOM title value if available (defaults to false)
16056         */
16057        interceptTitles : false,
16058         /**
16059         * @cfg {Boolean} trackMouse
16060         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
16061         */
16062        trackMouse : false,
16063         /**
16064         * @cfg {Boolean} hideOnClick
16065         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
16066         */
16067        hideOnClick : true,
16068         /**
16069         * @cfg {Number} showDelay
16070         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
16071         */
16072        showDelay : 500,
16073         /**
16074         * @cfg {Number} hideDelay
16075         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
16076         */
16077        hideDelay : 200,
16078         /**
16079         * @cfg {Boolean} autoHide
16080         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
16081         * Used in conjunction with hideDelay.
16082         */
16083        autoHide : true,
16084         /**
16085         * @cfg {Boolean}
16086         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
16087         * (defaults to true).  Used in conjunction with autoDismissDelay.
16088         */
16089        autoDismiss : true,
16090         /**
16091         * @cfg {Number}
16092         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
16093         */
16094        autoDismissDelay : 5000,
16095        /**
16096         * @cfg {Boolean} animate
16097         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
16098         */
16099        animate : false,
16100
16101        /**
16102         * @cfg {String} title
16103         * Title text to display (defaults to '').  This can be any valid HTML markup.
16104         */
16105         title: '',
16106        /**
16107         * @cfg {String} text
16108         * Body text to display (defaults to '').  This can be any valid HTML markup.
16109         */
16110         text : '',
16111        /**
16112         * @cfg {String} cls
16113         * A CSS class to apply to the base quick tip element (defaults to '').
16114         */
16115         cls : '',
16116        /**
16117         * @cfg {Number} width
16118         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
16119         * minWidth or maxWidth.
16120         */
16121         width : null,
16122
16123     /**
16124      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
16125      * or display QuickTips in a page.
16126      */
16127        init : function(){
16128           tm = Roo.QuickTips;
16129           cfg = tm.tagConfig;
16130           if(!inited){
16131               if(!Roo.isReady){ // allow calling of init() before onReady
16132                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
16133                   return;
16134               }
16135               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
16136               el.fxDefaults = {stopFx: true};
16137               // maximum custom styling
16138               //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>');
16139               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>');              
16140               tipTitle = el.child('h3');
16141               tipTitle.enableDisplayMode("block");
16142               tipBody = el.child('div.x-tip-bd');
16143               tipBodyText = el.child('div.x-tip-bd-inner');
16144               //bdLeft = el.child('div.x-tip-bd-left');
16145               //bdRight = el.child('div.x-tip-bd-right');
16146               close = el.child('div.x-tip-close');
16147               close.enableDisplayMode("block");
16148               close.on("click", hide);
16149               var d = Roo.get(document);
16150               d.on("mousedown", onDown);
16151               d.on("mouseover", onOver);
16152               d.on("mouseout", onOut);
16153               d.on("mousemove", onMove);
16154               esc = d.addKeyListener(27, hide);
16155               esc.disable();
16156               if(Roo.dd.DD){
16157                   dd = el.initDD("default", null, {
16158                       onDrag : function(){
16159                           el.sync();  
16160                       }
16161                   });
16162                   dd.setHandleElId(tipTitle.id);
16163                   dd.lock();
16164               }
16165               inited = true;
16166           }
16167           this.enable(); 
16168        },
16169
16170     /**
16171      * Configures a new quick tip instance and assigns it to a target element.  The following config options
16172      * are supported:
16173      * <pre>
16174 Property    Type                   Description
16175 ----------  ---------------------  ------------------------------------------------------------------------
16176 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
16177      * </ul>
16178      * @param {Object} config The config object
16179      */
16180        register : function(config){
16181            var cs = config instanceof Array ? config : arguments;
16182            for(var i = 0, len = cs.length; i < len; i++) {
16183                var c = cs[i];
16184                var target = c.target;
16185                if(target){
16186                    if(target instanceof Array){
16187                        for(var j = 0, jlen = target.length; j < jlen; j++){
16188                            tagEls[target[j]] = c;
16189                        }
16190                    }else{
16191                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
16192                    }
16193                }
16194            }
16195        },
16196
16197     /**
16198      * Removes this quick tip from its element and destroys it.
16199      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
16200      */
16201        unregister : function(el){
16202            delete tagEls[Roo.id(el)];
16203        },
16204
16205     /**
16206      * Enable this quick tip.
16207      */
16208        enable : function(){
16209            if(inited && disabled){
16210                locks.pop();
16211                if(locks.length < 1){
16212                    disabled = false;
16213                }
16214            }
16215        },
16216
16217     /**
16218      * Disable this quick tip.
16219      */
16220        disable : function(){
16221           disabled = true;
16222           clearTimeout(showProc);
16223           clearTimeout(hideProc);
16224           clearTimeout(dismissProc);
16225           if(ce){
16226               hide(true);
16227           }
16228           locks.push(1);
16229        },
16230
16231     /**
16232      * Returns true if the quick tip is enabled, else false.
16233      */
16234        isEnabled : function(){
16235             return !disabled;
16236        },
16237
16238         // private
16239        tagConfig : {
16240            namespace : "ext",
16241            attribute : "qtip",
16242            width : "width",
16243            target : "target",
16244            title : "qtitle",
16245            hide : "hide",
16246            cls : "qclass"
16247        }
16248    };
16249 }();
16250
16251 // backwards compat
16252 Roo.QuickTips.tips = Roo.QuickTips.register;/*
16253  * Based on:
16254  * Ext JS Library 1.1.1
16255  * Copyright(c) 2006-2007, Ext JS, LLC.
16256  *
16257  * Originally Released Under LGPL - original licence link has changed is not relivant.
16258  *
16259  * Fork - LGPL
16260  * <script type="text/javascript">
16261  */
16262  
16263
16264 /**
16265  * @class Roo.tree.TreePanel
16266  * @extends Roo.data.Tree
16267
16268  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
16269  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
16270  * @cfg {Boolean} enableDD true to enable drag and drop
16271  * @cfg {Boolean} enableDrag true to enable just drag
16272  * @cfg {Boolean} enableDrop true to enable just drop
16273  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
16274  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
16275  * @cfg {String} ddGroup The DD group this TreePanel belongs to
16276  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
16277  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
16278  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
16279  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
16280  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
16281  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
16282  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
16283  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
16284  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
16285  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
16286  * @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>
16287  * @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>
16288  * 
16289  * @constructor
16290  * @param {String/HTMLElement/Element} el The container element
16291  * @param {Object} config
16292  */
16293 Roo.tree.TreePanel = function(el, config){
16294     var root = false;
16295     var loader = false;
16296     if (config.root) {
16297         root = config.root;
16298         delete config.root;
16299     }
16300     if (config.loader) {
16301         loader = config.loader;
16302         delete config.loader;
16303     }
16304     
16305     Roo.apply(this, config);
16306     Roo.tree.TreePanel.superclass.constructor.call(this);
16307     this.el = Roo.get(el);
16308     this.el.addClass('x-tree');
16309     //console.log(root);
16310     if (root) {
16311         this.setRootNode( Roo.factory(root, Roo.tree));
16312     }
16313     if (loader) {
16314         this.loader = Roo.factory(loader, Roo.tree);
16315     }
16316    /**
16317     * Read-only. The id of the container element becomes this TreePanel's id.
16318     */
16319    this.id = this.el.id;
16320    this.addEvents({
16321         /**
16322         * @event beforeload
16323         * Fires before a node is loaded, return false to cancel
16324         * @param {Node} node The node being loaded
16325         */
16326         "beforeload" : true,
16327         /**
16328         * @event load
16329         * Fires when a node is loaded
16330         * @param {Node} node The node that was loaded
16331         */
16332         "load" : true,
16333         /**
16334         * @event textchange
16335         * Fires when the text for a node is changed
16336         * @param {Node} node The node
16337         * @param {String} text The new text
16338         * @param {String} oldText The old text
16339         */
16340         "textchange" : true,
16341         /**
16342         * @event beforeexpand
16343         * Fires before a node is expanded, return false to cancel.
16344         * @param {Node} node The node
16345         * @param {Boolean} deep
16346         * @param {Boolean} anim
16347         */
16348         "beforeexpand" : true,
16349         /**
16350         * @event beforecollapse
16351         * Fires before a node is collapsed, return false to cancel.
16352         * @param {Node} node The node
16353         * @param {Boolean} deep
16354         * @param {Boolean} anim
16355         */
16356         "beforecollapse" : true,
16357         /**
16358         * @event expand
16359         * Fires when a node is expanded
16360         * @param {Node} node The node
16361         */
16362         "expand" : true,
16363         /**
16364         * @event disabledchange
16365         * Fires when the disabled status of a node changes
16366         * @param {Node} node The node
16367         * @param {Boolean} disabled
16368         */
16369         "disabledchange" : true,
16370         /**
16371         * @event collapse
16372         * Fires when a node is collapsed
16373         * @param {Node} node The node
16374         */
16375         "collapse" : true,
16376         /**
16377         * @event beforeclick
16378         * Fires before click processing on a node. Return false to cancel the default action.
16379         * @param {Node} node The node
16380         * @param {Roo.EventObject} e The event object
16381         */
16382         "beforeclick":true,
16383         /**
16384         * @event checkchange
16385         * Fires when a node with a checkbox's checked property changes
16386         * @param {Node} this This node
16387         * @param {Boolean} checked
16388         */
16389         "checkchange":true,
16390         /**
16391         * @event click
16392         * Fires when a node is clicked
16393         * @param {Node} node The node
16394         * @param {Roo.EventObject} e The event object
16395         */
16396         "click":true,
16397         /**
16398         * @event dblclick
16399         * Fires when a node is double clicked
16400         * @param {Node} node The node
16401         * @param {Roo.EventObject} e The event object
16402         */
16403         "dblclick":true,
16404         /**
16405         * @event contextmenu
16406         * Fires when a node is right clicked
16407         * @param {Node} node The node
16408         * @param {Roo.EventObject} e The event object
16409         */
16410         "contextmenu":true,
16411         /**
16412         * @event beforechildrenrendered
16413         * Fires right before the child nodes for a node are rendered
16414         * @param {Node} node The node
16415         */
16416         "beforechildrenrendered":true,
16417        /**
16418              * @event startdrag
16419              * Fires when a node starts being dragged
16420              * @param {Roo.tree.TreePanel} this
16421              * @param {Roo.tree.TreeNode} node
16422              * @param {event} e The raw browser event
16423              */ 
16424             "startdrag" : true,
16425             /**
16426              * @event enddrag
16427              * Fires when a drag operation is complete
16428              * @param {Roo.tree.TreePanel} this
16429              * @param {Roo.tree.TreeNode} node
16430              * @param {event} e The raw browser event
16431              */
16432             "enddrag" : true,
16433             /**
16434              * @event dragdrop
16435              * Fires when a dragged node is dropped on a valid DD target
16436              * @param {Roo.tree.TreePanel} this
16437              * @param {Roo.tree.TreeNode} node
16438              * @param {DD} dd The dd it was dropped on
16439              * @param {event} e The raw browser event
16440              */
16441             "dragdrop" : true,
16442             /**
16443              * @event beforenodedrop
16444              * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
16445              * passed to handlers has the following properties:<br />
16446              * <ul style="padding:5px;padding-left:16px;">
16447              * <li>tree - The TreePanel</li>
16448              * <li>target - The node being targeted for the drop</li>
16449              * <li>data - The drag data from the drag source</li>
16450              * <li>point - The point of the drop - append, above or below</li>
16451              * <li>source - The drag source</li>
16452              * <li>rawEvent - Raw mouse event</li>
16453              * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
16454              * to be inserted by setting them on this object.</li>
16455              * <li>cancel - Set this to true to cancel the drop.</li>
16456              * </ul>
16457              * @param {Object} dropEvent
16458              */
16459             "beforenodedrop" : true,
16460             /**
16461              * @event nodedrop
16462              * Fires after a DD object is dropped on a node in this tree. The dropEvent
16463              * passed to handlers has the following properties:<br />
16464              * <ul style="padding:5px;padding-left:16px;">
16465              * <li>tree - The TreePanel</li>
16466              * <li>target - The node being targeted for the drop</li>
16467              * <li>data - The drag data from the drag source</li>
16468              * <li>point - The point of the drop - append, above or below</li>
16469              * <li>source - The drag source</li>
16470              * <li>rawEvent - Raw mouse event</li>
16471              * <li>dropNode - Dropped node(s).</li>
16472              * </ul>
16473              * @param {Object} dropEvent
16474              */
16475             "nodedrop" : true,
16476              /**
16477              * @event nodedragover
16478              * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
16479              * passed to handlers has the following properties:<br />
16480              * <ul style="padding:5px;padding-left:16px;">
16481              * <li>tree - The TreePanel</li>
16482              * <li>target - The node being targeted for the drop</li>
16483              * <li>data - The drag data from the drag source</li>
16484              * <li>point - The point of the drop - append, above or below</li>
16485              * <li>source - The drag source</li>
16486              * <li>rawEvent - Raw mouse event</li>
16487              * <li>dropNode - Drop node(s) provided by the source.</li>
16488              * <li>cancel - Set this to true to signal drop not allowed.</li>
16489              * </ul>
16490              * @param {Object} dragOverEvent
16491              */
16492             "nodedragover" : true
16493         
16494    });
16495    if(this.singleExpand){
16496        this.on("beforeexpand", this.restrictExpand, this);
16497    }
16498 };
16499 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
16500     rootVisible : true,
16501     animate: Roo.enableFx,
16502     lines : true,
16503     enableDD : false,
16504     hlDrop : Roo.enableFx,
16505   
16506     renderer: false,
16507     
16508     rendererTip: false,
16509     // private
16510     restrictExpand : function(node){
16511         var p = node.parentNode;
16512         if(p){
16513             if(p.expandedChild && p.expandedChild.parentNode == p){
16514                 p.expandedChild.collapse();
16515             }
16516             p.expandedChild = node;
16517         }
16518     },
16519
16520     // private override
16521     setRootNode : function(node){
16522         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
16523         if(!this.rootVisible){
16524             node.ui = new Roo.tree.RootTreeNodeUI(node);
16525         }
16526         return node;
16527     },
16528
16529     /**
16530      * Returns the container element for this TreePanel
16531      */
16532     getEl : function(){
16533         return this.el;
16534     },
16535
16536     /**
16537      * Returns the default TreeLoader for this TreePanel
16538      */
16539     getLoader : function(){
16540         return this.loader;
16541     },
16542
16543     /**
16544      * Expand all nodes
16545      */
16546     expandAll : function(){
16547         this.root.expand(true);
16548     },
16549
16550     /**
16551      * Collapse all nodes
16552      */
16553     collapseAll : function(){
16554         this.root.collapse(true);
16555     },
16556
16557     /**
16558      * Returns the selection model used by this TreePanel
16559      */
16560     getSelectionModel : function(){
16561         if(!this.selModel){
16562             this.selModel = new Roo.tree.DefaultSelectionModel();
16563         }
16564         return this.selModel;
16565     },
16566
16567     /**
16568      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
16569      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
16570      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
16571      * @return {Array}
16572      */
16573     getChecked : function(a, startNode){
16574         startNode = startNode || this.root;
16575         var r = [];
16576         var f = function(){
16577             if(this.attributes.checked){
16578                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
16579             }
16580         }
16581         startNode.cascade(f);
16582         return r;
16583     },
16584
16585     /**
16586      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16587      * @param {String} path
16588      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16589      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
16590      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
16591      */
16592     expandPath : function(path, attr, callback){
16593         attr = attr || "id";
16594         var keys = path.split(this.pathSeparator);
16595         var curNode = this.root;
16596         if(curNode.attributes[attr] != keys[1]){ // invalid root
16597             if(callback){
16598                 callback(false, null);
16599             }
16600             return;
16601         }
16602         var index = 1;
16603         var f = function(){
16604             if(++index == keys.length){
16605                 if(callback){
16606                     callback(true, curNode);
16607                 }
16608                 return;
16609             }
16610             var c = curNode.findChild(attr, keys[index]);
16611             if(!c){
16612                 if(callback){
16613                     callback(false, curNode);
16614                 }
16615                 return;
16616             }
16617             curNode = c;
16618             c.expand(false, false, f);
16619         };
16620         curNode.expand(false, false, f);
16621     },
16622
16623     /**
16624      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16625      * @param {String} path
16626      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16627      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
16628      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
16629      */
16630     selectPath : function(path, attr, callback){
16631         attr = attr || "id";
16632         var keys = path.split(this.pathSeparator);
16633         var v = keys.pop();
16634         if(keys.length > 0){
16635             var f = function(success, node){
16636                 if(success && node){
16637                     var n = node.findChild(attr, v);
16638                     if(n){
16639                         n.select();
16640                         if(callback){
16641                             callback(true, n);
16642                         }
16643                     }else if(callback){
16644                         callback(false, n);
16645                     }
16646                 }else{
16647                     if(callback){
16648                         callback(false, n);
16649                     }
16650                 }
16651             };
16652             this.expandPath(keys.join(this.pathSeparator), attr, f);
16653         }else{
16654             this.root.select();
16655             if(callback){
16656                 callback(true, this.root);
16657             }
16658         }
16659     },
16660
16661     getTreeEl : function(){
16662         return this.el;
16663     },
16664
16665     /**
16666      * Trigger rendering of this TreePanel
16667      */
16668     render : function(){
16669         if (this.innerCt) {
16670             return this; // stop it rendering more than once!!
16671         }
16672         
16673         this.innerCt = this.el.createChild({tag:"ul",
16674                cls:"x-tree-root-ct " +
16675                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
16676
16677         if(this.containerScroll){
16678             Roo.dd.ScrollManager.register(this.el);
16679         }
16680         if((this.enableDD || this.enableDrop) && !this.dropZone){
16681            /**
16682             * The dropZone used by this tree if drop is enabled
16683             * @type Roo.tree.TreeDropZone
16684             */
16685              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
16686                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
16687            });
16688         }
16689         if((this.enableDD || this.enableDrag) && !this.dragZone){
16690            /**
16691             * The dragZone used by this tree if drag is enabled
16692             * @type Roo.tree.TreeDragZone
16693             */
16694             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
16695                ddGroup: this.ddGroup || "TreeDD",
16696                scroll: this.ddScroll
16697            });
16698         }
16699         this.getSelectionModel().init(this);
16700         if (!this.root) {
16701             console.log("ROOT not set in tree");
16702             return;
16703         }
16704         this.root.render();
16705         if(!this.rootVisible){
16706             this.root.renderChildren();
16707         }
16708         return this;
16709     }
16710 });/*
16711  * Based on:
16712  * Ext JS Library 1.1.1
16713  * Copyright(c) 2006-2007, Ext JS, LLC.
16714  *
16715  * Originally Released Under LGPL - original licence link has changed is not relivant.
16716  *
16717  * Fork - LGPL
16718  * <script type="text/javascript">
16719  */
16720  
16721
16722 /**
16723  * @class Roo.tree.DefaultSelectionModel
16724  * @extends Roo.util.Observable
16725  * The default single selection for a TreePanel.
16726  */
16727 Roo.tree.DefaultSelectionModel = function(){
16728    this.selNode = null;
16729    
16730    this.addEvents({
16731        /**
16732         * @event selectionchange
16733         * Fires when the selected node changes
16734         * @param {DefaultSelectionModel} this
16735         * @param {TreeNode} node the new selection
16736         */
16737        "selectionchange" : true,
16738
16739        /**
16740         * @event beforeselect
16741         * Fires before the selected node changes, return false to cancel the change
16742         * @param {DefaultSelectionModel} this
16743         * @param {TreeNode} node the new selection
16744         * @param {TreeNode} node the old selection
16745         */
16746        "beforeselect" : true
16747    });
16748 };
16749
16750 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
16751     init : function(tree){
16752         this.tree = tree;
16753         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16754         tree.on("click", this.onNodeClick, this);
16755     },
16756     
16757     onNodeClick : function(node, e){
16758         if (e.ctrlKey && this.selNode == node)  {
16759             this.unselect(node);
16760             return;
16761         }
16762         this.select(node);
16763     },
16764     
16765     /**
16766      * Select a node.
16767      * @param {TreeNode} node The node to select
16768      * @return {TreeNode} The selected node
16769      */
16770     select : function(node){
16771         var last = this.selNode;
16772         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
16773             if(last){
16774                 last.ui.onSelectedChange(false);
16775             }
16776             this.selNode = node;
16777             node.ui.onSelectedChange(true);
16778             this.fireEvent("selectionchange", this, node, last);
16779         }
16780         return node;
16781     },
16782     
16783     /**
16784      * Deselect a node.
16785      * @param {TreeNode} node The node to unselect
16786      */
16787     unselect : function(node){
16788         if(this.selNode == node){
16789             this.clearSelections();
16790         }    
16791     },
16792     
16793     /**
16794      * Clear all selections
16795      */
16796     clearSelections : function(){
16797         var n = this.selNode;
16798         if(n){
16799             n.ui.onSelectedChange(false);
16800             this.selNode = null;
16801             this.fireEvent("selectionchange", this, null);
16802         }
16803         return n;
16804     },
16805     
16806     /**
16807      * Get the selected node
16808      * @return {TreeNode} The selected node
16809      */
16810     getSelectedNode : function(){
16811         return this.selNode;    
16812     },
16813     
16814     /**
16815      * Returns true if the node is selected
16816      * @param {TreeNode} node The node to check
16817      * @return {Boolean}
16818      */
16819     isSelected : function(node){
16820         return this.selNode == node;  
16821     },
16822
16823     /**
16824      * Selects the node above the selected node in the tree, intelligently walking the nodes
16825      * @return TreeNode The new selection
16826      */
16827     selectPrevious : function(){
16828         var s = this.selNode || this.lastSelNode;
16829         if(!s){
16830             return null;
16831         }
16832         var ps = s.previousSibling;
16833         if(ps){
16834             if(!ps.isExpanded() || ps.childNodes.length < 1){
16835                 return this.select(ps);
16836             } else{
16837                 var lc = ps.lastChild;
16838                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
16839                     lc = lc.lastChild;
16840                 }
16841                 return this.select(lc);
16842             }
16843         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
16844             return this.select(s.parentNode);
16845         }
16846         return null;
16847     },
16848
16849     /**
16850      * Selects the node above the selected node in the tree, intelligently walking the nodes
16851      * @return TreeNode The new selection
16852      */
16853     selectNext : function(){
16854         var s = this.selNode || this.lastSelNode;
16855         if(!s){
16856             return null;
16857         }
16858         if(s.firstChild && s.isExpanded()){
16859              return this.select(s.firstChild);
16860          }else if(s.nextSibling){
16861              return this.select(s.nextSibling);
16862          }else if(s.parentNode){
16863             var newS = null;
16864             s.parentNode.bubble(function(){
16865                 if(this.nextSibling){
16866                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
16867                     return false;
16868                 }
16869             });
16870             return newS;
16871          }
16872         return null;
16873     },
16874
16875     onKeyDown : function(e){
16876         var s = this.selNode || this.lastSelNode;
16877         // undesirable, but required
16878         var sm = this;
16879         if(!s){
16880             return;
16881         }
16882         var k = e.getKey();
16883         switch(k){
16884              case e.DOWN:
16885                  e.stopEvent();
16886                  this.selectNext();
16887              break;
16888              case e.UP:
16889                  e.stopEvent();
16890                  this.selectPrevious();
16891              break;
16892              case e.RIGHT:
16893                  e.preventDefault();
16894                  if(s.hasChildNodes()){
16895                      if(!s.isExpanded()){
16896                          s.expand();
16897                      }else if(s.firstChild){
16898                          this.select(s.firstChild, e);
16899                      }
16900                  }
16901              break;
16902              case e.LEFT:
16903                  e.preventDefault();
16904                  if(s.hasChildNodes() && s.isExpanded()){
16905                      s.collapse();
16906                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
16907                      this.select(s.parentNode, e);
16908                  }
16909              break;
16910         };
16911     }
16912 });
16913
16914 /**
16915  * @class Roo.tree.MultiSelectionModel
16916  * @extends Roo.util.Observable
16917  * Multi selection for a TreePanel.
16918  */
16919 Roo.tree.MultiSelectionModel = function(){
16920    this.selNodes = [];
16921    this.selMap = {};
16922    this.addEvents({
16923        /**
16924         * @event selectionchange
16925         * Fires when the selected nodes change
16926         * @param {MultiSelectionModel} this
16927         * @param {Array} nodes Array of the selected nodes
16928         */
16929        "selectionchange" : true
16930    });
16931 };
16932
16933 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
16934     init : function(tree){
16935         this.tree = tree;
16936         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16937         tree.on("click", this.onNodeClick, this);
16938     },
16939     
16940     onNodeClick : function(node, e){
16941         this.select(node, e, e.ctrlKey);
16942     },
16943     
16944     /**
16945      * Select a node.
16946      * @param {TreeNode} node The node to select
16947      * @param {EventObject} e (optional) An event associated with the selection
16948      * @param {Boolean} keepExisting True to retain existing selections
16949      * @return {TreeNode} The selected node
16950      */
16951     select : function(node, e, keepExisting){
16952         if(keepExisting !== true){
16953             this.clearSelections(true);
16954         }
16955         if(this.isSelected(node)){
16956             this.lastSelNode = node;
16957             return node;
16958         }
16959         this.selNodes.push(node);
16960         this.selMap[node.id] = node;
16961         this.lastSelNode = node;
16962         node.ui.onSelectedChange(true);
16963         this.fireEvent("selectionchange", this, this.selNodes);
16964         return node;
16965     },
16966     
16967     /**
16968      * Deselect a node.
16969      * @param {TreeNode} node The node to unselect
16970      */
16971     unselect : function(node){
16972         if(this.selMap[node.id]){
16973             node.ui.onSelectedChange(false);
16974             var sn = this.selNodes;
16975             var index = -1;
16976             if(sn.indexOf){
16977                 index = sn.indexOf(node);
16978             }else{
16979                 for(var i = 0, len = sn.length; i < len; i++){
16980                     if(sn[i] == node){
16981                         index = i;
16982                         break;
16983                     }
16984                 }
16985             }
16986             if(index != -1){
16987                 this.selNodes.splice(index, 1);
16988             }
16989             delete this.selMap[node.id];
16990             this.fireEvent("selectionchange", this, this.selNodes);
16991         }
16992     },
16993     
16994     /**
16995      * Clear all selections
16996      */
16997     clearSelections : function(suppressEvent){
16998         var sn = this.selNodes;
16999         if(sn.length > 0){
17000             for(var i = 0, len = sn.length; i < len; i++){
17001                 sn[i].ui.onSelectedChange(false);
17002             }
17003             this.selNodes = [];
17004             this.selMap = {};
17005             if(suppressEvent !== true){
17006                 this.fireEvent("selectionchange", this, this.selNodes);
17007             }
17008         }
17009     },
17010     
17011     /**
17012      * Returns true if the node is selected
17013      * @param {TreeNode} node The node to check
17014      * @return {Boolean}
17015      */
17016     isSelected : function(node){
17017         return this.selMap[node.id] ? true : false;  
17018     },
17019     
17020     /**
17021      * Returns an array of the selected nodes
17022      * @return {Array}
17023      */
17024     getSelectedNodes : function(){
17025         return this.selNodes;    
17026     },
17027
17028     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
17029
17030     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
17031
17032     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
17033 });/*
17034  * Based on:
17035  * Ext JS Library 1.1.1
17036  * Copyright(c) 2006-2007, Ext JS, LLC.
17037  *
17038  * Originally Released Under LGPL - original licence link has changed is not relivant.
17039  *
17040  * Fork - LGPL
17041  * <script type="text/javascript">
17042  */
17043  
17044 /**
17045  * @class Roo.tree.TreeNode
17046  * @extends Roo.data.Node
17047  * @cfg {String} text The text for this node
17048  * @cfg {Boolean} expanded true to start the node expanded
17049  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
17050  * @cfg {Boolean} allowDrop false if this node cannot be drop on
17051  * @cfg {Boolean} disabled true to start the node disabled
17052  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
17053  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
17054  * @cfg {String} cls A css class to be added to the node
17055  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
17056  * @cfg {String} href URL of the link used for the node (defaults to #)
17057  * @cfg {String} hrefTarget target frame for the link
17058  * @cfg {String} qtip An Ext QuickTip for the node
17059  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
17060  * @cfg {Boolean} singleClickExpand True for single click expand on this node
17061  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
17062  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
17063  * (defaults to undefined with no checkbox rendered)
17064  * @constructor
17065  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
17066  */
17067 Roo.tree.TreeNode = function(attributes){
17068     attributes = attributes || {};
17069     if(typeof attributes == "string"){
17070         attributes = {text: attributes};
17071     }
17072     this.childrenRendered = false;
17073     this.rendered = false;
17074     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
17075     this.expanded = attributes.expanded === true;
17076     this.isTarget = attributes.isTarget !== false;
17077     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
17078     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
17079
17080     /**
17081      * Read-only. The text for this node. To change it use setText().
17082      * @type String
17083      */
17084     this.text = attributes.text;
17085     /**
17086      * True if this node is disabled.
17087      * @type Boolean
17088      */
17089     this.disabled = attributes.disabled === true;
17090
17091     this.addEvents({
17092         /**
17093         * @event textchange
17094         * Fires when the text for this node is changed
17095         * @param {Node} this This node
17096         * @param {String} text The new text
17097         * @param {String} oldText The old text
17098         */
17099         "textchange" : true,
17100         /**
17101         * @event beforeexpand
17102         * Fires before this node is expanded, return false to cancel.
17103         * @param {Node} this This node
17104         * @param {Boolean} deep
17105         * @param {Boolean} anim
17106         */
17107         "beforeexpand" : true,
17108         /**
17109         * @event beforecollapse
17110         * Fires before this node is collapsed, return false to cancel.
17111         * @param {Node} this This node
17112         * @param {Boolean} deep
17113         * @param {Boolean} anim
17114         */
17115         "beforecollapse" : true,
17116         /**
17117         * @event expand
17118         * Fires when this node is expanded
17119         * @param {Node} this This node
17120         */
17121         "expand" : true,
17122         /**
17123         * @event disabledchange
17124         * Fires when the disabled status of this node changes
17125         * @param {Node} this This node
17126         * @param {Boolean} disabled
17127         */
17128         "disabledchange" : true,
17129         /**
17130         * @event collapse
17131         * Fires when this node is collapsed
17132         * @param {Node} this This node
17133         */
17134         "collapse" : true,
17135         /**
17136         * @event beforeclick
17137         * Fires before click processing. Return false to cancel the default action.
17138         * @param {Node} this This node
17139         * @param {Roo.EventObject} e The event object
17140         */
17141         "beforeclick":true,
17142         /**
17143         * @event checkchange
17144         * Fires when a node with a checkbox's checked property changes
17145         * @param {Node} this This node
17146         * @param {Boolean} checked
17147         */
17148         "checkchange":true,
17149         /**
17150         * @event click
17151         * Fires when this node is clicked
17152         * @param {Node} this This node
17153         * @param {Roo.EventObject} e The event object
17154         */
17155         "click":true,
17156         /**
17157         * @event dblclick
17158         * Fires when this node is double clicked
17159         * @param {Node} this This node
17160         * @param {Roo.EventObject} e The event object
17161         */
17162         "dblclick":true,
17163         /**
17164         * @event contextmenu
17165         * Fires when this node is right clicked
17166         * @param {Node} this This node
17167         * @param {Roo.EventObject} e The event object
17168         */
17169         "contextmenu":true,
17170         /**
17171         * @event beforechildrenrendered
17172         * Fires right before the child nodes for this node are rendered
17173         * @param {Node} this This node
17174         */
17175         "beforechildrenrendered":true
17176     });
17177
17178     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
17179
17180     /**
17181      * Read-only. The UI for this node
17182      * @type TreeNodeUI
17183      */
17184     this.ui = new uiClass(this);
17185 };
17186 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
17187     preventHScroll: true,
17188     /**
17189      * Returns true if this node is expanded
17190      * @return {Boolean}
17191      */
17192     isExpanded : function(){
17193         return this.expanded;
17194     },
17195
17196     /**
17197      * Returns the UI object for this node
17198      * @return {TreeNodeUI}
17199      */
17200     getUI : function(){
17201         return this.ui;
17202     },
17203
17204     // private override
17205     setFirstChild : function(node){
17206         var of = this.firstChild;
17207         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
17208         if(this.childrenRendered && of && node != of){
17209             of.renderIndent(true, true);
17210         }
17211         if(this.rendered){
17212             this.renderIndent(true, true);
17213         }
17214     },
17215
17216     // private override
17217     setLastChild : function(node){
17218         var ol = this.lastChild;
17219         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
17220         if(this.childrenRendered && ol && node != ol){
17221             ol.renderIndent(true, true);
17222         }
17223         if(this.rendered){
17224             this.renderIndent(true, true);
17225         }
17226     },
17227
17228     // these methods are overridden to provide lazy rendering support
17229     // private override
17230     appendChild : function(){
17231         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
17232         if(node && this.childrenRendered){
17233             node.render();
17234         }
17235         this.ui.updateExpandIcon();
17236         return node;
17237     },
17238
17239     // private override
17240     removeChild : function(node){
17241         this.ownerTree.getSelectionModel().unselect(node);
17242         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
17243         // if it's been rendered remove dom node
17244         if(this.childrenRendered){
17245             node.ui.remove();
17246         }
17247         if(this.childNodes.length < 1){
17248             this.collapse(false, false);
17249         }else{
17250             this.ui.updateExpandIcon();
17251         }
17252         if(!this.firstChild) {
17253             this.childrenRendered = false;
17254         }
17255         return node;
17256     },
17257
17258     // private override
17259     insertBefore : function(node, refNode){
17260         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
17261         if(newNode && refNode && this.childrenRendered){
17262             node.render();
17263         }
17264         this.ui.updateExpandIcon();
17265         return newNode;
17266     },
17267
17268     /**
17269      * Sets the text for this node
17270      * @param {String} text
17271      */
17272     setText : function(text){
17273         var oldText = this.text;
17274         this.text = text;
17275         this.attributes.text = text;
17276         if(this.rendered){ // event without subscribing
17277             this.ui.onTextChange(this, text, oldText);
17278         }
17279         this.fireEvent("textchange", this, text, oldText);
17280     },
17281
17282     /**
17283      * Triggers selection of this node
17284      */
17285     select : function(){
17286         this.getOwnerTree().getSelectionModel().select(this);
17287     },
17288
17289     /**
17290      * Triggers deselection of this node
17291      */
17292     unselect : function(){
17293         this.getOwnerTree().getSelectionModel().unselect(this);
17294     },
17295
17296     /**
17297      * Returns true if this node is selected
17298      * @return {Boolean}
17299      */
17300     isSelected : function(){
17301         return this.getOwnerTree().getSelectionModel().isSelected(this);
17302     },
17303
17304     /**
17305      * Expand this node.
17306      * @param {Boolean} deep (optional) True to expand all children as well
17307      * @param {Boolean} anim (optional) false to cancel the default animation
17308      * @param {Function} callback (optional) A callback to be called when
17309      * expanding this node completes (does not wait for deep expand to complete).
17310      * Called with 1 parameter, this node.
17311      */
17312     expand : function(deep, anim, callback){
17313         if(!this.expanded){
17314             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
17315                 return;
17316             }
17317             if(!this.childrenRendered){
17318                 this.renderChildren();
17319             }
17320             this.expanded = true;
17321             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
17322                 this.ui.animExpand(function(){
17323                     this.fireEvent("expand", this);
17324                     if(typeof callback == "function"){
17325                         callback(this);
17326                     }
17327                     if(deep === true){
17328                         this.expandChildNodes(true);
17329                     }
17330                 }.createDelegate(this));
17331                 return;
17332             }else{
17333                 this.ui.expand();
17334                 this.fireEvent("expand", this);
17335                 if(typeof callback == "function"){
17336                     callback(this);
17337                 }
17338             }
17339         }else{
17340            if(typeof callback == "function"){
17341                callback(this);
17342            }
17343         }
17344         if(deep === true){
17345             this.expandChildNodes(true);
17346         }
17347     },
17348
17349     isHiddenRoot : function(){
17350         return this.isRoot && !this.getOwnerTree().rootVisible;
17351     },
17352
17353     /**
17354      * Collapse this node.
17355      * @param {Boolean} deep (optional) True to collapse all children as well
17356      * @param {Boolean} anim (optional) false to cancel the default animation
17357      */
17358     collapse : function(deep, anim){
17359         if(this.expanded && !this.isHiddenRoot()){
17360             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
17361                 return;
17362             }
17363             this.expanded = false;
17364             if((this.getOwnerTree().animate && anim !== false) || anim){
17365                 this.ui.animCollapse(function(){
17366                     this.fireEvent("collapse", this);
17367                     if(deep === true){
17368                         this.collapseChildNodes(true);
17369                     }
17370                 }.createDelegate(this));
17371                 return;
17372             }else{
17373                 this.ui.collapse();
17374                 this.fireEvent("collapse", this);
17375             }
17376         }
17377         if(deep === true){
17378             var cs = this.childNodes;
17379             for(var i = 0, len = cs.length; i < len; i++) {
17380                 cs[i].collapse(true, false);
17381             }
17382         }
17383     },
17384
17385     // private
17386     delayedExpand : function(delay){
17387         if(!this.expandProcId){
17388             this.expandProcId = this.expand.defer(delay, this);
17389         }
17390     },
17391
17392     // private
17393     cancelExpand : function(){
17394         if(this.expandProcId){
17395             clearTimeout(this.expandProcId);
17396         }
17397         this.expandProcId = false;
17398     },
17399
17400     /**
17401      * Toggles expanded/collapsed state of the node
17402      */
17403     toggle : function(){
17404         if(this.expanded){
17405             this.collapse();
17406         }else{
17407             this.expand();
17408         }
17409     },
17410
17411     /**
17412      * Ensures all parent nodes are expanded
17413      */
17414     ensureVisible : function(callback){
17415         var tree = this.getOwnerTree();
17416         tree.expandPath(this.parentNode.getPath(), false, function(){
17417             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
17418             Roo.callback(callback);
17419         }.createDelegate(this));
17420     },
17421
17422     /**
17423      * Expand all child nodes
17424      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
17425      */
17426     expandChildNodes : function(deep){
17427         var cs = this.childNodes;
17428         for(var i = 0, len = cs.length; i < len; i++) {
17429                 cs[i].expand(deep);
17430         }
17431     },
17432
17433     /**
17434      * Collapse all child nodes
17435      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
17436      */
17437     collapseChildNodes : function(deep){
17438         var cs = this.childNodes;
17439         for(var i = 0, len = cs.length; i < len; i++) {
17440                 cs[i].collapse(deep);
17441         }
17442     },
17443
17444     /**
17445      * Disables this node
17446      */
17447     disable : function(){
17448         this.disabled = true;
17449         this.unselect();
17450         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17451             this.ui.onDisableChange(this, true);
17452         }
17453         this.fireEvent("disabledchange", this, true);
17454     },
17455
17456     /**
17457      * Enables this node
17458      */
17459     enable : function(){
17460         this.disabled = false;
17461         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17462             this.ui.onDisableChange(this, false);
17463         }
17464         this.fireEvent("disabledchange", this, false);
17465     },
17466
17467     // private
17468     renderChildren : function(suppressEvent){
17469         if(suppressEvent !== false){
17470             this.fireEvent("beforechildrenrendered", this);
17471         }
17472         var cs = this.childNodes;
17473         for(var i = 0, len = cs.length; i < len; i++){
17474             cs[i].render(true);
17475         }
17476         this.childrenRendered = true;
17477     },
17478
17479     // private
17480     sort : function(fn, scope){
17481         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
17482         if(this.childrenRendered){
17483             var cs = this.childNodes;
17484             for(var i = 0, len = cs.length; i < len; i++){
17485                 cs[i].render(true);
17486             }
17487         }
17488     },
17489
17490     // private
17491     render : function(bulkRender){
17492         this.ui.render(bulkRender);
17493         if(!this.rendered){
17494             this.rendered = true;
17495             if(this.expanded){
17496                 this.expanded = false;
17497                 this.expand(false, false);
17498             }
17499         }
17500     },
17501
17502     // private
17503     renderIndent : function(deep, refresh){
17504         if(refresh){
17505             this.ui.childIndent = null;
17506         }
17507         this.ui.renderIndent();
17508         if(deep === true && this.childrenRendered){
17509             var cs = this.childNodes;
17510             for(var i = 0, len = cs.length; i < len; i++){
17511                 cs[i].renderIndent(true, refresh);
17512             }
17513         }
17514     }
17515 });/*
17516  * Based on:
17517  * Ext JS Library 1.1.1
17518  * Copyright(c) 2006-2007, Ext JS, LLC.
17519  *
17520  * Originally Released Under LGPL - original licence link has changed is not relivant.
17521  *
17522  * Fork - LGPL
17523  * <script type="text/javascript">
17524  */
17525  
17526 /**
17527  * @class Roo.tree.AsyncTreeNode
17528  * @extends Roo.tree.TreeNode
17529  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
17530  * @constructor
17531  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
17532  */
17533  Roo.tree.AsyncTreeNode = function(config){
17534     this.loaded = false;
17535     this.loading = false;
17536     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
17537     /**
17538     * @event beforeload
17539     * Fires before this node is loaded, return false to cancel
17540     * @param {Node} this This node
17541     */
17542     this.addEvents({'beforeload':true, 'load': true});
17543     /**
17544     * @event load
17545     * Fires when this node is loaded
17546     * @param {Node} this This node
17547     */
17548     /**
17549      * The loader used by this node (defaults to using the tree's defined loader)
17550      * @type TreeLoader
17551      * @property loader
17552      */
17553 };
17554 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
17555     expand : function(deep, anim, callback){
17556         if(this.loading){ // if an async load is already running, waiting til it's done
17557             var timer;
17558             var f = function(){
17559                 if(!this.loading){ // done loading
17560                     clearInterval(timer);
17561                     this.expand(deep, anim, callback);
17562                 }
17563             }.createDelegate(this);
17564             timer = setInterval(f, 200);
17565             return;
17566         }
17567         if(!this.loaded){
17568             if(this.fireEvent("beforeload", this) === false){
17569                 return;
17570             }
17571             this.loading = true;
17572             this.ui.beforeLoad(this);
17573             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
17574             if(loader){
17575                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
17576                 return;
17577             }
17578         }
17579         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
17580     },
17581     
17582     /**
17583      * Returns true if this node is currently loading
17584      * @return {Boolean}
17585      */
17586     isLoading : function(){
17587         return this.loading;  
17588     },
17589     
17590     loadComplete : function(deep, anim, callback){
17591         this.loading = false;
17592         this.loaded = true;
17593         this.ui.afterLoad(this);
17594         this.fireEvent("load", this);
17595         this.expand(deep, anim, callback);
17596     },
17597     
17598     /**
17599      * Returns true if this node has been loaded
17600      * @return {Boolean}
17601      */
17602     isLoaded : function(){
17603         return this.loaded;
17604     },
17605     
17606     hasChildNodes : function(){
17607         if(!this.isLeaf() && !this.loaded){
17608             return true;
17609         }else{
17610             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
17611         }
17612     },
17613
17614     /**
17615      * Trigger a reload for this node
17616      * @param {Function} callback
17617      */
17618     reload : function(callback){
17619         this.collapse(false, false);
17620         while(this.firstChild){
17621             this.removeChild(this.firstChild);
17622         }
17623         this.childrenRendered = false;
17624         this.loaded = false;
17625         if(this.isHiddenRoot()){
17626             this.expanded = false;
17627         }
17628         this.expand(false, false, callback);
17629     }
17630 });/*
17631  * Based on:
17632  * Ext JS Library 1.1.1
17633  * Copyright(c) 2006-2007, Ext JS, LLC.
17634  *
17635  * Originally Released Under LGPL - original licence link has changed is not relivant.
17636  *
17637  * Fork - LGPL
17638  * <script type="text/javascript">
17639  */
17640  
17641 /**
17642  * @class Roo.tree.TreeNodeUI
17643  * @constructor
17644  * @param {Object} node The node to render
17645  * The TreeNode UI implementation is separate from the
17646  * tree implementation. Unless you are customizing the tree UI,
17647  * you should never have to use this directly.
17648  */
17649 Roo.tree.TreeNodeUI = function(node){
17650     this.node = node;
17651     this.rendered = false;
17652     this.animating = false;
17653     this.emptyIcon = Roo.BLANK_IMAGE_URL;
17654 };
17655
17656 Roo.tree.TreeNodeUI.prototype = {
17657     removeChild : function(node){
17658         if(this.rendered){
17659             this.ctNode.removeChild(node.ui.getEl());
17660         }
17661     },
17662
17663     beforeLoad : function(){
17664          this.addClass("x-tree-node-loading");
17665     },
17666
17667     afterLoad : function(){
17668          this.removeClass("x-tree-node-loading");
17669     },
17670
17671     onTextChange : function(node, text, oldText){
17672         if(this.rendered){
17673             this.textNode.innerHTML = text;
17674         }
17675     },
17676
17677     onDisableChange : function(node, state){
17678         this.disabled = state;
17679         if(state){
17680             this.addClass("x-tree-node-disabled");
17681         }else{
17682             this.removeClass("x-tree-node-disabled");
17683         }
17684     },
17685
17686     onSelectedChange : function(state){
17687         if(state){
17688             this.focus();
17689             this.addClass("x-tree-selected");
17690         }else{
17691             //this.blur();
17692             this.removeClass("x-tree-selected");
17693         }
17694     },
17695
17696     onMove : function(tree, node, oldParent, newParent, index, refNode){
17697         this.childIndent = null;
17698         if(this.rendered){
17699             var targetNode = newParent.ui.getContainer();
17700             if(!targetNode){//target not rendered
17701                 this.holder = document.createElement("div");
17702                 this.holder.appendChild(this.wrap);
17703                 return;
17704             }
17705             var insertBefore = refNode ? refNode.ui.getEl() : null;
17706             if(insertBefore){
17707                 targetNode.insertBefore(this.wrap, insertBefore);
17708             }else{
17709                 targetNode.appendChild(this.wrap);
17710             }
17711             this.node.renderIndent(true);
17712         }
17713     },
17714
17715     addClass : function(cls){
17716         if(this.elNode){
17717             Roo.fly(this.elNode).addClass(cls);
17718         }
17719     },
17720
17721     removeClass : function(cls){
17722         if(this.elNode){
17723             Roo.fly(this.elNode).removeClass(cls);
17724         }
17725     },
17726
17727     remove : function(){
17728         if(this.rendered){
17729             this.holder = document.createElement("div");
17730             this.holder.appendChild(this.wrap);
17731         }
17732     },
17733
17734     fireEvent : function(){
17735         return this.node.fireEvent.apply(this.node, arguments);
17736     },
17737
17738     initEvents : function(){
17739         this.node.on("move", this.onMove, this);
17740         var E = Roo.EventManager;
17741         var a = this.anchor;
17742
17743         var el = Roo.fly(a, '_treeui');
17744
17745         if(Roo.isOpera){ // opera render bug ignores the CSS
17746             el.setStyle("text-decoration", "none");
17747         }
17748
17749         el.on("click", this.onClick, this);
17750         el.on("dblclick", this.onDblClick, this);
17751
17752         if(this.checkbox){
17753             Roo.EventManager.on(this.checkbox,
17754                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
17755         }
17756
17757         el.on("contextmenu", this.onContextMenu, this);
17758
17759         var icon = Roo.fly(this.iconNode);
17760         icon.on("click", this.onClick, this);
17761         icon.on("dblclick", this.onDblClick, this);
17762         icon.on("contextmenu", this.onContextMenu, this);
17763         E.on(this.ecNode, "click", this.ecClick, this, true);
17764
17765         if(this.node.disabled){
17766             this.addClass("x-tree-node-disabled");
17767         }
17768         if(this.node.hidden){
17769             this.addClass("x-tree-node-disabled");
17770         }
17771         var ot = this.node.getOwnerTree();
17772         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
17773         if(dd && (!this.node.isRoot || ot.rootVisible)){
17774             Roo.dd.Registry.register(this.elNode, {
17775                 node: this.node,
17776                 handles: this.getDDHandles(),
17777                 isHandle: false
17778             });
17779         }
17780     },
17781
17782     getDDHandles : function(){
17783         return [this.iconNode, this.textNode];
17784     },
17785
17786     hide : function(){
17787         if(this.rendered){
17788             this.wrap.style.display = "none";
17789         }
17790     },
17791
17792     show : function(){
17793         if(this.rendered){
17794             this.wrap.style.display = "";
17795         }
17796     },
17797
17798     onContextMenu : function(e){
17799         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
17800             e.preventDefault();
17801             this.focus();
17802             this.fireEvent("contextmenu", this.node, e);
17803         }
17804     },
17805
17806     onClick : function(e){
17807         if(this.dropping){
17808             e.stopEvent();
17809             return;
17810         }
17811         if(this.fireEvent("beforeclick", this.node, e) !== false){
17812             if(!this.disabled && this.node.attributes.href){
17813                 this.fireEvent("click", this.node, e);
17814                 return;
17815             }
17816             e.preventDefault();
17817             if(this.disabled){
17818                 return;
17819             }
17820
17821             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
17822                 this.node.toggle();
17823             }
17824
17825             this.fireEvent("click", this.node, e);
17826         }else{
17827             e.stopEvent();
17828         }
17829     },
17830
17831     onDblClick : function(e){
17832         e.preventDefault();
17833         if(this.disabled){
17834             return;
17835         }
17836         if(this.checkbox){
17837             this.toggleCheck();
17838         }
17839         if(!this.animating && this.node.hasChildNodes()){
17840             this.node.toggle();
17841         }
17842         this.fireEvent("dblclick", this.node, e);
17843     },
17844
17845     onCheckChange : function(){
17846         var checked = this.checkbox.checked;
17847         this.node.attributes.checked = checked;
17848         this.fireEvent('checkchange', this.node, checked);
17849     },
17850
17851     ecClick : function(e){
17852         if(!this.animating && this.node.hasChildNodes()){
17853             this.node.toggle();
17854         }
17855     },
17856
17857     startDrop : function(){
17858         this.dropping = true;
17859     },
17860
17861     // delayed drop so the click event doesn't get fired on a drop
17862     endDrop : function(){
17863        setTimeout(function(){
17864            this.dropping = false;
17865        }.createDelegate(this), 50);
17866     },
17867
17868     expand : function(){
17869         this.updateExpandIcon();
17870         this.ctNode.style.display = "";
17871     },
17872
17873     focus : function(){
17874         if(!this.node.preventHScroll){
17875             try{this.anchor.focus();
17876             }catch(e){}
17877         }else if(!Roo.isIE){
17878             try{
17879                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
17880                 var l = noscroll.scrollLeft;
17881                 this.anchor.focus();
17882                 noscroll.scrollLeft = l;
17883             }catch(e){}
17884         }
17885     },
17886
17887     toggleCheck : function(value){
17888         var cb = this.checkbox;
17889         if(cb){
17890             cb.checked = (value === undefined ? !cb.checked : value);
17891         }
17892     },
17893
17894     blur : function(){
17895         try{
17896             this.anchor.blur();
17897         }catch(e){}
17898     },
17899
17900     animExpand : function(callback){
17901         var ct = Roo.get(this.ctNode);
17902         ct.stopFx();
17903         if(!this.node.hasChildNodes()){
17904             this.updateExpandIcon();
17905             this.ctNode.style.display = "";
17906             Roo.callback(callback);
17907             return;
17908         }
17909         this.animating = true;
17910         this.updateExpandIcon();
17911
17912         ct.slideIn('t', {
17913            callback : function(){
17914                this.animating = false;
17915                Roo.callback(callback);
17916             },
17917             scope: this,
17918             duration: this.node.ownerTree.duration || .25
17919         });
17920     },
17921
17922     highlight : function(){
17923         var tree = this.node.getOwnerTree();
17924         Roo.fly(this.wrap).highlight(
17925             tree.hlColor || "C3DAF9",
17926             {endColor: tree.hlBaseColor}
17927         );
17928     },
17929
17930     collapse : function(){
17931         this.updateExpandIcon();
17932         this.ctNode.style.display = "none";
17933     },
17934
17935     animCollapse : function(callback){
17936         var ct = Roo.get(this.ctNode);
17937         ct.enableDisplayMode('block');
17938         ct.stopFx();
17939
17940         this.animating = true;
17941         this.updateExpandIcon();
17942
17943         ct.slideOut('t', {
17944             callback : function(){
17945                this.animating = false;
17946                Roo.callback(callback);
17947             },
17948             scope: this,
17949             duration: this.node.ownerTree.duration || .25
17950         });
17951     },
17952
17953     getContainer : function(){
17954         return this.ctNode;
17955     },
17956
17957     getEl : function(){
17958         return this.wrap;
17959     },
17960
17961     appendDDGhost : function(ghostNode){
17962         ghostNode.appendChild(this.elNode.cloneNode(true));
17963     },
17964
17965     getDDRepairXY : function(){
17966         return Roo.lib.Dom.getXY(this.iconNode);
17967     },
17968
17969     onRender : function(){
17970         this.render();
17971     },
17972
17973     render : function(bulkRender){
17974         var n = this.node, a = n.attributes;
17975         var targetNode = n.parentNode ?
17976               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
17977
17978         if(!this.rendered){
17979             this.rendered = true;
17980
17981             this.renderElements(n, a, targetNode, bulkRender);
17982
17983             if(a.qtip){
17984                if(this.textNode.setAttributeNS){
17985                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
17986                    if(a.qtipTitle){
17987                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
17988                    }
17989                }else{
17990                    this.textNode.setAttribute("ext:qtip", a.qtip);
17991                    if(a.qtipTitle){
17992                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
17993                    }
17994                }
17995             }else if(a.qtipCfg){
17996                 a.qtipCfg.target = Roo.id(this.textNode);
17997                 Roo.QuickTips.register(a.qtipCfg);
17998             }
17999             this.initEvents();
18000             if(!this.node.expanded){
18001                 this.updateExpandIcon();
18002             }
18003         }else{
18004             if(bulkRender === true) {
18005                 targetNode.appendChild(this.wrap);
18006             }
18007         }
18008     },
18009
18010     renderElements : function(n, a, targetNode, bulkRender){
18011         // add some indent caching, this helps performance when rendering a large tree
18012         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
18013         var t = n.getOwnerTree();
18014         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
18015         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
18016         var cb = typeof a.checked == 'boolean';
18017         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
18018         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
18019             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
18020             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
18021             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
18022             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
18023             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
18024              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
18025                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
18026             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
18027             "</li>"];
18028
18029         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
18030             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
18031                                 n.nextSibling.ui.getEl(), buf.join(""));
18032         }else{
18033             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
18034         }
18035
18036         this.elNode = this.wrap.childNodes[0];
18037         this.ctNode = this.wrap.childNodes[1];
18038         var cs = this.elNode.childNodes;
18039         this.indentNode = cs[0];
18040         this.ecNode = cs[1];
18041         this.iconNode = cs[2];
18042         var index = 3;
18043         if(cb){
18044             this.checkbox = cs[3];
18045             index++;
18046         }
18047         this.anchor = cs[index];
18048         this.textNode = cs[index].firstChild;
18049     },
18050
18051     getAnchor : function(){
18052         return this.anchor;
18053     },
18054
18055     getTextEl : function(){
18056         return this.textNode;
18057     },
18058
18059     getIconEl : function(){
18060         return this.iconNode;
18061     },
18062
18063     isChecked : function(){
18064         return this.checkbox ? this.checkbox.checked : false;
18065     },
18066
18067     updateExpandIcon : function(){
18068         if(this.rendered){
18069             var n = this.node, c1, c2;
18070             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
18071             var hasChild = n.hasChildNodes();
18072             if(hasChild){
18073                 if(n.expanded){
18074                     cls += "-minus";
18075                     c1 = "x-tree-node-collapsed";
18076                     c2 = "x-tree-node-expanded";
18077                 }else{
18078                     cls += "-plus";
18079                     c1 = "x-tree-node-expanded";
18080                     c2 = "x-tree-node-collapsed";
18081                 }
18082                 if(this.wasLeaf){
18083                     this.removeClass("x-tree-node-leaf");
18084                     this.wasLeaf = false;
18085                 }
18086                 if(this.c1 != c1 || this.c2 != c2){
18087                     Roo.fly(this.elNode).replaceClass(c1, c2);
18088                     this.c1 = c1; this.c2 = c2;
18089                 }
18090             }else{
18091                 if(!this.wasLeaf){
18092                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
18093                     delete this.c1;
18094                     delete this.c2;
18095                     this.wasLeaf = true;
18096                 }
18097             }
18098             var ecc = "x-tree-ec-icon "+cls;
18099             if(this.ecc != ecc){
18100                 this.ecNode.className = ecc;
18101                 this.ecc = ecc;
18102             }
18103         }
18104     },
18105
18106     getChildIndent : function(){
18107         if(!this.childIndent){
18108             var buf = [];
18109             var p = this.node;
18110             while(p){
18111                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
18112                     if(!p.isLast()) {
18113                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
18114                     } else {
18115                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
18116                     }
18117                 }
18118                 p = p.parentNode;
18119             }
18120             this.childIndent = buf.join("");
18121         }
18122         return this.childIndent;
18123     },
18124
18125     renderIndent : function(){
18126         if(this.rendered){
18127             var indent = "";
18128             var p = this.node.parentNode;
18129             if(p){
18130                 indent = p.ui.getChildIndent();
18131             }
18132             if(this.indentMarkup != indent){ // don't rerender if not required
18133                 this.indentNode.innerHTML = indent;
18134                 this.indentMarkup = indent;
18135             }
18136             this.updateExpandIcon();
18137         }
18138     }
18139 };
18140
18141 Roo.tree.RootTreeNodeUI = function(){
18142     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
18143 };
18144 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
18145     render : function(){
18146         if(!this.rendered){
18147             var targetNode = this.node.ownerTree.innerCt.dom;
18148             this.node.expanded = true;
18149             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
18150             this.wrap = this.ctNode = targetNode.firstChild;
18151         }
18152     },
18153     collapse : function(){
18154     },
18155     expand : function(){
18156     }
18157 });/*
18158  * Based on:
18159  * Ext JS Library 1.1.1
18160  * Copyright(c) 2006-2007, Ext JS, LLC.
18161  *
18162  * Originally Released Under LGPL - original licence link has changed is not relivant.
18163  *
18164  * Fork - LGPL
18165  * <script type="text/javascript">
18166  */
18167 /**
18168  * @class Roo.tree.TreeLoader
18169  * @extends Roo.util.Observable
18170  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
18171  * nodes from a specified URL. The response must be a javascript Array definition
18172  * who's elements are node definition objects. eg:
18173  * <pre><code>
18174    [{ 'id': 1, 'text': 'A folder Node', 'leaf': false },
18175     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }]
18176 </code></pre>
18177  * <br><br>
18178  * A server request is sent, and child nodes are loaded only when a node is expanded.
18179  * The loading node's id is passed to the server under the parameter name "node" to
18180  * enable the server to produce the correct child nodes.
18181  * <br><br>
18182  * To pass extra parameters, an event handler may be attached to the "beforeload"
18183  * event, and the parameters specified in the TreeLoader's baseParams property:
18184  * <pre><code>
18185     myTreeLoader.on("beforeload", function(treeLoader, node) {
18186         this.baseParams.category = node.attributes.category;
18187     }, this);
18188 </code></pre><
18189  * This would pass an HTTP parameter called "category" to the server containing
18190  * the value of the Node's "category" attribute.
18191  * @constructor
18192  * Creates a new Treeloader.
18193  * @param {Object} config A config object containing config properties.
18194  */
18195 Roo.tree.TreeLoader = function(config){
18196     this.baseParams = {};
18197     this.requestMethod = "POST";
18198     Roo.apply(this, config);
18199
18200     this.addEvents({
18201     
18202         /**
18203          * @event beforeload
18204          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
18205          * @param {Object} This TreeLoader object.
18206          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18207          * @param {Object} callback The callback function specified in the {@link #load} call.
18208          */
18209         beforeload : true,
18210         /**
18211          * @event load
18212          * Fires when the node has been successfuly loaded.
18213          * @param {Object} This TreeLoader object.
18214          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18215          * @param {Object} response The response object containing the data from the server.
18216          */
18217         load : true,
18218         /**
18219          * @event loadexception
18220          * Fires if the network request failed.
18221          * @param {Object} This TreeLoader object.
18222          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18223          * @param {Object} response The response object containing the data from the server.
18224          */
18225         loadexception : true,
18226         /**
18227          * @event create
18228          * Fires before a node is created, enabling you to return custom Node types 
18229          * @param {Object} This TreeLoader object.
18230          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
18231          */
18232         create : true
18233     });
18234
18235     Roo.tree.TreeLoader.superclass.constructor.call(this);
18236 };
18237
18238 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
18239     /**
18240     * @cfg {String} dataUrl The URL from which to request a Json string which
18241     * specifies an array of node definition object representing the child nodes
18242     * to be loaded.
18243     */
18244     /**
18245     * @cfg {Object} baseParams (optional) An object containing properties which
18246     * specify HTTP parameters to be passed to each request for child nodes.
18247     */
18248     /**
18249     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
18250     * created by this loader. If the attributes sent by the server have an attribute in this object,
18251     * they take priority.
18252     */
18253     /**
18254     * @cfg {Object} uiProviders (optional) An object containing properties which
18255     * 
18256     * DEPRECIATED - use 'create' event handler to modify attributes - which affect creation.
18257     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
18258     * <i>uiProvider</i> attribute of a returned child node is a string rather
18259     * than a reference to a TreeNodeUI implementation, this that string value
18260     * is used as a property name in the uiProviders object. You can define the provider named
18261     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
18262     */
18263     uiProviders : {},
18264
18265     /**
18266     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
18267     * child nodes before loading.
18268     */
18269     clearOnLoad : true,
18270
18271     /**
18272     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
18273     * property on loading, rather than expecting an array. (eg. more compatible to a standard
18274     * Grid query { data : [ .....] }
18275     */
18276     
18277     root : false,
18278      /**
18279     * @cfg {String} queryParam (optional) 
18280     * Name of the query as it will be passed on the querystring (defaults to 'node')
18281     * eg. the request will be ?node=[id]
18282     */
18283     
18284     
18285     queryParam: false,
18286     
18287     /**
18288      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
18289      * This is called automatically when a node is expanded, but may be used to reload
18290      * a node (or append new children if the {@link #clearOnLoad} option is false.)
18291      * @param {Roo.tree.TreeNode} node
18292      * @param {Function} callback
18293      */
18294     load : function(node, callback){
18295         if(this.clearOnLoad){
18296             while(node.firstChild){
18297                 node.removeChild(node.firstChild);
18298             }
18299         }
18300         if(node.attributes.children){ // preloaded json children
18301             var cs = node.attributes.children;
18302             for(var i = 0, len = cs.length; i < len; i++){
18303                 node.appendChild(this.createNode(cs[i]));
18304             }
18305             if(typeof callback == "function"){
18306                 callback();
18307             }
18308         }else if(this.dataUrl){
18309             this.requestData(node, callback);
18310         }
18311     },
18312
18313     getParams: function(node){
18314         var buf = [], bp = this.baseParams;
18315         for(var key in bp){
18316             if(typeof bp[key] != "function"){
18317                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
18318             }
18319         }
18320         var n = this.queryParam === false ? 'node' : this.queryParam;
18321         buf.push(n + "=", encodeURIComponent(node.id));
18322         return buf.join("");
18323     },
18324
18325     requestData : function(node, callback){
18326         if(this.fireEvent("beforeload", this, node, callback) !== false){
18327             this.transId = Roo.Ajax.request({
18328                 method:this.requestMethod,
18329                 url: this.dataUrl||this.url,
18330                 success: this.handleResponse,
18331                 failure: this.handleFailure,
18332                 scope: this,
18333                 argument: {callback: callback, node: node},
18334                 params: this.getParams(node)
18335             });
18336         }else{
18337             // if the load is cancelled, make sure we notify
18338             // the node that we are done
18339             if(typeof callback == "function"){
18340                 callback();
18341             }
18342         }
18343     },
18344
18345     isLoading : function(){
18346         return this.transId ? true : false;
18347     },
18348
18349     abort : function(){
18350         if(this.isLoading()){
18351             Roo.Ajax.abort(this.transId);
18352         }
18353     },
18354
18355     // private
18356     createNode : function(attr){
18357         // apply baseAttrs, nice idea Corey!
18358         if(this.baseAttrs){
18359             Roo.applyIf(attr, this.baseAttrs);
18360         }
18361         if(this.applyLoader !== false){
18362             attr.loader = this;
18363         }
18364         // uiProvider = depreciated..
18365         
18366         if(typeof(attr.uiProvider) == 'string'){
18367            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
18368                 /**  eval:var:attr */ eval(attr.uiProvider);
18369         }
18370         if(typeof(this.uiProviders['default']) != 'undefined') {
18371             attr.uiProvider = this.uiProviders['default'];
18372         }
18373         
18374         this.fireEvent('create', this, attr);
18375         
18376         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
18377         return(attr.leaf ?
18378                         new Roo.tree.TreeNode(attr) :
18379                         new Roo.tree.AsyncTreeNode(attr));
18380     },
18381
18382     processResponse : function(response, node, callback){
18383         var json = response.responseText;
18384         try {
18385             
18386             var o = /**  eval:var:zzzzzzzzzz */ eval("("+json+")");
18387             if (this.root !== false) {
18388                 o = o[this.root];
18389             }
18390             
18391             for(var i = 0, len = o.length; i < len; i++){
18392                 var n = this.createNode(o[i]);
18393                 if(n){
18394                     node.appendChild(n);
18395                 }
18396             }
18397             if(typeof callback == "function"){
18398                 callback(this, node);
18399             }
18400         }catch(e){
18401             this.handleFailure(response);
18402         }
18403     },
18404
18405     handleResponse : function(response){
18406         this.transId = false;
18407         var a = response.argument;
18408         this.processResponse(response, a.node, a.callback);
18409         this.fireEvent("load", this, a.node, response);
18410     },
18411
18412     handleFailure : function(response){
18413         this.transId = false;
18414         var a = response.argument;
18415         this.fireEvent("loadexception", this, a.node, response);
18416         if(typeof a.callback == "function"){
18417             a.callback(this, a.node);
18418         }
18419     }
18420 });/*
18421  * Based on:
18422  * Ext JS Library 1.1.1
18423  * Copyright(c) 2006-2007, Ext JS, LLC.
18424  *
18425  * Originally Released Under LGPL - original licence link has changed is not relivant.
18426  *
18427  * Fork - LGPL
18428  * <script type="text/javascript">
18429  */
18430
18431 /**
18432 * @class Roo.tree.TreeFilter
18433 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
18434 * @param {TreePanel} tree
18435 * @param {Object} config (optional)
18436  */
18437 Roo.tree.TreeFilter = function(tree, config){
18438     this.tree = tree;
18439     this.filtered = {};
18440     Roo.apply(this, config);
18441 };
18442
18443 Roo.tree.TreeFilter.prototype = {
18444     clearBlank:false,
18445     reverse:false,
18446     autoClear:false,
18447     remove:false,
18448
18449      /**
18450      * Filter the data by a specific attribute.
18451      * @param {String/RegExp} value Either string that the attribute value
18452      * should start with or a RegExp to test against the attribute
18453      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
18454      * @param {TreeNode} startNode (optional) The node to start the filter at.
18455      */
18456     filter : function(value, attr, startNode){
18457         attr = attr || "text";
18458         var f;
18459         if(typeof value == "string"){
18460             var vlen = value.length;
18461             // auto clear empty filter
18462             if(vlen == 0 && this.clearBlank){
18463                 this.clear();
18464                 return;
18465             }
18466             value = value.toLowerCase();
18467             f = function(n){
18468                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
18469             };
18470         }else if(value.exec){ // regex?
18471             f = function(n){
18472                 return value.test(n.attributes[attr]);
18473             };
18474         }else{
18475             throw 'Illegal filter type, must be string or regex';
18476         }
18477         this.filterBy(f, null, startNode);
18478         },
18479
18480     /**
18481      * Filter by a function. The passed function will be called with each
18482      * node in the tree (or from the startNode). If the function returns true, the node is kept
18483      * otherwise it is filtered. If a node is filtered, its children are also filtered.
18484      * @param {Function} fn The filter function
18485      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
18486      */
18487     filterBy : function(fn, scope, startNode){
18488         startNode = startNode || this.tree.root;
18489         if(this.autoClear){
18490             this.clear();
18491         }
18492         var af = this.filtered, rv = this.reverse;
18493         var f = function(n){
18494             if(n == startNode){
18495                 return true;
18496             }
18497             if(af[n.id]){
18498                 return false;
18499             }
18500             var m = fn.call(scope || n, n);
18501             if(!m || rv){
18502                 af[n.id] = n;
18503                 n.ui.hide();
18504                 return false;
18505             }
18506             return true;
18507         };
18508         startNode.cascade(f);
18509         if(this.remove){
18510            for(var id in af){
18511                if(typeof id != "function"){
18512                    var n = af[id];
18513                    if(n && n.parentNode){
18514                        n.parentNode.removeChild(n);
18515                    }
18516                }
18517            }
18518         }
18519     },
18520
18521     /**
18522      * Clears the current filter. Note: with the "remove" option
18523      * set a filter cannot be cleared.
18524      */
18525     clear : function(){
18526         var t = this.tree;
18527         var af = this.filtered;
18528         for(var id in af){
18529             if(typeof id != "function"){
18530                 var n = af[id];
18531                 if(n){
18532                     n.ui.show();
18533                 }
18534             }
18535         }
18536         this.filtered = {};
18537     }
18538 };
18539 /*
18540  * Based on:
18541  * Ext JS Library 1.1.1
18542  * Copyright(c) 2006-2007, Ext JS, LLC.
18543  *
18544  * Originally Released Under LGPL - original licence link has changed is not relivant.
18545  *
18546  * Fork - LGPL
18547  * <script type="text/javascript">
18548  */
18549  
18550
18551 /**
18552  * @class Roo.tree.TreeSorter
18553  * Provides sorting of nodes in a TreePanel
18554  * 
18555  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
18556  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
18557  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
18558  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
18559  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
18560  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
18561  * @constructor
18562  * @param {TreePanel} tree
18563  * @param {Object} config
18564  */
18565 Roo.tree.TreeSorter = function(tree, config){
18566     Roo.apply(this, config);
18567     tree.on("beforechildrenrendered", this.doSort, this);
18568     tree.on("append", this.updateSort, this);
18569     tree.on("insert", this.updateSort, this);
18570     
18571     var dsc = this.dir && this.dir.toLowerCase() == "desc";
18572     var p = this.property || "text";
18573     var sortType = this.sortType;
18574     var fs = this.folderSort;
18575     var cs = this.caseSensitive === true;
18576     var leafAttr = this.leafAttr || 'leaf';
18577
18578     this.sortFn = function(n1, n2){
18579         if(fs){
18580             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
18581                 return 1;
18582             }
18583             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
18584                 return -1;
18585             }
18586         }
18587         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
18588         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
18589         if(v1 < v2){
18590                         return dsc ? +1 : -1;
18591                 }else if(v1 > v2){
18592                         return dsc ? -1 : +1;
18593         }else{
18594                 return 0;
18595         }
18596     };
18597 };
18598
18599 Roo.tree.TreeSorter.prototype = {
18600     doSort : function(node){
18601         node.sort(this.sortFn);
18602     },
18603     
18604     compareNodes : function(n1, n2){
18605         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
18606     },
18607     
18608     updateSort : function(tree, node){
18609         if(node.childrenRendered){
18610             this.doSort.defer(1, this, [node]);
18611         }
18612     }
18613 };/*
18614  * Based on:
18615  * Ext JS Library 1.1.1
18616  * Copyright(c) 2006-2007, Ext JS, LLC.
18617  *
18618  * Originally Released Under LGPL - original licence link has changed is not relivant.
18619  *
18620  * Fork - LGPL
18621  * <script type="text/javascript">
18622  */
18623
18624 if(Roo.dd.DropZone){
18625     
18626 Roo.tree.TreeDropZone = function(tree, config){
18627     this.allowParentInsert = false;
18628     this.allowContainerDrop = false;
18629     this.appendOnly = false;
18630     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
18631     this.tree = tree;
18632     this.lastInsertClass = "x-tree-no-status";
18633     this.dragOverData = {};
18634 };
18635
18636 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
18637     ddGroup : "TreeDD",
18638     
18639     expandDelay : 1000,
18640     
18641     expandNode : function(node){
18642         if(node.hasChildNodes() && !node.isExpanded()){
18643             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
18644         }
18645     },
18646     
18647     queueExpand : function(node){
18648         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
18649     },
18650     
18651     cancelExpand : function(){
18652         if(this.expandProcId){
18653             clearTimeout(this.expandProcId);
18654             this.expandProcId = false;
18655         }
18656     },
18657     
18658     isValidDropPoint : function(n, pt, dd, e, data){
18659         if(!n || !data){ return false; }
18660         var targetNode = n.node;
18661         var dropNode = data.node;
18662         // default drop rules
18663         if(!(targetNode && targetNode.isTarget && pt)){
18664             return false;
18665         }
18666         if(pt == "append" && targetNode.allowChildren === false){
18667             return false;
18668         }
18669         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
18670             return false;
18671         }
18672         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
18673             return false;
18674         }
18675         // reuse the object
18676         var overEvent = this.dragOverData;
18677         overEvent.tree = this.tree;
18678         overEvent.target = targetNode;
18679         overEvent.data = data;
18680         overEvent.point = pt;
18681         overEvent.source = dd;
18682         overEvent.rawEvent = e;
18683         overEvent.dropNode = dropNode;
18684         overEvent.cancel = false;  
18685         var result = this.tree.fireEvent("nodedragover", overEvent);
18686         return overEvent.cancel === false && result !== false;
18687     },
18688     
18689     getDropPoint : function(e, n, dd){
18690         var tn = n.node;
18691         if(tn.isRoot){
18692             return tn.allowChildren !== false ? "append" : false; // always append for root
18693         }
18694         var dragEl = n.ddel;
18695         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
18696         var y = Roo.lib.Event.getPageY(e);
18697         //var noAppend = tn.allowChildren === false || tn.isLeaf();
18698         
18699         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
18700         var noAppend = tn.allowChildren === false;
18701         if(this.appendOnly || tn.parentNode.allowChildren === false){
18702             return noAppend ? false : "append";
18703         }
18704         var noBelow = false;
18705         if(!this.allowParentInsert){
18706             noBelow = tn.hasChildNodes() && tn.isExpanded();
18707         }
18708         var q = (b - t) / (noAppend ? 2 : 3);
18709         if(y >= t && y < (t + q)){
18710             return "above";
18711         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
18712             return "below";
18713         }else{
18714             return "append";
18715         }
18716     },
18717     
18718     onNodeEnter : function(n, dd, e, data){
18719         this.cancelExpand();
18720     },
18721     
18722     onNodeOver : function(n, dd, e, data){
18723         var pt = this.getDropPoint(e, n, dd);
18724         var node = n.node;
18725         
18726         // auto node expand check
18727         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
18728             this.queueExpand(node);
18729         }else if(pt != "append"){
18730             this.cancelExpand();
18731         }
18732         
18733         // set the insert point style on the target node
18734         var returnCls = this.dropNotAllowed;
18735         if(this.isValidDropPoint(n, pt, dd, e, data)){
18736            if(pt){
18737                var el = n.ddel;
18738                var cls;
18739                if(pt == "above"){
18740                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
18741                    cls = "x-tree-drag-insert-above";
18742                }else if(pt == "below"){
18743                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
18744                    cls = "x-tree-drag-insert-below";
18745                }else{
18746                    returnCls = "x-tree-drop-ok-append";
18747                    cls = "x-tree-drag-append";
18748                }
18749                if(this.lastInsertClass != cls){
18750                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
18751                    this.lastInsertClass = cls;
18752                }
18753            }
18754        }
18755        return returnCls;
18756     },
18757     
18758     onNodeOut : function(n, dd, e, data){
18759         this.cancelExpand();
18760         this.removeDropIndicators(n);
18761     },
18762     
18763     onNodeDrop : function(n, dd, e, data){
18764         var point = this.getDropPoint(e, n, dd);
18765         var targetNode = n.node;
18766         targetNode.ui.startDrop();
18767         if(!this.isValidDropPoint(n, point, dd, e, data)){
18768             targetNode.ui.endDrop();
18769             return false;
18770         }
18771         // first try to find the drop node
18772         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
18773         var dropEvent = {
18774             tree : this.tree,
18775             target: targetNode,
18776             data: data,
18777             point: point,
18778             source: dd,
18779             rawEvent: e,
18780             dropNode: dropNode,
18781             cancel: !dropNode   
18782         };
18783         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
18784         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
18785             targetNode.ui.endDrop();
18786             return false;
18787         }
18788         // allow target changing
18789         targetNode = dropEvent.target;
18790         if(point == "append" && !targetNode.isExpanded()){
18791             targetNode.expand(false, null, function(){
18792                 this.completeDrop(dropEvent);
18793             }.createDelegate(this));
18794         }else{
18795             this.completeDrop(dropEvent);
18796         }
18797         return true;
18798     },
18799     
18800     completeDrop : function(de){
18801         var ns = de.dropNode, p = de.point, t = de.target;
18802         if(!(ns instanceof Array)){
18803             ns = [ns];
18804         }
18805         var n;
18806         for(var i = 0, len = ns.length; i < len; i++){
18807             n = ns[i];
18808             if(p == "above"){
18809                 t.parentNode.insertBefore(n, t);
18810             }else if(p == "below"){
18811                 t.parentNode.insertBefore(n, t.nextSibling);
18812             }else{
18813                 t.appendChild(n);
18814             }
18815         }
18816         n.ui.focus();
18817         if(this.tree.hlDrop){
18818             n.ui.highlight();
18819         }
18820         t.ui.endDrop();
18821         this.tree.fireEvent("nodedrop", de);
18822     },
18823     
18824     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
18825         if(this.tree.hlDrop){
18826             dropNode.ui.focus();
18827             dropNode.ui.highlight();
18828         }
18829         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
18830     },
18831     
18832     getTree : function(){
18833         return this.tree;
18834     },
18835     
18836     removeDropIndicators : function(n){
18837         if(n && n.ddel){
18838             var el = n.ddel;
18839             Roo.fly(el).removeClass([
18840                     "x-tree-drag-insert-above",
18841                     "x-tree-drag-insert-below",
18842                     "x-tree-drag-append"]);
18843             this.lastInsertClass = "_noclass";
18844         }
18845     },
18846     
18847     beforeDragDrop : function(target, e, id){
18848         this.cancelExpand();
18849         return true;
18850     },
18851     
18852     afterRepair : function(data){
18853         if(data && Roo.enableFx){
18854             data.node.ui.highlight();
18855         }
18856         this.hideProxy();
18857     }    
18858 });
18859
18860 }
18861 /*
18862  * Based on:
18863  * Ext JS Library 1.1.1
18864  * Copyright(c) 2006-2007, Ext JS, LLC.
18865  *
18866  * Originally Released Under LGPL - original licence link has changed is not relivant.
18867  *
18868  * Fork - LGPL
18869  * <script type="text/javascript">
18870  */
18871  
18872
18873 if(Roo.dd.DragZone){
18874 Roo.tree.TreeDragZone = function(tree, config){
18875     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
18876     this.tree = tree;
18877 };
18878
18879 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
18880     ddGroup : "TreeDD",
18881     
18882     onBeforeDrag : function(data, e){
18883         var n = data.node;
18884         return n && n.draggable && !n.disabled;
18885     },
18886     
18887     onInitDrag : function(e){
18888         var data = this.dragData;
18889         this.tree.getSelectionModel().select(data.node);
18890         this.proxy.update("");
18891         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
18892         this.tree.fireEvent("startdrag", this.tree, data.node, e);
18893     },
18894     
18895     getRepairXY : function(e, data){
18896         return data.node.ui.getDDRepairXY();
18897     },
18898     
18899     onEndDrag : function(data, e){
18900         this.tree.fireEvent("enddrag", this.tree, data.node, e);
18901     },
18902     
18903     onValidDrop : function(dd, e, id){
18904         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
18905         this.hideProxy();
18906     },
18907     
18908     beforeInvalidDrop : function(e, id){
18909         // this scrolls the original position back into view
18910         var sm = this.tree.getSelectionModel();
18911         sm.clearSelections();
18912         sm.select(this.dragData.node);
18913     }
18914 });
18915 }/*
18916  * Based on:
18917  * Ext JS Library 1.1.1
18918  * Copyright(c) 2006-2007, Ext JS, LLC.
18919  *
18920  * Originally Released Under LGPL - original licence link has changed is not relivant.
18921  *
18922  * Fork - LGPL
18923  * <script type="text/javascript">
18924  */
18925 /**
18926  * @class Roo.tree.TreeEditor
18927  * @extends Roo.Editor
18928  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
18929  * as the editor field.
18930  * @constructor
18931  * @param {TreePanel} tree
18932  * @param {Object} config Either a prebuilt {@link Roo.form.Field} instance or a Field config object
18933  */
18934 Roo.tree.TreeEditor = function(tree, config){
18935     config = config || {};
18936     var field = config.events ? config : new Roo.form.TextField(config);
18937     Roo.tree.TreeEditor.superclass.constructor.call(this, field);
18938
18939     this.tree = tree;
18940
18941     tree.on('beforeclick', this.beforeNodeClick, this);
18942     tree.getTreeEl().on('mousedown', this.hide, this);
18943     this.on('complete', this.updateNode, this);
18944     this.on('beforestartedit', this.fitToTree, this);
18945     this.on('startedit', this.bindScroll, this, {delay:10});
18946     this.on('specialkey', this.onSpecialKey, this);
18947 };
18948
18949 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
18950     /**
18951      * @cfg {String} alignment
18952      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
18953      */
18954     alignment: "l-l",
18955     // inherit
18956     autoSize: false,
18957     /**
18958      * @cfg {Boolean} hideEl
18959      * True to hide the bound element while the editor is displayed (defaults to false)
18960      */
18961     hideEl : false,
18962     /**
18963      * @cfg {String} cls
18964      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
18965      */
18966     cls: "x-small-editor x-tree-editor",
18967     /**
18968      * @cfg {Boolean} shim
18969      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
18970      */
18971     shim:false,
18972     // inherit
18973     shadow:"frame",
18974     /**
18975      * @cfg {Number} maxWidth
18976      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
18977      * the containing tree element's size, it will be automatically limited for you to the container width, taking
18978      * scroll and client offsets into account prior to each edit.
18979      */
18980     maxWidth: 250,
18981
18982     editDelay : 350,
18983
18984     // private
18985     fitToTree : function(ed, el){
18986         var td = this.tree.getTreeEl().dom, nd = el.dom;
18987         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
18988             td.scrollLeft = nd.offsetLeft;
18989         }
18990         var w = Math.min(
18991                 this.maxWidth,
18992                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
18993         this.setSize(w, '');
18994     },
18995
18996     // private
18997     triggerEdit : function(node){
18998         this.completeEdit();
18999         this.editNode = node;
19000         this.startEdit(node.ui.textNode, node.text);
19001     },
19002
19003     // private
19004     bindScroll : function(){
19005         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
19006     },
19007
19008     // private
19009     beforeNodeClick : function(node, e){
19010         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
19011         this.lastClick = new Date();
19012         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
19013             e.stopEvent();
19014             this.triggerEdit(node);
19015             return false;
19016         }
19017     },
19018
19019     // private
19020     updateNode : function(ed, value){
19021         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
19022         this.editNode.setText(value);
19023     },
19024
19025     // private
19026     onHide : function(){
19027         Roo.tree.TreeEditor.superclass.onHide.call(this);
19028         if(this.editNode){
19029             this.editNode.ui.focus();
19030         }
19031     },
19032
19033     // private
19034     onSpecialKey : function(field, e){
19035         var k = e.getKey();
19036         if(k == e.ESC){
19037             e.stopEvent();
19038             this.cancelEdit();
19039         }else if(k == e.ENTER && !e.hasModifier()){
19040             e.stopEvent();
19041             this.completeEdit();
19042         }
19043     }
19044 });//<Script type="text/javascript">
19045 /*
19046  * Based on:
19047  * Ext JS Library 1.1.1
19048  * Copyright(c) 2006-2007, Ext JS, LLC.
19049  *
19050  * Originally Released Under LGPL - original licence link has changed is not relivant.
19051  *
19052  * Fork - LGPL
19053  * <script type="text/javascript">
19054  */
19055  
19056 /**
19057  * Not documented??? - probably should be...
19058  */
19059
19060 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
19061     //focus: Roo.emptyFn, // prevent odd scrolling behavior
19062     
19063     renderElements : function(n, a, targetNode, bulkRender){
19064         //consel.log("renderElements?");
19065         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
19066
19067         var t = n.getOwnerTree();
19068         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
19069         
19070         var cols = t.columns;
19071         var bw = t.borderWidth;
19072         var c = cols[0];
19073         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
19074          var cb = typeof a.checked == "boolean";
19075         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19076         var colcls = 'x-t-' + tid + '-c0';
19077         var buf = [
19078             '<li class="x-tree-node">',
19079             
19080                 
19081                 '<div class="x-tree-node-el ', a.cls,'">',
19082                     // extran...
19083                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
19084                 
19085                 
19086                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
19087                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
19088                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
19089                            (a.icon ? ' x-tree-node-inline-icon' : ''),
19090                            (a.iconCls ? ' '+a.iconCls : ''),
19091                            '" unselectable="on" />',
19092                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
19093                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
19094                              
19095                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19096                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
19097                             '<span unselectable="on" qtip="' + tx + '">',
19098                              tx,
19099                              '</span></a>' ,
19100                     '</div>',
19101                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19102                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
19103                  ];
19104         for(var i = 1, len = cols.length; i < len; i++){
19105             c = cols[i];
19106             colcls = 'x-t-' + tid + '-c' +i;
19107             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19108             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
19109                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
19110                       "</div>");
19111          }
19112          
19113          buf.push(
19114             '</a>',
19115             '<div class="x-clear"></div></div>',
19116             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
19117             "</li>");
19118         
19119         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
19120             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
19121                                 n.nextSibling.ui.getEl(), buf.join(""));
19122         }else{
19123             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
19124         }
19125         var el = this.wrap.firstChild;
19126         this.elRow = el;
19127         this.elNode = el.firstChild;
19128         this.ranchor = el.childNodes[1];
19129         this.ctNode = this.wrap.childNodes[1];
19130         var cs = el.firstChild.childNodes;
19131         this.indentNode = cs[0];
19132         this.ecNode = cs[1];
19133         this.iconNode = cs[2];
19134         var index = 3;
19135         if(cb){
19136             this.checkbox = cs[3];
19137             index++;
19138         }
19139         this.anchor = cs[index];
19140         
19141         this.textNode = cs[index].firstChild;
19142         
19143         //el.on("click", this.onClick, this);
19144         //el.on("dblclick", this.onDblClick, this);
19145         
19146         
19147        // console.log(this);
19148     },
19149     initEvents : function(){
19150         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
19151         
19152             
19153         var a = this.ranchor;
19154
19155         var el = Roo.get(a);
19156
19157         if(Roo.isOpera){ // opera render bug ignores the CSS
19158             el.setStyle("text-decoration", "none");
19159         }
19160
19161         el.on("click", this.onClick, this);
19162         el.on("dblclick", this.onDblClick, this);
19163         el.on("contextmenu", this.onContextMenu, this);
19164         
19165     },
19166     
19167     /*onSelectedChange : function(state){
19168         if(state){
19169             this.focus();
19170             this.addClass("x-tree-selected");
19171         }else{
19172             //this.blur();
19173             this.removeClass("x-tree-selected");
19174         }
19175     },*/
19176     addClass : function(cls){
19177         if(this.elRow){
19178             Roo.fly(this.elRow).addClass(cls);
19179         }
19180         
19181     },
19182     
19183     
19184     removeClass : function(cls){
19185         if(this.elRow){
19186             Roo.fly(this.elRow).removeClass(cls);
19187         }
19188     }
19189
19190     
19191     
19192 });//<Script type="text/javascript">
19193
19194 /*
19195  * Based on:
19196  * Ext JS Library 1.1.1
19197  * Copyright(c) 2006-2007, Ext JS, LLC.
19198  *
19199  * Originally Released Under LGPL - original licence link has changed is not relivant.
19200  *
19201  * Fork - LGPL
19202  * <script type="text/javascript">
19203  */
19204  
19205
19206 /**
19207  * @class Roo.tree.ColumnTree
19208  * @extends Roo.data.TreePanel
19209  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
19210  * @cfg {int} borderWidth  compined right/left border allowance
19211  * @constructor
19212  * @param {String/HTMLElement/Element} el The container element
19213  * @param {Object} config
19214  */
19215 Roo.tree.ColumnTree =  function(el, config)
19216 {
19217    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
19218    this.addEvents({
19219         /**
19220         * @event resize
19221         * Fire this event on a container when it resizes
19222         * @param {int} w Width
19223         * @param {int} h Height
19224         */
19225        "resize" : true
19226     });
19227     this.on('resize', this.onResize, this);
19228 };
19229
19230 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
19231     //lines:false,
19232     
19233     
19234     borderWidth: Roo.isBorderBox ? 0 : 2, 
19235     headEls : false,
19236     
19237     render : function(){
19238         // add the header.....
19239        
19240         Roo.tree.ColumnTree.superclass.render.apply(this);
19241         
19242         this.el.addClass('x-column-tree');
19243         
19244         this.headers = this.el.createChild(
19245             {cls:'x-tree-headers'},this.innerCt.dom);
19246    
19247         var cols = this.columns, c;
19248         var totalWidth = 0;
19249         this.headEls = [];
19250         var  len = cols.length;
19251         for(var i = 0; i < len; i++){
19252              c = cols[i];
19253              totalWidth += c.width;
19254             this.headEls.push(this.headers.createChild({
19255                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
19256                  cn: {
19257                      cls:'x-tree-hd-text',
19258                      html: c.header
19259                  },
19260                  style:'width:'+(c.width-this.borderWidth)+'px;'
19261              }));
19262         }
19263         this.headers.createChild({cls:'x-clear'});
19264         // prevent floats from wrapping when clipped
19265         this.headers.setWidth(totalWidth);
19266         //this.innerCt.setWidth(totalWidth);
19267         this.innerCt.setStyle({ overflow: 'auto' });
19268         this.onResize(this.width, this.height);
19269              
19270         
19271     },
19272     onResize : function(w,h)
19273     {
19274         this.height = h;
19275         this.width = w;
19276         // resize cols..
19277         this.innerCt.setWidth(this.width);
19278         this.innerCt.setHeight(this.height-20);
19279         
19280         // headers...
19281         var cols = this.columns, c;
19282         var totalWidth = 0;
19283         var expEl = false;
19284         var len = cols.length;
19285         for(var i = 0; i < len; i++){
19286             c = cols[i];
19287             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
19288                 // it's the expander..
19289                 expEl  = this.headEls[i];
19290                 continue;
19291             }
19292             totalWidth += c.width;
19293             
19294         }
19295         if (expEl) {
19296             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
19297         }
19298         this.headers.setWidth(w-20);
19299
19300         
19301         
19302         
19303     }
19304 });
19305 /*
19306  * Based on:
19307  * Ext JS Library 1.1.1
19308  * Copyright(c) 2006-2007, Ext JS, LLC.
19309  *
19310  * Originally Released Under LGPL - original licence link has changed is not relivant.
19311  *
19312  * Fork - LGPL
19313  * <script type="text/javascript">
19314  */
19315  
19316 /**
19317  * @class Roo.menu.Menu
19318  * @extends Roo.util.Observable
19319  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
19320  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
19321  * @constructor
19322  * Creates a new Menu
19323  * @param {Object} config Configuration options
19324  */
19325 Roo.menu.Menu = function(config){
19326     Roo.apply(this, config);
19327     this.id = this.id || Roo.id();
19328     this.addEvents({
19329         /**
19330          * @event beforeshow
19331          * Fires before this menu is displayed
19332          * @param {Roo.menu.Menu} this
19333          */
19334         beforeshow : true,
19335         /**
19336          * @event beforehide
19337          * Fires before this menu is hidden
19338          * @param {Roo.menu.Menu} this
19339          */
19340         beforehide : true,
19341         /**
19342          * @event show
19343          * Fires after this menu is displayed
19344          * @param {Roo.menu.Menu} this
19345          */
19346         show : true,
19347         /**
19348          * @event hide
19349          * Fires after this menu is hidden
19350          * @param {Roo.menu.Menu} this
19351          */
19352         hide : true,
19353         /**
19354          * @event click
19355          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19356          * @param {Roo.menu.Menu} this
19357          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19358          * @param {Roo.EventObject} e
19359          */
19360         click : true,
19361         /**
19362          * @event mouseover
19363          * Fires when the mouse is hovering over this menu
19364          * @param {Roo.menu.Menu} this
19365          * @param {Roo.EventObject} e
19366          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19367          */
19368         mouseover : true,
19369         /**
19370          * @event mouseout
19371          * Fires when the mouse exits this menu
19372          * @param {Roo.menu.Menu} this
19373          * @param {Roo.EventObject} e
19374          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19375          */
19376         mouseout : true,
19377         /**
19378          * @event itemclick
19379          * Fires when a menu item contained in this menu is clicked
19380          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
19381          * @param {Roo.EventObject} e
19382          */
19383         itemclick: true
19384     });
19385     if (this.registerMenu) {
19386         Roo.menu.MenuMgr.register(this);
19387     }
19388     
19389     var mis = this.items;
19390     this.items = new Roo.util.MixedCollection();
19391     if(mis){
19392         this.add.apply(this, mis);
19393     }
19394 };
19395
19396 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
19397     /**
19398      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
19399      */
19400     minWidth : 120,
19401     /**
19402      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
19403      * for bottom-right shadow (defaults to "sides")
19404      */
19405     shadow : "sides",
19406     /**
19407      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
19408      * this menu (defaults to "tl-tr?")
19409      */
19410     subMenuAlign : "tl-tr?",
19411     /**
19412      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
19413      * relative to its element of origin (defaults to "tl-bl?")
19414      */
19415     defaultAlign : "tl-bl?",
19416     /**
19417      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
19418      */
19419     allowOtherMenus : false,
19420     /**
19421      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
19422      */
19423     registerMenu : true,
19424
19425     hidden:true,
19426
19427     // private
19428     render : function(){
19429         if(this.el){
19430             return;
19431         }
19432         var el = this.el = new Roo.Layer({
19433             cls: "x-menu",
19434             shadow:this.shadow,
19435             constrain: false,
19436             parentEl: this.parentEl || document.body,
19437             zindex:15000
19438         });
19439
19440         this.keyNav = new Roo.menu.MenuNav(this);
19441
19442         if(this.plain){
19443             el.addClass("x-menu-plain");
19444         }
19445         if(this.cls){
19446             el.addClass(this.cls);
19447         }
19448         // generic focus element
19449         this.focusEl = el.createChild({
19450             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
19451         });
19452         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
19453         ul.on("click", this.onClick, this);
19454         ul.on("mouseover", this.onMouseOver, this);
19455         ul.on("mouseout", this.onMouseOut, this);
19456         this.items.each(function(item){
19457             var li = document.createElement("li");
19458             li.className = "x-menu-list-item";
19459             ul.dom.appendChild(li);
19460             item.render(li, this);
19461         }, this);
19462         this.ul = ul;
19463         this.autoWidth();
19464     },
19465
19466     // private
19467     autoWidth : function(){
19468         var el = this.el, ul = this.ul;
19469         if(!el){
19470             return;
19471         }
19472         var w = this.width;
19473         if(w){
19474             el.setWidth(w);
19475         }else if(Roo.isIE){
19476             el.setWidth(this.minWidth);
19477             var t = el.dom.offsetWidth; // force recalc
19478             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
19479         }
19480     },
19481
19482     // private
19483     delayAutoWidth : function(){
19484         if(this.rendered){
19485             if(!this.awTask){
19486                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
19487             }
19488             this.awTask.delay(20);
19489         }
19490     },
19491
19492     // private
19493     findTargetItem : function(e){
19494         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
19495         if(t && t.menuItemId){
19496             return this.items.get(t.menuItemId);
19497         }
19498     },
19499
19500     // private
19501     onClick : function(e){
19502         var t;
19503         if(t = this.findTargetItem(e)){
19504             t.onClick(e);
19505             this.fireEvent("click", this, t, e);
19506         }
19507     },
19508
19509     // private
19510     setActiveItem : function(item, autoExpand){
19511         if(item != this.activeItem){
19512             if(this.activeItem){
19513                 this.activeItem.deactivate();
19514             }
19515             this.activeItem = item;
19516             item.activate(autoExpand);
19517         }else if(autoExpand){
19518             item.expandMenu();
19519         }
19520     },
19521
19522     // private
19523     tryActivate : function(start, step){
19524         var items = this.items;
19525         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
19526             var item = items.get(i);
19527             if(!item.disabled && item.canActivate){
19528                 this.setActiveItem(item, false);
19529                 return item;
19530             }
19531         }
19532         return false;
19533     },
19534
19535     // private
19536     onMouseOver : function(e){
19537         var t;
19538         if(t = this.findTargetItem(e)){
19539             if(t.canActivate && !t.disabled){
19540                 this.setActiveItem(t, true);
19541             }
19542         }
19543         this.fireEvent("mouseover", this, e, t);
19544     },
19545
19546     // private
19547     onMouseOut : function(e){
19548         var t;
19549         if(t = this.findTargetItem(e)){
19550             if(t == this.activeItem && t.shouldDeactivate(e)){
19551                 this.activeItem.deactivate();
19552                 delete this.activeItem;
19553             }
19554         }
19555         this.fireEvent("mouseout", this, e, t);
19556     },
19557
19558     /**
19559      * Read-only.  Returns true if the menu is currently displayed, else false.
19560      * @type Boolean
19561      */
19562     isVisible : function(){
19563         return this.el && !this.hidden;
19564     },
19565
19566     /**
19567      * Displays this menu relative to another element
19568      * @param {String/HTMLElement/Roo.Element} element The element to align to
19569      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
19570      * the element (defaults to this.defaultAlign)
19571      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19572      */
19573     show : function(el, pos, parentMenu){
19574         this.parentMenu = parentMenu;
19575         if(!this.el){
19576             this.render();
19577         }
19578         this.fireEvent("beforeshow", this);
19579         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
19580     },
19581
19582     /**
19583      * Displays this menu at a specific xy position
19584      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
19585      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19586      */
19587     showAt : function(xy, parentMenu, /* private: */_e){
19588         this.parentMenu = parentMenu;
19589         if(!this.el){
19590             this.render();
19591         }
19592         if(_e !== false){
19593             this.fireEvent("beforeshow", this);
19594             xy = this.el.adjustForConstraints(xy);
19595         }
19596         this.el.setXY(xy);
19597         this.el.show();
19598         this.hidden = false;
19599         this.focus();
19600         this.fireEvent("show", this);
19601     },
19602
19603     focus : function(){
19604         if(!this.hidden){
19605             this.doFocus.defer(50, this);
19606         }
19607     },
19608
19609     doFocus : function(){
19610         if(!this.hidden){
19611             this.focusEl.focus();
19612         }
19613     },
19614
19615     /**
19616      * Hides this menu and optionally all parent menus
19617      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
19618      */
19619     hide : function(deep){
19620         if(this.el && this.isVisible()){
19621             this.fireEvent("beforehide", this);
19622             if(this.activeItem){
19623                 this.activeItem.deactivate();
19624                 this.activeItem = null;
19625             }
19626             this.el.hide();
19627             this.hidden = true;
19628             this.fireEvent("hide", this);
19629         }
19630         if(deep === true && this.parentMenu){
19631             this.parentMenu.hide(true);
19632         }
19633     },
19634
19635     /**
19636      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
19637      * Any of the following are valid:
19638      * <ul>
19639      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
19640      * <li>An HTMLElement object which will be converted to a menu item</li>
19641      * <li>A menu item config object that will be created as a new menu item</li>
19642      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
19643      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
19644      * </ul>
19645      * Usage:
19646      * <pre><code>
19647 // Create the menu
19648 var menu = new Roo.menu.Menu();
19649
19650 // Create a menu item to add by reference
19651 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
19652
19653 // Add a bunch of items at once using different methods.
19654 // Only the last item added will be returned.
19655 var item = menu.add(
19656     menuItem,                // add existing item by ref
19657     'Dynamic Item',          // new TextItem
19658     '-',                     // new separator
19659     { text: 'Config Item' }  // new item by config
19660 );
19661 </code></pre>
19662      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
19663      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
19664      */
19665     add : function(){
19666         var a = arguments, l = a.length, item;
19667         for(var i = 0; i < l; i++){
19668             var el = a[i];
19669             if ((typeof(el) == "object") && el.xtype && el.xns) {
19670                 el = Roo.factory(el, Roo.menu);
19671             }
19672             
19673             if(el.render){ // some kind of Item
19674                 item = this.addItem(el);
19675             }else if(typeof el == "string"){ // string
19676                 if(el == "separator" || el == "-"){
19677                     item = this.addSeparator();
19678                 }else{
19679                     item = this.addText(el);
19680                 }
19681             }else if(el.tagName || el.el){ // element
19682                 item = this.addElement(el);
19683             }else if(typeof el == "object"){ // must be menu item config?
19684                 item = this.addMenuItem(el);
19685             }
19686         }
19687         return item;
19688     },
19689
19690     /**
19691      * Returns this menu's underlying {@link Roo.Element} object
19692      * @return {Roo.Element} The element
19693      */
19694     getEl : function(){
19695         if(!this.el){
19696             this.render();
19697         }
19698         return this.el;
19699     },
19700
19701     /**
19702      * Adds a separator bar to the menu
19703      * @return {Roo.menu.Item} The menu item that was added
19704      */
19705     addSeparator : function(){
19706         return this.addItem(new Roo.menu.Separator());
19707     },
19708
19709     /**
19710      * Adds an {@link Roo.Element} object to the menu
19711      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
19712      * @return {Roo.menu.Item} The menu item that was added
19713      */
19714     addElement : function(el){
19715         return this.addItem(new Roo.menu.BaseItem(el));
19716     },
19717
19718     /**
19719      * Adds an existing object based on {@link Roo.menu.Item} to the menu
19720      * @param {Roo.menu.Item} item The menu item to add
19721      * @return {Roo.menu.Item} The menu item that was added
19722      */
19723     addItem : function(item){
19724         this.items.add(item);
19725         if(this.ul){
19726             var li = document.createElement("li");
19727             li.className = "x-menu-list-item";
19728             this.ul.dom.appendChild(li);
19729             item.render(li, this);
19730             this.delayAutoWidth();
19731         }
19732         return item;
19733     },
19734
19735     /**
19736      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
19737      * @param {Object} config A MenuItem config object
19738      * @return {Roo.menu.Item} The menu item that was added
19739      */
19740     addMenuItem : function(config){
19741         if(!(config instanceof Roo.menu.Item)){
19742             if(typeof config.checked == "boolean"){ // must be check menu item config?
19743                 config = new Roo.menu.CheckItem(config);
19744             }else{
19745                 config = new Roo.menu.Item(config);
19746             }
19747         }
19748         return this.addItem(config);
19749     },
19750
19751     /**
19752      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
19753      * @param {String} text The text to display in the menu item
19754      * @return {Roo.menu.Item} The menu item that was added
19755      */
19756     addText : function(text){
19757         return this.addItem(new Roo.menu.TextItem({ text : text }));
19758     },
19759
19760     /**
19761      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
19762      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
19763      * @param {Roo.menu.Item} item The menu item to add
19764      * @return {Roo.menu.Item} The menu item that was added
19765      */
19766     insert : function(index, item){
19767         this.items.insert(index, item);
19768         if(this.ul){
19769             var li = document.createElement("li");
19770             li.className = "x-menu-list-item";
19771             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
19772             item.render(li, this);
19773             this.delayAutoWidth();
19774         }
19775         return item;
19776     },
19777
19778     /**
19779      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
19780      * @param {Roo.menu.Item} item The menu item to remove
19781      */
19782     remove : function(item){
19783         this.items.removeKey(item.id);
19784         item.destroy();
19785     },
19786
19787     /**
19788      * Removes and destroys all items in the menu
19789      */
19790     removeAll : function(){
19791         var f;
19792         while(f = this.items.first()){
19793             this.remove(f);
19794         }
19795     }
19796 });
19797
19798 // MenuNav is a private utility class used internally by the Menu
19799 Roo.menu.MenuNav = function(menu){
19800     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
19801     this.scope = this.menu = menu;
19802 };
19803
19804 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
19805     doRelay : function(e, h){
19806         var k = e.getKey();
19807         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
19808             this.menu.tryActivate(0, 1);
19809             return false;
19810         }
19811         return h.call(this.scope || this, e, this.menu);
19812     },
19813
19814     up : function(e, m){
19815         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
19816             m.tryActivate(m.items.length-1, -1);
19817         }
19818     },
19819
19820     down : function(e, m){
19821         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
19822             m.tryActivate(0, 1);
19823         }
19824     },
19825
19826     right : function(e, m){
19827         if(m.activeItem){
19828             m.activeItem.expandMenu(true);
19829         }
19830     },
19831
19832     left : function(e, m){
19833         m.hide();
19834         if(m.parentMenu && m.parentMenu.activeItem){
19835             m.parentMenu.activeItem.activate();
19836         }
19837     },
19838
19839     enter : function(e, m){
19840         if(m.activeItem){
19841             e.stopPropagation();
19842             m.activeItem.onClick(e);
19843             m.fireEvent("click", this, m.activeItem);
19844             return true;
19845         }
19846     }
19847 });/*
19848  * Based on:
19849  * Ext JS Library 1.1.1
19850  * Copyright(c) 2006-2007, Ext JS, LLC.
19851  *
19852  * Originally Released Under LGPL - original licence link has changed is not relivant.
19853  *
19854  * Fork - LGPL
19855  * <script type="text/javascript">
19856  */
19857  
19858 /**
19859  * @class Roo.menu.MenuMgr
19860  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
19861  * @singleton
19862  */
19863 Roo.menu.MenuMgr = function(){
19864    var menus, active, groups = {}, attached = false, lastShow = new Date();
19865
19866    // private - called when first menu is created
19867    function init(){
19868        menus = {};
19869        active = new Roo.util.MixedCollection();
19870        Roo.get(document).addKeyListener(27, function(){
19871            if(active.length > 0){
19872                hideAll();
19873            }
19874        });
19875    }
19876
19877    // private
19878    function hideAll(){
19879        if(active && active.length > 0){
19880            var c = active.clone();
19881            c.each(function(m){
19882                m.hide();
19883            });
19884        }
19885    }
19886
19887    // private
19888    function onHide(m){
19889        active.remove(m);
19890        if(active.length < 1){
19891            Roo.get(document).un("mousedown", onMouseDown);
19892            attached = false;
19893        }
19894    }
19895
19896    // private
19897    function onShow(m){
19898        var last = active.last();
19899        lastShow = new Date();
19900        active.add(m);
19901        if(!attached){
19902            Roo.get(document).on("mousedown", onMouseDown);
19903            attached = true;
19904        }
19905        if(m.parentMenu){
19906           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
19907           m.parentMenu.activeChild = m;
19908        }else if(last && last.isVisible()){
19909           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
19910        }
19911    }
19912
19913    // private
19914    function onBeforeHide(m){
19915        if(m.activeChild){
19916            m.activeChild.hide();
19917        }
19918        if(m.autoHideTimer){
19919            clearTimeout(m.autoHideTimer);
19920            delete m.autoHideTimer;
19921        }
19922    }
19923
19924    // private
19925    function onBeforeShow(m){
19926        var pm = m.parentMenu;
19927        if(!pm && !m.allowOtherMenus){
19928            hideAll();
19929        }else if(pm && pm.activeChild && active != m){
19930            pm.activeChild.hide();
19931        }
19932    }
19933
19934    // private
19935    function onMouseDown(e){
19936        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
19937            hideAll();
19938        }
19939    }
19940
19941    // private
19942    function onBeforeCheck(mi, state){
19943        if(state){
19944            var g = groups[mi.group];
19945            for(var i = 0, l = g.length; i < l; i++){
19946                if(g[i] != mi){
19947                    g[i].setChecked(false);
19948                }
19949            }
19950        }
19951    }
19952
19953    return {
19954
19955        /**
19956         * Hides all menus that are currently visible
19957         */
19958        hideAll : function(){
19959             hideAll();  
19960        },
19961
19962        // private
19963        register : function(menu){
19964            if(!menus){
19965                init();
19966            }
19967            menus[menu.id] = menu;
19968            menu.on("beforehide", onBeforeHide);
19969            menu.on("hide", onHide);
19970            menu.on("beforeshow", onBeforeShow);
19971            menu.on("show", onShow);
19972            var g = menu.group;
19973            if(g && menu.events["checkchange"]){
19974                if(!groups[g]){
19975                    groups[g] = [];
19976                }
19977                groups[g].push(menu);
19978                menu.on("checkchange", onCheck);
19979            }
19980        },
19981
19982         /**
19983          * Returns a {@link Roo.menu.Menu} object
19984          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
19985          * be used to generate and return a new Menu instance.
19986          */
19987        get : function(menu){
19988            if(typeof menu == "string"){ // menu id
19989                return menus[menu];
19990            }else if(menu.events){  // menu instance
19991                return menu;
19992            }else if(typeof menu.length == 'number'){ // array of menu items?
19993                return new Roo.menu.Menu({items:menu});
19994            }else{ // otherwise, must be a config
19995                return new Roo.menu.Menu(menu);
19996            }
19997        },
19998
19999        // private
20000        unregister : function(menu){
20001            delete menus[menu.id];
20002            menu.un("beforehide", onBeforeHide);
20003            menu.un("hide", onHide);
20004            menu.un("beforeshow", onBeforeShow);
20005            menu.un("show", onShow);
20006            var g = menu.group;
20007            if(g && menu.events["checkchange"]){
20008                groups[g].remove(menu);
20009                menu.un("checkchange", onCheck);
20010            }
20011        },
20012
20013        // private
20014        registerCheckable : function(menuItem){
20015            var g = menuItem.group;
20016            if(g){
20017                if(!groups[g]){
20018                    groups[g] = [];
20019                }
20020                groups[g].push(menuItem);
20021                menuItem.on("beforecheckchange", onBeforeCheck);
20022            }
20023        },
20024
20025        // private
20026        unregisterCheckable : function(menuItem){
20027            var g = menuItem.group;
20028            if(g){
20029                groups[g].remove(menuItem);
20030                menuItem.un("beforecheckchange", onBeforeCheck);
20031            }
20032        }
20033    };
20034 }();/*
20035  * Based on:
20036  * Ext JS Library 1.1.1
20037  * Copyright(c) 2006-2007, Ext JS, LLC.
20038  *
20039  * Originally Released Under LGPL - original licence link has changed is not relivant.
20040  *
20041  * Fork - LGPL
20042  * <script type="text/javascript">
20043  */
20044  
20045
20046 /**
20047  * @class Roo.menu.BaseItem
20048  * @extends Roo.Component
20049  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
20050  * management and base configuration options shared by all menu components.
20051  * @constructor
20052  * Creates a new BaseItem
20053  * @param {Object} config Configuration options
20054  */
20055 Roo.menu.BaseItem = function(config){
20056     Roo.menu.BaseItem.superclass.constructor.call(this, config);
20057
20058     this.addEvents({
20059         /**
20060          * @event click
20061          * Fires when this item is clicked
20062          * @param {Roo.menu.BaseItem} this
20063          * @param {Roo.EventObject} e
20064          */
20065         click: true,
20066         /**
20067          * @event activate
20068          * Fires when this item is activated
20069          * @param {Roo.menu.BaseItem} this
20070          */
20071         activate : true,
20072         /**
20073          * @event deactivate
20074          * Fires when this item is deactivated
20075          * @param {Roo.menu.BaseItem} this
20076          */
20077         deactivate : true
20078     });
20079
20080     if(this.handler){
20081         this.on("click", this.handler, this.scope, true);
20082     }
20083 };
20084
20085 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
20086     /**
20087      * @cfg {Function} handler
20088      * A function that will handle the click event of this menu item (defaults to undefined)
20089      */
20090     /**
20091      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
20092      */
20093     canActivate : false,
20094     /**
20095      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
20096      */
20097     activeClass : "x-menu-item-active",
20098     /**
20099      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
20100      */
20101     hideOnClick : true,
20102     /**
20103      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
20104      */
20105     hideDelay : 100,
20106
20107     // private
20108     ctype: "Roo.menu.BaseItem",
20109
20110     // private
20111     actionMode : "container",
20112
20113     // private
20114     render : function(container, parentMenu){
20115         this.parentMenu = parentMenu;
20116         Roo.menu.BaseItem.superclass.render.call(this, container);
20117         this.container.menuItemId = this.id;
20118     },
20119
20120     // private
20121     onRender : function(container, position){
20122         this.el = Roo.get(this.el);
20123         container.dom.appendChild(this.el.dom);
20124     },
20125
20126     // private
20127     onClick : function(e){
20128         if(!this.disabled && this.fireEvent("click", this, e) !== false
20129                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
20130             this.handleClick(e);
20131         }else{
20132             e.stopEvent();
20133         }
20134     },
20135
20136     // private
20137     activate : function(){
20138         if(this.disabled){
20139             return false;
20140         }
20141         var li = this.container;
20142         li.addClass(this.activeClass);
20143         this.region = li.getRegion().adjust(2, 2, -2, -2);
20144         this.fireEvent("activate", this);
20145         return true;
20146     },
20147
20148     // private
20149     deactivate : function(){
20150         this.container.removeClass(this.activeClass);
20151         this.fireEvent("deactivate", this);
20152     },
20153
20154     // private
20155     shouldDeactivate : function(e){
20156         return !this.region || !this.region.contains(e.getPoint());
20157     },
20158
20159     // private
20160     handleClick : function(e){
20161         if(this.hideOnClick){
20162             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
20163         }
20164     },
20165
20166     // private
20167     expandMenu : function(autoActivate){
20168         // do nothing
20169     },
20170
20171     // private
20172     hideMenu : function(){
20173         // do nothing
20174     }
20175 });/*
20176  * Based on:
20177  * Ext JS Library 1.1.1
20178  * Copyright(c) 2006-2007, Ext JS, LLC.
20179  *
20180  * Originally Released Under LGPL - original licence link has changed is not relivant.
20181  *
20182  * Fork - LGPL
20183  * <script type="text/javascript">
20184  */
20185  
20186 /**
20187  * @class Roo.menu.Adapter
20188  * @extends Roo.menu.BaseItem
20189  * 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.
20190  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
20191  * @constructor
20192  * Creates a new Adapter
20193  * @param {Object} config Configuration options
20194  */
20195 Roo.menu.Adapter = function(component, config){
20196     Roo.menu.Adapter.superclass.constructor.call(this, config);
20197     this.component = component;
20198 };
20199 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
20200     // private
20201     canActivate : true,
20202
20203     // private
20204     onRender : function(container, position){
20205         this.component.render(container);
20206         this.el = this.component.getEl();
20207     },
20208
20209     // private
20210     activate : function(){
20211         if(this.disabled){
20212             return false;
20213         }
20214         this.component.focus();
20215         this.fireEvent("activate", this);
20216         return true;
20217     },
20218
20219     // private
20220     deactivate : function(){
20221         this.fireEvent("deactivate", this);
20222     },
20223
20224     // private
20225     disable : function(){
20226         this.component.disable();
20227         Roo.menu.Adapter.superclass.disable.call(this);
20228     },
20229
20230     // private
20231     enable : function(){
20232         this.component.enable();
20233         Roo.menu.Adapter.superclass.enable.call(this);
20234     }
20235 });/*
20236  * Based on:
20237  * Ext JS Library 1.1.1
20238  * Copyright(c) 2006-2007, Ext JS, LLC.
20239  *
20240  * Originally Released Under LGPL - original licence link has changed is not relivant.
20241  *
20242  * Fork - LGPL
20243  * <script type="text/javascript">
20244  */
20245
20246 /**
20247  * @class Roo.menu.TextItem
20248  * @extends Roo.menu.BaseItem
20249  * Adds a static text string to a menu, usually used as either a heading or group separator.
20250  * Note: old style constructor with text is still supported.
20251  * 
20252  * @constructor
20253  * Creates a new TextItem
20254  * @param {Object} cfg Configuration
20255  */
20256 Roo.menu.TextItem = function(cfg){
20257     if (typeof(cfg) == 'string') {
20258         this.text = cfg;
20259     } else {
20260         Roo.apply(this,cfg);
20261     }
20262     
20263     Roo.menu.TextItem.superclass.constructor.call(this);
20264 };
20265
20266 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
20267     /**
20268      * @cfg {Boolean} text Text to show on item.
20269      */
20270     text : '',
20271     
20272     /**
20273      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20274      */
20275     hideOnClick : false,
20276     /**
20277      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
20278      */
20279     itemCls : "x-menu-text",
20280
20281     // private
20282     onRender : function(){
20283         var s = document.createElement("span");
20284         s.className = this.itemCls;
20285         s.innerHTML = this.text;
20286         this.el = s;
20287         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
20288     }
20289 });/*
20290  * Based on:
20291  * Ext JS Library 1.1.1
20292  * Copyright(c) 2006-2007, Ext JS, LLC.
20293  *
20294  * Originally Released Under LGPL - original licence link has changed is not relivant.
20295  *
20296  * Fork - LGPL
20297  * <script type="text/javascript">
20298  */
20299
20300 /**
20301  * @class Roo.menu.Separator
20302  * @extends Roo.menu.BaseItem
20303  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
20304  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
20305  * @constructor
20306  * @param {Object} config Configuration options
20307  */
20308 Roo.menu.Separator = function(config){
20309     Roo.menu.Separator.superclass.constructor.call(this, config);
20310 };
20311
20312 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
20313     /**
20314      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
20315      */
20316     itemCls : "x-menu-sep",
20317     /**
20318      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20319      */
20320     hideOnClick : false,
20321
20322     // private
20323     onRender : function(li){
20324         var s = document.createElement("span");
20325         s.className = this.itemCls;
20326         s.innerHTML = "&#160;";
20327         this.el = s;
20328         li.addClass("x-menu-sep-li");
20329         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
20330     }
20331 });/*
20332  * Based on:
20333  * Ext JS Library 1.1.1
20334  * Copyright(c) 2006-2007, Ext JS, LLC.
20335  *
20336  * Originally Released Under LGPL - original licence link has changed is not relivant.
20337  *
20338  * Fork - LGPL
20339  * <script type="text/javascript">
20340  */
20341 /**
20342  * @class Roo.menu.Item
20343  * @extends Roo.menu.BaseItem
20344  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
20345  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
20346  * activation and click handling.
20347  * @constructor
20348  * Creates a new Item
20349  * @param {Object} config Configuration options
20350  */
20351 Roo.menu.Item = function(config){
20352     Roo.menu.Item.superclass.constructor.call(this, config);
20353     if(this.menu){
20354         this.menu = Roo.menu.MenuMgr.get(this.menu);
20355     }
20356 };
20357 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
20358     
20359     /**
20360      * @cfg {String} text
20361      * The text to show on the menu item.
20362      */
20363     text: '',
20364      /**
20365      * @cfg {String} HTML to render in menu
20366      * The text to show on the menu item (HTML version).
20367      */
20368     html: '',
20369     /**
20370      * @cfg {String} icon
20371      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
20372      */
20373     icon: undefined,
20374     /**
20375      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
20376      */
20377     itemCls : "x-menu-item",
20378     /**
20379      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
20380      */
20381     canActivate : true,
20382     /**
20383      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
20384      */
20385     showDelay: 200,
20386     // doc'd in BaseItem
20387     hideDelay: 200,
20388
20389     // private
20390     ctype: "Roo.menu.Item",
20391     
20392     // private
20393     onRender : function(container, position){
20394         var el = document.createElement("a");
20395         el.hideFocus = true;
20396         el.unselectable = "on";
20397         el.href = this.href || "#";
20398         if(this.hrefTarget){
20399             el.target = this.hrefTarget;
20400         }
20401         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
20402         
20403         var html = this.html.length ? this.html  : String.format('{0}',this.text);
20404         
20405         el.innerHTML = String.format(
20406                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
20407                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
20408         this.el = el;
20409         Roo.menu.Item.superclass.onRender.call(this, container, position);
20410     },
20411
20412     /**
20413      * Sets the text to display in this menu item
20414      * @param {String} text The text to display
20415      * @param {Boolean} isHTML true to indicate text is pure html.
20416      */
20417     setText : function(text, isHTML){
20418         if (isHTML) {
20419             this.html = text;
20420         } else {
20421             this.text = text;
20422             this.html = '';
20423         }
20424         if(this.rendered){
20425             var html = this.html.length ? this.html  : String.format('{0}',this.text);
20426      
20427             this.el.update(String.format(
20428                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
20429                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
20430             this.parentMenu.autoWidth();
20431         }
20432     },
20433
20434     // private
20435     handleClick : function(e){
20436         if(!this.href){ // if no link defined, stop the event automatically
20437             e.stopEvent();
20438         }
20439         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
20440     },
20441
20442     // private
20443     activate : function(autoExpand){
20444         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
20445             this.focus();
20446             if(autoExpand){
20447                 this.expandMenu();
20448             }
20449         }
20450         return true;
20451     },
20452
20453     // private
20454     shouldDeactivate : function(e){
20455         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
20456             if(this.menu && this.menu.isVisible()){
20457                 return !this.menu.getEl().getRegion().contains(e.getPoint());
20458             }
20459             return true;
20460         }
20461         return false;
20462     },
20463
20464     // private
20465     deactivate : function(){
20466         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
20467         this.hideMenu();
20468     },
20469
20470     // private
20471     expandMenu : function(autoActivate){
20472         if(!this.disabled && this.menu){
20473             clearTimeout(this.hideTimer);
20474             delete this.hideTimer;
20475             if(!this.menu.isVisible() && !this.showTimer){
20476                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
20477             }else if (this.menu.isVisible() && autoActivate){
20478                 this.menu.tryActivate(0, 1);
20479             }
20480         }
20481     },
20482
20483     // private
20484     deferExpand : function(autoActivate){
20485         delete this.showTimer;
20486         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
20487         if(autoActivate){
20488             this.menu.tryActivate(0, 1);
20489         }
20490     },
20491
20492     // private
20493     hideMenu : function(){
20494         clearTimeout(this.showTimer);
20495         delete this.showTimer;
20496         if(!this.hideTimer && this.menu && this.menu.isVisible()){
20497             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
20498         }
20499     },
20500
20501     // private
20502     deferHide : function(){
20503         delete this.hideTimer;
20504         this.menu.hide();
20505     }
20506 });/*
20507  * Based on:
20508  * Ext JS Library 1.1.1
20509  * Copyright(c) 2006-2007, Ext JS, LLC.
20510  *
20511  * Originally Released Under LGPL - original licence link has changed is not relivant.
20512  *
20513  * Fork - LGPL
20514  * <script type="text/javascript">
20515  */
20516  
20517 /**
20518  * @class Roo.menu.CheckItem
20519  * @extends Roo.menu.Item
20520  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
20521  * @constructor
20522  * Creates a new CheckItem
20523  * @param {Object} config Configuration options
20524  */
20525 Roo.menu.CheckItem = function(config){
20526     Roo.menu.CheckItem.superclass.constructor.call(this, config);
20527     this.addEvents({
20528         /**
20529          * @event beforecheckchange
20530          * Fires before the checked value is set, providing an opportunity to cancel if needed
20531          * @param {Roo.menu.CheckItem} this
20532          * @param {Boolean} checked The new checked value that will be set
20533          */
20534         "beforecheckchange" : true,
20535         /**
20536          * @event checkchange
20537          * Fires after the checked value has been set
20538          * @param {Roo.menu.CheckItem} this
20539          * @param {Boolean} checked The checked value that was set
20540          */
20541         "checkchange" : true
20542     });
20543     if(this.checkHandler){
20544         this.on('checkchange', this.checkHandler, this.scope);
20545     }
20546 };
20547 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
20548     /**
20549      * @cfg {String} group
20550      * All check items with the same group name will automatically be grouped into a single-select
20551      * radio button group (defaults to '')
20552      */
20553     /**
20554      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
20555      */
20556     itemCls : "x-menu-item x-menu-check-item",
20557     /**
20558      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
20559      */
20560     groupClass : "x-menu-group-item",
20561
20562     /**
20563      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
20564      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
20565      * initialized with checked = true will be rendered as checked.
20566      */
20567     checked: false,
20568
20569     // private
20570     ctype: "Roo.menu.CheckItem",
20571
20572     // private
20573     onRender : function(c){
20574         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
20575         if(this.group){
20576             this.el.addClass(this.groupClass);
20577         }
20578         Roo.menu.MenuMgr.registerCheckable(this);
20579         if(this.checked){
20580             this.checked = false;
20581             this.setChecked(true, true);
20582         }
20583     },
20584
20585     // private
20586     destroy : function(){
20587         if(this.rendered){
20588             Roo.menu.MenuMgr.unregisterCheckable(this);
20589         }
20590         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
20591     },
20592
20593     /**
20594      * Set the checked state of this item
20595      * @param {Boolean} checked The new checked value
20596      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
20597      */
20598     setChecked : function(state, suppressEvent){
20599         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
20600             if(this.container){
20601                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
20602             }
20603             this.checked = state;
20604             if(suppressEvent !== true){
20605                 this.fireEvent("checkchange", this, state);
20606             }
20607         }
20608     },
20609
20610     // private
20611     handleClick : function(e){
20612        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
20613            this.setChecked(!this.checked);
20614        }
20615        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
20616     }
20617 });/*
20618  * Based on:
20619  * Ext JS Library 1.1.1
20620  * Copyright(c) 2006-2007, Ext JS, LLC.
20621  *
20622  * Originally Released Under LGPL - original licence link has changed is not relivant.
20623  *
20624  * Fork - LGPL
20625  * <script type="text/javascript">
20626  */
20627  
20628 /**
20629  * @class Roo.menu.DateItem
20630  * @extends Roo.menu.Adapter
20631  * A menu item that wraps the {@link Roo.DatPicker} component.
20632  * @constructor
20633  * Creates a new DateItem
20634  * @param {Object} config Configuration options
20635  */
20636 Roo.menu.DateItem = function(config){
20637     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
20638     /** The Roo.DatePicker object @type Roo.DatePicker */
20639     this.picker = this.component;
20640     this.addEvents({select: true});
20641     
20642     this.picker.on("render", function(picker){
20643         picker.getEl().swallowEvent("click");
20644         picker.container.addClass("x-menu-date-item");
20645     });
20646
20647     this.picker.on("select", this.onSelect, this);
20648 };
20649
20650 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
20651     // private
20652     onSelect : function(picker, date){
20653         this.fireEvent("select", this, date, picker);
20654         Roo.menu.DateItem.superclass.handleClick.call(this);
20655     }
20656 });/*
20657  * Based on:
20658  * Ext JS Library 1.1.1
20659  * Copyright(c) 2006-2007, Ext JS, LLC.
20660  *
20661  * Originally Released Under LGPL - original licence link has changed is not relivant.
20662  *
20663  * Fork - LGPL
20664  * <script type="text/javascript">
20665  */
20666  
20667 /**
20668  * @class Roo.menu.ColorItem
20669  * @extends Roo.menu.Adapter
20670  * A menu item that wraps the {@link Roo.ColorPalette} component.
20671  * @constructor
20672  * Creates a new ColorItem
20673  * @param {Object} config Configuration options
20674  */
20675 Roo.menu.ColorItem = function(config){
20676     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
20677     /** The Roo.ColorPalette object @type Roo.ColorPalette */
20678     this.palette = this.component;
20679     this.relayEvents(this.palette, ["select"]);
20680     if(this.selectHandler){
20681         this.on('select', this.selectHandler, this.scope);
20682     }
20683 };
20684 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
20685  * Based on:
20686  * Ext JS Library 1.1.1
20687  * Copyright(c) 2006-2007, Ext JS, LLC.
20688  *
20689  * Originally Released Under LGPL - original licence link has changed is not relivant.
20690  *
20691  * Fork - LGPL
20692  * <script type="text/javascript">
20693  */
20694  
20695
20696 /**
20697  * @class Roo.menu.DateMenu
20698  * @extends Roo.menu.Menu
20699  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
20700  * @constructor
20701  * Creates a new DateMenu
20702  * @param {Object} config Configuration options
20703  */
20704 Roo.menu.DateMenu = function(config){
20705     Roo.menu.DateMenu.superclass.constructor.call(this, config);
20706     this.plain = true;
20707     var di = new Roo.menu.DateItem(config);
20708     this.add(di);
20709     /**
20710      * The {@link Roo.DatePicker} instance for this DateMenu
20711      * @type DatePicker
20712      */
20713     this.picker = di.picker;
20714     /**
20715      * @event select
20716      * @param {DatePicker} picker
20717      * @param {Date} date
20718      */
20719     this.relayEvents(di, ["select"]);
20720
20721     this.on('beforeshow', function(){
20722         if(this.picker){
20723             this.picker.hideMonthPicker(true);
20724         }
20725     }, this);
20726 };
20727 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
20728     cls:'x-date-menu'
20729 });/*
20730  * Based on:
20731  * Ext JS Library 1.1.1
20732  * Copyright(c) 2006-2007, Ext JS, LLC.
20733  *
20734  * Originally Released Under LGPL - original licence link has changed is not relivant.
20735  *
20736  * Fork - LGPL
20737  * <script type="text/javascript">
20738  */
20739  
20740
20741 /**
20742  * @class Roo.menu.ColorMenu
20743  * @extends Roo.menu.Menu
20744  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
20745  * @constructor
20746  * Creates a new ColorMenu
20747  * @param {Object} config Configuration options
20748  */
20749 Roo.menu.ColorMenu = function(config){
20750     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
20751     this.plain = true;
20752     var ci = new Roo.menu.ColorItem(config);
20753     this.add(ci);
20754     /**
20755      * The {@link Roo.ColorPalette} instance for this ColorMenu
20756      * @type ColorPalette
20757      */
20758     this.palette = ci.palette;
20759     /**
20760      * @event select
20761      * @param {ColorPalette} palette
20762      * @param {String} color
20763      */
20764     this.relayEvents(ci, ["select"]);
20765 };
20766 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
20767  * Based on:
20768  * Ext JS Library 1.1.1
20769  * Copyright(c) 2006-2007, Ext JS, LLC.
20770  *
20771  * Originally Released Under LGPL - original licence link has changed is not relivant.
20772  *
20773  * Fork - LGPL
20774  * <script type="text/javascript">
20775  */
20776  
20777 /**
20778  * @class Roo.form.Field
20779  * @extends Roo.BoxComponent
20780  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
20781  * @constructor
20782  * Creates a new Field
20783  * @param {Object} config Configuration options
20784  */
20785 Roo.form.Field = function(config){
20786     Roo.form.Field.superclass.constructor.call(this, config);
20787 };
20788
20789 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
20790     /**
20791      * @cfg {String} fieldLabel Label to use when rendering a form.
20792      */
20793        /**
20794      * @cfg {String} qtip Mouse over tip
20795      */
20796      
20797     /**
20798      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
20799      */
20800     invalidClass : "x-form-invalid",
20801     /**
20802      * @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")
20803      */
20804     invalidText : "The value in this field is invalid",
20805     /**
20806      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
20807      */
20808     focusClass : "x-form-focus",
20809     /**
20810      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
20811       automatic validation (defaults to "keyup").
20812      */
20813     validationEvent : "keyup",
20814     /**
20815      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
20816      */
20817     validateOnBlur : true,
20818     /**
20819      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
20820      */
20821     validationDelay : 250,
20822     /**
20823      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20824      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
20825      */
20826     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
20827     /**
20828      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
20829      */
20830     fieldClass : "x-form-field",
20831     /**
20832      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
20833      *<pre>
20834 Value         Description
20835 -----------   ----------------------------------------------------------------------
20836 qtip          Display a quick tip when the user hovers over the field
20837 title         Display a default browser title attribute popup
20838 under         Add a block div beneath the field containing the error text
20839 side          Add an error icon to the right of the field with a popup on hover
20840 [element id]  Add the error text directly to the innerHTML of the specified element
20841 </pre>
20842      */
20843     msgTarget : 'qtip',
20844     /**
20845      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
20846      */
20847     msgFx : 'normal',
20848
20849     /**
20850      * @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.
20851      */
20852     readOnly : false,
20853
20854     /**
20855      * @cfg {Boolean} disabled True to disable the field (defaults to false).
20856      */
20857     disabled : false,
20858
20859     /**
20860      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
20861      */
20862     inputType : undefined,
20863     
20864     /**
20865      * @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).
20866          */
20867         tabIndex : undefined,
20868         
20869     // private
20870     isFormField : true,
20871
20872     // private
20873     hasFocus : false,
20874     /**
20875      * @property {Roo.Element} fieldEl
20876      * Element Containing the rendered Field (with label etc.)
20877      */
20878     /**
20879      * @cfg {Mixed} value A value to initialize this field with.
20880      */
20881     value : undefined,
20882
20883     /**
20884      * @cfg {String} name The field's HTML name attribute.
20885      */
20886     /**
20887      * @cfg {String} cls A CSS class to apply to the field's underlying element.
20888      */
20889
20890         // private ??
20891         initComponent : function(){
20892         Roo.form.Field.superclass.initComponent.call(this);
20893         this.addEvents({
20894             /**
20895              * @event focus
20896              * Fires when this field receives input focus.
20897              * @param {Roo.form.Field} this
20898              */
20899             focus : true,
20900             /**
20901              * @event blur
20902              * Fires when this field loses input focus.
20903              * @param {Roo.form.Field} this
20904              */
20905             blur : true,
20906             /**
20907              * @event specialkey
20908              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
20909              * {@link Roo.EventObject#getKey} to determine which key was pressed.
20910              * @param {Roo.form.Field} this
20911              * @param {Roo.EventObject} e The event object
20912              */
20913             specialkey : true,
20914             /**
20915              * @event change
20916              * Fires just before the field blurs if the field value has changed.
20917              * @param {Roo.form.Field} this
20918              * @param {Mixed} newValue The new value
20919              * @param {Mixed} oldValue The original value
20920              */
20921             change : true,
20922             /**
20923              * @event invalid
20924              * Fires after the field has been marked as invalid.
20925              * @param {Roo.form.Field} this
20926              * @param {String} msg The validation message
20927              */
20928             invalid : true,
20929             /**
20930              * @event valid
20931              * Fires after the field has been validated with no errors.
20932              * @param {Roo.form.Field} this
20933              */
20934             valid : true,
20935              /**
20936              * @event keyup
20937              * Fires after the key up
20938              * @param {Roo.form.Field} this
20939              * @param {Roo.EventObject}  e The event Object
20940              */
20941             keyup : true
20942         });
20943     },
20944
20945     /**
20946      * Returns the name attribute of the field if available
20947      * @return {String} name The field name
20948      */
20949     getName: function(){
20950          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
20951     },
20952
20953     // private
20954     onRender : function(ct, position){
20955         Roo.form.Field.superclass.onRender.call(this, ct, position);
20956         if(!this.el){
20957             var cfg = this.getAutoCreate();
20958             if(!cfg.name){
20959                 cfg.name = this.name || this.id;
20960             }
20961             if(this.inputType){
20962                 cfg.type = this.inputType;
20963             }
20964             this.el = ct.createChild(cfg, position);
20965         }
20966         var type = this.el.dom.type;
20967         if(type){
20968             if(type == 'password'){
20969                 type = 'text';
20970             }
20971             this.el.addClass('x-form-'+type);
20972         }
20973         if(this.readOnly){
20974             this.el.dom.readOnly = true;
20975         }
20976         if(this.tabIndex !== undefined){
20977             this.el.dom.setAttribute('tabIndex', this.tabIndex);
20978         }
20979
20980         this.el.addClass([this.fieldClass, this.cls]);
20981         this.initValue();
20982     },
20983
20984     /**
20985      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
20986      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
20987      * @return {Roo.form.Field} this
20988      */
20989     applyTo : function(target){
20990         this.allowDomMove = false;
20991         this.el = Roo.get(target);
20992         this.render(this.el.dom.parentNode);
20993         return this;
20994     },
20995
20996     // private
20997     initValue : function(){
20998         if(this.value !== undefined){
20999             this.setValue(this.value);
21000         }else if(this.el.dom.value.length > 0){
21001             this.setValue(this.el.dom.value);
21002         }
21003     },
21004
21005     /**
21006      * Returns true if this field has been changed since it was originally loaded and is not disabled.
21007      */
21008     isDirty : function() {
21009         if(this.disabled) {
21010             return false;
21011         }
21012         return String(this.getValue()) !== String(this.originalValue);
21013     },
21014
21015     // private
21016     afterRender : function(){
21017         Roo.form.Field.superclass.afterRender.call(this);
21018         this.initEvents();
21019     },
21020
21021     // private
21022     fireKey : function(e){
21023         //Roo.log('field ' + e.getKey());
21024         if(e.isNavKeyPress()){
21025             this.fireEvent("specialkey", this, e);
21026         }
21027     },
21028
21029     /**
21030      * Resets the current field value to the originally loaded value and clears any validation messages
21031      */
21032     reset : function(){
21033         this.setValue(this.originalValue);
21034         this.clearInvalid();
21035     },
21036
21037     // private
21038     initEvents : function(){
21039         // safari killled keypress - so keydown is now used..
21040         this.el.on("keydown" , this.fireKey,  this);
21041         this.el.on("focus", this.onFocus,  this);
21042         this.el.on("blur", this.onBlur,  this);
21043         this.el.relayEvent('keyup', this);
21044
21045         // reference to original value for reset
21046         this.originalValue = this.getValue();
21047     },
21048
21049     // private
21050     onFocus : function(){
21051         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21052             this.el.addClass(this.focusClass);
21053         }
21054         if(!this.hasFocus){
21055             this.hasFocus = true;
21056             this.startValue = this.getValue();
21057             this.fireEvent("focus", this);
21058         }
21059     },
21060
21061     beforeBlur : Roo.emptyFn,
21062
21063     // private
21064     onBlur : function(){
21065         this.beforeBlur();
21066         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21067             this.el.removeClass(this.focusClass);
21068         }
21069         this.hasFocus = false;
21070         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
21071             this.validate();
21072         }
21073         var v = this.getValue();
21074         if(String(v) !== String(this.startValue)){
21075             this.fireEvent('change', this, v, this.startValue);
21076         }
21077         this.fireEvent("blur", this);
21078     },
21079
21080     /**
21081      * Returns whether or not the field value is currently valid
21082      * @param {Boolean} preventMark True to disable marking the field invalid
21083      * @return {Boolean} True if the value is valid, else false
21084      */
21085     isValid : function(preventMark){
21086         if(this.disabled){
21087             return true;
21088         }
21089         var restore = this.preventMark;
21090         this.preventMark = preventMark === true;
21091         var v = this.validateValue(this.processValue(this.getRawValue()));
21092         this.preventMark = restore;
21093         return v;
21094     },
21095
21096     /**
21097      * Validates the field value
21098      * @return {Boolean} True if the value is valid, else false
21099      */
21100     validate : function(){
21101         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
21102             this.clearInvalid();
21103             return true;
21104         }
21105         return false;
21106     },
21107
21108     processValue : function(value){
21109         return value;
21110     },
21111
21112     // private
21113     // Subclasses should provide the validation implementation by overriding this
21114     validateValue : function(value){
21115         return true;
21116     },
21117
21118     /**
21119      * Mark this field as invalid
21120      * @param {String} msg The validation message
21121      */
21122     markInvalid : function(msg){
21123         if(!this.rendered || this.preventMark){ // not rendered
21124             return;
21125         }
21126         this.el.addClass(this.invalidClass);
21127         msg = msg || this.invalidText;
21128         switch(this.msgTarget){
21129             case 'qtip':
21130                 this.el.dom.qtip = msg;
21131                 this.el.dom.qclass = 'x-form-invalid-tip';
21132                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
21133                     Roo.QuickTips.enable();
21134                 }
21135                 break;
21136             case 'title':
21137                 this.el.dom.title = msg;
21138                 break;
21139             case 'under':
21140                 if(!this.errorEl){
21141                     var elp = this.el.findParent('.x-form-element', 5, true);
21142                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
21143                     this.errorEl.setWidth(elp.getWidth(true)-20);
21144                 }
21145                 this.errorEl.update(msg);
21146                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
21147                 break;
21148             case 'side':
21149                 if(!this.errorIcon){
21150                     var elp = this.el.findParent('.x-form-element', 5, true);
21151                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
21152                 }
21153                 this.alignErrorIcon();
21154                 this.errorIcon.dom.qtip = msg;
21155                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
21156                 this.errorIcon.show();
21157                 this.on('resize', this.alignErrorIcon, this);
21158                 break;
21159             default:
21160                 var t = Roo.getDom(this.msgTarget);
21161                 t.innerHTML = msg;
21162                 t.style.display = this.msgDisplay;
21163                 break;
21164         }
21165         this.fireEvent('invalid', this, msg);
21166     },
21167
21168     // private
21169     alignErrorIcon : function(){
21170         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
21171     },
21172
21173     /**
21174      * Clear any invalid styles/messages for this field
21175      */
21176     clearInvalid : function(){
21177         if(!this.rendered || this.preventMark){ // not rendered
21178             return;
21179         }
21180         this.el.removeClass(this.invalidClass);
21181         switch(this.msgTarget){
21182             case 'qtip':
21183                 this.el.dom.qtip = '';
21184                 break;
21185             case 'title':
21186                 this.el.dom.title = '';
21187                 break;
21188             case 'under':
21189                 if(this.errorEl){
21190                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
21191                 }
21192                 break;
21193             case 'side':
21194                 if(this.errorIcon){
21195                     this.errorIcon.dom.qtip = '';
21196                     this.errorIcon.hide();
21197                     this.un('resize', this.alignErrorIcon, this);
21198                 }
21199                 break;
21200             default:
21201                 var t = Roo.getDom(this.msgTarget);
21202                 t.innerHTML = '';
21203                 t.style.display = 'none';
21204                 break;
21205         }
21206         this.fireEvent('valid', this);
21207     },
21208
21209     /**
21210      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
21211      * @return {Mixed} value The field value
21212      */
21213     getRawValue : function(){
21214         var v = this.el.getValue();
21215         if(v === this.emptyText){
21216             v = '';
21217         }
21218         return v;
21219     },
21220
21221     /**
21222      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
21223      * @return {Mixed} value The field value
21224      */
21225     getValue : function(){
21226         var v = this.el.getValue();
21227         if(v === this.emptyText || v === undefined){
21228             v = '';
21229         }
21230         return v;
21231     },
21232
21233     /**
21234      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
21235      * @param {Mixed} value The value to set
21236      */
21237     setRawValue : function(v){
21238         return this.el.dom.value = (v === null || v === undefined ? '' : v);
21239     },
21240
21241     /**
21242      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
21243      * @param {Mixed} value The value to set
21244      */
21245     setValue : function(v){
21246         this.value = v;
21247         if(this.rendered){
21248             this.el.dom.value = (v === null || v === undefined ? '' : v);
21249             this.validate();
21250         }
21251     },
21252
21253     adjustSize : function(w, h){
21254         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
21255         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
21256         return s;
21257     },
21258
21259     adjustWidth : function(tag, w){
21260         tag = tag.toLowerCase();
21261         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
21262             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
21263                 if(tag == 'input'){
21264                     return w + 2;
21265                 }
21266                 if(tag = 'textarea'){
21267                     return w-2;
21268                 }
21269             }else if(Roo.isOpera){
21270                 if(tag == 'input'){
21271                     return w + 2;
21272                 }
21273                 if(tag = 'textarea'){
21274                     return w-2;
21275                 }
21276             }
21277         }
21278         return w;
21279     }
21280 });
21281
21282
21283 // anything other than normal should be considered experimental
21284 Roo.form.Field.msgFx = {
21285     normal : {
21286         show: function(msgEl, f){
21287             msgEl.setDisplayed('block');
21288         },
21289
21290         hide : function(msgEl, f){
21291             msgEl.setDisplayed(false).update('');
21292         }
21293     },
21294
21295     slide : {
21296         show: function(msgEl, f){
21297             msgEl.slideIn('t', {stopFx:true});
21298         },
21299
21300         hide : function(msgEl, f){
21301             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
21302         }
21303     },
21304
21305     slideRight : {
21306         show: function(msgEl, f){
21307             msgEl.fixDisplay();
21308             msgEl.alignTo(f.el, 'tl-tr');
21309             msgEl.slideIn('l', {stopFx:true});
21310         },
21311
21312         hide : function(msgEl, f){
21313             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
21314         }
21315     }
21316 };/*
21317  * Based on:
21318  * Ext JS Library 1.1.1
21319  * Copyright(c) 2006-2007, Ext JS, LLC.
21320  *
21321  * Originally Released Under LGPL - original licence link has changed is not relivant.
21322  *
21323  * Fork - LGPL
21324  * <script type="text/javascript">
21325  */
21326  
21327
21328 /**
21329  * @class Roo.form.TextField
21330  * @extends Roo.form.Field
21331  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
21332  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
21333  * @constructor
21334  * Creates a new TextField
21335  * @param {Object} config Configuration options
21336  */
21337 Roo.form.TextField = function(config){
21338     Roo.form.TextField.superclass.constructor.call(this, config);
21339     this.addEvents({
21340         /**
21341          * @event autosize
21342          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
21343          * according to the default logic, but this event provides a hook for the developer to apply additional
21344          * logic at runtime to resize the field if needed.
21345              * @param {Roo.form.Field} this This text field
21346              * @param {Number} width The new field width
21347              */
21348         autosize : true
21349     });
21350 };
21351
21352 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
21353     /**
21354      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
21355      */
21356     grow : false,
21357     /**
21358      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
21359      */
21360     growMin : 30,
21361     /**
21362      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
21363      */
21364     growMax : 800,
21365     /**
21366      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
21367      */
21368     vtype : null,
21369     /**
21370      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
21371      */
21372     maskRe : null,
21373     /**
21374      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
21375      */
21376     disableKeyFilter : false,
21377     /**
21378      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
21379      */
21380     allowBlank : true,
21381     /**
21382      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
21383      */
21384     minLength : 0,
21385     /**
21386      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
21387      */
21388     maxLength : Number.MAX_VALUE,
21389     /**
21390      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
21391      */
21392     minLengthText : "The minimum length for this field is {0}",
21393     /**
21394      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
21395      */
21396     maxLengthText : "The maximum length for this field is {0}",
21397     /**
21398      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
21399      */
21400     selectOnFocus : false,
21401     /**
21402      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
21403      */
21404     blankText : "This field is required",
21405     /**
21406      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
21407      * If available, this function will be called only after the basic validators all return true, and will be passed the
21408      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
21409      */
21410     validator : null,
21411     /**
21412      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
21413      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
21414      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
21415      */
21416     regex : null,
21417     /**
21418      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
21419      */
21420     regexText : "",
21421     /**
21422      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
21423      */
21424     emptyText : null,
21425     /**
21426      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
21427      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
21428      */
21429     emptyClass : 'x-form-empty-field',
21430
21431     // private
21432     initEvents : function(){
21433         Roo.form.TextField.superclass.initEvents.call(this);
21434         if(this.validationEvent == 'keyup'){
21435             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
21436             this.el.on('keyup', this.filterValidation, this);
21437         }
21438         else if(this.validationEvent !== false){
21439             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
21440         }
21441         if(this.selectOnFocus || this.emptyText){
21442             this.on("focus", this.preFocus, this);
21443             if(this.emptyText){
21444                 this.on('blur', this.postBlur, this);
21445                 this.applyEmptyText();
21446             }
21447         }
21448         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
21449             this.el.on("keypress", this.filterKeys, this);
21450         }
21451         if(this.grow){
21452             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
21453             this.el.on("click", this.autoSize,  this);
21454         }
21455     },
21456
21457     processValue : function(value){
21458         if(this.stripCharsRe){
21459             var newValue = value.replace(this.stripCharsRe, '');
21460             if(newValue !== value){
21461                 this.setRawValue(newValue);
21462                 return newValue;
21463             }
21464         }
21465         return value;
21466     },
21467
21468     filterValidation : function(e){
21469         if(!e.isNavKeyPress()){
21470             this.validationTask.delay(this.validationDelay);
21471         }
21472     },
21473
21474     // private
21475     onKeyUp : function(e){
21476         if(!e.isNavKeyPress()){
21477             this.autoSize();
21478         }
21479     },
21480
21481     /**
21482      * Resets the current field value to the originally-loaded value and clears any validation messages.
21483      * Also adds emptyText and emptyClass if the original value was blank.
21484      */
21485     reset : function(){
21486         Roo.form.TextField.superclass.reset.call(this);
21487         this.applyEmptyText();
21488     },
21489
21490     applyEmptyText : function(){
21491         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
21492             this.setRawValue(this.emptyText);
21493             this.el.addClass(this.emptyClass);
21494         }
21495     },
21496
21497     // private
21498     preFocus : function(){
21499         if(this.emptyText){
21500             if(this.el.dom.value == this.emptyText){
21501                 this.setRawValue('');
21502             }
21503             this.el.removeClass(this.emptyClass);
21504         }
21505         if(this.selectOnFocus){
21506             this.el.dom.select();
21507         }
21508     },
21509
21510     // private
21511     postBlur : function(){
21512         this.applyEmptyText();
21513     },
21514
21515     // private
21516     filterKeys : function(e){
21517         var k = e.getKey();
21518         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
21519             return;
21520         }
21521         var c = e.getCharCode(), cc = String.fromCharCode(c);
21522         if(Roo.isIE && (e.isSpecialKey() || !cc)){
21523             return;
21524         }
21525         if(!this.maskRe.test(cc)){
21526             e.stopEvent();
21527         }
21528     },
21529
21530     setValue : function(v){
21531         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
21532             this.el.removeClass(this.emptyClass);
21533         }
21534         Roo.form.TextField.superclass.setValue.apply(this, arguments);
21535         this.applyEmptyText();
21536         this.autoSize();
21537     },
21538
21539     /**
21540      * Validates a value according to the field's validation rules and marks the field as invalid
21541      * if the validation fails
21542      * @param {Mixed} value The value to validate
21543      * @return {Boolean} True if the value is valid, else false
21544      */
21545     validateValue : function(value){
21546         if(value.length < 1 || value === this.emptyText){ // if it's blank
21547              if(this.allowBlank){
21548                 this.clearInvalid();
21549                 return true;
21550              }else{
21551                 this.markInvalid(this.blankText);
21552                 return false;
21553              }
21554         }
21555         if(value.length < this.minLength){
21556             this.markInvalid(String.format(this.minLengthText, this.minLength));
21557             return false;
21558         }
21559         if(value.length > this.maxLength){
21560             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
21561             return false;
21562         }
21563         if(this.vtype){
21564             var vt = Roo.form.VTypes;
21565             if(!vt[this.vtype](value, this)){
21566                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
21567                 return false;
21568             }
21569         }
21570         if(typeof this.validator == "function"){
21571             var msg = this.validator(value);
21572             if(msg !== true){
21573                 this.markInvalid(msg);
21574                 return false;
21575             }
21576         }
21577         if(this.regex && !this.regex.test(value)){
21578             this.markInvalid(this.regexText);
21579             return false;
21580         }
21581         return true;
21582     },
21583
21584     /**
21585      * Selects text in this field
21586      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
21587      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
21588      */
21589     selectText : function(start, end){
21590         var v = this.getRawValue();
21591         if(v.length > 0){
21592             start = start === undefined ? 0 : start;
21593             end = end === undefined ? v.length : end;
21594             var d = this.el.dom;
21595             if(d.setSelectionRange){
21596                 d.setSelectionRange(start, end);
21597             }else if(d.createTextRange){
21598                 var range = d.createTextRange();
21599                 range.moveStart("character", start);
21600                 range.moveEnd("character", v.length-end);
21601                 range.select();
21602             }
21603         }
21604     },
21605
21606     /**
21607      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
21608      * This only takes effect if grow = true, and fires the autosize event.
21609      */
21610     autoSize : function(){
21611         if(!this.grow || !this.rendered){
21612             return;
21613         }
21614         if(!this.metrics){
21615             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
21616         }
21617         var el = this.el;
21618         var v = el.dom.value;
21619         var d = document.createElement('div');
21620         d.appendChild(document.createTextNode(v));
21621         v = d.innerHTML;
21622         d = null;
21623         v += "&#160;";
21624         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
21625         this.el.setWidth(w);
21626         this.fireEvent("autosize", this, w);
21627     }
21628 });/*
21629  * Based on:
21630  * Ext JS Library 1.1.1
21631  * Copyright(c) 2006-2007, Ext JS, LLC.
21632  *
21633  * Originally Released Under LGPL - original licence link has changed is not relivant.
21634  *
21635  * Fork - LGPL
21636  * <script type="text/javascript">
21637  */
21638  
21639 /**
21640  * @class Roo.form.Hidden
21641  * @extends Roo.form.TextField
21642  * Simple Hidden element used on forms 
21643  * 
21644  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
21645  * 
21646  * @constructor
21647  * Creates a new Hidden form element.
21648  * @param {Object} config Configuration options
21649  */
21650
21651
21652
21653 // easy hidden field...
21654 Roo.form.Hidden = function(config){
21655     Roo.form.Hidden.superclass.constructor.call(this, config);
21656 };
21657   
21658 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
21659     fieldLabel:      '',
21660     inputType:      'hidden',
21661     width:          50,
21662     allowBlank:     true,
21663     labelSeparator: '',
21664     hidden:         true,
21665     itemCls :       'x-form-item-display-none'
21666
21667
21668 });
21669
21670
21671 /*
21672  * Based on:
21673  * Ext JS Library 1.1.1
21674  * Copyright(c) 2006-2007, Ext JS, LLC.
21675  *
21676  * Originally Released Under LGPL - original licence link has changed is not relivant.
21677  *
21678  * Fork - LGPL
21679  * <script type="text/javascript">
21680  */
21681  
21682 /**
21683  * @class Roo.form.TriggerField
21684  * @extends Roo.form.TextField
21685  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
21686  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
21687  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
21688  * for which you can provide a custom implementation.  For example:
21689  * <pre><code>
21690 var trigger = new Roo.form.TriggerField();
21691 trigger.onTriggerClick = myTriggerFn;
21692 trigger.applyTo('my-field');
21693 </code></pre>
21694  *
21695  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
21696  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
21697  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
21698  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
21699  * @constructor
21700  * Create a new TriggerField.
21701  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
21702  * to the base TextField)
21703  */
21704 Roo.form.TriggerField = function(config){
21705     this.mimicing = false;
21706     Roo.form.TriggerField.superclass.constructor.call(this, config);
21707 };
21708
21709 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
21710     /**
21711      * @cfg {String} triggerClass A CSS class to apply to the trigger
21712      */
21713     /**
21714      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21715      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
21716      */
21717     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
21718     /**
21719      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
21720      */
21721     hideTrigger:false,
21722
21723     /** @cfg {Boolean} grow @hide */
21724     /** @cfg {Number} growMin @hide */
21725     /** @cfg {Number} growMax @hide */
21726
21727     /**
21728      * @hide 
21729      * @method
21730      */
21731     autoSize: Roo.emptyFn,
21732     // private
21733     monitorTab : true,
21734     // private
21735     deferHeight : true,
21736
21737     
21738     actionMode : 'wrap',
21739     // private
21740     onResize : function(w, h){
21741         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
21742         if(typeof w == 'number'){
21743             var x = w - this.trigger.getWidth();
21744             this.el.setWidth(this.adjustWidth('input', x));
21745             this.trigger.setStyle('left', x+'px');
21746         }
21747     },
21748
21749     // private
21750     adjustSize : Roo.BoxComponent.prototype.adjustSize,
21751
21752     // private
21753     getResizeEl : function(){
21754         return this.wrap;
21755     },
21756
21757     // private
21758     getPositionEl : function(){
21759         return this.wrap;
21760     },
21761
21762     // private
21763     alignErrorIcon : function(){
21764         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
21765     },
21766
21767     // private
21768     onRender : function(ct, position){
21769         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
21770         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
21771         this.trigger = this.wrap.createChild(this.triggerConfig ||
21772                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
21773         if(this.hideTrigger){
21774             this.trigger.setDisplayed(false);
21775         }
21776         this.initTrigger();
21777         if(!this.width){
21778             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
21779         }
21780     },
21781
21782     // private
21783     initTrigger : function(){
21784         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
21785         this.trigger.addClassOnOver('x-form-trigger-over');
21786         this.trigger.addClassOnClick('x-form-trigger-click');
21787     },
21788
21789     // private
21790     onDestroy : function(){
21791         if(this.trigger){
21792             this.trigger.removeAllListeners();
21793             this.trigger.remove();
21794         }
21795         if(this.wrap){
21796             this.wrap.remove();
21797         }
21798         Roo.form.TriggerField.superclass.onDestroy.call(this);
21799     },
21800
21801     // private
21802     onFocus : function(){
21803         Roo.form.TriggerField.superclass.onFocus.call(this);
21804         if(!this.mimicing){
21805             this.wrap.addClass('x-trigger-wrap-focus');
21806             this.mimicing = true;
21807             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
21808             if(this.monitorTab){
21809                 this.el.on("keydown", this.checkTab, this);
21810             }
21811         }
21812     },
21813
21814     // private
21815     checkTab : function(e){
21816         if(e.getKey() == e.TAB){
21817             this.triggerBlur();
21818         }
21819     },
21820
21821     // private
21822     onBlur : function(){
21823         // do nothing
21824     },
21825
21826     // private
21827     mimicBlur : function(e, t){
21828         if(!this.wrap.contains(t) && this.validateBlur()){
21829             this.triggerBlur();
21830         }
21831     },
21832
21833     // private
21834     triggerBlur : function(){
21835         this.mimicing = false;
21836         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
21837         if(this.monitorTab){
21838             this.el.un("keydown", this.checkTab, this);
21839         }
21840         this.wrap.removeClass('x-trigger-wrap-focus');
21841         Roo.form.TriggerField.superclass.onBlur.call(this);
21842     },
21843
21844     // private
21845     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
21846     validateBlur : function(e, t){
21847         return true;
21848     },
21849
21850     // private
21851     onDisable : function(){
21852         Roo.form.TriggerField.superclass.onDisable.call(this);
21853         if(this.wrap){
21854             this.wrap.addClass('x-item-disabled');
21855         }
21856     },
21857
21858     // private
21859     onEnable : function(){
21860         Roo.form.TriggerField.superclass.onEnable.call(this);
21861         if(this.wrap){
21862             this.wrap.removeClass('x-item-disabled');
21863         }
21864     },
21865
21866     // private
21867     onShow : function(){
21868         var ae = this.getActionEl();
21869         
21870         if(ae){
21871             ae.dom.style.display = '';
21872             ae.dom.style.visibility = 'visible';
21873         }
21874     },
21875
21876     // private
21877     
21878     onHide : function(){
21879         var ae = this.getActionEl();
21880         ae.dom.style.display = 'none';
21881     },
21882
21883     /**
21884      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
21885      * by an implementing function.
21886      * @method
21887      * @param {EventObject} e
21888      */
21889     onTriggerClick : Roo.emptyFn
21890 });
21891
21892 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
21893 // to be extended by an implementing class.  For an example of implementing this class, see the custom
21894 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
21895 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
21896     initComponent : function(){
21897         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
21898
21899         this.triggerConfig = {
21900             tag:'span', cls:'x-form-twin-triggers', cn:[
21901             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
21902             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
21903         ]};
21904     },
21905
21906     getTrigger : function(index){
21907         return this.triggers[index];
21908     },
21909
21910     initTrigger : function(){
21911         var ts = this.trigger.select('.x-form-trigger', true);
21912         this.wrap.setStyle('overflow', 'hidden');
21913         var triggerField = this;
21914         ts.each(function(t, all, index){
21915             t.hide = function(){
21916                 var w = triggerField.wrap.getWidth();
21917                 this.dom.style.display = 'none';
21918                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21919             };
21920             t.show = function(){
21921                 var w = triggerField.wrap.getWidth();
21922                 this.dom.style.display = '';
21923                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21924             };
21925             var triggerIndex = 'Trigger'+(index+1);
21926
21927             if(this['hide'+triggerIndex]){
21928                 t.dom.style.display = 'none';
21929             }
21930             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
21931             t.addClassOnOver('x-form-trigger-over');
21932             t.addClassOnClick('x-form-trigger-click');
21933         }, this);
21934         this.triggers = ts.elements;
21935     },
21936
21937     onTrigger1Click : Roo.emptyFn,
21938     onTrigger2Click : Roo.emptyFn
21939 });/*
21940  * Based on:
21941  * Ext JS Library 1.1.1
21942  * Copyright(c) 2006-2007, Ext JS, LLC.
21943  *
21944  * Originally Released Under LGPL - original licence link has changed is not relivant.
21945  *
21946  * Fork - LGPL
21947  * <script type="text/javascript">
21948  */
21949  
21950 /**
21951  * @class Roo.form.TextArea
21952  * @extends Roo.form.TextField
21953  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
21954  * support for auto-sizing.
21955  * @constructor
21956  * Creates a new TextArea
21957  * @param {Object} config Configuration options
21958  */
21959 Roo.form.TextArea = function(config){
21960     Roo.form.TextArea.superclass.constructor.call(this, config);
21961     // these are provided exchanges for backwards compat
21962     // minHeight/maxHeight were replaced by growMin/growMax to be
21963     // compatible with TextField growing config values
21964     if(this.minHeight !== undefined){
21965         this.growMin = this.minHeight;
21966     }
21967     if(this.maxHeight !== undefined){
21968         this.growMax = this.maxHeight;
21969     }
21970 };
21971
21972 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
21973     /**
21974      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
21975      */
21976     growMin : 60,
21977     /**
21978      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
21979      */
21980     growMax: 1000,
21981     /**
21982      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
21983      * in the field (equivalent to setting overflow: hidden, defaults to false)
21984      */
21985     preventScrollbars: false,
21986     /**
21987      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21988      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
21989      */
21990
21991     // private
21992     onRender : function(ct, position){
21993         if(!this.el){
21994             this.defaultAutoCreate = {
21995                 tag: "textarea",
21996                 style:"width:300px;height:60px;",
21997                 autocomplete: "off"
21998             };
21999         }
22000         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
22001         if(this.grow){
22002             this.textSizeEl = Roo.DomHelper.append(document.body, {
22003                 tag: "pre", cls: "x-form-grow-sizer"
22004             });
22005             if(this.preventScrollbars){
22006                 this.el.setStyle("overflow", "hidden");
22007             }
22008             this.el.setHeight(this.growMin);
22009         }
22010     },
22011
22012     onDestroy : function(){
22013         if(this.textSizeEl){
22014             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
22015         }
22016         Roo.form.TextArea.superclass.onDestroy.call(this);
22017     },
22018
22019     // private
22020     onKeyUp : function(e){
22021         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
22022             this.autoSize();
22023         }
22024     },
22025
22026     /**
22027      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
22028      * This only takes effect if grow = true, and fires the autosize event if the height changes.
22029      */
22030     autoSize : function(){
22031         if(!this.grow || !this.textSizeEl){
22032             return;
22033         }
22034         var el = this.el;
22035         var v = el.dom.value;
22036         var ts = this.textSizeEl;
22037
22038         ts.innerHTML = '';
22039         ts.appendChild(document.createTextNode(v));
22040         v = ts.innerHTML;
22041
22042         Roo.fly(ts).setWidth(this.el.getWidth());
22043         if(v.length < 1){
22044             v = "&#160;&#160;";
22045         }else{
22046             if(Roo.isIE){
22047                 v = v.replace(/\n/g, '<p>&#160;</p>');
22048             }
22049             v += "&#160;\n&#160;";
22050         }
22051         ts.innerHTML = v;
22052         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
22053         if(h != this.lastHeight){
22054             this.lastHeight = h;
22055             this.el.setHeight(h);
22056             this.fireEvent("autosize", this, h);
22057         }
22058     }
22059 });/*
22060  * Based on:
22061  * Ext JS Library 1.1.1
22062  * Copyright(c) 2006-2007, Ext JS, LLC.
22063  *
22064  * Originally Released Under LGPL - original licence link has changed is not relivant.
22065  *
22066  * Fork - LGPL
22067  * <script type="text/javascript">
22068  */
22069  
22070
22071 /**
22072  * @class Roo.form.NumberField
22073  * @extends Roo.form.TextField
22074  * Numeric text field that provides automatic keystroke filtering and numeric validation.
22075  * @constructor
22076  * Creates a new NumberField
22077  * @param {Object} config Configuration options
22078  */
22079 Roo.form.NumberField = function(config){
22080     Roo.form.NumberField.superclass.constructor.call(this, config);
22081 };
22082
22083 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
22084     /**
22085      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
22086      */
22087     fieldClass: "x-form-field x-form-num-field",
22088     /**
22089      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
22090      */
22091     allowDecimals : true,
22092     /**
22093      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
22094      */
22095     decimalSeparator : ".",
22096     /**
22097      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
22098      */
22099     decimalPrecision : 2,
22100     /**
22101      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
22102      */
22103     allowNegative : true,
22104     /**
22105      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
22106      */
22107     minValue : Number.NEGATIVE_INFINITY,
22108     /**
22109      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
22110      */
22111     maxValue : Number.MAX_VALUE,
22112     /**
22113      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
22114      */
22115     minText : "The minimum value for this field is {0}",
22116     /**
22117      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
22118      */
22119     maxText : "The maximum value for this field is {0}",
22120     /**
22121      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
22122      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
22123      */
22124     nanText : "{0} is not a valid number",
22125
22126     // private
22127     initEvents : function(){
22128         Roo.form.NumberField.superclass.initEvents.call(this);
22129         var allowed = "0123456789";
22130         if(this.allowDecimals){
22131             allowed += this.decimalSeparator;
22132         }
22133         if(this.allowNegative){
22134             allowed += "-";
22135         }
22136         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
22137         var keyPress = function(e){
22138             var k = e.getKey();
22139             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
22140                 return;
22141             }
22142             var c = e.getCharCode();
22143             if(allowed.indexOf(String.fromCharCode(c)) === -1){
22144                 e.stopEvent();
22145             }
22146         };
22147         this.el.on("keypress", keyPress, this);
22148     },
22149
22150     // private
22151     validateValue : function(value){
22152         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
22153             return false;
22154         }
22155         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22156              return true;
22157         }
22158         var num = this.parseValue(value);
22159         if(isNaN(num)){
22160             this.markInvalid(String.format(this.nanText, value));
22161             return false;
22162         }
22163         if(num < this.minValue){
22164             this.markInvalid(String.format(this.minText, this.minValue));
22165             return false;
22166         }
22167         if(num > this.maxValue){
22168             this.markInvalid(String.format(this.maxText, this.maxValue));
22169             return false;
22170         }
22171         return true;
22172     },
22173
22174     getValue : function(){
22175         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
22176     },
22177
22178     // private
22179     parseValue : function(value){
22180         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
22181         return isNaN(value) ? '' : value;
22182     },
22183
22184     // private
22185     fixPrecision : function(value){
22186         var nan = isNaN(value);
22187         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
22188             return nan ? '' : value;
22189         }
22190         return parseFloat(value).toFixed(this.decimalPrecision);
22191     },
22192
22193     setValue : function(v){
22194         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
22195     },
22196
22197     // private
22198     decimalPrecisionFcn : function(v){
22199         return Math.floor(v);
22200     },
22201
22202     beforeBlur : function(){
22203         var v = this.parseValue(this.getRawValue());
22204         if(v){
22205             this.setValue(this.fixPrecision(v));
22206         }
22207     }
22208 });/*
22209  * Based on:
22210  * Ext JS Library 1.1.1
22211  * Copyright(c) 2006-2007, Ext JS, LLC.
22212  *
22213  * Originally Released Under LGPL - original licence link has changed is not relivant.
22214  *
22215  * Fork - LGPL
22216  * <script type="text/javascript">
22217  */
22218  
22219 /**
22220  * @class Roo.form.DateField
22221  * @extends Roo.form.TriggerField
22222  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22223 * @constructor
22224 * Create a new DateField
22225 * @param {Object} config
22226  */
22227 Roo.form.DateField = function(config){
22228     Roo.form.DateField.superclass.constructor.call(this, config);
22229     
22230       this.addEvents({
22231          
22232         /**
22233          * @event select
22234          * Fires when a date is selected
22235              * @param {Roo.form.DateField} combo This combo box
22236              * @param {Date} date The date selected
22237              */
22238         'select' : true
22239          
22240     });
22241     
22242     
22243     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22244     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22245     this.ddMatch = null;
22246     if(this.disabledDates){
22247         var dd = this.disabledDates;
22248         var re = "(?:";
22249         for(var i = 0; i < dd.length; i++){
22250             re += dd[i];
22251             if(i != dd.length-1) re += "|";
22252         }
22253         this.ddMatch = new RegExp(re + ")");
22254     }
22255 };
22256
22257 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
22258     /**
22259      * @cfg {String} format
22260      * The default date format string which can be overriden for localization support.  The format must be
22261      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22262      */
22263     format : "m/d/y",
22264     /**
22265      * @cfg {String} altFormats
22266      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22267      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22268      */
22269     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22270     /**
22271      * @cfg {Array} disabledDays
22272      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22273      */
22274     disabledDays : null,
22275     /**
22276      * @cfg {String} disabledDaysText
22277      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22278      */
22279     disabledDaysText : "Disabled",
22280     /**
22281      * @cfg {Array} disabledDates
22282      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22283      * expression so they are very powerful. Some examples:
22284      * <ul>
22285      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22286      * <li>["03/08", "09/16"] would disable those days for every year</li>
22287      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22288      * <li>["03/../2006"] would disable every day in March 2006</li>
22289      * <li>["^03"] would disable every day in every March</li>
22290      * </ul>
22291      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22292      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22293      */
22294     disabledDates : null,
22295     /**
22296      * @cfg {String} disabledDatesText
22297      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22298      */
22299     disabledDatesText : "Disabled",
22300     /**
22301      * @cfg {Date/String} minValue
22302      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22303      * valid format (defaults to null).
22304      */
22305     minValue : null,
22306     /**
22307      * @cfg {Date/String} maxValue
22308      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22309      * valid format (defaults to null).
22310      */
22311     maxValue : null,
22312     /**
22313      * @cfg {String} minText
22314      * The error text to display when the date in the cell is before minValue (defaults to
22315      * 'The date in this field must be after {minValue}').
22316      */
22317     minText : "The date in this field must be equal to or after {0}",
22318     /**
22319      * @cfg {String} maxText
22320      * The error text to display when the date in the cell is after maxValue (defaults to
22321      * 'The date in this field must be before {maxValue}').
22322      */
22323     maxText : "The date in this field must be equal to or before {0}",
22324     /**
22325      * @cfg {String} invalidText
22326      * The error text to display when the date in the field is invalid (defaults to
22327      * '{value} is not a valid date - it must be in the format {format}').
22328      */
22329     invalidText : "{0} is not a valid date - it must be in the format {1}",
22330     /**
22331      * @cfg {String} triggerClass
22332      * An additional CSS class used to style the trigger button.  The trigger will always get the
22333      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22334      * which displays a calendar icon).
22335      */
22336     triggerClass : 'x-form-date-trigger',
22337     
22338
22339     /**
22340      * @cfg {bool} useIso
22341      * if enabled, then the date field will use a hidden field to store the 
22342      * real value as iso formated date. default (false)
22343      */ 
22344     useIso : false,
22345     /**
22346      * @cfg {String/Object} autoCreate
22347      * A DomHelper element spec, or true for a default element spec (defaults to
22348      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22349      */ 
22350     // private
22351     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
22352     
22353     // private
22354     hiddenField: false,
22355     
22356     onRender : function(ct, position)
22357     {
22358         Roo.form.DateField.superclass.onRender.call(this, ct, position);
22359         if (this.useIso) {
22360             this.el.dom.removeAttribute('name'); 
22361             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22362                     'before', true);
22363             this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
22364             // prevent input submission
22365             this.hiddenName = this.name;
22366         }
22367             
22368             
22369     },
22370     
22371     // private
22372     validateValue : function(value)
22373     {
22374         value = this.formatDate(value);
22375         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
22376             return false;
22377         }
22378         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22379              return true;
22380         }
22381         var svalue = value;
22382         value = this.parseDate(value);
22383         if(!value){
22384             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22385             return false;
22386         }
22387         var time = value.getTime();
22388         if(this.minValue && time < this.minValue.getTime()){
22389             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22390             return false;
22391         }
22392         if(this.maxValue && time > this.maxValue.getTime()){
22393             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22394             return false;
22395         }
22396         if(this.disabledDays){
22397             var day = value.getDay();
22398             for(var i = 0; i < this.disabledDays.length; i++) {
22399                 if(day === this.disabledDays[i]){
22400                     this.markInvalid(this.disabledDaysText);
22401                     return false;
22402                 }
22403             }
22404         }
22405         var fvalue = this.formatDate(value);
22406         if(this.ddMatch && this.ddMatch.test(fvalue)){
22407             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22408             return false;
22409         }
22410         return true;
22411     },
22412
22413     // private
22414     // Provides logic to override the default TriggerField.validateBlur which just returns true
22415     validateBlur : function(){
22416         return !this.menu || !this.menu.isVisible();
22417     },
22418
22419     /**
22420      * Returns the current date value of the date field.
22421      * @return {Date} The date value
22422      */
22423     getValue : function(){
22424         
22425         return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
22426     },
22427
22428     /**
22429      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22430      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
22431      * (the default format used is "m/d/y").
22432      * <br />Usage:
22433      * <pre><code>
22434 //All of these calls set the same date value (May 4, 2006)
22435
22436 //Pass a date object:
22437 var dt = new Date('5/4/06');
22438 dateField.setValue(dt);
22439
22440 //Pass a date string (default format):
22441 dateField.setValue('5/4/06');
22442
22443 //Pass a date string (custom format):
22444 dateField.format = 'Y-m-d';
22445 dateField.setValue('2006-5-4');
22446 </code></pre>
22447      * @param {String/Date} date The date or valid date string
22448      */
22449     setValue : function(date){
22450         if (this.hiddenField) {
22451             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22452         }
22453         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22454     },
22455
22456     // private
22457     parseDate : function(value){
22458         if(!value || value instanceof Date){
22459             return value;
22460         }
22461         var v = Date.parseDate(value, this.format);
22462         if(!v && this.altFormats){
22463             if(!this.altFormatsArray){
22464                 this.altFormatsArray = this.altFormats.split("|");
22465             }
22466             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22467                 v = Date.parseDate(value, this.altFormatsArray[i]);
22468             }
22469         }
22470         return v;
22471     },
22472
22473     // private
22474     formatDate : function(date, fmt){
22475         return (!date || !(date instanceof Date)) ?
22476                date : date.dateFormat(fmt || this.format);
22477     },
22478
22479     // private
22480     menuListeners : {
22481         select: function(m, d){
22482             this.setValue(d);
22483             this.fireEvent('select', this, d);
22484         },
22485         show : function(){ // retain focus styling
22486             this.onFocus();
22487         },
22488         hide : function(){
22489             this.focus.defer(10, this);
22490             var ml = this.menuListeners;
22491             this.menu.un("select", ml.select,  this);
22492             this.menu.un("show", ml.show,  this);
22493             this.menu.un("hide", ml.hide,  this);
22494         }
22495     },
22496
22497     // private
22498     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22499     onTriggerClick : function(){
22500         if(this.disabled){
22501             return;
22502         }
22503         if(this.menu == null){
22504             this.menu = new Roo.menu.DateMenu();
22505         }
22506         Roo.apply(this.menu.picker,  {
22507             showClear: this.allowBlank,
22508             minDate : this.minValue,
22509             maxDate : this.maxValue,
22510             disabledDatesRE : this.ddMatch,
22511             disabledDatesText : this.disabledDatesText,
22512             disabledDays : this.disabledDays,
22513             disabledDaysText : this.disabledDaysText,
22514             format : this.format,
22515             minText : String.format(this.minText, this.formatDate(this.minValue)),
22516             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22517         });
22518         this.menu.on(Roo.apply({}, this.menuListeners, {
22519             scope:this
22520         }));
22521         this.menu.picker.setValue(this.getValue() || new Date());
22522         this.menu.show(this.el, "tl-bl?");
22523     },
22524
22525     beforeBlur : function(){
22526         var v = this.parseDate(this.getRawValue());
22527         if(v){
22528             this.setValue(v);
22529         }
22530     }
22531
22532     /** @cfg {Boolean} grow @hide */
22533     /** @cfg {Number} growMin @hide */
22534     /** @cfg {Number} growMax @hide */
22535     /**
22536      * @hide
22537      * @method autoSize
22538      */
22539 });/*
22540  * Based on:
22541  * Ext JS Library 1.1.1
22542  * Copyright(c) 2006-2007, Ext JS, LLC.
22543  *
22544  * Originally Released Under LGPL - original licence link has changed is not relivant.
22545  *
22546  * Fork - LGPL
22547  * <script type="text/javascript">
22548  */
22549  
22550
22551 /**
22552  * @class Roo.form.ComboBox
22553  * @extends Roo.form.TriggerField
22554  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
22555  * @constructor
22556  * Create a new ComboBox.
22557  * @param {Object} config Configuration options
22558  */
22559 Roo.form.ComboBox = function(config){
22560     Roo.form.ComboBox.superclass.constructor.call(this, config);
22561     this.addEvents({
22562         /**
22563          * @event expand
22564          * Fires when the dropdown list is expanded
22565              * @param {Roo.form.ComboBox} combo This combo box
22566              */
22567         'expand' : true,
22568         /**
22569          * @event collapse
22570          * Fires when the dropdown list is collapsed
22571              * @param {Roo.form.ComboBox} combo This combo box
22572              */
22573         'collapse' : true,
22574         /**
22575          * @event beforeselect
22576          * Fires before a list item is selected. Return false to cancel the selection.
22577              * @param {Roo.form.ComboBox} combo This combo box
22578              * @param {Roo.data.Record} record The data record returned from the underlying store
22579              * @param {Number} index The index of the selected item in the dropdown list
22580              */
22581         'beforeselect' : true,
22582         /**
22583          * @event select
22584          * Fires when a list item is selected
22585              * @param {Roo.form.ComboBox} combo This combo box
22586              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
22587              * @param {Number} index The index of the selected item in the dropdown list
22588              */
22589         'select' : true,
22590         /**
22591          * @event beforequery
22592          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
22593          * The event object passed has these properties:
22594              * @param {Roo.form.ComboBox} combo This combo box
22595              * @param {String} query The query
22596              * @param {Boolean} forceAll true to force "all" query
22597              * @param {Boolean} cancel true to cancel the query
22598              * @param {Object} e The query event object
22599              */
22600         'beforequery': true,
22601          /**
22602          * @event add
22603          * Fires when the 'add' icon is pressed (add a listener to enable add button)
22604              * @param {Roo.form.ComboBox} combo This combo box
22605              */
22606         'add' : true,
22607         /**
22608          * @event edit
22609          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
22610              * @param {Roo.form.ComboBox} combo This combo box
22611              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
22612              */
22613         'edit' : true
22614         
22615         
22616     });
22617     if(this.transform){
22618         this.allowDomMove = false;
22619         var s = Roo.getDom(this.transform);
22620         if(!this.hiddenName){
22621             this.hiddenName = s.name;
22622         }
22623         if(!this.store){
22624             this.mode = 'local';
22625             var d = [], opts = s.options;
22626             for(var i = 0, len = opts.length;i < len; i++){
22627                 var o = opts[i];
22628                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
22629                 if(o.selected) {
22630                     this.value = value;
22631                 }
22632                 d.push([value, o.text]);
22633             }
22634             this.store = new Roo.data.SimpleStore({
22635                 'id': 0,
22636                 fields: ['value', 'text'],
22637                 data : d
22638             });
22639             this.valueField = 'value';
22640             this.displayField = 'text';
22641         }
22642         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
22643         if(!this.lazyRender){
22644             this.target = true;
22645             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
22646             s.parentNode.removeChild(s); // remove it
22647             this.render(this.el.parentNode);
22648         }else{
22649             s.parentNode.removeChild(s); // remove it
22650         }
22651
22652     }
22653     if (this.store) {
22654         this.store = Roo.factory(this.store, Roo.data);
22655     }
22656     
22657     this.selectedIndex = -1;
22658     if(this.mode == 'local'){
22659         if(config.queryDelay === undefined){
22660             this.queryDelay = 10;
22661         }
22662         if(config.minChars === undefined){
22663             this.minChars = 0;
22664         }
22665     }
22666 };
22667
22668 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
22669     /**
22670      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
22671      */
22672     /**
22673      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
22674      * rendering into an Roo.Editor, defaults to false)
22675      */
22676     /**
22677      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
22678      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
22679      */
22680     /**
22681      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
22682      */
22683     /**
22684      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
22685      * the dropdown list (defaults to undefined, with no header element)
22686      */
22687
22688      /**
22689      * @cfg {String/Roo.Template} tpl The template to use to render the output
22690      */
22691      
22692     // private
22693     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
22694     /**
22695      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
22696      */
22697     listWidth: undefined,
22698     /**
22699      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
22700      * mode = 'remote' or 'text' if mode = 'local')
22701      */
22702     displayField: undefined,
22703     /**
22704      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
22705      * mode = 'remote' or 'value' if mode = 'local'). 
22706      * Note: use of a valueField requires the user make a selection
22707      * in order for a value to be mapped.
22708      */
22709     valueField: undefined,
22710     /**
22711      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
22712      * field's data value (defaults to the underlying DOM element's name)
22713      */
22714     hiddenName: undefined,
22715     /**
22716      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
22717      */
22718     listClass: '',
22719     /**
22720      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
22721      */
22722     selectedClass: 'x-combo-selected',
22723     /**
22724      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
22725      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
22726      * which displays a downward arrow icon).
22727      */
22728     triggerClass : 'x-form-arrow-trigger',
22729     /**
22730      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
22731      */
22732     shadow:'sides',
22733     /**
22734      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
22735      * anchor positions (defaults to 'tl-bl')
22736      */
22737     listAlign: 'tl-bl?',
22738     /**
22739      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
22740      */
22741     maxHeight: 300,
22742     /**
22743      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
22744      * query specified by the allQuery config option (defaults to 'query')
22745      */
22746     triggerAction: 'query',
22747     /**
22748      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
22749      * (defaults to 4, does not apply if editable = false)
22750      */
22751     minChars : 4,
22752     /**
22753      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
22754      * delay (typeAheadDelay) if it matches a known value (defaults to false)
22755      */
22756     typeAhead: false,
22757     /**
22758      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
22759      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
22760      */
22761     queryDelay: 500,
22762     /**
22763      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
22764      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
22765      */
22766     pageSize: 0,
22767     /**
22768      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
22769      * when editable = true (defaults to false)
22770      */
22771     selectOnFocus:false,
22772     /**
22773      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
22774      */
22775     queryParam: 'query',
22776     /**
22777      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
22778      * when mode = 'remote' (defaults to 'Loading...')
22779      */
22780     loadingText: 'Loading...',
22781     /**
22782      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
22783      */
22784     resizable: false,
22785     /**
22786      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
22787      */
22788     handleHeight : 8,
22789     /**
22790      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
22791      * traditional select (defaults to true)
22792      */
22793     editable: true,
22794     /**
22795      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
22796      */
22797     allQuery: '',
22798     /**
22799      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
22800      */
22801     mode: 'remote',
22802     /**
22803      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
22804      * listWidth has a higher value)
22805      */
22806     minListWidth : 70,
22807     /**
22808      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
22809      * allow the user to set arbitrary text into the field (defaults to false)
22810      */
22811     forceSelection:false,
22812     /**
22813      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
22814      * if typeAhead = true (defaults to 250)
22815      */
22816     typeAheadDelay : 250,
22817     /**
22818      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
22819      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
22820      */
22821     valueNotFoundText : undefined,
22822     /**
22823      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
22824      */
22825     blockFocus : false,
22826     
22827     /**
22828      * @cfg {Boolean} disableClear Disable showing of clear button.
22829      */
22830     disableClear : false,
22831     /**
22832      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
22833      */
22834     alwaysQuery : false,
22835     
22836     //private
22837     addicon : false,
22838     editicon: false,
22839     
22840     
22841     // private
22842     onRender : function(ct, position){
22843         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
22844         if(this.hiddenName){
22845             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
22846                     'before', true);
22847             this.hiddenField.value =
22848                 this.hiddenValue !== undefined ? this.hiddenValue :
22849                 this.value !== undefined ? this.value : '';
22850
22851             // prevent input submission
22852             this.el.dom.removeAttribute('name');
22853         }
22854         if(Roo.isGecko){
22855             this.el.dom.setAttribute('autocomplete', 'off');
22856         }
22857
22858         var cls = 'x-combo-list';
22859
22860         this.list = new Roo.Layer({
22861             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
22862         });
22863
22864         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
22865         this.list.setWidth(lw);
22866         this.list.swallowEvent('mousewheel');
22867         this.assetHeight = 0;
22868
22869         if(this.title){
22870             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
22871             this.assetHeight += this.header.getHeight();
22872         }
22873
22874         this.innerList = this.list.createChild({cls:cls+'-inner'});
22875         this.innerList.on('mouseover', this.onViewOver, this);
22876         this.innerList.on('mousemove', this.onViewMove, this);
22877         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
22878         
22879         if(this.allowBlank && !this.pageSize && !this.disableClear){
22880             this.footer = this.list.createChild({cls:cls+'-ft'});
22881             this.pageTb = new Roo.Toolbar(this.footer);
22882            
22883         }
22884         if(this.pageSize){
22885             this.footer = this.list.createChild({cls:cls+'-ft'});
22886             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
22887                     {pageSize: this.pageSize});
22888             
22889         }
22890         
22891         if (this.pageTb && this.allowBlank && !this.disableClear) {
22892             var _this = this;
22893             this.pageTb.add(new Roo.Toolbar.Fill(), {
22894                 cls: 'x-btn-icon x-btn-clear',
22895                 text: '&#160;',
22896                 handler: function()
22897                 {
22898                     _this.collapse();
22899                     _this.clearValue();
22900                     _this.onSelect(false, -1);
22901                 }
22902             });
22903         }
22904         if (this.footer) {
22905             this.assetHeight += this.footer.getHeight();
22906         }
22907         
22908
22909         if(!this.tpl){
22910             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
22911         }
22912
22913         this.view = new Roo.View(this.innerList, this.tpl, {
22914             singleSelect:true, store: this.store, selectedClass: this.selectedClass
22915         });
22916
22917         this.view.on('click', this.onViewClick, this);
22918
22919         this.store.on('beforeload', this.onBeforeLoad, this);
22920         this.store.on('load', this.onLoad, this);
22921         this.store.on('loadexception', this.collapse, this);
22922
22923         if(this.resizable){
22924             this.resizer = new Roo.Resizable(this.list,  {
22925                pinned:true, handles:'se'
22926             });
22927             this.resizer.on('resize', function(r, w, h){
22928                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
22929                 this.listWidth = w;
22930                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
22931                 this.restrictHeight();
22932             }, this);
22933             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
22934         }
22935         if(!this.editable){
22936             this.editable = true;
22937             this.setEditable(false);
22938         }  
22939         
22940         
22941         if (typeof(this.events.add.listeners) != 'undefined') {
22942             
22943             this.addicon = this.wrap.createChild(
22944                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
22945        
22946             this.addicon.on('click', function(e) {
22947                 this.fireEvent('add', this);
22948             }, this);
22949         }
22950         if (typeof(this.events.edit.listeners) != 'undefined') {
22951             
22952             this.editicon = this.wrap.createChild(
22953                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
22954             if (this.addicon) {
22955                 this.editicon.setStyle('margin-left', '40px');
22956             }
22957             this.editicon.on('click', function(e) {
22958                 
22959                 // we fire even  if inothing is selected..
22960                 this.fireEvent('edit', this, this.lastData );
22961                 
22962             }, this);
22963         }
22964         
22965         
22966         
22967     },
22968
22969     // private
22970     initEvents : function(){
22971         Roo.form.ComboBox.superclass.initEvents.call(this);
22972
22973         this.keyNav = new Roo.KeyNav(this.el, {
22974             "up" : function(e){
22975                 this.inKeyMode = true;
22976                 this.selectPrev();
22977             },
22978
22979             "down" : function(e){
22980                 if(!this.isExpanded()){
22981                     this.onTriggerClick();
22982                 }else{
22983                     this.inKeyMode = true;
22984                     this.selectNext();
22985                 }
22986             },
22987
22988             "enter" : function(e){
22989                 this.onViewClick();
22990                 //return true;
22991             },
22992
22993             "esc" : function(e){
22994                 this.collapse();
22995             },
22996
22997             "tab" : function(e){
22998                 this.onViewClick(false);
22999                 return true;
23000             },
23001
23002             scope : this,
23003
23004             doRelay : function(foo, bar, hname){
23005                 if(hname == 'down' || this.scope.isExpanded()){
23006                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
23007                 }
23008                 return true;
23009             },
23010
23011             forceKeyDown: true
23012         });
23013         this.queryDelay = Math.max(this.queryDelay || 10,
23014                 this.mode == 'local' ? 10 : 250);
23015         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
23016         if(this.typeAhead){
23017             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
23018         }
23019         if(this.editable !== false){
23020             this.el.on("keyup", this.onKeyUp, this);
23021         }
23022         if(this.forceSelection){
23023             this.on('blur', this.doForce, this);
23024         }
23025     },
23026
23027     onDestroy : function(){
23028         if(this.view){
23029             this.view.setStore(null);
23030             this.view.el.removeAllListeners();
23031             this.view.el.remove();
23032             this.view.purgeListeners();
23033         }
23034         if(this.list){
23035             this.list.destroy();
23036         }
23037         if(this.store){
23038             this.store.un('beforeload', this.onBeforeLoad, this);
23039             this.store.un('load', this.onLoad, this);
23040             this.store.un('loadexception', this.collapse, this);
23041         }
23042         Roo.form.ComboBox.superclass.onDestroy.call(this);
23043     },
23044
23045     // private
23046     fireKey : function(e){
23047         if(e.isNavKeyPress() && !this.list.isVisible()){
23048             this.fireEvent("specialkey", this, e);
23049         }
23050     },
23051
23052     // private
23053     onResize: function(w, h){
23054         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
23055         
23056         if(typeof w != 'number'){
23057             // we do not handle it!?!?
23058             return;
23059         }
23060         var tw = this.trigger.getWidth();
23061         tw += this.addicon ? this.addicon.getWidth() : 0;
23062         tw += this.editicon ? this.editicon.getWidth() : 0;
23063         var x = w - tw;
23064         this.el.setWidth( this.adjustWidth('input', x));
23065             
23066         this.trigger.setStyle('left', x+'px');
23067         
23068         if(this.list && this.listWidth === undefined){
23069             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
23070             this.list.setWidth(lw);
23071             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23072         }
23073         
23074     
23075         
23076     },
23077
23078     /**
23079      * Allow or prevent the user from directly editing the field text.  If false is passed,
23080      * the user will only be able to select from the items defined in the dropdown list.  This method
23081      * is the runtime equivalent of setting the 'editable' config option at config time.
23082      * @param {Boolean} value True to allow the user to directly edit the field text
23083      */
23084     setEditable : function(value){
23085         if(value == this.editable){
23086             return;
23087         }
23088         this.editable = value;
23089         if(!value){
23090             this.el.dom.setAttribute('readOnly', true);
23091             this.el.on('mousedown', this.onTriggerClick,  this);
23092             this.el.addClass('x-combo-noedit');
23093         }else{
23094             this.el.dom.setAttribute('readOnly', false);
23095             this.el.un('mousedown', this.onTriggerClick,  this);
23096             this.el.removeClass('x-combo-noedit');
23097         }
23098     },
23099
23100     // private
23101     onBeforeLoad : function(){
23102         if(!this.hasFocus){
23103             return;
23104         }
23105         this.innerList.update(this.loadingText ?
23106                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
23107         this.restrictHeight();
23108         this.selectedIndex = -1;
23109     },
23110
23111     // private
23112     onLoad : function(){
23113         if(!this.hasFocus){
23114             return;
23115         }
23116         if(this.store.getCount() > 0){
23117             this.expand();
23118             this.restrictHeight();
23119             if(this.lastQuery == this.allQuery){
23120                 if(this.editable){
23121                     this.el.dom.select();
23122                 }
23123                 if(!this.selectByValue(this.value, true)){
23124                     this.select(0, true);
23125                 }
23126             }else{
23127                 this.selectNext();
23128                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
23129                     this.taTask.delay(this.typeAheadDelay);
23130                 }
23131             }
23132         }else{
23133             this.onEmptyResults();
23134         }
23135         //this.el.focus();
23136     },
23137
23138     // private
23139     onTypeAhead : function(){
23140         if(this.store.getCount() > 0){
23141             var r = this.store.getAt(0);
23142             var newValue = r.data[this.displayField];
23143             var len = newValue.length;
23144             var selStart = this.getRawValue().length;
23145             if(selStart != len){
23146                 this.setRawValue(newValue);
23147                 this.selectText(selStart, newValue.length);
23148             }
23149         }
23150     },
23151
23152     // private
23153     onSelect : function(record, index){
23154         if(this.fireEvent('beforeselect', this, record, index) !== false){
23155             this.setFromData(index > -1 ? record.data : false);
23156             this.collapse();
23157             this.fireEvent('select', this, record, index);
23158         }
23159     },
23160
23161     /**
23162      * Returns the currently selected field value or empty string if no value is set.
23163      * @return {String} value The selected value
23164      */
23165     getValue : function(){
23166         if(this.valueField){
23167             return typeof this.value != 'undefined' ? this.value : '';
23168         }else{
23169             return Roo.form.ComboBox.superclass.getValue.call(this);
23170         }
23171     },
23172
23173     /**
23174      * Clears any text/value currently set in the field
23175      */
23176     clearValue : function(){
23177         if(this.hiddenField){
23178             this.hiddenField.value = '';
23179         }
23180         this.value = '';
23181         this.setRawValue('');
23182         this.lastSelectionText = '';
23183         this.applyEmptyText();
23184     },
23185
23186     /**
23187      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
23188      * will be displayed in the field.  If the value does not match the data value of an existing item,
23189      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
23190      * Otherwise the field will be blank (although the value will still be set).
23191      * @param {String} value The value to match
23192      */
23193     setValue : function(v){
23194         var text = v;
23195         if(this.valueField){
23196             var r = this.findRecord(this.valueField, v);
23197             if(r){
23198                 text = r.data[this.displayField];
23199             }else if(this.valueNotFoundText !== undefined){
23200                 text = this.valueNotFoundText;
23201             }
23202         }
23203         this.lastSelectionText = text;
23204         if(this.hiddenField){
23205             this.hiddenField.value = v;
23206         }
23207         Roo.form.ComboBox.superclass.setValue.call(this, text);
23208         this.value = v;
23209     },
23210     /**
23211      * @property {Object} the last set data for the element
23212      */
23213     
23214     lastData : false,
23215     /**
23216      * Sets the value of the field based on a object which is related to the record format for the store.
23217      * @param {Object} value the value to set as. or false on reset?
23218      */
23219     setFromData : function(o){
23220         var dv = ''; // display value
23221         var vv = ''; // value value..
23222         this.lastData = o;
23223         if (this.displayField) {
23224             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
23225         } else {
23226             // this is an error condition!!!
23227             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
23228         }
23229         
23230         if(this.valueField){
23231             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
23232         }
23233         if(this.hiddenField){
23234             this.hiddenField.value = vv;
23235             
23236             this.lastSelectionText = dv;
23237             Roo.form.ComboBox.superclass.setValue.call(this, dv);
23238             this.value = vv;
23239             return;
23240         }
23241         // no hidden field.. - we store the value in 'value', but still display
23242         // display field!!!!
23243         this.lastSelectionText = dv;
23244         Roo.form.ComboBox.superclass.setValue.call(this, dv);
23245         this.value = vv;
23246         
23247         
23248     },
23249     // private
23250     reset : function(){
23251         // overridden so that last data is reset..
23252         this.setValue(this.originalValue);
23253         this.clearInvalid();
23254         this.lastData = false;
23255     },
23256     // private
23257     findRecord : function(prop, value){
23258         var record;
23259         if(this.store.getCount() > 0){
23260             this.store.each(function(r){
23261                 if(r.data[prop] == value){
23262                     record = r;
23263                     return false;
23264                 }
23265             });
23266         }
23267         return record;
23268     },
23269
23270     // private
23271     onViewMove : function(e, t){
23272         this.inKeyMode = false;
23273     },
23274
23275     // private
23276     onViewOver : function(e, t){
23277         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
23278             return;
23279         }
23280         var item = this.view.findItemFromChild(t);
23281         if(item){
23282             var index = this.view.indexOf(item);
23283             this.select(index, false);
23284         }
23285     },
23286
23287     // private
23288     onViewClick : function(doFocus){
23289         var index = this.view.getSelectedIndexes()[0];
23290         var r = this.store.getAt(index);
23291         if(r){
23292             this.onSelect(r, index);
23293         }
23294         if(doFocus !== false && !this.blockFocus){
23295             this.el.focus();
23296         }
23297     },
23298
23299     // private
23300     restrictHeight : function(){
23301         this.innerList.dom.style.height = '';
23302         var inner = this.innerList.dom;
23303         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
23304         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
23305         this.list.beginUpdate();
23306         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
23307         this.list.alignTo(this.el, this.listAlign);
23308         this.list.endUpdate();
23309     },
23310
23311     // private
23312     onEmptyResults : function(){
23313         this.collapse();
23314     },
23315
23316     /**
23317      * Returns true if the dropdown list is expanded, else false.
23318      */
23319     isExpanded : function(){
23320         return this.list.isVisible();
23321     },
23322
23323     /**
23324      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
23325      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23326      * @param {String} value The data value of the item to select
23327      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23328      * selected item if it is not currently in view (defaults to true)
23329      * @return {Boolean} True if the value matched an item in the list, else false
23330      */
23331     selectByValue : function(v, scrollIntoView){
23332         if(v !== undefined && v !== null){
23333             var r = this.findRecord(this.valueField || this.displayField, v);
23334             if(r){
23335                 this.select(this.store.indexOf(r), scrollIntoView);
23336                 return true;
23337             }
23338         }
23339         return false;
23340     },
23341
23342     /**
23343      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
23344      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23345      * @param {Number} index The zero-based index of the list item to select
23346      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23347      * selected item if it is not currently in view (defaults to true)
23348      */
23349     select : function(index, scrollIntoView){
23350         this.selectedIndex = index;
23351         this.view.select(index);
23352         if(scrollIntoView !== false){
23353             var el = this.view.getNode(index);
23354             if(el){
23355                 this.innerList.scrollChildIntoView(el, false);
23356             }
23357         }
23358     },
23359
23360     // private
23361     selectNext : function(){
23362         var ct = this.store.getCount();
23363         if(ct > 0){
23364             if(this.selectedIndex == -1){
23365                 this.select(0);
23366             }else if(this.selectedIndex < ct-1){
23367                 this.select(this.selectedIndex+1);
23368             }
23369         }
23370     },
23371
23372     // private
23373     selectPrev : function(){
23374         var ct = this.store.getCount();
23375         if(ct > 0){
23376             if(this.selectedIndex == -1){
23377                 this.select(0);
23378             }else if(this.selectedIndex != 0){
23379                 this.select(this.selectedIndex-1);
23380             }
23381         }
23382     },
23383
23384     // private
23385     onKeyUp : function(e){
23386         if(this.editable !== false && !e.isSpecialKey()){
23387             this.lastKey = e.getKey();
23388             this.dqTask.delay(this.queryDelay);
23389         }
23390     },
23391
23392     // private
23393     validateBlur : function(){
23394         return !this.list || !this.list.isVisible();   
23395     },
23396
23397     // private
23398     initQuery : function(){
23399         this.doQuery(this.getRawValue());
23400     },
23401
23402     // private
23403     doForce : function(){
23404         if(this.el.dom.value.length > 0){
23405             this.el.dom.value =
23406                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
23407             this.applyEmptyText();
23408         }
23409     },
23410
23411     /**
23412      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
23413      * query allowing the query action to be canceled if needed.
23414      * @param {String} query The SQL query to execute
23415      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
23416      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
23417      * saved in the current store (defaults to false)
23418      */
23419     doQuery : function(q, forceAll){
23420         if(q === undefined || q === null){
23421             q = '';
23422         }
23423         var qe = {
23424             query: q,
23425             forceAll: forceAll,
23426             combo: this,
23427             cancel:false
23428         };
23429         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
23430             return false;
23431         }
23432         q = qe.query;
23433         forceAll = qe.forceAll;
23434         if(forceAll === true || (q.length >= this.minChars)){
23435             if(this.lastQuery != q || this.alwaysQuery){
23436                 this.lastQuery = q;
23437                 if(this.mode == 'local'){
23438                     this.selectedIndex = -1;
23439                     if(forceAll){
23440                         this.store.clearFilter();
23441                     }else{
23442                         this.store.filter(this.displayField, q);
23443                     }
23444                     this.onLoad();
23445                 }else{
23446                     this.store.baseParams[this.queryParam] = q;
23447                     this.store.load({
23448                         params: this.getParams(q)
23449                     });
23450                     this.expand();
23451                 }
23452             }else{
23453                 this.selectedIndex = -1;
23454                 this.onLoad();   
23455             }
23456         }
23457     },
23458
23459     // private
23460     getParams : function(q){
23461         var p = {};
23462         //p[this.queryParam] = q;
23463         if(this.pageSize){
23464             p.start = 0;
23465             p.limit = this.pageSize;
23466         }
23467         return p;
23468     },
23469
23470     /**
23471      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
23472      */
23473     collapse : function(){
23474         if(!this.isExpanded()){
23475             return;
23476         }
23477         this.list.hide();
23478         Roo.get(document).un('mousedown', this.collapseIf, this);
23479         Roo.get(document).un('mousewheel', this.collapseIf, this);
23480         if (!this.editable) {
23481             Roo.get(document).un('keydown', this.listKeyPress, this);
23482         }
23483         this.fireEvent('collapse', this);
23484     },
23485
23486     // private
23487     collapseIf : function(e){
23488         if(!e.within(this.wrap) && !e.within(this.list)){
23489             this.collapse();
23490         }
23491     },
23492
23493     /**
23494      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
23495      */
23496     expand : function(){
23497         if(this.isExpanded() || !this.hasFocus){
23498             return;
23499         }
23500         this.list.alignTo(this.el, this.listAlign);
23501         this.list.show();
23502         Roo.get(document).on('mousedown', this.collapseIf, this);
23503         Roo.get(document).on('mousewheel', this.collapseIf, this);
23504         if (!this.editable) {
23505             Roo.get(document).on('keydown', this.listKeyPress, this);
23506         }
23507         
23508         this.fireEvent('expand', this);
23509     },
23510
23511     // private
23512     // Implements the default empty TriggerField.onTriggerClick function
23513     onTriggerClick : function(){
23514         if(this.disabled){
23515             return;
23516         }
23517         if(this.isExpanded()){
23518             this.collapse();
23519             if (!this.blockFocus) {
23520                 this.el.focus();
23521             }
23522             
23523         }else {
23524             this.hasFocus = true;
23525             if(this.triggerAction == 'all') {
23526                 this.doQuery(this.allQuery, true);
23527             } else {
23528                 this.doQuery(this.getRawValue());
23529             }
23530             if (!this.blockFocus) {
23531                 this.el.focus();
23532             }
23533         }
23534     },
23535     listKeyPress : function(e)
23536     {
23537         //Roo.log('listkeypress');
23538         // scroll to first matching element based on key pres..
23539         if (e.isSpecialKey()) {
23540             return false;
23541         }
23542         var k = String.fromCharCode(e.getKey()).toUpperCase();
23543         //Roo.log(k);
23544         var match  = false;
23545         var csel = this.view.getSelectedNodes();
23546         var cselitem = false;
23547         if (csel.length) {
23548             var ix = this.view.indexOf(csel[0]);
23549             cselitem  = this.store.getAt(ix);
23550             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
23551                 cselitem = false;
23552             }
23553             
23554         }
23555         
23556         this.store.each(function(v) { 
23557             if (cselitem) {
23558                 // start at existing selection.
23559                 if (cselitem.id == v.id) {
23560                     cselitem = false;
23561                 }
23562                 return;
23563             }
23564                 
23565             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
23566                 match = this.store.indexOf(v);
23567                 return false;
23568             }
23569         }, this);
23570         
23571         if (match === false) {
23572             return true; // no more action?
23573         }
23574         // scroll to?
23575         this.view.select(match);
23576         var sn = Roo.get(this.view.getSelectedNodes()[0])
23577         sn.scrollIntoView(sn.dom.parentNode, false);
23578     }
23579
23580     /** 
23581     * @cfg {Boolean} grow 
23582     * @hide 
23583     */
23584     /** 
23585     * @cfg {Number} growMin 
23586     * @hide 
23587     */
23588     /** 
23589     * @cfg {Number} growMax 
23590     * @hide 
23591     */
23592     /**
23593      * @hide
23594      * @method autoSize
23595      */
23596 });/*
23597  * Based on:
23598  * Ext JS Library 1.1.1
23599  * Copyright(c) 2006-2007, Ext JS, LLC.
23600  *
23601  * Originally Released Under LGPL - original licence link has changed is not relivant.
23602  *
23603  * Fork - LGPL
23604  * <script type="text/javascript">
23605  */
23606 /**
23607  * @class Roo.form.Checkbox
23608  * @extends Roo.form.Field
23609  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
23610  * @constructor
23611  * Creates a new Checkbox
23612  * @param {Object} config Configuration options
23613  */
23614 Roo.form.Checkbox = function(config){
23615     Roo.form.Checkbox.superclass.constructor.call(this, config);
23616     this.addEvents({
23617         /**
23618          * @event check
23619          * Fires when the checkbox is checked or unchecked.
23620              * @param {Roo.form.Checkbox} this This checkbox
23621              * @param {Boolean} checked The new checked value
23622              */
23623         check : true
23624     });
23625 };
23626
23627 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
23628     /**
23629      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
23630      */
23631     focusClass : undefined,
23632     /**
23633      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
23634      */
23635     fieldClass: "x-form-field",
23636     /**
23637      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
23638      */
23639     checked: false,
23640     /**
23641      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
23642      * {tag: "input", type: "checkbox", autocomplete: "off"})
23643      */
23644     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
23645     /**
23646      * @cfg {String} boxLabel The text that appears beside the checkbox
23647      */
23648     boxLabel : "",
23649     /**
23650      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
23651      */  
23652     inputValue : '1',
23653     /**
23654      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23655      */
23656      valueOff: '0', // value when not checked..
23657
23658     actionMode : 'viewEl', 
23659     //
23660     // private
23661     itemCls : 'x-menu-check-item x-form-item',
23662     groupClass : 'x-menu-group-item',
23663     inputType : 'hidden',
23664     
23665     
23666     inSetChecked: false, // check that we are not calling self...
23667     
23668     inputElement: false, // real input element?
23669     basedOn: false, // ????
23670     
23671     isFormField: true, // not sure where this is needed!!!!
23672
23673     onResize : function(){
23674         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
23675         if(!this.boxLabel){
23676             this.el.alignTo(this.wrap, 'c-c');
23677         }
23678     },
23679
23680     initEvents : function(){
23681         Roo.form.Checkbox.superclass.initEvents.call(this);
23682         this.el.on("click", this.onClick,  this);
23683         this.el.on("change", this.onClick,  this);
23684     },
23685
23686
23687     getResizeEl : function(){
23688         return this.wrap;
23689     },
23690
23691     getPositionEl : function(){
23692         return this.wrap;
23693     },
23694
23695     // private
23696     onRender : function(ct, position){
23697         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
23698         /*
23699         if(this.inputValue !== undefined){
23700             this.el.dom.value = this.inputValue;
23701         }
23702         */
23703         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
23704         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
23705         var viewEl = this.wrap.createChild({ 
23706             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
23707         this.viewEl = viewEl;   
23708         this.wrap.on('click', this.onClick,  this); 
23709         
23710         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
23711         this.el.on('propertychange', this.setFromHidden,  this);  //ie
23712         
23713         
23714         
23715         if(this.boxLabel){
23716             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
23717         //    viewEl.on('click', this.onClick,  this); 
23718         }
23719         //if(this.checked){
23720             this.setChecked(this.checked);
23721         //}else{
23722             //this.checked = this.el.dom;
23723         //}
23724
23725     },
23726
23727     // private
23728     initValue : Roo.emptyFn,
23729
23730     /**
23731      * Returns the checked state of the checkbox.
23732      * @return {Boolean} True if checked, else false
23733      */
23734     getValue : function(){
23735         if(this.el){
23736             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
23737         }
23738         return this.valueOff;
23739         
23740     },
23741
23742         // private
23743     onClick : function(){ 
23744         this.setChecked(!this.checked);
23745
23746         //if(this.el.dom.checked != this.checked){
23747         //    this.setValue(this.el.dom.checked);
23748        // }
23749     },
23750
23751     /**
23752      * Sets the checked state of the checkbox.
23753      * On is always based on a string comparison between inputValue and the param.
23754      * @param {Boolean/String} value - the value to set 
23755      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
23756      */
23757     setValue : function(v,suppressEvent){
23758         
23759         
23760         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
23761         //if(this.el && this.el.dom){
23762         //    this.el.dom.checked = this.checked;
23763         //    this.el.dom.defaultChecked = this.checked;
23764         //}
23765         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
23766         //this.fireEvent("check", this, this.checked);
23767     },
23768     // private..
23769     setChecked : function(state,suppressEvent)
23770     {
23771         if (this.inSetChecked) {
23772             this.checked = state;
23773             return;
23774         }
23775         
23776     
23777         if(this.wrap){
23778             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
23779         }
23780         this.checked = state;
23781         if(suppressEvent !== true){
23782             this.fireEvent('check', this, state);
23783         }
23784         this.inSetChecked = true;
23785         this.el.dom.value = state ? this.inputValue : this.valueOff;
23786         this.inSetChecked = false;
23787         
23788     },
23789     // handle setting of hidden value by some other method!!?!?
23790     setFromHidden: function()
23791     {
23792         if(!this.el){
23793             return;
23794         }
23795         //console.log("SET FROM HIDDEN");
23796         //alert('setFrom hidden');
23797         this.setValue(this.el.dom.value);
23798     },
23799     
23800     onDestroy : function()
23801     {
23802         if(this.viewEl){
23803             Roo.get(this.viewEl).remove();
23804         }
23805          
23806         Roo.form.Checkbox.superclass.onDestroy.call(this);
23807     }
23808
23809 });/*
23810  * Based on:
23811  * Ext JS Library 1.1.1
23812  * Copyright(c) 2006-2007, Ext JS, LLC.
23813  *
23814  * Originally Released Under LGPL - original licence link has changed is not relivant.
23815  *
23816  * Fork - LGPL
23817  * <script type="text/javascript">
23818  */
23819  
23820 /**
23821  * @class Roo.form.Radio
23822  * @extends Roo.form.Checkbox
23823  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
23824  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
23825  * @constructor
23826  * Creates a new Radio
23827  * @param {Object} config Configuration options
23828  */
23829 Roo.form.Radio = function(){
23830     Roo.form.Radio.superclass.constructor.apply(this, arguments);
23831 };
23832 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
23833     inputType: 'radio',
23834
23835     /**
23836      * If this radio is part of a group, it will return the selected value
23837      * @return {String}
23838      */
23839     getGroupValue : function(){
23840         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
23841     }
23842 });//<script type="text/javascript">
23843
23844 /*
23845  * Ext JS Library 1.1.1
23846  * Copyright(c) 2006-2007, Ext JS, LLC.
23847  * licensing@extjs.com
23848  * 
23849  * http://www.extjs.com/license
23850  */
23851  
23852  /*
23853   * 
23854   * Known bugs:
23855   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
23856   * - IE ? - no idea how much works there.
23857   * 
23858   * 
23859   * 
23860   */
23861  
23862
23863 /**
23864  * @class Ext.form.HtmlEditor
23865  * @extends Ext.form.Field
23866  * Provides a lightweight HTML Editor component.
23867  * WARNING - THIS CURRENTlY ONLY WORKS ON FIREFOX - USE FCKeditor for a cross platform version
23868  * 
23869  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
23870  * supported by this editor.</b><br/><br/>
23871  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
23872  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23873  */
23874 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
23875       /**
23876      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23877      */
23878     toolbars : false,
23879     /**
23880      * @cfg {String} createLinkText The default text for the create link prompt
23881      */
23882     createLinkText : 'Please enter the URL for the link:',
23883     /**
23884      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
23885      */
23886     defaultLinkValue : 'http:/'+'/',
23887    
23888     
23889     // id of frame..
23890     frameId: false,
23891     
23892     // private properties
23893     validationEvent : false,
23894     deferHeight: true,
23895     initialized : false,
23896     activated : false,
23897     sourceEditMode : false,
23898     onFocus : Roo.emptyFn,
23899     iframePad:3,
23900     hideMode:'offsets',
23901     defaultAutoCreate : {
23902         tag: "textarea",
23903         style:"width:500px;height:300px;",
23904         autocomplete: "off"
23905     },
23906
23907     // private
23908     initComponent : function(){
23909         this.addEvents({
23910             /**
23911              * @event initialize
23912              * Fires when the editor is fully initialized (including the iframe)
23913              * @param {HtmlEditor} this
23914              */
23915             initialize: true,
23916             /**
23917              * @event activate
23918              * Fires when the editor is first receives the focus. Any insertion must wait
23919              * until after this event.
23920              * @param {HtmlEditor} this
23921              */
23922             activate: true,
23923              /**
23924              * @event beforesync
23925              * Fires before the textarea is updated with content from the editor iframe. Return false
23926              * to cancel the sync.
23927              * @param {HtmlEditor} this
23928              * @param {String} html
23929              */
23930             beforesync: true,
23931              /**
23932              * @event beforepush
23933              * Fires before the iframe editor is updated with content from the textarea. Return false
23934              * to cancel the push.
23935              * @param {HtmlEditor} this
23936              * @param {String} html
23937              */
23938             beforepush: true,
23939              /**
23940              * @event sync
23941              * Fires when the textarea is updated with content from the editor iframe.
23942              * @param {HtmlEditor} this
23943              * @param {String} html
23944              */
23945             sync: true,
23946              /**
23947              * @event push
23948              * Fires when the iframe editor is updated with content from the textarea.
23949              * @param {HtmlEditor} this
23950              * @param {String} html
23951              */
23952             push: true,
23953              /**
23954              * @event editmodechange
23955              * Fires when the editor switches edit modes
23956              * @param {HtmlEditor} this
23957              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23958              */
23959             editmodechange: true,
23960             /**
23961              * @event editorevent
23962              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23963              * @param {HtmlEditor} this
23964              */
23965             editorevent: true
23966         })
23967     },
23968
23969     /**
23970      * Protected method that will not generally be called directly. It
23971      * is called when the editor creates its toolbar. Override this method if you need to
23972      * add custom toolbar buttons.
23973      * @param {HtmlEditor} editor
23974      */
23975     createToolbar : function(editor){
23976         if (!editor.toolbars || !editor.toolbars.length) {
23977             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
23978         }
23979         
23980         for (var i =0 ; i < editor.toolbars.length;i++) {
23981             editor.toolbars[i] = Roo.factory(editor.toolbars[i], Roo.form.HtmlEditor);
23982             editor.toolbars[i].init(editor);
23983         }
23984          
23985         
23986     },
23987
23988     /**
23989      * Protected method that will not generally be called directly. It
23990      * is called when the editor initializes the iframe with HTML contents. Override this method if you
23991      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
23992      */
23993     getDocMarkup : function(){
23994         return '<html><head><style type="text/css">body{border:0;margin:0;padding:3px;height:98%;cursor:text;}</style></head><body></body></html>';
23995     },
23996
23997     // private
23998     onRender : function(ct, position){
23999         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
24000         this.el.dom.style.border = '0 none';
24001         this.el.dom.setAttribute('tabIndex', -1);
24002         this.el.addClass('x-hidden');
24003         if(Roo.isIE){ // fix IE 1px bogus margin
24004             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24005         }
24006         this.wrap = this.el.wrap({
24007             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
24008         });
24009
24010         this.frameId = Roo.id();
24011         this.createToolbar(this);
24012         
24013         
24014         
24015         
24016       
24017         
24018         var iframe = this.wrap.createChild({
24019             tag: 'iframe',
24020             id: this.frameId,
24021             name: this.frameId,
24022             frameBorder : 'no',
24023             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24024         });
24025         
24026        // console.log(iframe);
24027         //this.wrap.dom.appendChild(iframe);
24028
24029         this.iframe = iframe.dom;
24030
24031          this.assignDocWin();
24032         
24033         this.doc.designMode = 'on';
24034        
24035         this.doc.open();
24036         this.doc.write(this.getDocMarkup());
24037         this.doc.close();
24038
24039         
24040         var task = { // must defer to wait for browser to be ready
24041             run : function(){
24042                 //console.log("run task?" + this.doc.readyState);
24043                 this.assignDocWin();
24044                 if(this.doc.body || this.doc.readyState == 'complete'){
24045                     try {
24046                         this.doc.designMode="on";
24047                     } catch (e) {
24048                         return;
24049                     }
24050                     Roo.TaskMgr.stop(task);
24051                     this.initEditor.defer(10, this);
24052                 }
24053             },
24054             interval : 10,
24055             duration:10000,
24056             scope: this
24057         };
24058         Roo.TaskMgr.start(task);
24059
24060         if(!this.width){
24061             this.setSize(this.el.getSize());
24062         }
24063     },
24064
24065     // private
24066     onResize : function(w, h){
24067         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
24068         if(this.el && this.iframe){
24069             if(typeof w == 'number'){
24070                 var aw = w - this.wrap.getFrameWidth('lr');
24071                 this.el.setWidth(this.adjustWidth('textarea', aw));
24072                 this.iframe.style.width = aw + 'px';
24073             }
24074             if(typeof h == 'number'){
24075                 var tbh = 0;
24076                 for (var i =0; i < this.toolbars.length;i++) {
24077                     // fixme - ask toolbars for heights?
24078                     tbh += this.toolbars[i].tb.el.getHeight();
24079                 }
24080                 
24081                 
24082                 
24083                 
24084                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
24085                 this.el.setHeight(this.adjustWidth('textarea', ah));
24086                 this.iframe.style.height = ah + 'px';
24087                 if(this.doc){
24088                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
24089                 }
24090             }
24091         }
24092     },
24093
24094     /**
24095      * Toggles the editor between standard and source edit mode.
24096      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24097      */
24098     toggleSourceEdit : function(sourceEditMode){
24099         
24100         this.sourceEditMode = sourceEditMode === true;
24101         
24102         if(this.sourceEditMode){
24103           
24104             this.syncValue();
24105             this.iframe.className = 'x-hidden';
24106             this.el.removeClass('x-hidden');
24107             this.el.dom.removeAttribute('tabIndex');
24108             this.el.focus();
24109         }else{
24110              
24111             this.pushValue();
24112             this.iframe.className = '';
24113             this.el.addClass('x-hidden');
24114             this.el.dom.setAttribute('tabIndex', -1);
24115             this.deferFocus();
24116         }
24117         this.setSize(this.wrap.getSize());
24118         this.fireEvent('editmodechange', this, this.sourceEditMode);
24119     },
24120
24121     // private used internally
24122     createLink : function(){
24123         var url = prompt(this.createLinkText, this.defaultLinkValue);
24124         if(url && url != 'http:/'+'/'){
24125             this.relayCmd('createlink', url);
24126         }
24127     },
24128
24129     // private (for BoxComponent)
24130     adjustSize : Roo.BoxComponent.prototype.adjustSize,
24131
24132     // private (for BoxComponent)
24133     getResizeEl : function(){
24134         return this.wrap;
24135     },
24136
24137     // private (for BoxComponent)
24138     getPositionEl : function(){
24139         return this.wrap;
24140     },
24141
24142     // private
24143     initEvents : function(){
24144         this.originalValue = this.getValue();
24145     },
24146
24147     /**
24148      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24149      * @method
24150      */
24151     markInvalid : Roo.emptyFn,
24152     /**
24153      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24154      * @method
24155      */
24156     clearInvalid : Roo.emptyFn,
24157
24158     setValue : function(v){
24159         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
24160         this.pushValue();
24161     },
24162
24163     /**
24164      * Protected method that will not generally be called directly. If you need/want
24165      * custom HTML cleanup, this is the method you should override.
24166      * @param {String} html The HTML to be cleaned
24167      * return {String} The cleaned HTML
24168      */
24169     cleanHtml : function(html){
24170         html = String(html);
24171         if(html.length > 5){
24172             if(Roo.isSafari){ // strip safari nonsense
24173                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24174             }
24175         }
24176         if(html == '&nbsp;'){
24177             html = '';
24178         }
24179         return html;
24180     },
24181
24182     /**
24183      * Protected method that will not generally be called directly. Syncs the contents
24184      * of the editor iframe with the textarea.
24185      */
24186     syncValue : function(){
24187         if(this.initialized){
24188             var bd = (this.doc.body || this.doc.documentElement);
24189             var html = bd.innerHTML;
24190             if(Roo.isSafari){
24191                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24192                 var m = bs.match(/text-align:(.*?);/i);
24193                 if(m && m[1]){
24194                     html = '<div style="'+m[0]+'">' + html + '</div>';
24195                 }
24196             }
24197             html = this.cleanHtml(html);
24198             if(this.fireEvent('beforesync', this, html) !== false){
24199                 this.el.dom.value = html;
24200                 this.fireEvent('sync', this, html);
24201             }
24202         }
24203     },
24204
24205     /**
24206      * Protected method that will not generally be called directly. Pushes the value of the textarea
24207      * into the iframe editor.
24208      */
24209     pushValue : function(){
24210         if(this.initialized){
24211             var v = this.el.dom.value;
24212             if(v.length < 1){
24213                 v = '&#160;';
24214             }
24215             if(this.fireEvent('beforepush', this, v) !== false){
24216                 (this.doc.body || this.doc.documentElement).innerHTML = v;
24217                 this.fireEvent('push', this, v);
24218             }
24219         }
24220     },
24221
24222     // private
24223     deferFocus : function(){
24224         this.focus.defer(10, this);
24225     },
24226
24227     // doc'ed in Field
24228     focus : function(){
24229         if(this.win && !this.sourceEditMode){
24230             this.win.focus();
24231         }else{
24232             this.el.focus();
24233         }
24234     },
24235     
24236     assignDocWin: function()
24237     {
24238         var iframe = this.iframe;
24239         
24240          if(Roo.isIE){
24241             this.doc = iframe.contentWindow.document;
24242             this.win = iframe.contentWindow;
24243         } else {
24244             if (!Roo.get(this.frameId)) {
24245                 return;
24246             }
24247             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24248             this.win = Roo.get(this.frameId).dom.contentWindow;
24249         }
24250     },
24251     
24252     // private
24253     initEditor : function(){
24254         //console.log("INIT EDITOR");
24255         this.assignDocWin();
24256         
24257         
24258         
24259         this.doc.designMode="on";
24260         this.doc.open();
24261         this.doc.write(this.getDocMarkup());
24262         this.doc.close();
24263         
24264         var dbody = (this.doc.body || this.doc.documentElement);
24265         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24266         // this copies styles from the containing element into thsi one..
24267         // not sure why we need all of this..
24268         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24269         ss['background-attachment'] = 'fixed'; // w3c
24270         dbody.bgProperties = 'fixed'; // ie
24271         Roo.DomHelper.applyStyles(dbody, ss);
24272         Roo.EventManager.on(this.doc, {
24273             'mousedown': this.onEditorEvent,
24274             'dblclick': this.onEditorEvent,
24275             'click': this.onEditorEvent,
24276             'keyup': this.onEditorEvent,
24277             buffer:100,
24278             scope: this
24279         });
24280         if(Roo.isGecko){
24281             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24282         }
24283         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24284             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24285         }
24286         this.initialized = true;
24287
24288         this.fireEvent('initialize', this);
24289         this.pushValue();
24290     },
24291
24292     // private
24293     onDestroy : function(){
24294         
24295         
24296         
24297         if(this.rendered){
24298             
24299             for (var i =0; i < this.toolbars.length;i++) {
24300                 // fixme - ask toolbars for heights?
24301                 this.toolbars[i].onDestroy();
24302             }
24303             
24304             this.wrap.dom.innerHTML = '';
24305             this.wrap.remove();
24306         }
24307     },
24308
24309     // private
24310     onFirstFocus : function(){
24311         
24312         this.assignDocWin();
24313         
24314         
24315         this.activated = true;
24316         for (var i =0; i < this.toolbars.length;i++) {
24317             this.toolbars[i].onFirstFocus();
24318         }
24319        
24320         if(Roo.isGecko){ // prevent silly gecko errors
24321             this.win.focus();
24322             var s = this.win.getSelection();
24323             if(!s.focusNode || s.focusNode.nodeType != 3){
24324                 var r = s.getRangeAt(0);
24325                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24326                 r.collapse(true);
24327                 this.deferFocus();
24328             }
24329             try{
24330                 this.execCmd('useCSS', true);
24331                 this.execCmd('styleWithCSS', false);
24332             }catch(e){}
24333         }
24334         this.fireEvent('activate', this);
24335     },
24336
24337     // private
24338     adjustFont: function(btn){
24339         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24340         //if(Roo.isSafari){ // safari
24341         //    adjust *= 2;
24342        // }
24343         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24344         if(Roo.isSafari){ // safari
24345             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24346             v =  (v < 10) ? 10 : v;
24347             v =  (v > 48) ? 48 : v;
24348             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24349             
24350         }
24351         
24352         
24353         v = Math.max(1, v+adjust);
24354         
24355         this.execCmd('FontSize', v  );
24356     },
24357
24358     onEditorEvent : function(e){
24359         this.fireEvent('editorevent', this, e);
24360       //  this.updateToolbar();
24361         this.syncValue();
24362     },
24363
24364     insertTag : function(tg)
24365     {
24366         // could be a bit smarter... -> wrap the current selected tRoo..
24367         
24368         this.execCmd("formatblock",   tg);
24369         
24370     },
24371     
24372     insertText : function(txt)
24373     {
24374         
24375         
24376         range = this.createRange();
24377         range.deleteContents();
24378                //alert(Sender.getAttribute('label'));
24379                
24380         range.insertNode(this.doc.createTextNode(txt));
24381     } ,
24382     
24383     // private
24384     relayBtnCmd : function(btn){
24385         this.relayCmd(btn.cmd);
24386     },
24387
24388     /**
24389      * Executes a Midas editor command on the editor document and performs necessary focus and
24390      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24391      * @param {String} cmd The Midas command
24392      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24393      */
24394     relayCmd : function(cmd, value){
24395         this.win.focus();
24396         this.execCmd(cmd, value);
24397         this.fireEvent('editorevent', this);
24398         //this.updateToolbar();
24399         this.deferFocus();
24400     },
24401
24402     /**
24403      * Executes a Midas editor command directly on the editor document.
24404      * For visual commands, you should use {@link #relayCmd} instead.
24405      * <b>This should only be called after the editor is initialized.</b>
24406      * @param {String} cmd The Midas command
24407      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24408      */
24409     execCmd : function(cmd, value){
24410         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24411         this.syncValue();
24412     },
24413
24414    
24415     /**
24416      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24417      * to insert tRoo.
24418      * @param {String} text
24419      */
24420     insertAtCursor : function(text){
24421         if(!this.activated){
24422             return;
24423         }
24424         if(Roo.isIE){
24425             this.win.focus();
24426             var r = this.doc.selection.createRange();
24427             if(r){
24428                 r.collapse(true);
24429                 r.pasteHTML(text);
24430                 this.syncValue();
24431                 this.deferFocus();
24432             }
24433         }else if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24434             this.win.focus();
24435             this.execCmd('InsertHTML', text);
24436             this.deferFocus();
24437         }
24438     },
24439  // private
24440     mozKeyPress : function(e){
24441         if(e.ctrlKey){
24442             var c = e.getCharCode(), cmd;
24443           
24444             if(c > 0){
24445                 c = String.fromCharCode(c).toLowerCase();
24446                 switch(c){
24447                     case 'b':
24448                         cmd = 'bold';
24449                     break;
24450                     case 'i':
24451                         cmd = 'italic';
24452                     break;
24453                     case 'u':
24454                         cmd = 'underline';
24455                     case 'v':
24456                         this.cleanUpPaste.defer(100, this);
24457                         return;
24458                     break;
24459                 }
24460                 if(cmd){
24461                     this.win.focus();
24462                     this.execCmd(cmd);
24463                     this.deferFocus();
24464                     e.preventDefault();
24465                 }
24466                 
24467             }
24468         }
24469     },
24470
24471     // private
24472     fixKeys : function(){ // load time branching for fastest keydown performance
24473         if(Roo.isIE){
24474             return function(e){
24475                 var k = e.getKey(), r;
24476                 if(k == e.TAB){
24477                     e.stopEvent();
24478                     r = this.doc.selection.createRange();
24479                     if(r){
24480                         r.collapse(true);
24481                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24482                         this.deferFocus();
24483                     }
24484                     return;
24485                 }
24486                 
24487                 if(k == e.ENTER){
24488                     r = this.doc.selection.createRange();
24489                     if(r){
24490                         var target = r.parentElement();
24491                         if(!target || target.tagName.toLowerCase() != 'li'){
24492                             e.stopEvent();
24493                             r.pasteHTML('<br />');
24494                             r.collapse(false);
24495                             r.select();
24496                         }
24497                     }
24498                 }
24499                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24500                     this.cleanUpPaste.defer(100, this);
24501                     return;
24502                 }
24503                 
24504                 
24505             };
24506         }else if(Roo.isOpera){
24507             return function(e){
24508                 var k = e.getKey();
24509                 if(k == e.TAB){
24510                     e.stopEvent();
24511                     this.win.focus();
24512                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24513                     this.deferFocus();
24514                 }
24515                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24516                     this.cleanUpPaste.defer(100, this);
24517                     return;
24518                 }
24519                 
24520             };
24521         }else if(Roo.isSafari){
24522             return function(e){
24523                 var k = e.getKey();
24524                 
24525                 if(k == e.TAB){
24526                     e.stopEvent();
24527                     this.execCmd('InsertText','\t');
24528                     this.deferFocus();
24529                     return;
24530                 }
24531                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24532                     this.cleanUpPaste.defer(100, this);
24533                     return;
24534                 }
24535                 
24536              };
24537         }
24538     }(),
24539     
24540     getAllAncestors: function()
24541     {
24542         var p = this.getSelectedNode();
24543         var a = [];
24544         if (!p) {
24545             a.push(p); // push blank onto stack..
24546             p = this.getParentElement();
24547         }
24548         
24549         
24550         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24551             a.push(p);
24552             p = p.parentNode;
24553         }
24554         a.push(this.doc.body);
24555         return a;
24556     },
24557     lastSel : false,
24558     lastSelNode : false,
24559     
24560     
24561     getSelection : function() 
24562     {
24563         this.assignDocWin();
24564         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24565     },
24566     
24567     getSelectedNode: function() 
24568     {
24569         // this may only work on Gecko!!!
24570         
24571         // should we cache this!!!!
24572         
24573         
24574         
24575          
24576         var range = this.createRange(this.getSelection());
24577         
24578         if (Roo.isIE) {
24579             var parent = range.parentElement();
24580             while (true) {
24581                 var testRange = range.duplicate();
24582                 testRange.moveToElementText(parent);
24583                 if (testRange.inRange(range)) {
24584                     break;
24585                 }
24586                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24587                     break;
24588                 }
24589                 parent = parent.parentElement;
24590             }
24591             return parent;
24592         }
24593         
24594         
24595         var ar = range.endContainer.childNodes;
24596         if (!ar.length) {
24597             ar = range.commonAncestorContainer.childNodes;
24598             //alert(ar.length);
24599         }
24600         var nodes = [];
24601         var other_nodes = [];
24602         var has_other_nodes = false;
24603         for (var i=0;i<ar.length;i++) {
24604             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24605                 continue;
24606             }
24607             // fullly contained node.
24608             
24609             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24610                 nodes.push(ar[i]);
24611                 continue;
24612             }
24613             
24614             // probably selected..
24615             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24616                 other_nodes.push(ar[i]);
24617                 continue;
24618             }
24619             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24620                 continue;
24621             }
24622             
24623             
24624             has_other_nodes = true;
24625         }
24626         if (!nodes.length && other_nodes.length) {
24627             nodes= other_nodes;
24628         }
24629         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24630             return false;
24631         }
24632         
24633         return nodes[0];
24634     },
24635     createRange: function(sel)
24636     {
24637         // this has strange effects when using with 
24638         // top toolbar - not sure if it's a great idea.
24639         //this.editor.contentWindow.focus();
24640         if (typeof sel != "undefined") {
24641             try {
24642                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24643             } catch(e) {
24644                 return this.doc.createRange();
24645             }
24646         } else {
24647             return this.doc.createRange();
24648         }
24649     },
24650     getParentElement: function()
24651     {
24652         
24653         this.assignDocWin();
24654         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24655         
24656         var range = this.createRange(sel);
24657          
24658         try {
24659             var p = range.commonAncestorContainer;
24660             while (p.nodeType == 3) { // text node
24661                 p = p.parentNode;
24662             }
24663             return p;
24664         } catch (e) {
24665             return null;
24666         }
24667     
24668     },
24669     
24670     
24671     
24672     // BC Hacks - cause I cant work out what i was trying to do..
24673     rangeIntersectsNode : function(range, node)
24674     {
24675         var nodeRange = node.ownerDocument.createRange();
24676         try {
24677             nodeRange.selectNode(node);
24678         }
24679         catch (e) {
24680             nodeRange.selectNodeContents(node);
24681         }
24682
24683         return range.compareBoundaryPoints(Range.END_TO_START, nodeRange) == -1 &&
24684                  range.compareBoundaryPoints(Range.START_TO_END, nodeRange) == 1;
24685     },
24686     rangeCompareNode : function(range, node) {
24687         var nodeRange = node.ownerDocument.createRange();
24688         try {
24689             nodeRange.selectNode(node);
24690         } catch (e) {
24691             nodeRange.selectNodeContents(node);
24692         }
24693         var nodeIsBefore = range.compareBoundaryPoints(Range.START_TO_START, nodeRange) == 1;
24694         var nodeIsAfter = range.compareBoundaryPoints(Range.END_TO_END, nodeRange) == -1;
24695
24696         if (nodeIsBefore && !nodeIsAfter)
24697             return 0;
24698         if (!nodeIsBefore && nodeIsAfter)
24699             return 1;
24700         if (nodeIsBefore && nodeIsAfter)
24701             return 2;
24702
24703         return 3;
24704     },
24705
24706     // private? - in a new class?
24707     cleanUpPaste :  function()
24708     {
24709         // cleans up the whole document..
24710       //  console.log('cleanuppaste');
24711         this.cleanUpChildren(this.doc.body)
24712         
24713         
24714     },
24715     cleanUpChildren : function (n)
24716     {
24717         if (!n.childNodes.length) {
24718             return;
24719         }
24720         for (var i = n.childNodes.length-1; i > -1 ; i--) {
24721            this.cleanUpChild(n.childNodes[i]);
24722         }
24723     },
24724     
24725     
24726         
24727     
24728     cleanUpChild : function (node)
24729     {
24730         //console.log(node);
24731         if (node.nodeName == "#text") {
24732             // clean up silly Windows -- stuff?
24733             return; 
24734         }
24735         if (node.nodeName == "#comment") {
24736             node.parentNode.removeChild(node);
24737             // clean up silly Windows -- stuff?
24738             return; 
24739         }
24740         
24741         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
24742             // remove node.
24743             node.parentNode.removeChild(node);
24744             return;
24745             
24746         }
24747         if (!node.attributes || !node.attributes.length) {
24748             this.cleanUpChildren(node);
24749             return;
24750         }
24751         
24752         function cleanAttr(n,v)
24753         {
24754             
24755             if (v.match(/^\./) || v.match(/^\//)) {
24756                 return;
24757             }
24758             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
24759                 return;
24760             }
24761             Roo.log("(REMOVE)"+ node.tagName +'.' + n + '=' + v);
24762             node.removeAttribute(n);
24763             
24764         }
24765         
24766         function cleanStyle(n,v)
24767         {
24768             if (v.match(/expression/)) { //XSS?? should we even bother..
24769                 node.removeAttribute(n);
24770                 return;
24771             }
24772             
24773             
24774             var parts = v.split(/;/);
24775             Roo.each(parts, function(p) {
24776                 p = p.replace(/\s+/g,'');
24777                 if (!p.length) {
24778                     return;
24779                 }
24780                 var l = p.split(':').shift().replace(/\s+/g,'');
24781                 
24782                 if (Roo.form.HtmlEditor.cwhite.indexOf(l) < 0) {
24783                     Roo.log('(REMOVE)' + node.tagName +'.' + n + ':'+l + '=' + v);
24784                     node.removeAttribute(n);
24785                     return false;
24786                 }
24787             });
24788             
24789             
24790         }
24791         
24792         
24793         for (var i = node.attributes.length-1; i > -1 ; i--) {
24794             var a = node.attributes[i];
24795             //console.log(a);
24796             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
24797                 node.removeAttribute(a.name);
24798                 return;
24799             }
24800             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
24801                 cleanAttr(a.name,a.value); // fixme..
24802                 return;
24803             }
24804             if (a.name == 'style') {
24805                 cleanStyle(a.name,a.value);
24806             }
24807             /// clean up MS crap..
24808             if (a.name == 'class') {
24809                 if (a.value.match(/^Mso/)) {
24810                     node.className = '';
24811                 }
24812             }
24813             
24814             // style cleanup!?
24815             // class cleanup?
24816             
24817         }
24818         
24819         
24820         this.cleanUpChildren(node);
24821         
24822         
24823     }
24824     
24825     
24826     // hide stuff that is not compatible
24827     /**
24828      * @event blur
24829      * @hide
24830      */
24831     /**
24832      * @event change
24833      * @hide
24834      */
24835     /**
24836      * @event focus
24837      * @hide
24838      */
24839     /**
24840      * @event specialkey
24841      * @hide
24842      */
24843     /**
24844      * @cfg {String} fieldClass @hide
24845      */
24846     /**
24847      * @cfg {String} focusClass @hide
24848      */
24849     /**
24850      * @cfg {String} autoCreate @hide
24851      */
24852     /**
24853      * @cfg {String} inputType @hide
24854      */
24855     /**
24856      * @cfg {String} invalidClass @hide
24857      */
24858     /**
24859      * @cfg {String} invalidText @hide
24860      */
24861     /**
24862      * @cfg {String} msgFx @hide
24863      */
24864     /**
24865      * @cfg {String} validateOnBlur @hide
24866      */
24867 });
24868
24869 Roo.form.HtmlEditor.white = [
24870         'area', 'br', 'img', 'input', 'hr', 'wbr',
24871         
24872        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
24873        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
24874        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
24875        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
24876        'table',   'ul',         'xmp', 
24877        
24878        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
24879       'thead',   'tr', 
24880      
24881       'dir', 'menu', 'ol', 'ul', 'dl',
24882        
24883       'embed',  'object'
24884 ];
24885
24886
24887 Roo.form.HtmlEditor.black = [
24888     //    'embed',  'object', // enable - backend responsiblity to clean thiese
24889         'applet', // 
24890         'base',   'basefont', 'bgsound', 'blink',  'body', 
24891         'frame',  'frameset', 'head',    'html',   'ilayer', 
24892         'iframe', 'layer',  'link',     'meta',    'object',   
24893         'script', 'style' ,'title',  'xml' // clean later..
24894 ];
24895 Roo.form.HtmlEditor.clean = [
24896     'script', 'style', 'title', 'xml'
24897 ];
24898
24899 // attributes..
24900
24901 Roo.form.HtmlEditor.ablack = [
24902     'on'
24903 ];
24904     
24905 Roo.form.HtmlEditor.aclean = [ 
24906     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
24907 ];
24908
24909 // protocols..
24910 Roo.form.HtmlEditor.pwhite= [
24911         'http',  'https',  'mailto'
24912 ];
24913
24914 Roo.form.HtmlEditor.cwhite= [
24915         'text-align',
24916         'font-size'
24917 ];
24918
24919 // <script type="text/javascript">
24920 /*
24921  * Based on
24922  * Ext JS Library 1.1.1
24923  * Copyright(c) 2006-2007, Ext JS, LLC.
24924  *  
24925  
24926  */
24927
24928 /**
24929  * @class Roo.form.HtmlEditorToolbar1
24930  * Basic Toolbar
24931  * 
24932  * Usage:
24933  *
24934  new Roo.form.HtmlEditor({
24935     ....
24936     toolbars : [
24937         new Roo.form.HtmlEditorToolbar1({
24938             disable : { fonts: 1 , format: 1, ..., ... , ...],
24939             btns : [ .... ]
24940         })
24941     }
24942      
24943  * 
24944  * @cfg {Object} disable List of elements to disable..
24945  * @cfg {Array} btns List of additional buttons.
24946  * 
24947  * 
24948  * NEEDS Extra CSS? 
24949  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
24950  */
24951  
24952 Roo.form.HtmlEditor.ToolbarStandard = function(config)
24953 {
24954     
24955     Roo.apply(this, config);
24956     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
24957     // dont call parent... till later.
24958 }
24959
24960 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
24961     
24962     tb: false,
24963     
24964     rendered: false,
24965     
24966     editor : false,
24967     /**
24968      * @cfg {Object} disable  List of toolbar elements to disable
24969          
24970      */
24971     disable : false,
24972       /**
24973      * @cfg {Array} fontFamilies An array of available font families
24974      */
24975     fontFamilies : [
24976         'Arial',
24977         'Courier New',
24978         'Tahoma',
24979         'Times New Roman',
24980         'Verdana'
24981     ],
24982     
24983     specialChars : [
24984            "&#169;",
24985           "&#174;",     
24986           "&#8482;",    
24987           "&#163;" ,    
24988          // "&#8212;",    
24989           "&#8230;",    
24990           "&#247;" ,    
24991         //  "&#225;" ,     ?? a acute?
24992            "&#8364;"    , //Euro
24993        //   "&#8220;"    ,
24994         //  "&#8221;"    ,
24995         //  "&#8226;"    ,
24996           "&#176;"  //   , // degrees
24997
24998          // "&#233;"     , // e ecute
24999          // "&#250;"     , // u ecute?
25000     ],
25001     inputElements : [ 
25002             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
25003             "input:submit", "input:button", "select", "textarea", "label" ],
25004     formats : [
25005         ["p"] ,  
25006         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
25007         ["pre"],[ "code"], 
25008         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
25009     ],
25010      /**
25011      * @cfg {String} defaultFont default font to use.
25012      */
25013     defaultFont: 'tahoma',
25014    
25015     fontSelect : false,
25016     
25017     
25018     formatCombo : false,
25019     
25020     init : function(editor)
25021     {
25022         this.editor = editor;
25023         
25024         
25025         var fid = editor.frameId;
25026         var etb = this;
25027         function btn(id, toggle, handler){
25028             var xid = fid + '-'+ id ;
25029             return {
25030                 id : xid,
25031                 cmd : id,
25032                 cls : 'x-btn-icon x-edit-'+id,
25033                 enableToggle:toggle !== false,
25034                 scope: editor, // was editor...
25035                 handler:handler||editor.relayBtnCmd,
25036                 clickEvent:'mousedown',
25037                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
25038                 tabIndex:-1
25039             };
25040         }
25041         
25042         
25043         
25044         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
25045         this.tb = tb;
25046          // stop form submits
25047         tb.el.on('click', function(e){
25048             e.preventDefault(); // what does this do?
25049         });
25050
25051         if(!this.disable.font && !Roo.isSafari){
25052             /* why no safari for fonts
25053             editor.fontSelect = tb.el.createChild({
25054                 tag:'select',
25055                 tabIndex: -1,
25056                 cls:'x-font-select',
25057                 html: editor.createFontOptions()
25058             });
25059             editor.fontSelect.on('change', function(){
25060                 var font = editor.fontSelect.dom.value;
25061                 editor.relayCmd('fontname', font);
25062                 editor.deferFocus();
25063             }, editor);
25064             tb.add(
25065                 editor.fontSelect.dom,
25066                 '-'
25067             );
25068             */
25069         };
25070         if(!this.disable.formats){
25071             this.formatCombo = new Roo.form.ComboBox({
25072                 store: new Roo.data.SimpleStore({
25073                     id : 'tag',
25074                     fields: ['tag'],
25075                     data : this.formats // from states.js
25076                 }),
25077                 blockFocus : true,
25078                 //autoCreate : {tag: "div",  size: "20"},
25079                 displayField:'tag',
25080                 typeAhead: false,
25081                 mode: 'local',
25082                 editable : false,
25083                 triggerAction: 'all',
25084                 emptyText:'Add tag',
25085                 selectOnFocus:true,
25086                 width:135,
25087                 listeners : {
25088                     'select': function(c, r, i) {
25089                         editor.insertTag(r.get('tag'));
25090                         editor.focus();
25091                     }
25092                 }
25093
25094             });
25095             tb.addField(this.formatCombo);
25096             
25097         }
25098         
25099         if(!this.disable.format){
25100             tb.add(
25101                 btn('bold'),
25102                 btn('italic'),
25103                 btn('underline')
25104             );
25105         };
25106         if(!this.disable.fontSize){
25107             tb.add(
25108                 '-',
25109                 
25110                 
25111                 btn('increasefontsize', false, editor.adjustFont),
25112                 btn('decreasefontsize', false, editor.adjustFont)
25113             );
25114         };
25115         
25116         
25117         if(this.disable.colors){
25118             tb.add(
25119                 '-', {
25120                     id:editor.frameId +'-forecolor',
25121                     cls:'x-btn-icon x-edit-forecolor',
25122                     clickEvent:'mousedown',
25123                     tooltip: this.buttonTips['forecolor'] || undefined,
25124                     tabIndex:-1,
25125                     menu : new Roo.menu.ColorMenu({
25126                         allowReselect: true,
25127                         focus: Roo.emptyFn,
25128                         value:'000000',
25129                         plain:true,
25130                         selectHandler: function(cp, color){
25131                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
25132                             editor.deferFocus();
25133                         },
25134                         scope: editor,
25135                         clickEvent:'mousedown'
25136                     })
25137                 }, {
25138                     id:editor.frameId +'backcolor',
25139                     cls:'x-btn-icon x-edit-backcolor',
25140                     clickEvent:'mousedown',
25141                     tooltip: this.buttonTips['backcolor'] || undefined,
25142                     tabIndex:-1,
25143                     menu : new Roo.menu.ColorMenu({
25144                         focus: Roo.emptyFn,
25145                         value:'FFFFFF',
25146                         plain:true,
25147                         allowReselect: true,
25148                         selectHandler: function(cp, color){
25149                             if(Roo.isGecko){
25150                                 editor.execCmd('useCSS', false);
25151                                 editor.execCmd('hilitecolor', color);
25152                                 editor.execCmd('useCSS', true);
25153                                 editor.deferFocus();
25154                             }else{
25155                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
25156                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
25157                                 editor.deferFocus();
25158                             }
25159                         },
25160                         scope:editor,
25161                         clickEvent:'mousedown'
25162                     })
25163                 }
25164             );
25165         };
25166         // now add all the items...
25167         
25168
25169         if(!this.disable.alignments){
25170             tb.add(
25171                 '-',
25172                 btn('justifyleft'),
25173                 btn('justifycenter'),
25174                 btn('justifyright')
25175             );
25176         };
25177
25178         //if(!Roo.isSafari){
25179             if(!this.disable.links){
25180                 tb.add(
25181                     '-',
25182                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
25183                 );
25184             };
25185
25186             if(!this.disable.lists){
25187                 tb.add(
25188                     '-',
25189                     btn('insertorderedlist'),
25190                     btn('insertunorderedlist')
25191                 );
25192             }
25193             if(!this.disable.sourceEdit){
25194                 tb.add(
25195                     '-',
25196                     btn('sourceedit', true, function(btn){
25197                         this.toggleSourceEdit(btn.pressed);
25198                     })
25199                 );
25200             }
25201         //}
25202         
25203         var smenu = { };
25204         // special menu.. - needs to be tidied up..
25205         if (!this.disable.special) {
25206             smenu = {
25207                 text: "&#169;",
25208                 cls: 'x-edit-none',
25209                 menu : {
25210                     items : []
25211                    }
25212             };
25213             for (var i =0; i < this.specialChars.length; i++) {
25214                 smenu.menu.items.push({
25215                     
25216                     html: this.specialChars[i],
25217                     handler: function(a,b) {
25218                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
25219                         
25220                     },
25221                     tabIndex:-1
25222                 });
25223             }
25224             
25225             
25226             tb.add(smenu);
25227             
25228             
25229         }
25230         if (this.btns) {
25231             for(var i =0; i< this.btns.length;i++) {
25232                 var b = this.btns[i];
25233                 b.cls =  'x-edit-none';
25234                 b.scope = editor;
25235                 tb.add(b);
25236             }
25237         
25238         }
25239         
25240         
25241         
25242         // disable everything...
25243         
25244         this.tb.items.each(function(item){
25245            if(item.id != editor.frameId+ '-sourceedit'){
25246                 item.disable();
25247             }
25248         });
25249         this.rendered = true;
25250         
25251         // the all the btns;
25252         editor.on('editorevent', this.updateToolbar, this);
25253         // other toolbars need to implement this..
25254         //editor.on('editmodechange', this.updateToolbar, this);
25255     },
25256     
25257     
25258     
25259     /**
25260      * Protected method that will not generally be called directly. It triggers
25261      * a toolbar update by reading the markup state of the current selection in the editor.
25262      */
25263     updateToolbar: function(){
25264
25265         if(!this.editor.activated){
25266             this.editor.onFirstFocus();
25267             return;
25268         }
25269
25270         var btns = this.tb.items.map, 
25271             doc = this.editor.doc,
25272             frameId = this.editor.frameId;
25273
25274         if(!this.disable.font && !Roo.isSafari){
25275             /*
25276             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
25277             if(name != this.fontSelect.dom.value){
25278                 this.fontSelect.dom.value = name;
25279             }
25280             */
25281         }
25282         if(!this.disable.format){
25283             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
25284             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
25285             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
25286         }
25287         if(!this.disable.alignments){
25288             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
25289             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
25290             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
25291         }
25292         if(!Roo.isSafari && !this.disable.lists){
25293             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
25294             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
25295         }
25296         
25297         var ans = this.editor.getAllAncestors();
25298         if (this.formatCombo) {
25299             
25300             
25301             var store = this.formatCombo.store;
25302             this.formatCombo.setValue("");
25303             for (var i =0; i < ans.length;i++) {
25304                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
25305                     // select it..
25306                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
25307                     break;
25308                 }
25309             }
25310         }
25311         
25312         
25313         
25314         // hides menus... - so this cant be on a menu...
25315         Roo.menu.MenuMgr.hideAll();
25316
25317         //this.editorsyncValue();
25318     },
25319    
25320     
25321     createFontOptions : function(){
25322         var buf = [], fs = this.fontFamilies, ff, lc;
25323         for(var i = 0, len = fs.length; i< len; i++){
25324             ff = fs[i];
25325             lc = ff.toLowerCase();
25326             buf.push(
25327                 '<option value="',lc,'" style="font-family:',ff,';"',
25328                     (this.defaultFont == lc ? ' selected="true">' : '>'),
25329                     ff,
25330                 '</option>'
25331             );
25332         }
25333         return buf.join('');
25334     },
25335     
25336     toggleSourceEdit : function(sourceEditMode){
25337         if(sourceEditMode === undefined){
25338             sourceEditMode = !this.sourceEditMode;
25339         }
25340         this.sourceEditMode = sourceEditMode === true;
25341         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
25342         // just toggle the button?
25343         if(btn.pressed !== this.editor.sourceEditMode){
25344             btn.toggle(this.editor.sourceEditMode);
25345             return;
25346         }
25347         
25348         if(this.sourceEditMode){
25349             this.tb.items.each(function(item){
25350                 if(item.cmd != 'sourceedit'){
25351                     item.disable();
25352                 }
25353             });
25354           
25355         }else{
25356             if(this.initialized){
25357                 this.tb.items.each(function(item){
25358                     item.enable();
25359                 });
25360             }
25361             
25362         }
25363         // tell the editor that it's been pressed..
25364         this.editor.toggleSourceEdit(sourceEditMode);
25365        
25366     },
25367      /**
25368      * Object collection of toolbar tooltips for the buttons in the editor. The key
25369      * is the command id associated with that button and the value is a valid QuickTips object.
25370      * For example:
25371 <pre><code>
25372 {
25373     bold : {
25374         title: 'Bold (Ctrl+B)',
25375         text: 'Make the selected text bold.',
25376         cls: 'x-html-editor-tip'
25377     },
25378     italic : {
25379         title: 'Italic (Ctrl+I)',
25380         text: 'Make the selected text italic.',
25381         cls: 'x-html-editor-tip'
25382     },
25383     ...
25384 </code></pre>
25385     * @type Object
25386      */
25387     buttonTips : {
25388         bold : {
25389             title: 'Bold (Ctrl+B)',
25390             text: 'Make the selected text bold.',
25391             cls: 'x-html-editor-tip'
25392         },
25393         italic : {
25394             title: 'Italic (Ctrl+I)',
25395             text: 'Make the selected text italic.',
25396             cls: 'x-html-editor-tip'
25397         },
25398         underline : {
25399             title: 'Underline (Ctrl+U)',
25400             text: 'Underline the selected text.',
25401             cls: 'x-html-editor-tip'
25402         },
25403         increasefontsize : {
25404             title: 'Grow Text',
25405             text: 'Increase the font size.',
25406             cls: 'x-html-editor-tip'
25407         },
25408         decreasefontsize : {
25409             title: 'Shrink Text',
25410             text: 'Decrease the font size.',
25411             cls: 'x-html-editor-tip'
25412         },
25413         backcolor : {
25414             title: 'Text Highlight Color',
25415             text: 'Change the background color of the selected text.',
25416             cls: 'x-html-editor-tip'
25417         },
25418         forecolor : {
25419             title: 'Font Color',
25420             text: 'Change the color of the selected text.',
25421             cls: 'x-html-editor-tip'
25422         },
25423         justifyleft : {
25424             title: 'Align Text Left',
25425             text: 'Align text to the left.',
25426             cls: 'x-html-editor-tip'
25427         },
25428         justifycenter : {
25429             title: 'Center Text',
25430             text: 'Center text in the editor.',
25431             cls: 'x-html-editor-tip'
25432         },
25433         justifyright : {
25434             title: 'Align Text Right',
25435             text: 'Align text to the right.',
25436             cls: 'x-html-editor-tip'
25437         },
25438         insertunorderedlist : {
25439             title: 'Bullet List',
25440             text: 'Start a bulleted list.',
25441             cls: 'x-html-editor-tip'
25442         },
25443         insertorderedlist : {
25444             title: 'Numbered List',
25445             text: 'Start a numbered list.',
25446             cls: 'x-html-editor-tip'
25447         },
25448         createlink : {
25449             title: 'Hyperlink',
25450             text: 'Make the selected text a hyperlink.',
25451             cls: 'x-html-editor-tip'
25452         },
25453         sourceedit : {
25454             title: 'Source Edit',
25455             text: 'Switch to source editing mode.',
25456             cls: 'x-html-editor-tip'
25457         }
25458     },
25459     // private
25460     onDestroy : function(){
25461         if(this.rendered){
25462             
25463             this.tb.items.each(function(item){
25464                 if(item.menu){
25465                     item.menu.removeAll();
25466                     if(item.menu.el){
25467                         item.menu.el.destroy();
25468                     }
25469                 }
25470                 item.destroy();
25471             });
25472              
25473         }
25474     },
25475     onFirstFocus: function() {
25476         this.tb.items.each(function(item){
25477            item.enable();
25478         });
25479     }
25480 });
25481
25482
25483
25484
25485 // <script type="text/javascript">
25486 /*
25487  * Based on
25488  * Ext JS Library 1.1.1
25489  * Copyright(c) 2006-2007, Ext JS, LLC.
25490  *  
25491  
25492  */
25493
25494  
25495 /**
25496  * @class Roo.form.HtmlEditor.ToolbarContext
25497  * Context Toolbar
25498  * 
25499  * Usage:
25500  *
25501  new Roo.form.HtmlEditor({
25502     ....
25503     toolbars : [
25504         new Roo.form.HtmlEditor.ToolbarStandard(),
25505         new Roo.form.HtmlEditor.ToolbarContext()
25506         })
25507     }
25508      
25509  * 
25510  * @config : {Object} disable List of elements to disable.. (not done yet.)
25511  * 
25512  * 
25513  */
25514
25515 Roo.form.HtmlEditor.ToolbarContext = function(config)
25516 {
25517     
25518     Roo.apply(this, config);
25519     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
25520     // dont call parent... till later.
25521 }
25522 Roo.form.HtmlEditor.ToolbarContext.types = {
25523     'IMG' : {
25524         width : {
25525             title: "Width",
25526             width: 40
25527         },
25528         height:  {
25529             title: "Height",
25530             width: 40
25531         },
25532         align: {
25533             title: "Align",
25534             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
25535             width : 80
25536             
25537         },
25538         border: {
25539             title: "Border",
25540             width: 40
25541         },
25542         alt: {
25543             title: "Alt",
25544             width: 120
25545         },
25546         src : {
25547             title: "Src",
25548             width: 220
25549         }
25550         
25551     },
25552     'A' : {
25553         name : {
25554             title: "Name",
25555             width: 50
25556         },
25557         href:  {
25558             title: "Href",
25559             width: 220
25560         } // border?
25561         
25562     },
25563     'TABLE' : {
25564         rows : {
25565             title: "Rows",
25566             width: 20
25567         },
25568         cols : {
25569             title: "Cols",
25570             width: 20
25571         },
25572         width : {
25573             title: "Width",
25574             width: 40
25575         },
25576         height : {
25577             title: "Height",
25578             width: 40
25579         },
25580         border : {
25581             title: "Border",
25582             width: 20
25583         }
25584     },
25585     'TD' : {
25586         width : {
25587             title: "Width",
25588             width: 40
25589         },
25590         height : {
25591             title: "Height",
25592             width: 40
25593         },   
25594         align: {
25595             title: "Align",
25596             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
25597             width: 40
25598         },
25599         valign: {
25600             title: "Valign",
25601             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
25602             width: 40
25603         },
25604         colspan: {
25605             title: "Colspan",
25606             width: 20
25607             
25608         }
25609     },
25610     'INPUT' : {
25611         name : {
25612             title: "name",
25613             width: 120
25614         },
25615         value : {
25616             title: "Value",
25617             width: 120
25618         },
25619         width : {
25620             title: "Width",
25621             width: 40
25622         }
25623     },
25624     'LABEL' : {
25625         'for' : {
25626             title: "For",
25627             width: 120
25628         }
25629     },
25630     'TEXTAREA' : {
25631           name : {
25632             title: "name",
25633             width: 120
25634         },
25635         rows : {
25636             title: "Rows",
25637             width: 20
25638         },
25639         cols : {
25640             title: "Cols",
25641             width: 20
25642         }
25643     },
25644     'SELECT' : {
25645         name : {
25646             title: "name",
25647             width: 120
25648         },
25649         selectoptions : {
25650             title: "Options",
25651             width: 200
25652         }
25653     },
25654     'BODY' : {
25655         title : {
25656             title: "title",
25657             width: 120,
25658             disabled : true
25659         }
25660     }
25661 };
25662
25663
25664
25665 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
25666     
25667     tb: false,
25668     
25669     rendered: false,
25670     
25671     editor : false,
25672     /**
25673      * @cfg {Object} disable  List of toolbar elements to disable
25674          
25675      */
25676     disable : false,
25677     
25678     
25679     
25680     toolbars : false,
25681     
25682     init : function(editor)
25683     {
25684         this.editor = editor;
25685         
25686         
25687         var fid = editor.frameId;
25688         var etb = this;
25689         function btn(id, toggle, handler){
25690             var xid = fid + '-'+ id ;
25691             return {
25692                 id : xid,
25693                 cmd : id,
25694                 cls : 'x-btn-icon x-edit-'+id,
25695                 enableToggle:toggle !== false,
25696                 scope: editor, // was editor...
25697                 handler:handler||editor.relayBtnCmd,
25698                 clickEvent:'mousedown',
25699                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
25700                 tabIndex:-1
25701             };
25702         }
25703         // create a new element.
25704         var wdiv = editor.wrap.createChild({
25705                 tag: 'div'
25706             }, editor.wrap.dom.firstChild.nextSibling, true);
25707         
25708         // can we do this more than once??
25709         
25710          // stop form submits
25711       
25712  
25713         // disable everything...
25714         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
25715         this.toolbars = {};
25716            
25717         for (var i in  ty) {
25718           
25719             this.toolbars[i] = this.buildToolbar(ty[i],i);
25720         }
25721         this.tb = this.toolbars.BODY;
25722         this.tb.el.show();
25723         
25724          
25725         this.rendered = true;
25726         
25727         // the all the btns;
25728         editor.on('editorevent', this.updateToolbar, this);
25729         // other toolbars need to implement this..
25730         //editor.on('editmodechange', this.updateToolbar, this);
25731     },
25732     
25733     
25734     
25735     /**
25736      * Protected method that will not generally be called directly. It triggers
25737      * a toolbar update by reading the markup state of the current selection in the editor.
25738      */
25739     updateToolbar: function(){
25740
25741         if(!this.editor.activated){
25742             this.editor.onFirstFocus();
25743             return;
25744         }
25745
25746         
25747         var ans = this.editor.getAllAncestors();
25748         
25749         // pick
25750         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
25751         var sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
25752         sel = sel ? sel : this.editor.doc.body;
25753         sel = sel.tagName.length ? sel : this.editor.doc.body;
25754         var tn = sel.tagName.toUpperCase();
25755         sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
25756         tn = sel.tagName.toUpperCase();
25757         if (this.tb.name  == tn) {
25758             return; // no change
25759         }
25760         this.tb.el.hide();
25761         ///console.log("show: " + tn);
25762         this.tb =  this.toolbars[tn];
25763         this.tb.el.show();
25764         this.tb.fields.each(function(e) {
25765             e.setValue(sel.getAttribute(e.name));
25766         });
25767         this.tb.selectedNode = sel;
25768         
25769         
25770         Roo.menu.MenuMgr.hideAll();
25771
25772         //this.editorsyncValue();
25773     },
25774    
25775        
25776     // private
25777     onDestroy : function(){
25778         if(this.rendered){
25779             
25780             this.tb.items.each(function(item){
25781                 if(item.menu){
25782                     item.menu.removeAll();
25783                     if(item.menu.el){
25784                         item.menu.el.destroy();
25785                     }
25786                 }
25787                 item.destroy();
25788             });
25789              
25790         }
25791     },
25792     onFirstFocus: function() {
25793         // need to do this for all the toolbars..
25794         this.tb.items.each(function(item){
25795            item.enable();
25796         });
25797     },
25798     buildToolbar: function(tlist, nm)
25799     {
25800         var editor = this.editor;
25801          // create a new element.
25802         var wdiv = editor.wrap.createChild({
25803                 tag: 'div'
25804             }, editor.wrap.dom.firstChild.nextSibling, true);
25805         
25806        
25807         var tb = new Roo.Toolbar(wdiv);
25808         tb.add(nm+ ":&nbsp;");
25809         for (var i in tlist) {
25810             var item = tlist[i];
25811             tb.add(item.title + ":&nbsp;");
25812             if (item.opts) {
25813                 // fixme
25814                 
25815               
25816                 tb.addField( new Roo.form.ComboBox({
25817                     store: new Roo.data.SimpleStore({
25818                         id : 'val',
25819                         fields: ['val'],
25820                         data : item.opts // from states.js
25821                     }),
25822                     name : i,
25823                     displayField:'val',
25824                     typeAhead: false,
25825                     mode: 'local',
25826                     editable : false,
25827                     triggerAction: 'all',
25828                     emptyText:'Select',
25829                     selectOnFocus:true,
25830                     width: item.width ? item.width  : 130,
25831                     listeners : {
25832                         'select': function(c, r, i) {
25833                             tb.selectedNode.setAttribute(c.name, r.get('val'));
25834                         }
25835                     }
25836
25837                 }));
25838                 continue;
25839                     
25840                 
25841                 
25842                 
25843                 
25844                 tb.addField( new Roo.form.TextField({
25845                     name: i,
25846                     width: 100,
25847                     //allowBlank:false,
25848                     value: ''
25849                 }));
25850                 continue;
25851             }
25852             tb.addField( new Roo.form.TextField({
25853                 name: i,
25854                 width: item.width,
25855                 //allowBlank:true,
25856                 value: '',
25857                 listeners: {
25858                     'change' : function(f, nv, ov) {
25859                         tb.selectedNode.setAttribute(f.name, nv);
25860                     }
25861                 }
25862             }));
25863              
25864         }
25865         tb.el.on('click', function(e){
25866             e.preventDefault(); // what does this do?
25867         });
25868         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
25869         tb.el.hide();
25870         tb.name = nm;
25871         // dont need to disable them... as they will get hidden
25872         return tb;
25873          
25874         
25875     }
25876     
25877     
25878     
25879     
25880 });
25881
25882
25883
25884
25885
25886 /*
25887  * Based on:
25888  * Ext JS Library 1.1.1
25889  * Copyright(c) 2006-2007, Ext JS, LLC.
25890  *
25891  * Originally Released Under LGPL - original licence link has changed is not relivant.
25892  *
25893  * Fork - LGPL
25894  * <script type="text/javascript">
25895  */
25896  
25897 /**
25898  * @class Roo.form.BasicForm
25899  * @extends Roo.util.Observable
25900  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
25901  * @constructor
25902  * @param {String/HTMLElement/Roo.Element} el The form element or its id
25903  * @param {Object} config Configuration options
25904  */
25905 Roo.form.BasicForm = function(el, config){
25906     this.allItems = [];
25907     this.childForms = [];
25908     Roo.apply(this, config);
25909     /*
25910      * The Roo.form.Field items in this form.
25911      * @type MixedCollection
25912      */
25913      
25914      
25915     this.items = new Roo.util.MixedCollection(false, function(o){
25916         return o.id || (o.id = Roo.id());
25917     });
25918     this.addEvents({
25919         /**
25920          * @event beforeaction
25921          * Fires before any action is performed. Return false to cancel the action.
25922          * @param {Form} this
25923          * @param {Action} action The action to be performed
25924          */
25925         beforeaction: true,
25926         /**
25927          * @event actionfailed
25928          * Fires when an action fails.
25929          * @param {Form} this
25930          * @param {Action} action The action that failed
25931          */
25932         actionfailed : true,
25933         /**
25934          * @event actioncomplete
25935          * Fires when an action is completed.
25936          * @param {Form} this
25937          * @param {Action} action The action that completed
25938          */
25939         actioncomplete : true
25940     });
25941     if(el){
25942         this.initEl(el);
25943     }
25944     Roo.form.BasicForm.superclass.constructor.call(this);
25945 };
25946
25947 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
25948     /**
25949      * @cfg {String} method
25950      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
25951      */
25952     /**
25953      * @cfg {DataReader} reader
25954      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
25955      * This is optional as there is built-in support for processing JSON.
25956      */
25957     /**
25958      * @cfg {DataReader} errorReader
25959      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
25960      * This is completely optional as there is built-in support for processing JSON.
25961      */
25962     /**
25963      * @cfg {String} url
25964      * The URL to use for form actions if one isn't supplied in the action options.
25965      */
25966     /**
25967      * @cfg {Boolean} fileUpload
25968      * Set to true if this form is a file upload.
25969      */
25970     /**
25971      * @cfg {Object} baseParams
25972      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
25973      */
25974     /**
25975      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
25976      */
25977     timeout: 30,
25978
25979     // private
25980     activeAction : null,
25981
25982     /**
25983      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
25984      * or setValues() data instead of when the form was first created.
25985      */
25986     trackResetOnLoad : false,
25987     
25988     
25989     /**
25990      * childForms - used for multi-tab forms
25991      * @type {Array}
25992      */
25993     childForms : false,
25994     
25995     /**
25996      * allItems - full list of fields.
25997      * @type {Array}
25998      */
25999     allItems : false,
26000     
26001     /**
26002      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
26003      * element by passing it or its id or mask the form itself by passing in true.
26004      * @type Mixed
26005      */
26006     waitMsgTarget : undefined,
26007
26008     // private
26009     initEl : function(el){
26010         this.el = Roo.get(el);
26011         this.id = this.el.id || Roo.id();
26012         this.el.on('submit', this.onSubmit, this);
26013         this.el.addClass('x-form');
26014     },
26015
26016     // private
26017     onSubmit : function(e){
26018         e.stopEvent();
26019     },
26020
26021     /**
26022      * Returns true if client-side validation on the form is successful.
26023      * @return Boolean
26024      */
26025     isValid : function(){
26026         var valid = true;
26027         this.items.each(function(f){
26028            if(!f.validate()){
26029                valid = false;
26030            }
26031         });
26032         return valid;
26033     },
26034
26035     /**
26036      * Returns true if any fields in this form have changed since their original load.
26037      * @return Boolean
26038      */
26039     isDirty : function(){
26040         var dirty = false;
26041         this.items.each(function(f){
26042            if(f.isDirty()){
26043                dirty = true;
26044                return false;
26045            }
26046         });
26047         return dirty;
26048     },
26049
26050     /**
26051      * Performs a predefined action (submit or load) or custom actions you define on this form.
26052      * @param {String} actionName The name of the action type
26053      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
26054      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
26055      * accept other config options):
26056      * <pre>
26057 Property          Type             Description
26058 ----------------  ---------------  ----------------------------------------------------------------------------------
26059 url               String           The url for the action (defaults to the form's url)
26060 method            String           The form method to use (defaults to the form's method, or POST if not defined)
26061 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
26062 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
26063                                    validate the form on the client (defaults to false)
26064      * </pre>
26065      * @return {BasicForm} this
26066      */
26067     doAction : function(action, options){
26068         if(typeof action == 'string'){
26069             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
26070         }
26071         if(this.fireEvent('beforeaction', this, action) !== false){
26072             this.beforeAction(action);
26073             action.run.defer(100, action);
26074         }
26075         return this;
26076     },
26077
26078     /**
26079      * Shortcut to do a submit action.
26080      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
26081      * @return {BasicForm} this
26082      */
26083     submit : function(options){
26084         this.doAction('submit', options);
26085         return this;
26086     },
26087
26088     /**
26089      * Shortcut to do a load action.
26090      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
26091      * @return {BasicForm} this
26092      */
26093     load : function(options){
26094         this.doAction('load', options);
26095         return this;
26096     },
26097
26098     /**
26099      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
26100      * @param {Record} record The record to edit
26101      * @return {BasicForm} this
26102      */
26103     updateRecord : function(record){
26104         record.beginEdit();
26105         var fs = record.fields;
26106         fs.each(function(f){
26107             var field = this.findField(f.name);
26108             if(field){
26109                 record.set(f.name, field.getValue());
26110             }
26111         }, this);
26112         record.endEdit();
26113         return this;
26114     },
26115
26116     /**
26117      * Loads an Roo.data.Record into this form.
26118      * @param {Record} record The record to load
26119      * @return {BasicForm} this
26120      */
26121     loadRecord : function(record){
26122         this.setValues(record.data);
26123         return this;
26124     },
26125
26126     // private
26127     beforeAction : function(action){
26128         var o = action.options;
26129         if(o.waitMsg){
26130             if(this.waitMsgTarget === true){
26131                 this.el.mask(o.waitMsg, 'x-mask-loading');
26132             }else if(this.waitMsgTarget){
26133                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
26134                 this.waitMsgTarget.mask(o.waitMsg, 'x-mask-loading');
26135             }else{
26136                 Roo.MessageBox.wait(o.waitMsg, o.waitTitle || this.waitTitle || 'Please Wait...');
26137             }
26138         }
26139     },
26140
26141     // private
26142     afterAction : function(action, success){
26143         this.activeAction = null;
26144         var o = action.options;
26145         if(o.waitMsg){
26146             if(this.waitMsgTarget === true){
26147                 this.el.unmask();
26148             }else if(this.waitMsgTarget){
26149                 this.waitMsgTarget.unmask();
26150             }else{
26151                 Roo.MessageBox.updateProgress(1);
26152                 Roo.MessageBox.hide();
26153             }
26154         }
26155         if(success){
26156             if(o.reset){
26157                 this.reset();
26158             }
26159             Roo.callback(o.success, o.scope, [this, action]);
26160             this.fireEvent('actioncomplete', this, action);
26161         }else{
26162             Roo.callback(o.failure, o.scope, [this, action]);
26163             this.fireEvent('actionfailed', this, action);
26164         }
26165     },
26166
26167     /**
26168      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
26169      * @param {String} id The value to search for
26170      * @return Field
26171      */
26172     findField : function(id){
26173         var field = this.items.get(id);
26174         if(!field){
26175             this.items.each(function(f){
26176                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
26177                     field = f;
26178                     return false;
26179                 }
26180             });
26181         }
26182         return field || null;
26183     },
26184
26185     /**
26186      * Add a secondary form to this one, 
26187      * Used to provide tabbed forms. One form is primary, with hidden values 
26188      * which mirror the elements from the other forms.
26189      * 
26190      * @param {Roo.form.Form} form to add.
26191      * 
26192      */
26193     addForm : function(form)
26194     {
26195        
26196         if (this.childForms.indexOf(form) > -1) {
26197             // already added..
26198             return;
26199         }
26200         this.childForms.push(form);
26201         var n = '';
26202         Roo.each(form.allItems, function (fe) {
26203             
26204             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
26205             if (this.findField(n)) { // already added..
26206                 return;
26207             }
26208             var add = new Roo.form.Hidden({
26209                 name : n
26210             });
26211             add.render(this.el);
26212             
26213             this.add( add );
26214         }, this);
26215         
26216     },
26217     /**
26218      * Mark fields in this form invalid in bulk.
26219      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
26220      * @return {BasicForm} this
26221      */
26222     markInvalid : function(errors){
26223         if(errors instanceof Array){
26224             for(var i = 0, len = errors.length; i < len; i++){
26225                 var fieldError = errors[i];
26226                 var f = this.findField(fieldError.id);
26227                 if(f){
26228                     f.markInvalid(fieldError.msg);
26229                 }
26230             }
26231         }else{
26232             var field, id;
26233             for(id in errors){
26234                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
26235                     field.markInvalid(errors[id]);
26236                 }
26237             }
26238         }
26239         Roo.each(this.childForms || [], function (f) {
26240             f.markInvalid(errors);
26241         });
26242         
26243         return this;
26244     },
26245
26246     /**
26247      * Set values for fields in this form in bulk.
26248      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
26249      * @return {BasicForm} this
26250      */
26251     setValues : function(values){
26252         if(values instanceof Array){ // array of objects
26253             for(var i = 0, len = values.length; i < len; i++){
26254                 var v = values[i];
26255                 var f = this.findField(v.id);
26256                 if(f){
26257                     f.setValue(v.value);
26258                     if(this.trackResetOnLoad){
26259                         f.originalValue = f.getValue();
26260                     }
26261                 }
26262             }
26263         }else{ // object hash
26264             var field, id;
26265             for(id in values){
26266                 if(typeof values[id] != 'function' && (field = this.findField(id))){
26267                     
26268                     if (field.setFromData && 
26269                         field.valueField && 
26270                         field.displayField &&
26271                         // combos' with local stores can 
26272                         // be queried via setValue()
26273                         // to set their value..
26274                         (field.store && !field.store.isLocal)
26275                         ) {
26276                         // it's a combo
26277                         var sd = { };
26278                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
26279                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
26280                         field.setFromData(sd);
26281                         
26282                     } else {
26283                         field.setValue(values[id]);
26284                     }
26285                     
26286                     
26287                     if(this.trackResetOnLoad){
26288                         field.originalValue = field.getValue();
26289                     }
26290                 }
26291             }
26292         }
26293          
26294         Roo.each(this.childForms || [], function (f) {
26295             f.setValues(values);
26296         });
26297                 
26298         return this;
26299     },
26300
26301     /**
26302      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
26303      * they are returned as an array.
26304      * @param {Boolean} asString
26305      * @return {Object}
26306      */
26307     getValues : function(asString){
26308         if (this.childForms) {
26309             // copy values from the child forms
26310             Roo.each(this.childForms, function (f) {
26311                 this.setValues(f.getValues());
26312             }, this);
26313         }
26314         
26315         
26316         
26317         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
26318         if(asString === true){
26319             return fs;
26320         }
26321         return Roo.urlDecode(fs);
26322     },
26323     
26324     /**
26325      * Returns the fields in this form as an object with key/value pairs. 
26326      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
26327      * @return {Object}
26328      */
26329     getFieldValues : function()
26330     {
26331         if (this.childForms) {
26332             // copy values from the child forms
26333             Roo.each(this.childForms, function (f) {
26334                 this.setValues(f.getValues());
26335             }, this);
26336         }
26337         
26338         var ret = {};
26339         this.items.each(function(f){
26340             if (!f.getName()) {
26341                 return;
26342             }
26343             var v = f.getValue();
26344             if ((typeof(v) == 'object') && f.getRawValue) {
26345                 v = f.getRawValue() ; // dates..
26346             }
26347             ret[f.getName()] = v;
26348         });
26349         
26350         return ret;
26351     },
26352
26353     /**
26354      * Clears all invalid messages in this form.
26355      * @return {BasicForm} this
26356      */
26357     clearInvalid : function(){
26358         this.items.each(function(f){
26359            f.clearInvalid();
26360         });
26361         
26362         Roo.each(this.childForms || [], function (f) {
26363             f.clearInvalid();
26364         });
26365         
26366         
26367         return this;
26368     },
26369
26370     /**
26371      * Resets this form.
26372      * @return {BasicForm} this
26373      */
26374     reset : function(){
26375         this.items.each(function(f){
26376             f.reset();
26377         });
26378         
26379         Roo.each(this.childForms || [], function (f) {
26380             f.reset();
26381         });
26382        
26383         
26384         return this;
26385     },
26386
26387     /**
26388      * Add Roo.form components to this form.
26389      * @param {Field} field1
26390      * @param {Field} field2 (optional)
26391      * @param {Field} etc (optional)
26392      * @return {BasicForm} this
26393      */
26394     add : function(){
26395         this.items.addAll(Array.prototype.slice.call(arguments, 0));
26396         return this;
26397     },
26398
26399
26400     /**
26401      * Removes a field from the items collection (does NOT remove its markup).
26402      * @param {Field} field
26403      * @return {BasicForm} this
26404      */
26405     remove : function(field){
26406         this.items.remove(field);
26407         return this;
26408     },
26409
26410     /**
26411      * Looks at the fields in this form, checks them for an id attribute,
26412      * and calls applyTo on the existing dom element with that id.
26413      * @return {BasicForm} this
26414      */
26415     render : function(){
26416         this.items.each(function(f){
26417             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
26418                 f.applyTo(f.id);
26419             }
26420         });
26421         return this;
26422     },
26423
26424     /**
26425      * Calls {@link Ext#apply} for all fields in this form with the passed object.
26426      * @param {Object} values
26427      * @return {BasicForm} this
26428      */
26429     applyToFields : function(o){
26430         this.items.each(function(f){
26431            Roo.apply(f, o);
26432         });
26433         return this;
26434     },
26435
26436     /**
26437      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
26438      * @param {Object} values
26439      * @return {BasicForm} this
26440      */
26441     applyIfToFields : function(o){
26442         this.items.each(function(f){
26443            Roo.applyIf(f, o);
26444         });
26445         return this;
26446     }
26447 });
26448
26449 // back compat
26450 Roo.BasicForm = Roo.form.BasicForm;/*
26451  * Based on:
26452  * Ext JS Library 1.1.1
26453  * Copyright(c) 2006-2007, Ext JS, LLC.
26454  *
26455  * Originally Released Under LGPL - original licence link has changed is not relivant.
26456  *
26457  * Fork - LGPL
26458  * <script type="text/javascript">
26459  */
26460
26461 /**
26462  * @class Roo.form.Form
26463  * @extends Roo.form.BasicForm
26464  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
26465  * @constructor
26466  * @param {Object} config Configuration options
26467  */
26468 Roo.form.Form = function(config){
26469     var xitems =  [];
26470     if (config.items) {
26471         xitems = config.items;
26472         delete config.items;
26473     }
26474    
26475     
26476     Roo.form.Form.superclass.constructor.call(this, null, config);
26477     this.url = this.url || this.action;
26478     if(!this.root){
26479         this.root = new Roo.form.Layout(Roo.applyIf({
26480             id: Roo.id()
26481         }, config));
26482     }
26483     this.active = this.root;
26484     /**
26485      * Array of all the buttons that have been added to this form via {@link addButton}
26486      * @type Array
26487      */
26488     this.buttons = [];
26489     this.allItems = [];
26490     this.addEvents({
26491         /**
26492          * @event clientvalidation
26493          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
26494          * @param {Form} this
26495          * @param {Boolean} valid true if the form has passed client-side validation
26496          */
26497         clientvalidation: true,
26498         /**
26499          * @event rendered
26500          * Fires when the form is rendered
26501          * @param {Roo.form.Form} form
26502          */
26503         rendered : true
26504     });
26505     
26506     if (this.progressUrl) {
26507             // push a hidden field onto the list of fields..
26508             this.addxtype( {
26509                     xns: Roo.form, 
26510                     xtype : 'Hidden', 
26511                     name : 'UPLOAD_IDENTIFIER' 
26512             });
26513         }
26514         
26515     
26516     Roo.each(xitems, this.addxtype, this);
26517     
26518     
26519     
26520 };
26521
26522 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
26523     /**
26524      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
26525      */
26526     /**
26527      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
26528      */
26529     /**
26530      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
26531      */
26532     buttonAlign:'center',
26533
26534     /**
26535      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
26536      */
26537     minButtonWidth:75,
26538
26539     /**
26540      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
26541      * This property cascades to child containers if not set.
26542      */
26543     labelAlign:'left',
26544
26545     /**
26546      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
26547      * fires a looping event with that state. This is required to bind buttons to the valid
26548      * state using the config value formBind:true on the button.
26549      */
26550     monitorValid : false,
26551
26552     /**
26553      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
26554      */
26555     monitorPoll : 200,
26556     
26557     /**
26558      * @cfg {String} progressUrl - Url to return progress data 
26559      */
26560     
26561     progressUrl : false,
26562   
26563     /**
26564      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
26565      * fields are added and the column is closed. If no fields are passed the column remains open
26566      * until end() is called.
26567      * @param {Object} config The config to pass to the column
26568      * @param {Field} field1 (optional)
26569      * @param {Field} field2 (optional)
26570      * @param {Field} etc (optional)
26571      * @return Column The column container object
26572      */
26573     column : function(c){
26574         var col = new Roo.form.Column(c);
26575         this.start(col);
26576         if(arguments.length > 1){ // duplicate code required because of Opera
26577             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
26578             this.end();
26579         }
26580         return col;
26581     },
26582
26583     /**
26584      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
26585      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
26586      * until end() is called.
26587      * @param {Object} config The config to pass to the fieldset
26588      * @param {Field} field1 (optional)
26589      * @param {Field} field2 (optional)
26590      * @param {Field} etc (optional)
26591      * @return FieldSet The fieldset container object
26592      */
26593     fieldset : function(c){
26594         var fs = new Roo.form.FieldSet(c);
26595         this.start(fs);
26596         if(arguments.length > 1){ // duplicate code required because of Opera
26597             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
26598             this.end();
26599         }
26600         return fs;
26601     },
26602
26603     /**
26604      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
26605      * fields are added and the container is closed. If no fields are passed the container remains open
26606      * until end() is called.
26607      * @param {Object} config The config to pass to the Layout
26608      * @param {Field} field1 (optional)
26609      * @param {Field} field2 (optional)
26610      * @param {Field} etc (optional)
26611      * @return Layout The container object
26612      */
26613     container : function(c){
26614         var l = new Roo.form.Layout(c);
26615         this.start(l);
26616         if(arguments.length > 1){ // duplicate code required because of Opera
26617             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
26618             this.end();
26619         }
26620         return l;
26621     },
26622
26623     /**
26624      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
26625      * @param {Object} container A Roo.form.Layout or subclass of Layout
26626      * @return {Form} this
26627      */
26628     start : function(c){
26629         // cascade label info
26630         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
26631         this.active.stack.push(c);
26632         c.ownerCt = this.active;
26633         this.active = c;
26634         return this;
26635     },
26636
26637     /**
26638      * Closes the current open container
26639      * @return {Form} this
26640      */
26641     end : function(){
26642         if(this.active == this.root){
26643             return this;
26644         }
26645         this.active = this.active.ownerCt;
26646         return this;
26647     },
26648
26649     /**
26650      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
26651      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
26652      * as the label of the field.
26653      * @param {Field} field1
26654      * @param {Field} field2 (optional)
26655      * @param {Field} etc. (optional)
26656      * @return {Form} this
26657      */
26658     add : function(){
26659         this.active.stack.push.apply(this.active.stack, arguments);
26660         this.allItems.push.apply(this.allItems,arguments);
26661         var r = [];
26662         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
26663             if(a[i].isFormField){
26664                 r.push(a[i]);
26665             }
26666         }
26667         if(r.length > 0){
26668             Roo.form.Form.superclass.add.apply(this, r);
26669         }
26670         return this;
26671     },
26672     
26673
26674     
26675     
26676     
26677      /**
26678      * Find any element that has been added to a form, using it's ID or name
26679      * This can include framesets, columns etc. along with regular fields..
26680      * @param {String} id - id or name to find.
26681      
26682      * @return {Element} e - or false if nothing found.
26683      */
26684     findbyId : function(id)
26685     {
26686         var ret = false;
26687         if (!id) {
26688             return ret;
26689         }
26690         Ext.each(this.allItems, function(f){
26691             if (f.id == id || f.name == id ){
26692                 ret = f;
26693                 return false;
26694             }
26695         });
26696         return ret;
26697     },
26698
26699     
26700     
26701     /**
26702      * Render this form into the passed container. This should only be called once!
26703      * @param {String/HTMLElement/Element} container The element this component should be rendered into
26704      * @return {Form} this
26705      */
26706     render : function(ct)
26707     {
26708         
26709         
26710         
26711         ct = Roo.get(ct);
26712         var o = this.autoCreate || {
26713             tag: 'form',
26714             method : this.method || 'POST',
26715             id : this.id || Roo.id()
26716         };
26717         this.initEl(ct.createChild(o));
26718
26719         this.root.render(this.el);
26720         
26721        
26722              
26723         this.items.each(function(f){
26724             f.render('x-form-el-'+f.id);
26725         });
26726
26727         if(this.buttons.length > 0){
26728             // tables are required to maintain order and for correct IE layout
26729             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
26730                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
26731                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
26732             }}, null, true);
26733             var tr = tb.getElementsByTagName('tr')[0];
26734             for(var i = 0, len = this.buttons.length; i < len; i++) {
26735                 var b = this.buttons[i];
26736                 var td = document.createElement('td');
26737                 td.className = 'x-form-btn-td';
26738                 b.render(tr.appendChild(td));
26739             }
26740         }
26741         if(this.monitorValid){ // initialize after render
26742             this.startMonitoring();
26743         }
26744         this.fireEvent('rendered', this);
26745         return this;
26746     },
26747
26748     /**
26749      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
26750      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
26751      * object or a valid Roo.DomHelper element config
26752      * @param {Function} handler The function called when the button is clicked
26753      * @param {Object} scope (optional) The scope of the handler function
26754      * @return {Roo.Button}
26755      */
26756     addButton : function(config, handler, scope){
26757         var bc = {
26758             handler: handler,
26759             scope: scope,
26760             minWidth: this.minButtonWidth,
26761             hideParent:true
26762         };
26763         if(typeof config == "string"){
26764             bc.text = config;
26765         }else{
26766             Roo.apply(bc, config);
26767         }
26768         var btn = new Roo.Button(null, bc);
26769         this.buttons.push(btn);
26770         return btn;
26771     },
26772
26773      /**
26774      * Adds a series of form elements (using the xtype property as the factory method.
26775      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
26776      * @param {Object} config 
26777      */
26778     
26779     addxtype : function()
26780     {
26781         var ar = Array.prototype.slice.call(arguments, 0);
26782         var ret = false;
26783         for(var i = 0; i < ar.length; i++) {
26784             if (!ar[i]) {
26785                 continue; // skip -- if this happends something invalid got sent, we 
26786                 // should ignore it, as basically that interface element will not show up
26787                 // and that should be pretty obvious!!
26788             }
26789             
26790             if (Roo.form[ar[i].xtype]) {
26791                 ar[i].form = this;
26792                 var fe = Roo.factory(ar[i], Roo.form);
26793                 if (!ret) {
26794                     ret = fe;
26795                 }
26796                 fe.form = this;
26797                 if (fe.store) {
26798                     fe.store.form = this;
26799                 }
26800                 if (fe.isLayout) {  
26801                          
26802                     this.start(fe);
26803                     this.allItems.push(fe);
26804                     if (fe.items && fe.addxtype) {
26805                         fe.addxtype.apply(fe, fe.items);
26806                         delete fe.items;
26807                     }
26808                      this.end();
26809                     continue;
26810                 }
26811                 
26812                 
26813                  
26814                 this.add(fe);
26815               //  console.log('adding ' + ar[i].xtype);
26816             }
26817             if (ar[i].xtype == 'Button') {  
26818                 //console.log('adding button');
26819                 //console.log(ar[i]);
26820                 this.addButton(ar[i]);
26821                 this.allItems.push(fe);
26822                 continue;
26823             }
26824             
26825             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
26826                 alert('end is not supported on xtype any more, use items');
26827             //    this.end();
26828             //    //console.log('adding end');
26829             }
26830             
26831         }
26832         return ret;
26833     },
26834     
26835     /**
26836      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
26837      * option "monitorValid"
26838      */
26839     startMonitoring : function(){
26840         if(!this.bound){
26841             this.bound = true;
26842             Roo.TaskMgr.start({
26843                 run : this.bindHandler,
26844                 interval : this.monitorPoll || 200,
26845                 scope: this
26846             });
26847         }
26848     },
26849
26850     /**
26851      * Stops monitoring of the valid state of this form
26852      */
26853     stopMonitoring : function(){
26854         this.bound = false;
26855     },
26856
26857     // private
26858     bindHandler : function(){
26859         if(!this.bound){
26860             return false; // stops binding
26861         }
26862         var valid = true;
26863         this.items.each(function(f){
26864             if(!f.isValid(true)){
26865                 valid = false;
26866                 return false;
26867             }
26868         });
26869         for(var i = 0, len = this.buttons.length; i < len; i++){
26870             var btn = this.buttons[i];
26871             if(btn.formBind === true && btn.disabled === valid){
26872                 btn.setDisabled(!valid);
26873             }
26874         }
26875         this.fireEvent('clientvalidation', this, valid);
26876     }
26877     
26878     
26879     
26880     
26881     
26882     
26883     
26884     
26885 });
26886
26887
26888 // back compat
26889 Roo.Form = Roo.form.Form;
26890 /*
26891  * Based on:
26892  * Ext JS Library 1.1.1
26893  * Copyright(c) 2006-2007, Ext JS, LLC.
26894  *
26895  * Originally Released Under LGPL - original licence link has changed is not relivant.
26896  *
26897  * Fork - LGPL
26898  * <script type="text/javascript">
26899  */
26900  
26901  /**
26902  * @class Roo.form.Action
26903  * Internal Class used to handle form actions
26904  * @constructor
26905  * @param {Roo.form.BasicForm} el The form element or its id
26906  * @param {Object} config Configuration options
26907  */
26908  
26909  
26910 // define the action interface
26911 Roo.form.Action = function(form, options){
26912     this.form = form;
26913     this.options = options || {};
26914 };
26915 /**
26916  * Client Validation Failed
26917  * @const 
26918  */
26919 Roo.form.Action.CLIENT_INVALID = 'client';
26920 /**
26921  * Server Validation Failed
26922  * @const 
26923  */
26924  Roo.form.Action.SERVER_INVALID = 'server';
26925  /**
26926  * Connect to Server Failed
26927  * @const 
26928  */
26929 Roo.form.Action.CONNECT_FAILURE = 'connect';
26930 /**
26931  * Reading Data from Server Failed
26932  * @const 
26933  */
26934 Roo.form.Action.LOAD_FAILURE = 'load';
26935
26936 Roo.form.Action.prototype = {
26937     type : 'default',
26938     failureType : undefined,
26939     response : undefined,
26940     result : undefined,
26941
26942     // interface method
26943     run : function(options){
26944
26945     },
26946
26947     // interface method
26948     success : function(response){
26949
26950     },
26951
26952     // interface method
26953     handleResponse : function(response){
26954
26955     },
26956
26957     // default connection failure
26958     failure : function(response){
26959         this.response = response;
26960         this.failureType = Roo.form.Action.CONNECT_FAILURE;
26961         this.form.afterAction(this, false);
26962     },
26963
26964     processResponse : function(response){
26965         this.response = response;
26966         if(!response.responseText){
26967             return true;
26968         }
26969         this.result = this.handleResponse(response);
26970         return this.result;
26971     },
26972
26973     // utility functions used internally
26974     getUrl : function(appendParams){
26975         var url = this.options.url || this.form.url || this.form.el.dom.action;
26976         if(appendParams){
26977             var p = this.getParams();
26978             if(p){
26979                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
26980             }
26981         }
26982         return url;
26983     },
26984
26985     getMethod : function(){
26986         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
26987     },
26988
26989     getParams : function(){
26990         var bp = this.form.baseParams;
26991         var p = this.options.params;
26992         if(p){
26993             if(typeof p == "object"){
26994                 p = Roo.urlEncode(Roo.applyIf(p, bp));
26995             }else if(typeof p == 'string' && bp){
26996                 p += '&' + Roo.urlEncode(bp);
26997             }
26998         }else if(bp){
26999             p = Roo.urlEncode(bp);
27000         }
27001         return p;
27002     },
27003
27004     createCallback : function(){
27005         return {
27006             success: this.success,
27007             failure: this.failure,
27008             scope: this,
27009             timeout: (this.form.timeout*1000),
27010             upload: this.form.fileUpload ? this.success : undefined
27011         };
27012     }
27013 };
27014
27015 Roo.form.Action.Submit = function(form, options){
27016     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
27017 };
27018
27019 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
27020     type : 'submit',
27021
27022     haveProgress : false,
27023     uploadComplete : false,
27024     
27025     // uploadProgress indicator.
27026     uploadProgress : function()
27027     {
27028         if (!this.form.progressUrl) {
27029             return;
27030         }
27031         
27032         if (!this.haveProgress) {
27033             Roo.MessageBox.progress("Uploading", "Uploading");
27034         }
27035         if (this.uploadComplete) {
27036            Roo.MessageBox.hide();
27037            return;
27038         }
27039         
27040         this.haveProgress = true;
27041    
27042         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
27043         
27044         var c = new Roo.data.Connection();
27045         c.request({
27046             url : this.form.progressUrl,
27047             params: {
27048                 id : uid
27049             },
27050             method: 'GET',
27051             success : function(req){
27052                //console.log(data);
27053                 var rdata = false;
27054                 var edata;
27055                 try  {
27056                    rdata = Roo.decode(req.responseText)
27057                 } catch (e) {
27058                     Roo.log("Invalid data from server..");
27059                     Roo.log(edata);
27060                     return;
27061                 }
27062                 if (!rdata || !rdata.success) {
27063                     Roo.log(rdata);
27064                     return;
27065                 }
27066                 var data = rdata.data;
27067                 
27068                 if (this.uploadComplete) {
27069                    Roo.MessageBox.hide();
27070                    return;
27071                 }
27072                    
27073                 if (data){
27074                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
27075                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
27076                     );
27077                 }
27078                 this.uploadProgress.defer(2000,this);
27079             },
27080        
27081             failure: function(data) {
27082                 Roo.log('progress url failed ');
27083                 Roo.log(data);
27084             },
27085             scope : this
27086         });
27087            
27088     },
27089     
27090     
27091     run : function()
27092     {
27093         // run get Values on the form, so it syncs any secondary forms.
27094         this.form.getValues();
27095         
27096         var o = this.options;
27097         var method = this.getMethod();
27098         var isPost = method == 'POST';
27099         if(o.clientValidation === false || this.form.isValid()){
27100             
27101             if (this.form.progressUrl) {
27102                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
27103                     (new Date() * 1) + '' + Math.random());
27104                     
27105             } 
27106             
27107             Roo.Ajax.request(Roo.apply(this.createCallback(), {
27108                 form:this.form.el.dom,
27109                 url:this.getUrl(!isPost),
27110                 method: method,
27111                 params:isPost ? this.getParams() : null,
27112                 isUpload: this.form.fileUpload
27113             }));
27114             
27115             this.uploadProgress();
27116
27117         }else if (o.clientValidation !== false){ // client validation failed
27118             this.failureType = Roo.form.Action.CLIENT_INVALID;
27119             this.form.afterAction(this, false);
27120         }
27121     },
27122
27123     success : function(response)
27124     {
27125         this.uploadComplete= true;
27126         if (this.haveProgress) {
27127             Roo.MessageBox.hide();
27128         }
27129         
27130         var result = this.processResponse(response);
27131         if(result === true || result.success){
27132             this.form.afterAction(this, true);
27133             return;
27134         }
27135         if(result.errors){
27136             this.form.markInvalid(result.errors);
27137             this.failureType = Roo.form.Action.SERVER_INVALID;
27138         }
27139         this.form.afterAction(this, false);
27140     },
27141     failure : function(response)
27142     {
27143         this.uploadComplete= true;
27144         if (this.haveProgress) {
27145             Roo.MessageBox.hide();
27146         }
27147         
27148         this.response = response;
27149         this.failureType = Roo.form.Action.CONNECT_FAILURE;
27150         this.form.afterAction(this, false);
27151     },
27152     
27153     handleResponse : function(response){
27154         if(this.form.errorReader){
27155             var rs = this.form.errorReader.read(response);
27156             var errors = [];
27157             if(rs.records){
27158                 for(var i = 0, len = rs.records.length; i < len; i++) {
27159                     var r = rs.records[i];
27160                     errors[i] = r.data;
27161                 }
27162             }
27163             if(errors.length < 1){
27164                 errors = null;
27165             }
27166             return {
27167                 success : rs.success,
27168                 errors : errors
27169             };
27170         }
27171         var ret = false;
27172         try {
27173             ret = Roo.decode(response.responseText);
27174         } catch (e) {
27175             ret = {
27176                 success: false,
27177                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
27178                 errors : []
27179             };
27180         }
27181         return ret;
27182         
27183     }
27184 });
27185
27186
27187 Roo.form.Action.Load = function(form, options){
27188     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
27189     this.reader = this.form.reader;
27190 };
27191
27192 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
27193     type : 'load',
27194
27195     run : function(){
27196         Roo.Ajax.request(Roo.apply(
27197                 this.createCallback(), {
27198                     method:this.getMethod(),
27199                     url:this.getUrl(false),
27200                     params:this.getParams()
27201         }));
27202     },
27203
27204     success : function(response){
27205         var result = this.processResponse(response);
27206         if(result === true || !result.success || !result.data){
27207             this.failureType = Roo.form.Action.LOAD_FAILURE;
27208             this.form.afterAction(this, false);
27209             return;
27210         }
27211         this.form.clearInvalid();
27212         this.form.setValues(result.data);
27213         this.form.afterAction(this, true);
27214     },
27215
27216     handleResponse : function(response){
27217         if(this.form.reader){
27218             var rs = this.form.reader.read(response);
27219             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
27220             return {
27221                 success : rs.success,
27222                 data : data
27223             };
27224         }
27225         return Roo.decode(response.responseText);
27226     }
27227 });
27228
27229 Roo.form.Action.ACTION_TYPES = {
27230     'load' : Roo.form.Action.Load,
27231     'submit' : Roo.form.Action.Submit
27232 };/*
27233  * Based on:
27234  * Ext JS Library 1.1.1
27235  * Copyright(c) 2006-2007, Ext JS, LLC.
27236  *
27237  * Originally Released Under LGPL - original licence link has changed is not relivant.
27238  *
27239  * Fork - LGPL
27240  * <script type="text/javascript">
27241  */
27242  
27243 /**
27244  * @class Roo.form.Layout
27245  * @extends Roo.Component
27246  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
27247  * @constructor
27248  * @param {Object} config Configuration options
27249  */
27250 Roo.form.Layout = function(config){
27251     var xitems = [];
27252     if (config.items) {
27253         xitems = config.items;
27254         delete config.items;
27255     }
27256     Roo.form.Layout.superclass.constructor.call(this, config);
27257     this.stack = [];
27258     Roo.each(xitems, this.addxtype, this);
27259      
27260 };
27261
27262 Roo.extend(Roo.form.Layout, Roo.Component, {
27263     /**
27264      * @cfg {String/Object} autoCreate
27265      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
27266      */
27267     /**
27268      * @cfg {String/Object/Function} style
27269      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
27270      * a function which returns such a specification.
27271      */
27272     /**
27273      * @cfg {String} labelAlign
27274      * Valid values are "left," "top" and "right" (defaults to "left")
27275      */
27276     /**
27277      * @cfg {Number} labelWidth
27278      * Fixed width in pixels of all field labels (defaults to undefined)
27279      */
27280     /**
27281      * @cfg {Boolean} clear
27282      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
27283      */
27284     clear : true,
27285     /**
27286      * @cfg {String} labelSeparator
27287      * The separator to use after field labels (defaults to ':')
27288      */
27289     labelSeparator : ':',
27290     /**
27291      * @cfg {Boolean} hideLabels
27292      * True to suppress the display of field labels in this layout (defaults to false)
27293      */
27294     hideLabels : false,
27295
27296     // private
27297     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
27298     
27299     isLayout : true,
27300     
27301     // private
27302     onRender : function(ct, position){
27303         if(this.el){ // from markup
27304             this.el = Roo.get(this.el);
27305         }else {  // generate
27306             var cfg = this.getAutoCreate();
27307             this.el = ct.createChild(cfg, position);
27308         }
27309         if(this.style){
27310             this.el.applyStyles(this.style);
27311         }
27312         if(this.labelAlign){
27313             this.el.addClass('x-form-label-'+this.labelAlign);
27314         }
27315         if(this.hideLabels){
27316             this.labelStyle = "display:none";
27317             this.elementStyle = "padding-left:0;";
27318         }else{
27319             if(typeof this.labelWidth == 'number'){
27320                 this.labelStyle = "width:"+this.labelWidth+"px;";
27321                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
27322             }
27323             if(this.labelAlign == 'top'){
27324                 this.labelStyle = "width:auto;";
27325                 this.elementStyle = "padding-left:0;";
27326             }
27327         }
27328         var stack = this.stack;
27329         var slen = stack.length;
27330         if(slen > 0){
27331             if(!this.fieldTpl){
27332                 var t = new Roo.Template(
27333                     '<div class="x-form-item {5}">',
27334                         '<label for="{0}" style="{2}">{1}{4}</label>',
27335                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
27336                         '</div>',
27337                     '</div><div class="x-form-clear-left"></div>'
27338                 );
27339                 t.disableFormats = true;
27340                 t.compile();
27341                 Roo.form.Layout.prototype.fieldTpl = t;
27342             }
27343             for(var i = 0; i < slen; i++) {
27344                 if(stack[i].isFormField){
27345                     this.renderField(stack[i]);
27346                 }else{
27347                     this.renderComponent(stack[i]);
27348                 }
27349             }
27350         }
27351         if(this.clear){
27352             this.el.createChild({cls:'x-form-clear'});
27353         }
27354     },
27355
27356     // private
27357     renderField : function(f){
27358         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
27359                f.id, //0
27360                f.fieldLabel, //1
27361                f.labelStyle||this.labelStyle||'', //2
27362                this.elementStyle||'', //3
27363                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
27364                f.itemCls||this.itemCls||''  //5
27365        ], true).getPrevSibling());
27366     },
27367
27368     // private
27369     renderComponent : function(c){
27370         c.render(c.isLayout ? this.el : this.el.createChild());    
27371     },
27372     /**
27373      * Adds a object form elements (using the xtype property as the factory method.)
27374      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
27375      * @param {Object} config 
27376      */
27377     addxtype : function(o)
27378     {
27379         // create the lement.
27380         o.form = this.form;
27381         var fe = Roo.factory(o, Roo.form);
27382         this.form.allItems.push(fe);
27383         this.stack.push(fe);
27384         
27385         if (fe.isFormField) {
27386             this.form.items.add(fe);
27387         }
27388          
27389         return fe;
27390     }
27391 });
27392
27393 /**
27394  * @class Roo.form.Column
27395  * @extends Roo.form.Layout
27396  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
27397  * @constructor
27398  * @param {Object} config Configuration options
27399  */
27400 Roo.form.Column = function(config){
27401     Roo.form.Column.superclass.constructor.call(this, config);
27402 };
27403
27404 Roo.extend(Roo.form.Column, Roo.form.Layout, {
27405     /**
27406      * @cfg {Number/String} width
27407      * The fixed width of the column in pixels or CSS value (defaults to "auto")
27408      */
27409     /**
27410      * @cfg {String/Object} autoCreate
27411      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
27412      */
27413
27414     // private
27415     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
27416
27417     // private
27418     onRender : function(ct, position){
27419         Roo.form.Column.superclass.onRender.call(this, ct, position);
27420         if(this.width){
27421             this.el.setWidth(this.width);
27422         }
27423     }
27424 });
27425
27426
27427 /**
27428  * @class Roo.form.Row
27429  * @extends Roo.form.Layout
27430  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
27431  * @constructor
27432  * @param {Object} config Configuration options
27433  */
27434
27435  
27436 Roo.form.Row = function(config){
27437     Roo.form.Row.superclass.constructor.call(this, config);
27438 };
27439  
27440 Roo.extend(Roo.form.Row, Roo.form.Layout, {
27441       /**
27442      * @cfg {Number/String} width
27443      * The fixed width of the column in pixels or CSS value (defaults to "auto")
27444      */
27445     /**
27446      * @cfg {Number/String} height
27447      * The fixed height of the column in pixels or CSS value (defaults to "auto")
27448      */
27449     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
27450     
27451     padWidth : 20,
27452     // private
27453     onRender : function(ct, position){
27454         //console.log('row render');
27455         if(!this.rowTpl){
27456             var t = new Roo.Template(
27457                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
27458                     '<label for="{0}" style="{2}">{1}{4}</label>',
27459                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
27460                     '</div>',
27461                 '</div>'
27462             );
27463             t.disableFormats = true;
27464             t.compile();
27465             Roo.form.Layout.prototype.rowTpl = t;
27466         }
27467         this.fieldTpl = this.rowTpl;
27468         
27469         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
27470         var labelWidth = 100;
27471         
27472         if ((this.labelAlign != 'top')) {
27473             if (typeof this.labelWidth == 'number') {
27474                 labelWidth = this.labelWidth
27475             }
27476             this.padWidth =  20 + labelWidth;
27477             
27478         }
27479         
27480         Roo.form.Column.superclass.onRender.call(this, ct, position);
27481         if(this.width){
27482             this.el.setWidth(this.width);
27483         }
27484         if(this.height){
27485             this.el.setHeight(this.height);
27486         }
27487     },
27488     
27489     // private
27490     renderField : function(f){
27491         f.fieldEl = this.fieldTpl.append(this.el, [
27492                f.id, f.fieldLabel,
27493                f.labelStyle||this.labelStyle||'',
27494                this.elementStyle||'',
27495                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
27496                f.itemCls||this.itemCls||'',
27497                f.width ? f.width + this.padWidth : 160 + this.padWidth
27498        ],true);
27499     }
27500 });
27501  
27502
27503 /**
27504  * @class Roo.form.FieldSet
27505  * @extends Roo.form.Layout
27506  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
27507  * @constructor
27508  * @param {Object} config Configuration options
27509  */
27510 Roo.form.FieldSet = function(config){
27511     Roo.form.FieldSet.superclass.constructor.call(this, config);
27512 };
27513
27514 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
27515     /**
27516      * @cfg {String} legend
27517      * The text to display as the legend for the FieldSet (defaults to '')
27518      */
27519     /**
27520      * @cfg {String/Object} autoCreate
27521      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
27522      */
27523
27524     // private
27525     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
27526
27527     // private
27528     onRender : function(ct, position){
27529         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
27530         if(this.legend){
27531             this.setLegend(this.legend);
27532         }
27533     },
27534
27535     // private
27536     setLegend : function(text){
27537         if(this.rendered){
27538             this.el.child('legend').update(text);
27539         }
27540     }
27541 });/*
27542  * Based on:
27543  * Ext JS Library 1.1.1
27544  * Copyright(c) 2006-2007, Ext JS, LLC.
27545  *
27546  * Originally Released Under LGPL - original licence link has changed is not relivant.
27547  *
27548  * Fork - LGPL
27549  * <script type="text/javascript">
27550  */
27551 /**
27552  * @class Roo.form.VTypes
27553  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
27554  * @singleton
27555  */
27556 Roo.form.VTypes = function(){
27557     // closure these in so they are only created once.
27558     var alpha = /^[a-zA-Z_]+$/;
27559     var alphanum = /^[a-zA-Z0-9_]+$/;
27560     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
27561     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
27562
27563     // All these messages and functions are configurable
27564     return {
27565         /**
27566          * The function used to validate email addresses
27567          * @param {String} value The email address
27568          */
27569         'email' : function(v){
27570             return email.test(v);
27571         },
27572         /**
27573          * The error text to display when the email validation function returns false
27574          * @type String
27575          */
27576         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
27577         /**
27578          * The keystroke filter mask to be applied on email input
27579          * @type RegExp
27580          */
27581         'emailMask' : /[a-z0-9_\.\-@]/i,
27582
27583         /**
27584          * The function used to validate URLs
27585          * @param {String} value The URL
27586          */
27587         'url' : function(v){
27588             return url.test(v);
27589         },
27590         /**
27591          * The error text to display when the url validation function returns false
27592          * @type String
27593          */
27594         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
27595         
27596         /**
27597          * The function used to validate alpha values
27598          * @param {String} value The value
27599          */
27600         'alpha' : function(v){
27601             return alpha.test(v);
27602         },
27603         /**
27604          * The error text to display when the alpha validation function returns false
27605          * @type String
27606          */
27607         'alphaText' : 'This field should only contain letters and _',
27608         /**
27609          * The keystroke filter mask to be applied on alpha input
27610          * @type RegExp
27611          */
27612         'alphaMask' : /[a-z_]/i,
27613
27614         /**
27615          * The function used to validate alphanumeric values
27616          * @param {String} value The value
27617          */
27618         'alphanum' : function(v){
27619             return alphanum.test(v);
27620         },
27621         /**
27622          * The error text to display when the alphanumeric validation function returns false
27623          * @type String
27624          */
27625         'alphanumText' : 'This field should only contain letters, numbers and _',
27626         /**
27627          * The keystroke filter mask to be applied on alphanumeric input
27628          * @type RegExp
27629          */
27630         'alphanumMask' : /[a-z0-9_]/i
27631     };
27632 }();//<script type="text/javascript">
27633
27634 /**
27635  * @class Roo.form.FCKeditor
27636  * @extends Roo.form.TextArea
27637  * Wrapper around the FCKEditor http://www.fckeditor.net
27638  * @constructor
27639  * Creates a new FCKeditor
27640  * @param {Object} config Configuration options
27641  */
27642 Roo.form.FCKeditor = function(config){
27643     Roo.form.FCKeditor.superclass.constructor.call(this, config);
27644     this.addEvents({
27645          /**
27646          * @event editorinit
27647          * Fired when the editor is initialized - you can add extra handlers here..
27648          * @param {FCKeditor} this
27649          * @param {Object} the FCK object.
27650          */
27651         editorinit : true
27652     });
27653     
27654     
27655 };
27656 Roo.form.FCKeditor.editors = { };
27657 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
27658 {
27659     //defaultAutoCreate : {
27660     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
27661     //},
27662     // private
27663     /**
27664      * @cfg {Object} fck options - see fck manual for details.
27665      */
27666     fckconfig : false,
27667     
27668     /**
27669      * @cfg {Object} fck toolbar set (Basic or Default)
27670      */
27671     toolbarSet : 'Basic',
27672     /**
27673      * @cfg {Object} fck BasePath
27674      */ 
27675     basePath : '/fckeditor/',
27676     
27677     
27678     frame : false,
27679     
27680     value : '',
27681     
27682    
27683     onRender : function(ct, position)
27684     {
27685         if(!this.el){
27686             this.defaultAutoCreate = {
27687                 tag: "textarea",
27688                 style:"width:300px;height:60px;",
27689                 autocomplete: "off"
27690             };
27691         }
27692         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
27693         /*
27694         if(this.grow){
27695             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
27696             if(this.preventScrollbars){
27697                 this.el.setStyle("overflow", "hidden");
27698             }
27699             this.el.setHeight(this.growMin);
27700         }
27701         */
27702         //console.log('onrender' + this.getId() );
27703         Roo.form.FCKeditor.editors[this.getId()] = this;
27704          
27705
27706         this.replaceTextarea() ;
27707         
27708     },
27709     
27710     getEditor : function() {
27711         return this.fckEditor;
27712     },
27713     /**
27714      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
27715      * @param {Mixed} value The value to set
27716      */
27717     
27718     
27719     setValue : function(value)
27720     {
27721         //console.log('setValue: ' + value);
27722         
27723         if(typeof(value) == 'undefined') { // not sure why this is happending...
27724             return;
27725         }
27726         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
27727         
27728         //if(!this.el || !this.getEditor()) {
27729         //    this.value = value;
27730             //this.setValue.defer(100,this,[value]);    
27731         //    return;
27732         //} 
27733         
27734         if(!this.getEditor()) {
27735             return;
27736         }
27737         
27738         this.getEditor().SetData(value);
27739         
27740         //
27741
27742     },
27743
27744     /**
27745      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
27746      * @return {Mixed} value The field value
27747      */
27748     getValue : function()
27749     {
27750         
27751         if (this.frame && this.frame.dom.style.display == 'none') {
27752             return Roo.form.FCKeditor.superclass.getValue.call(this);
27753         }
27754         
27755         if(!this.el || !this.getEditor()) {
27756            
27757            // this.getValue.defer(100,this); 
27758             return this.value;
27759         }
27760        
27761         
27762         var value=this.getEditor().GetData();
27763         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
27764         return Roo.form.FCKeditor.superclass.getValue.call(this);
27765         
27766
27767     },
27768
27769     /**
27770      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
27771      * @return {Mixed} value The field value
27772      */
27773     getRawValue : function()
27774     {
27775         if (this.frame && this.frame.dom.style.display == 'none') {
27776             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
27777         }
27778         
27779         if(!this.el || !this.getEditor()) {
27780             //this.getRawValue.defer(100,this); 
27781             return this.value;
27782             return;
27783         }
27784         
27785         
27786         
27787         var value=this.getEditor().GetData();
27788         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
27789         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
27790          
27791     },
27792     
27793     setSize : function(w,h) {
27794         
27795         
27796         
27797         //if (this.frame && this.frame.dom.style.display == 'none') {
27798         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
27799         //    return;
27800         //}
27801         //if(!this.el || !this.getEditor()) {
27802         //    this.setSize.defer(100,this, [w,h]); 
27803         //    return;
27804         //}
27805         
27806         
27807         
27808         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
27809         
27810         this.frame.dom.setAttribute('width', w);
27811         this.frame.dom.setAttribute('height', h);
27812         this.frame.setSize(w,h);
27813         
27814     },
27815     
27816     toggleSourceEdit : function(value) {
27817         
27818       
27819          
27820         this.el.dom.style.display = value ? '' : 'none';
27821         this.frame.dom.style.display = value ?  'none' : '';
27822         
27823     },
27824     
27825     
27826     focus: function(tag)
27827     {
27828         if (this.frame.dom.style.display == 'none') {
27829             return Roo.form.FCKeditor.superclass.focus.call(this);
27830         }
27831         if(!this.el || !this.getEditor()) {
27832             this.focus.defer(100,this, [tag]); 
27833             return;
27834         }
27835         
27836         
27837         
27838         
27839         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
27840         this.getEditor().Focus();
27841         if (tgs.length) {
27842             if (!this.getEditor().Selection.GetSelection()) {
27843                 this.focus.defer(100,this, [tag]); 
27844                 return;
27845             }
27846             
27847             
27848             var r = this.getEditor().EditorDocument.createRange();
27849             r.setStart(tgs[0],0);
27850             r.setEnd(tgs[0],0);
27851             this.getEditor().Selection.GetSelection().removeAllRanges();
27852             this.getEditor().Selection.GetSelection().addRange(r);
27853             this.getEditor().Focus();
27854         }
27855         
27856     },
27857     
27858     
27859     
27860     replaceTextarea : function()
27861     {
27862         if ( document.getElementById( this.getId() + '___Frame' ) )
27863             return ;
27864         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
27865         //{
27866             // We must check the elements firstly using the Id and then the name.
27867         var oTextarea = document.getElementById( this.getId() );
27868         
27869         var colElementsByName = document.getElementsByName( this.getId() ) ;
27870          
27871         oTextarea.style.display = 'none' ;
27872
27873         if ( oTextarea.tabIndex ) {            
27874             this.TabIndex = oTextarea.tabIndex ;
27875         }
27876         
27877         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
27878         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
27879         this.frame = Roo.get(this.getId() + '___Frame')
27880     },
27881     
27882     _getConfigHtml : function()
27883     {
27884         var sConfig = '' ;
27885
27886         for ( var o in this.fckconfig ) {
27887             sConfig += sConfig.length > 0  ? '&amp;' : '';
27888             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
27889         }
27890
27891         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
27892     },
27893     
27894     
27895     _getIFrameHtml : function()
27896     {
27897         var sFile = 'fckeditor.html' ;
27898         /* no idea what this is about..
27899         try
27900         {
27901             if ( (/fcksource=true/i).test( window.top.location.search ) )
27902                 sFile = 'fckeditor.original.html' ;
27903         }
27904         catch (e) { 
27905         */
27906
27907         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
27908         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
27909         
27910         
27911         var html = '<iframe id="' + this.getId() +
27912             '___Frame" src="' + sLink +
27913             '" width="' + this.width +
27914             '" height="' + this.height + '"' +
27915             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
27916             ' frameborder="0" scrolling="no"></iframe>' ;
27917
27918         return html ;
27919     },
27920     
27921     _insertHtmlBefore : function( html, element )
27922     {
27923         if ( element.insertAdjacentHTML )       {
27924             // IE
27925             element.insertAdjacentHTML( 'beforeBegin', html ) ;
27926         } else { // Gecko
27927             var oRange = document.createRange() ;
27928             oRange.setStartBefore( element ) ;
27929             var oFragment = oRange.createContextualFragment( html );
27930             element.parentNode.insertBefore( oFragment, element ) ;
27931         }
27932     }
27933     
27934     
27935   
27936     
27937     
27938     
27939     
27940
27941 });
27942
27943 //Roo.reg('fckeditor', Roo.form.FCKeditor);
27944
27945 function FCKeditor_OnComplete(editorInstance){
27946     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
27947     f.fckEditor = editorInstance;
27948     //console.log("loaded");
27949     f.fireEvent('editorinit', f, editorInstance);
27950
27951   
27952
27953  
27954
27955
27956
27957
27958
27959
27960
27961
27962
27963
27964
27965
27966
27967
27968
27969 //<script type="text/javascript">
27970 /**
27971  * @class Roo.form.GridField
27972  * @extends Roo.form.Field
27973  * Embed a grid (or editable grid into a form)
27974  * STATUS ALPHA
27975  * 
27976  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
27977  * it needs 
27978  * xgrid.store = Roo.data.Store
27979  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
27980  * xgrid.store.reader = Roo.data.JsonReader 
27981  * 
27982  * 
27983  * @constructor
27984  * Creates a new GridField
27985  * @param {Object} config Configuration options
27986  */
27987 Roo.form.GridField = function(config){
27988     Roo.form.GridField.superclass.constructor.call(this, config);
27989      
27990 };
27991
27992 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
27993     /**
27994      * @cfg {Number} width  - used to restrict width of grid..
27995      */
27996     width : 100,
27997     /**
27998      * @cfg {Number} height - used to restrict height of grid..
27999      */
28000     height : 50,
28001      /**
28002      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
28003          * 
28004          *}
28005      */
28006     xgrid : false, 
28007     /**
28008      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
28009      * {tag: "input", type: "checkbox", autocomplete: "off"})
28010      */
28011    // defaultAutoCreate : { tag: 'div' },
28012     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
28013     /**
28014      * @cfg {String} addTitle Text to include for adding a title.
28015      */
28016     addTitle : false,
28017     //
28018     onResize : function(){
28019         Roo.form.Field.superclass.onResize.apply(this, arguments);
28020     },
28021
28022     initEvents : function(){
28023         // Roo.form.Checkbox.superclass.initEvents.call(this);
28024         // has no events...
28025        
28026     },
28027
28028
28029     getResizeEl : function(){
28030         return this.wrap;
28031     },
28032
28033     getPositionEl : function(){
28034         return this.wrap;
28035     },
28036
28037     // private
28038     onRender : function(ct, position){
28039         
28040         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
28041         var style = this.style;
28042         delete this.style;
28043         
28044         Roo.form.GridField.superclass.onRender.call(this, ct, position);
28045         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
28046         this.viewEl = this.wrap.createChild({ tag: 'div' });
28047         if (style) {
28048             this.viewEl.applyStyles(style);
28049         }
28050         if (this.width) {
28051             this.viewEl.setWidth(this.width);
28052         }
28053         if (this.height) {
28054             this.viewEl.setHeight(this.height);
28055         }
28056         //if(this.inputValue !== undefined){
28057         //this.setValue(this.value);
28058         
28059         
28060         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
28061         
28062         
28063         this.grid.render();
28064         this.grid.getDataSource().on('remove', this.refreshValue, this);
28065         this.grid.getDataSource().on('update', this.refreshValue, this);
28066         this.grid.on('afteredit', this.refreshValue, this);
28067  
28068     },
28069      
28070     
28071     /**
28072      * Sets the value of the item. 
28073      * @param {String} either an object  or a string..
28074      */
28075     setValue : function(v){
28076         //this.value = v;
28077         v = v || []; // empty set..
28078         // this does not seem smart - it really only affects memoryproxy grids..
28079         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
28080             var ds = this.grid.getDataSource();
28081             // assumes a json reader..
28082             var data = {}
28083             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
28084             ds.loadData( data);
28085         }
28086         Roo.form.GridField.superclass.setValue.call(this, v);
28087         this.refreshValue();
28088         // should load data in the grid really....
28089     },
28090     
28091     // private
28092     refreshValue: function() {
28093          var val = [];
28094         this.grid.getDataSource().each(function(r) {
28095             val.push(r.data);
28096         });
28097         this.el.dom.value = Roo.encode(val);
28098     }
28099     
28100      
28101     
28102     
28103 });/*
28104  * Based on:
28105  * Ext JS Library 1.1.1
28106  * Copyright(c) 2006-2007, Ext JS, LLC.
28107  *
28108  * Originally Released Under LGPL - original licence link has changed is not relivant.
28109  *
28110  * Fork - LGPL
28111  * <script type="text/javascript">
28112  */
28113 /**
28114  * @class Roo.form.DisplayField
28115  * @extends Roo.form.Field
28116  * A generic Field to display non-editable data.
28117  * @constructor
28118  * Creates a new Display Field item.
28119  * @param {Object} config Configuration options
28120  */
28121 Roo.form.DisplayField = function(config){
28122     Roo.form.DisplayField.superclass.constructor.call(this, config);
28123     
28124 };
28125
28126 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
28127     inputType:      'hidden',
28128     allowBlank:     true,
28129     readOnly:         true,
28130     
28131  
28132     /**
28133      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
28134      */
28135     focusClass : undefined,
28136     /**
28137      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
28138      */
28139     fieldClass: 'x-form-field',
28140     
28141      /**
28142      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
28143      */
28144     valueRenderer: undefined,
28145     
28146     width: 100,
28147     /**
28148      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
28149      * {tag: "input", type: "checkbox", autocomplete: "off"})
28150      */
28151      
28152  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
28153
28154     onResize : function(){
28155         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
28156         
28157     },
28158
28159     initEvents : function(){
28160         // Roo.form.Checkbox.superclass.initEvents.call(this);
28161         // has no events...
28162        
28163     },
28164
28165
28166     getResizeEl : function(){
28167         return this.wrap;
28168     },
28169
28170     getPositionEl : function(){
28171         return this.wrap;
28172     },
28173
28174     // private
28175     onRender : function(ct, position){
28176         
28177         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
28178         //if(this.inputValue !== undefined){
28179         this.wrap = this.el.wrap();
28180         
28181         this.viewEl = this.wrap.createChild({ tag: 'div'});
28182         
28183         if (this.bodyStyle) {
28184             this.viewEl.applyStyles(this.bodyStyle);
28185         }
28186         //this.viewEl.setStyle('padding', '2px');
28187         
28188         this.setValue(this.value);
28189         
28190     },
28191 /*
28192     // private
28193     initValue : Roo.emptyFn,
28194
28195   */
28196
28197         // private
28198     onClick : function(){
28199         
28200     },
28201
28202     /**
28203      * Sets the checked state of the checkbox.
28204      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
28205      */
28206     setValue : function(v){
28207         this.value = v;
28208         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
28209         // this might be called before we have a dom element..
28210         if (!this.viewEl) {
28211             return;
28212         }
28213         this.viewEl.dom.innerHTML = html;
28214         Roo.form.DisplayField.superclass.setValue.call(this, v);
28215
28216     }
28217 });//<script type="text/javasscript">
28218  
28219
28220 /**
28221  * @class Roo.DDView
28222  * A DnD enabled version of Roo.View.
28223  * @param {Element/String} container The Element in which to create the View.
28224  * @param {String} tpl The template string used to create the markup for each element of the View
28225  * @param {Object} config The configuration properties. These include all the config options of
28226  * {@link Roo.View} plus some specific to this class.<br>
28227  * <p>
28228  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
28229  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
28230  * <p>
28231  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
28232 .x-view-drag-insert-above {
28233         border-top:1px dotted #3366cc;
28234 }
28235 .x-view-drag-insert-below {
28236         border-bottom:1px dotted #3366cc;
28237 }
28238 </code></pre>
28239  * 
28240  */
28241  
28242 Roo.DDView = function(container, tpl, config) {
28243     Roo.DDView.superclass.constructor.apply(this, arguments);
28244     this.getEl().setStyle("outline", "0px none");
28245     this.getEl().unselectable();
28246     if (this.dragGroup) {
28247                 this.setDraggable(this.dragGroup.split(","));
28248     }
28249     if (this.dropGroup) {
28250                 this.setDroppable(this.dropGroup.split(","));
28251     }
28252     if (this.deletable) {
28253         this.setDeletable();
28254     }
28255     this.isDirtyFlag = false;
28256         this.addEvents({
28257                 "drop" : true
28258         });
28259 };
28260
28261 Roo.extend(Roo.DDView, Roo.View, {
28262 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
28263 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
28264 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
28265 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
28266
28267         isFormField: true,
28268
28269         reset: Roo.emptyFn,
28270         
28271         clearInvalid: Roo.form.Field.prototype.clearInvalid,
28272
28273         validate: function() {
28274                 return true;
28275         },
28276         
28277         destroy: function() {
28278                 this.purgeListeners();
28279                 this.getEl.removeAllListeners();
28280                 this.getEl().remove();
28281                 if (this.dragZone) {
28282                         if (this.dragZone.destroy) {
28283                                 this.dragZone.destroy();
28284                         }
28285                 }
28286                 if (this.dropZone) {
28287                         if (this.dropZone.destroy) {
28288                                 this.dropZone.destroy();
28289                         }
28290                 }
28291         },
28292
28293 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
28294         getName: function() {
28295                 return this.name;
28296         },
28297
28298 /**     Loads the View from a JSON string representing the Records to put into the Store. */
28299         setValue: function(v) {
28300                 if (!this.store) {
28301                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
28302                 }
28303                 var data = {};
28304                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
28305                 this.store.proxy = new Roo.data.MemoryProxy(data);
28306                 this.store.load();
28307         },
28308
28309 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
28310         getValue: function() {
28311                 var result = '(';
28312                 this.store.each(function(rec) {
28313                         result += rec.id + ',';
28314                 });
28315                 return result.substr(0, result.length - 1) + ')';
28316         },
28317         
28318         getIds: function() {
28319                 var i = 0, result = new Array(this.store.getCount());
28320                 this.store.each(function(rec) {
28321                         result[i++] = rec.id;
28322                 });
28323                 return result;
28324         },
28325         
28326         isDirty: function() {
28327                 return this.isDirtyFlag;
28328         },
28329
28330 /**
28331  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
28332  *      whole Element becomes the target, and this causes the drop gesture to append.
28333  */
28334     getTargetFromEvent : function(e) {
28335                 var target = e.getTarget();
28336                 while ((target !== null) && (target.parentNode != this.el.dom)) {
28337                 target = target.parentNode;
28338                 }
28339                 if (!target) {
28340                         target = this.el.dom.lastChild || this.el.dom;
28341                 }
28342                 return target;
28343     },
28344
28345 /**
28346  *      Create the drag data which consists of an object which has the property "ddel" as
28347  *      the drag proxy element. 
28348  */
28349     getDragData : function(e) {
28350         var target = this.findItemFromChild(e.getTarget());
28351                 if(target) {
28352                         this.handleSelection(e);
28353                         var selNodes = this.getSelectedNodes();
28354             var dragData = {
28355                 source: this,
28356                 copy: this.copy || (this.allowCopy && e.ctrlKey),
28357                 nodes: selNodes,
28358                 records: []
28359                         };
28360                         var selectedIndices = this.getSelectedIndexes();
28361                         for (var i = 0; i < selectedIndices.length; i++) {
28362                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
28363                         }
28364                         if (selNodes.length == 1) {
28365                                 dragData.ddel = target.cloneNode(true); // the div element
28366                         } else {
28367                                 var div = document.createElement('div'); // create the multi element drag "ghost"
28368                                 div.className = 'multi-proxy';
28369                                 for (var i = 0, len = selNodes.length; i < len; i++) {
28370                                         div.appendChild(selNodes[i].cloneNode(true));
28371                                 }
28372                                 dragData.ddel = div;
28373                         }
28374             //console.log(dragData)
28375             //console.log(dragData.ddel.innerHTML)
28376                         return dragData;
28377                 }
28378         //console.log('nodragData')
28379                 return false;
28380     },
28381     
28382 /**     Specify to which ddGroup items in this DDView may be dragged. */
28383     setDraggable: function(ddGroup) {
28384         if (ddGroup instanceof Array) {
28385                 Roo.each(ddGroup, this.setDraggable, this);
28386                 return;
28387         }
28388         if (this.dragZone) {
28389                 this.dragZone.addToGroup(ddGroup);
28390         } else {
28391                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
28392                                 containerScroll: true,
28393                                 ddGroup: ddGroup 
28394
28395                         });
28396 //                      Draggability implies selection. DragZone's mousedown selects the element.
28397                         if (!this.multiSelect) { this.singleSelect = true; }
28398
28399 //                      Wire the DragZone's handlers up to methods in *this*
28400                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
28401                 }
28402     },
28403
28404 /**     Specify from which ddGroup this DDView accepts drops. */
28405     setDroppable: function(ddGroup) {
28406         if (ddGroup instanceof Array) {
28407                 Roo.each(ddGroup, this.setDroppable, this);
28408                 return;
28409         }
28410         if (this.dropZone) {
28411                 this.dropZone.addToGroup(ddGroup);
28412         } else {
28413                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
28414                                 containerScroll: true,
28415                                 ddGroup: ddGroup
28416                         });
28417
28418 //                      Wire the DropZone's handlers up to methods in *this*
28419                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
28420                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
28421                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
28422                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
28423                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
28424                 }
28425     },
28426
28427 /**     Decide whether to drop above or below a View node. */
28428     getDropPoint : function(e, n, dd){
28429         if (n == this.el.dom) { return "above"; }
28430                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
28431                 var c = t + (b - t) / 2;
28432                 var y = Roo.lib.Event.getPageY(e);
28433                 if(y <= c) {
28434                         return "above";
28435                 }else{
28436                         return "below";
28437                 }
28438     },
28439
28440     onNodeEnter : function(n, dd, e, data){
28441                 return false;
28442     },
28443     
28444     onNodeOver : function(n, dd, e, data){
28445                 var pt = this.getDropPoint(e, n, dd);
28446                 // set the insert point style on the target node
28447                 var dragElClass = this.dropNotAllowed;
28448                 if (pt) {
28449                         var targetElClass;
28450                         if (pt == "above"){
28451                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
28452                                 targetElClass = "x-view-drag-insert-above";
28453                         } else {
28454                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
28455                                 targetElClass = "x-view-drag-insert-below";
28456                         }
28457                         if (this.lastInsertClass != targetElClass){
28458                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
28459                                 this.lastInsertClass = targetElClass;
28460                         }
28461                 }
28462                 return dragElClass;
28463         },
28464
28465     onNodeOut : function(n, dd, e, data){
28466                 this.removeDropIndicators(n);
28467     },
28468
28469     onNodeDrop : function(n, dd, e, data){
28470         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
28471                 return false;
28472         }
28473         var pt = this.getDropPoint(e, n, dd);
28474                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
28475                 if (pt == "below") { insertAt++; }
28476                 for (var i = 0; i < data.records.length; i++) {
28477                         var r = data.records[i];
28478                         var dup = this.store.getById(r.id);
28479                         if (dup && (dd != this.dragZone)) {
28480                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
28481                         } else {
28482                                 if (data.copy) {
28483                                         this.store.insert(insertAt++, r.copy());
28484                                 } else {
28485                                         data.source.isDirtyFlag = true;
28486                                         r.store.remove(r);
28487                                         this.store.insert(insertAt++, r);
28488                                 }
28489                                 this.isDirtyFlag = true;
28490                         }
28491                 }
28492                 this.dragZone.cachedTarget = null;
28493                 return true;
28494     },
28495
28496     removeDropIndicators : function(n){
28497                 if(n){
28498                         Roo.fly(n).removeClass([
28499                                 "x-view-drag-insert-above",
28500                                 "x-view-drag-insert-below"]);
28501                         this.lastInsertClass = "_noclass";
28502                 }
28503     },
28504
28505 /**
28506  *      Utility method. Add a delete option to the DDView's context menu.
28507  *      @param {String} imageUrl The URL of the "delete" icon image.
28508  */
28509         setDeletable: function(imageUrl) {
28510                 if (!this.singleSelect && !this.multiSelect) {
28511                         this.singleSelect = true;
28512                 }
28513                 var c = this.getContextMenu();
28514                 this.contextMenu.on("itemclick", function(item) {
28515                         switch (item.id) {
28516                                 case "delete":
28517                                         this.remove(this.getSelectedIndexes());
28518                                         break;
28519                         }
28520                 }, this);
28521                 this.contextMenu.add({
28522                         icon: imageUrl,
28523                         id: "delete",
28524                         text: 'Delete'
28525                 });
28526         },
28527         
28528 /**     Return the context menu for this DDView. */
28529         getContextMenu: function() {
28530                 if (!this.contextMenu) {
28531 //                      Create the View's context menu
28532                         this.contextMenu = new Roo.menu.Menu({
28533                                 id: this.id + "-contextmenu"
28534                         });
28535                         this.el.on("contextmenu", this.showContextMenu, this);
28536                 }
28537                 return this.contextMenu;
28538         },
28539         
28540         disableContextMenu: function() {
28541                 if (this.contextMenu) {
28542                         this.el.un("contextmenu", this.showContextMenu, this);
28543                 }
28544         },
28545
28546         showContextMenu: function(e, item) {
28547         item = this.findItemFromChild(e.getTarget());
28548                 if (item) {
28549                         e.stopEvent();
28550                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
28551                         this.contextMenu.showAt(e.getXY());
28552             }
28553     },
28554
28555 /**
28556  *      Remove {@link Roo.data.Record}s at the specified indices.
28557  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
28558  */
28559     remove: function(selectedIndices) {
28560                 selectedIndices = [].concat(selectedIndices);
28561                 for (var i = 0; i < selectedIndices.length; i++) {
28562                         var rec = this.store.getAt(selectedIndices[i]);
28563                         this.store.remove(rec);
28564                 }
28565     },
28566
28567 /**
28568  *      Double click fires the event, but also, if this is draggable, and there is only one other
28569  *      related DropZone, it transfers the selected node.
28570  */
28571     onDblClick : function(e){
28572         var item = this.findItemFromChild(e.getTarget());
28573         if(item){
28574             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
28575                 return false;
28576             }
28577             if (this.dragGroup) {
28578                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
28579                     while (targets.indexOf(this.dropZone) > -1) {
28580                             targets.remove(this.dropZone);
28581                                 }
28582                     if (targets.length == 1) {
28583                                         this.dragZone.cachedTarget = null;
28584                         var el = Roo.get(targets[0].getEl());
28585                         var box = el.getBox(true);
28586                         targets[0].onNodeDrop(el.dom, {
28587                                 target: el.dom,
28588                                 xy: [box.x, box.y + box.height - 1]
28589                         }, null, this.getDragData(e));
28590                     }
28591                 }
28592         }
28593     },
28594     
28595     handleSelection: function(e) {
28596                 this.dragZone.cachedTarget = null;
28597         var item = this.findItemFromChild(e.getTarget());
28598         if (!item) {
28599                 this.clearSelections(true);
28600                 return;
28601         }
28602                 if (item && (this.multiSelect || this.singleSelect)){
28603                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
28604                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
28605                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
28606                                 this.unselect(item);
28607                         } else {
28608                                 this.select(item, this.multiSelect && e.ctrlKey);
28609                                 this.lastSelection = item;
28610                         }
28611                 }
28612     },
28613
28614     onItemClick : function(item, index, e){
28615                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
28616                         return false;
28617                 }
28618                 return true;
28619     },
28620
28621     unselect : function(nodeInfo, suppressEvent){
28622                 var node = this.getNode(nodeInfo);
28623                 if(node && this.isSelected(node)){
28624                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28625                                 Roo.fly(node).removeClass(this.selectedClass);
28626                                 this.selections.remove(node);
28627                                 if(!suppressEvent){
28628                                         this.fireEvent("selectionchange", this, this.selections);
28629                                 }
28630                         }
28631                 }
28632     }
28633 });
28634 /*
28635  * Based on:
28636  * Ext JS Library 1.1.1
28637  * Copyright(c) 2006-2007, Ext JS, LLC.
28638  *
28639  * Originally Released Under LGPL - original licence link has changed is not relivant.
28640  *
28641  * Fork - LGPL
28642  * <script type="text/javascript">
28643  */
28644  
28645 /**
28646  * @class Roo.LayoutManager
28647  * @extends Roo.util.Observable
28648  * Base class for layout managers.
28649  */
28650 Roo.LayoutManager = function(container, config){
28651     Roo.LayoutManager.superclass.constructor.call(this);
28652     this.el = Roo.get(container);
28653     // ie scrollbar fix
28654     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
28655         document.body.scroll = "no";
28656     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
28657         this.el.position('relative');
28658     }
28659     this.id = this.el.id;
28660     this.el.addClass("x-layout-container");
28661     /** false to disable window resize monitoring @type Boolean */
28662     this.monitorWindowResize = true;
28663     this.regions = {};
28664     this.addEvents({
28665         /**
28666          * @event layout
28667          * Fires when a layout is performed. 
28668          * @param {Roo.LayoutManager} this
28669          */
28670         "layout" : true,
28671         /**
28672          * @event regionresized
28673          * Fires when the user resizes a region. 
28674          * @param {Roo.LayoutRegion} region The resized region
28675          * @param {Number} newSize The new size (width for east/west, height for north/south)
28676          */
28677         "regionresized" : true,
28678         /**
28679          * @event regioncollapsed
28680          * Fires when a region is collapsed. 
28681          * @param {Roo.LayoutRegion} region The collapsed region
28682          */
28683         "regioncollapsed" : true,
28684         /**
28685          * @event regionexpanded
28686          * Fires when a region is expanded.  
28687          * @param {Roo.LayoutRegion} region The expanded region
28688          */
28689         "regionexpanded" : true
28690     });
28691     this.updating = false;
28692     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
28693 };
28694
28695 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
28696     /**
28697      * Returns true if this layout is currently being updated
28698      * @return {Boolean}
28699      */
28700     isUpdating : function(){
28701         return this.updating; 
28702     },
28703     
28704     /**
28705      * Suspend the LayoutManager from doing auto-layouts while
28706      * making multiple add or remove calls
28707      */
28708     beginUpdate : function(){
28709         this.updating = true;    
28710     },
28711     
28712     /**
28713      * Restore auto-layouts and optionally disable the manager from performing a layout
28714      * @param {Boolean} noLayout true to disable a layout update 
28715      */
28716     endUpdate : function(noLayout){
28717         this.updating = false;
28718         if(!noLayout){
28719             this.layout();
28720         }    
28721     },
28722     
28723     layout: function(){
28724         
28725     },
28726     
28727     onRegionResized : function(region, newSize){
28728         this.fireEvent("regionresized", region, newSize);
28729         this.layout();
28730     },
28731     
28732     onRegionCollapsed : function(region){
28733         this.fireEvent("regioncollapsed", region);
28734     },
28735     
28736     onRegionExpanded : function(region){
28737         this.fireEvent("regionexpanded", region);
28738     },
28739         
28740     /**
28741      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
28742      * performs box-model adjustments.
28743      * @return {Object} The size as an object {width: (the width), height: (the height)}
28744      */
28745     getViewSize : function(){
28746         var size;
28747         if(this.el.dom != document.body){
28748             size = this.el.getSize();
28749         }else{
28750             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
28751         }
28752         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
28753         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
28754         return size;
28755     },
28756     
28757     /**
28758      * Returns the Element this layout is bound to.
28759      * @return {Roo.Element}
28760      */
28761     getEl : function(){
28762         return this.el;
28763     },
28764     
28765     /**
28766      * Returns the specified region.
28767      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
28768      * @return {Roo.LayoutRegion}
28769      */
28770     getRegion : function(target){
28771         return this.regions[target.toLowerCase()];
28772     },
28773     
28774     onWindowResize : function(){
28775         if(this.monitorWindowResize){
28776             this.layout();
28777         }
28778     }
28779 });/*
28780  * Based on:
28781  * Ext JS Library 1.1.1
28782  * Copyright(c) 2006-2007, Ext JS, LLC.
28783  *
28784  * Originally Released Under LGPL - original licence link has changed is not relivant.
28785  *
28786  * Fork - LGPL
28787  * <script type="text/javascript">
28788  */
28789 /**
28790  * @class Roo.BorderLayout
28791  * @extends Roo.LayoutManager
28792  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
28793  * please see: <br><br>
28794  * <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>
28795  * <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>
28796  * Example:
28797  <pre><code>
28798  var layout = new Roo.BorderLayout(document.body, {
28799     north: {
28800         initialSize: 25,
28801         titlebar: false
28802     },
28803     west: {
28804         split:true,
28805         initialSize: 200,
28806         minSize: 175,
28807         maxSize: 400,
28808         titlebar: true,
28809         collapsible: true
28810     },
28811     east: {
28812         split:true,
28813         initialSize: 202,
28814         minSize: 175,
28815         maxSize: 400,
28816         titlebar: true,
28817         collapsible: true
28818     },
28819     south: {
28820         split:true,
28821         initialSize: 100,
28822         minSize: 100,
28823         maxSize: 200,
28824         titlebar: true,
28825         collapsible: true
28826     },
28827     center: {
28828         titlebar: true,
28829         autoScroll:true,
28830         resizeTabs: true,
28831         minTabWidth: 50,
28832         preferredTabWidth: 150
28833     }
28834 });
28835
28836 // shorthand
28837 var CP = Roo.ContentPanel;
28838
28839 layout.beginUpdate();
28840 layout.add("north", new CP("north", "North"));
28841 layout.add("south", new CP("south", {title: "South", closable: true}));
28842 layout.add("west", new CP("west", {title: "West"}));
28843 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
28844 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
28845 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
28846 layout.getRegion("center").showPanel("center1");
28847 layout.endUpdate();
28848 </code></pre>
28849
28850 <b>The container the layout is rendered into can be either the body element or any other element.
28851 If it is not the body element, the container needs to either be an absolute positioned element,
28852 or you will need to add "position:relative" to the css of the container.  You will also need to specify
28853 the container size if it is not the body element.</b>
28854
28855 * @constructor
28856 * Create a new BorderLayout
28857 * @param {String/HTMLElement/Element} container The container this layout is bound to
28858 * @param {Object} config Configuration options
28859  */
28860 Roo.BorderLayout = function(container, config){
28861     config = config || {};
28862     Roo.BorderLayout.superclass.constructor.call(this, container, config);
28863     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
28864     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
28865         var target = this.factory.validRegions[i];
28866         if(config[target]){
28867             this.addRegion(target, config[target]);
28868         }
28869     }
28870 };
28871
28872 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
28873     /**
28874      * Creates and adds a new region if it doesn't already exist.
28875      * @param {String} target The target region key (north, south, east, west or center).
28876      * @param {Object} config The regions config object
28877      * @return {BorderLayoutRegion} The new region
28878      */
28879     addRegion : function(target, config){
28880         if(!this.regions[target]){
28881             var r = this.factory.create(target, this, config);
28882             this.bindRegion(target, r);
28883         }
28884         return this.regions[target];
28885     },
28886
28887     // private (kinda)
28888     bindRegion : function(name, r){
28889         this.regions[name] = r;
28890         r.on("visibilitychange", this.layout, this);
28891         r.on("paneladded", this.layout, this);
28892         r.on("panelremoved", this.layout, this);
28893         r.on("invalidated", this.layout, this);
28894         r.on("resized", this.onRegionResized, this);
28895         r.on("collapsed", this.onRegionCollapsed, this);
28896         r.on("expanded", this.onRegionExpanded, this);
28897     },
28898
28899     /**
28900      * Performs a layout update.
28901      */
28902     layout : function(){
28903         if(this.updating) return;
28904         var size = this.getViewSize();
28905         var w = size.width;
28906         var h = size.height;
28907         var centerW = w;
28908         var centerH = h;
28909         var centerY = 0;
28910         var centerX = 0;
28911         //var x = 0, y = 0;
28912
28913         var rs = this.regions;
28914         var north = rs["north"];
28915         var south = rs["south"]; 
28916         var west = rs["west"];
28917         var east = rs["east"];
28918         var center = rs["center"];
28919         //if(this.hideOnLayout){ // not supported anymore
28920             //c.el.setStyle("display", "none");
28921         //}
28922         if(north && north.isVisible()){
28923             var b = north.getBox();
28924             var m = north.getMargins();
28925             b.width = w - (m.left+m.right);
28926             b.x = m.left;
28927             b.y = m.top;
28928             centerY = b.height + b.y + m.bottom;
28929             centerH -= centerY;
28930             north.updateBox(this.safeBox(b));
28931         }
28932         if(south && south.isVisible()){
28933             var b = south.getBox();
28934             var m = south.getMargins();
28935             b.width = w - (m.left+m.right);
28936             b.x = m.left;
28937             var totalHeight = (b.height + m.top + m.bottom);
28938             b.y = h - totalHeight + m.top;
28939             centerH -= totalHeight;
28940             south.updateBox(this.safeBox(b));
28941         }
28942         if(west && west.isVisible()){
28943             var b = west.getBox();
28944             var m = west.getMargins();
28945             b.height = centerH - (m.top+m.bottom);
28946             b.x = m.left;
28947             b.y = centerY + m.top;
28948             var totalWidth = (b.width + m.left + m.right);
28949             centerX += totalWidth;
28950             centerW -= totalWidth;
28951             west.updateBox(this.safeBox(b));
28952         }
28953         if(east && east.isVisible()){
28954             var b = east.getBox();
28955             var m = east.getMargins();
28956             b.height = centerH - (m.top+m.bottom);
28957             var totalWidth = (b.width + m.left + m.right);
28958             b.x = w - totalWidth + m.left;
28959             b.y = centerY + m.top;
28960             centerW -= totalWidth;
28961             east.updateBox(this.safeBox(b));
28962         }
28963         if(center){
28964             var m = center.getMargins();
28965             var centerBox = {
28966                 x: centerX + m.left,
28967                 y: centerY + m.top,
28968                 width: centerW - (m.left+m.right),
28969                 height: centerH - (m.top+m.bottom)
28970             };
28971             //if(this.hideOnLayout){
28972                 //center.el.setStyle("display", "block");
28973             //}
28974             center.updateBox(this.safeBox(centerBox));
28975         }
28976         this.el.repaint();
28977         this.fireEvent("layout", this);
28978     },
28979
28980     // private
28981     safeBox : function(box){
28982         box.width = Math.max(0, box.width);
28983         box.height = Math.max(0, box.height);
28984         return box;
28985     },
28986
28987     /**
28988      * Adds a ContentPanel (or subclass) to this layout.
28989      * @param {String} target The target region key (north, south, east, west or center).
28990      * @param {Roo.ContentPanel} panel The panel to add
28991      * @return {Roo.ContentPanel} The added panel
28992      */
28993     add : function(target, panel){
28994          
28995         target = target.toLowerCase();
28996         return this.regions[target].add(panel);
28997     },
28998
28999     /**
29000      * Remove a ContentPanel (or subclass) to this layout.
29001      * @param {String} target The target region key (north, south, east, west or center).
29002      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
29003      * @return {Roo.ContentPanel} The removed panel
29004      */
29005     remove : function(target, panel){
29006         target = target.toLowerCase();
29007         return this.regions[target].remove(panel);
29008     },
29009
29010     /**
29011      * Searches all regions for a panel with the specified id
29012      * @param {String} panelId
29013      * @return {Roo.ContentPanel} The panel or null if it wasn't found
29014      */
29015     findPanel : function(panelId){
29016         var rs = this.regions;
29017         for(var target in rs){
29018             if(typeof rs[target] != "function"){
29019                 var p = rs[target].getPanel(panelId);
29020                 if(p){
29021                     return p;
29022                 }
29023             }
29024         }
29025         return null;
29026     },
29027
29028     /**
29029      * Searches all regions for a panel with the specified id and activates (shows) it.
29030      * @param {String/ContentPanel} panelId The panels id or the panel itself
29031      * @return {Roo.ContentPanel} The shown panel or null
29032      */
29033     showPanel : function(panelId) {
29034       var rs = this.regions;
29035       for(var target in rs){
29036          var r = rs[target];
29037          if(typeof r != "function"){
29038             if(r.hasPanel(panelId)){
29039                return r.showPanel(panelId);
29040             }
29041          }
29042       }
29043       return null;
29044    },
29045
29046    /**
29047      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
29048      * @param {Roo.state.Provider} provider (optional) An alternate state provider
29049      */
29050     restoreState : function(provider){
29051         if(!provider){
29052             provider = Roo.state.Manager;
29053         }
29054         var sm = new Roo.LayoutStateManager();
29055         sm.init(this, provider);
29056     },
29057
29058     /**
29059      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
29060      * object should contain properties for each region to add ContentPanels to, and each property's value should be
29061      * a valid ContentPanel config object.  Example:
29062      * <pre><code>
29063 // Create the main layout
29064 var layout = new Roo.BorderLayout('main-ct', {
29065     west: {
29066         split:true,
29067         minSize: 175,
29068         titlebar: true
29069     },
29070     center: {
29071         title:'Components'
29072     }
29073 }, 'main-ct');
29074
29075 // Create and add multiple ContentPanels at once via configs
29076 layout.batchAdd({
29077    west: {
29078        id: 'source-files',
29079        autoCreate:true,
29080        title:'Ext Source Files',
29081        autoScroll:true,
29082        fitToFrame:true
29083    },
29084    center : {
29085        el: cview,
29086        autoScroll:true,
29087        fitToFrame:true,
29088        toolbar: tb,
29089        resizeEl:'cbody'
29090    }
29091 });
29092 </code></pre>
29093      * @param {Object} regions An object containing ContentPanel configs by region name
29094      */
29095     batchAdd : function(regions){
29096         this.beginUpdate();
29097         for(var rname in regions){
29098             var lr = this.regions[rname];
29099             if(lr){
29100                 this.addTypedPanels(lr, regions[rname]);
29101             }
29102         }
29103         this.endUpdate();
29104     },
29105
29106     // private
29107     addTypedPanels : function(lr, ps){
29108         if(typeof ps == 'string'){
29109             lr.add(new Roo.ContentPanel(ps));
29110         }
29111         else if(ps instanceof Array){
29112             for(var i =0, len = ps.length; i < len; i++){
29113                 this.addTypedPanels(lr, ps[i]);
29114             }
29115         }
29116         else if(!ps.events){ // raw config?
29117             var el = ps.el;
29118             delete ps.el; // prevent conflict
29119             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
29120         }
29121         else {  // panel object assumed!
29122             lr.add(ps);
29123         }
29124     },
29125     /**
29126      * Adds a xtype elements to the layout.
29127      * <pre><code>
29128
29129 layout.addxtype({
29130        xtype : 'ContentPanel',
29131        region: 'west',
29132        items: [ .... ]
29133    }
29134 );
29135
29136 layout.addxtype({
29137         xtype : 'NestedLayoutPanel',
29138         region: 'west',
29139         layout: {
29140            center: { },
29141            west: { }   
29142         },
29143         items : [ ... list of content panels or nested layout panels.. ]
29144    }
29145 );
29146 </code></pre>
29147      * @param {Object} cfg Xtype definition of item to add.
29148      */
29149     addxtype : function(cfg)
29150     {
29151         // basically accepts a pannel...
29152         // can accept a layout region..!?!?
29153        // console.log('BorderLayout add ' + cfg.xtype)
29154         
29155         if (!cfg.xtype.match(/Panel$/)) {
29156             return false;
29157         }
29158         var ret = false;
29159         var region = cfg.region;
29160         delete cfg.region;
29161         
29162           
29163         var xitems = [];
29164         if (cfg.items) {
29165             xitems = cfg.items;
29166             delete cfg.items;
29167         }
29168         
29169         
29170         switch(cfg.xtype) 
29171         {
29172             case 'ContentPanel':  // ContentPanel (el, cfg)
29173             case 'ScrollPanel':  // ContentPanel (el, cfg)
29174                 if(cfg.autoCreate) {
29175                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29176                 } else {
29177                     var el = this.el.createChild();
29178                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
29179                 }
29180                 
29181                 this.add(region, ret);
29182                 break;
29183             
29184             
29185             case 'TreePanel': // our new panel!
29186                 cfg.el = this.el.createChild();
29187                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29188                 this.add(region, ret);
29189                 break;
29190             
29191             case 'NestedLayoutPanel': 
29192                 // create a new Layout (which is  a Border Layout...
29193                 var el = this.el.createChild();
29194                 var clayout = cfg.layout;
29195                 delete cfg.layout;
29196                 clayout.items   = clayout.items  || [];
29197                 // replace this exitems with the clayout ones..
29198                 xitems = clayout.items;
29199                  
29200                 
29201                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
29202                     cfg.background = false;
29203                 }
29204                 var layout = new Roo.BorderLayout(el, clayout);
29205                 
29206                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
29207                 //console.log('adding nested layout panel '  + cfg.toSource());
29208                 this.add(region, ret);
29209                 
29210                 break;
29211                 
29212             case 'GridPanel': 
29213             
29214                 // needs grid and region
29215                 
29216                 //var el = this.getRegion(region).el.createChild();
29217                 var el = this.el.createChild();
29218                 // create the grid first...
29219                 
29220                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
29221                 delete cfg.grid;
29222                 if (region == 'center' && this.active ) {
29223                     cfg.background = false;
29224                 }
29225                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
29226                 
29227                 this.add(region, ret);
29228                 if (cfg.background) {
29229                     ret.on('activate', function(gp) {
29230                         if (!gp.grid.rendered) {
29231                             gp.grid.render();
29232                         }
29233                     });
29234                 } else {
29235                     grid.render();
29236                 }
29237                 break;
29238            
29239                
29240                 
29241                 
29242             default: 
29243                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
29244                 return;
29245              // GridPanel (grid, cfg)
29246             
29247         }
29248         this.beginUpdate();
29249         // add children..
29250         Roo.each(xitems, function(i)  {
29251             ret.addxtype(i);
29252         });
29253         this.endUpdate();
29254         return ret;
29255         
29256     }
29257 });
29258
29259 /**
29260  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
29261  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
29262  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
29263  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
29264  * <pre><code>
29265 // shorthand
29266 var CP = Roo.ContentPanel;
29267
29268 var layout = Roo.BorderLayout.create({
29269     north: {
29270         initialSize: 25,
29271         titlebar: false,
29272         panels: [new CP("north", "North")]
29273     },
29274     west: {
29275         split:true,
29276         initialSize: 200,
29277         minSize: 175,
29278         maxSize: 400,
29279         titlebar: true,
29280         collapsible: true,
29281         panels: [new CP("west", {title: "West"})]
29282     },
29283     east: {
29284         split:true,
29285         initialSize: 202,
29286         minSize: 175,
29287         maxSize: 400,
29288         titlebar: true,
29289         collapsible: true,
29290         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
29291     },
29292     south: {
29293         split:true,
29294         initialSize: 100,
29295         minSize: 100,
29296         maxSize: 200,
29297         titlebar: true,
29298         collapsible: true,
29299         panels: [new CP("south", {title: "South", closable: true})]
29300     },
29301     center: {
29302         titlebar: true,
29303         autoScroll:true,
29304         resizeTabs: true,
29305         minTabWidth: 50,
29306         preferredTabWidth: 150,
29307         panels: [
29308             new CP("center1", {title: "Close Me", closable: true}),
29309             new CP("center2", {title: "Center Panel", closable: false})
29310         ]
29311     }
29312 }, document.body);
29313
29314 layout.getRegion("center").showPanel("center1");
29315 </code></pre>
29316  * @param config
29317  * @param targetEl
29318  */
29319 Roo.BorderLayout.create = function(config, targetEl){
29320     var layout = new Roo.BorderLayout(targetEl || document.body, config);
29321     layout.beginUpdate();
29322     var regions = Roo.BorderLayout.RegionFactory.validRegions;
29323     for(var j = 0, jlen = regions.length; j < jlen; j++){
29324         var lr = regions[j];
29325         if(layout.regions[lr] && config[lr].panels){
29326             var r = layout.regions[lr];
29327             var ps = config[lr].panels;
29328             layout.addTypedPanels(r, ps);
29329         }
29330     }
29331     layout.endUpdate();
29332     return layout;
29333 };
29334
29335 // private
29336 Roo.BorderLayout.RegionFactory = {
29337     // private
29338     validRegions : ["north","south","east","west","center"],
29339
29340     // private
29341     create : function(target, mgr, config){
29342         target = target.toLowerCase();
29343         if(config.lightweight || config.basic){
29344             return new Roo.BasicLayoutRegion(mgr, config, target);
29345         }
29346         switch(target){
29347             case "north":
29348                 return new Roo.NorthLayoutRegion(mgr, config);
29349             case "south":
29350                 return new Roo.SouthLayoutRegion(mgr, config);
29351             case "east":
29352                 return new Roo.EastLayoutRegion(mgr, config);
29353             case "west":
29354                 return new Roo.WestLayoutRegion(mgr, config);
29355             case "center":
29356                 return new Roo.CenterLayoutRegion(mgr, config);
29357         }
29358         throw 'Layout region "'+target+'" not supported.';
29359     }
29360 };/*
29361  * Based on:
29362  * Ext JS Library 1.1.1
29363  * Copyright(c) 2006-2007, Ext JS, LLC.
29364  *
29365  * Originally Released Under LGPL - original licence link has changed is not relivant.
29366  *
29367  * Fork - LGPL
29368  * <script type="text/javascript">
29369  */
29370  
29371 /**
29372  * @class Roo.BasicLayoutRegion
29373  * @extends Roo.util.Observable
29374  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
29375  * and does not have a titlebar, tabs or any other features. All it does is size and position 
29376  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
29377  */
29378 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
29379     this.mgr = mgr;
29380     this.position  = pos;
29381     this.events = {
29382         /**
29383          * @scope Roo.BasicLayoutRegion
29384          */
29385         
29386         /**
29387          * @event beforeremove
29388          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
29389          * @param {Roo.LayoutRegion} this
29390          * @param {Roo.ContentPanel} panel The panel
29391          * @param {Object} e The cancel event object
29392          */
29393         "beforeremove" : true,
29394         /**
29395          * @event invalidated
29396          * Fires when the layout for this region is changed.
29397          * @param {Roo.LayoutRegion} this
29398          */
29399         "invalidated" : true,
29400         /**
29401          * @event visibilitychange
29402          * Fires when this region is shown or hidden 
29403          * @param {Roo.LayoutRegion} this
29404          * @param {Boolean} visibility true or false
29405          */
29406         "visibilitychange" : true,
29407         /**
29408          * @event paneladded
29409          * Fires when a panel is added. 
29410          * @param {Roo.LayoutRegion} this
29411          * @param {Roo.ContentPanel} panel The panel
29412          */
29413         "paneladded" : true,
29414         /**
29415          * @event panelremoved
29416          * Fires when a panel is removed. 
29417          * @param {Roo.LayoutRegion} this
29418          * @param {Roo.ContentPanel} panel The panel
29419          */
29420         "panelremoved" : true,
29421         /**
29422          * @event collapsed
29423          * Fires when this region is collapsed.
29424          * @param {Roo.LayoutRegion} this
29425          */
29426         "collapsed" : true,
29427         /**
29428          * @event expanded
29429          * Fires when this region is expanded.
29430          * @param {Roo.LayoutRegion} this
29431          */
29432         "expanded" : true,
29433         /**
29434          * @event slideshow
29435          * Fires when this region is slid into view.
29436          * @param {Roo.LayoutRegion} this
29437          */
29438         "slideshow" : true,
29439         /**
29440          * @event slidehide
29441          * Fires when this region slides out of view. 
29442          * @param {Roo.LayoutRegion} this
29443          */
29444         "slidehide" : true,
29445         /**
29446          * @event panelactivated
29447          * Fires when a panel is activated. 
29448          * @param {Roo.LayoutRegion} this
29449          * @param {Roo.ContentPanel} panel The activated panel
29450          */
29451         "panelactivated" : true,
29452         /**
29453          * @event resized
29454          * Fires when the user resizes this region. 
29455          * @param {Roo.LayoutRegion} this
29456          * @param {Number} newSize The new size (width for east/west, height for north/south)
29457          */
29458         "resized" : true
29459     };
29460     /** A collection of panels in this region. @type Roo.util.MixedCollection */
29461     this.panels = new Roo.util.MixedCollection();
29462     this.panels.getKey = this.getPanelId.createDelegate(this);
29463     this.box = null;
29464     this.activePanel = null;
29465     // ensure listeners are added...
29466     
29467     if (config.listeners || config.events) {
29468         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
29469             listeners : config.listeners || {},
29470             events : config.events || {}
29471         });
29472     }
29473     
29474     if(skipConfig !== true){
29475         this.applyConfig(config);
29476     }
29477 };
29478
29479 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
29480     getPanelId : function(p){
29481         return p.getId();
29482     },
29483     
29484     applyConfig : function(config){
29485         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
29486         this.config = config;
29487         
29488     },
29489     
29490     /**
29491      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
29492      * the width, for horizontal (north, south) the height.
29493      * @param {Number} newSize The new width or height
29494      */
29495     resizeTo : function(newSize){
29496         var el = this.el ? this.el :
29497                  (this.activePanel ? this.activePanel.getEl() : null);
29498         if(el){
29499             switch(this.position){
29500                 case "east":
29501                 case "west":
29502                     el.setWidth(newSize);
29503                     this.fireEvent("resized", this, newSize);
29504                 break;
29505                 case "north":
29506                 case "south":
29507                     el.setHeight(newSize);
29508                     this.fireEvent("resized", this, newSize);
29509                 break;                
29510             }
29511         }
29512     },
29513     
29514     getBox : function(){
29515         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
29516     },
29517     
29518     getMargins : function(){
29519         return this.margins;
29520     },
29521     
29522     updateBox : function(box){
29523         this.box = box;
29524         var el = this.activePanel.getEl();
29525         el.dom.style.left = box.x + "px";
29526         el.dom.style.top = box.y + "px";
29527         this.activePanel.setSize(box.width, box.height);
29528     },
29529     
29530     /**
29531      * Returns the container element for this region.
29532      * @return {Roo.Element}
29533      */
29534     getEl : function(){
29535         return this.activePanel;
29536     },
29537     
29538     /**
29539      * Returns true if this region is currently visible.
29540      * @return {Boolean}
29541      */
29542     isVisible : function(){
29543         return this.activePanel ? true : false;
29544     },
29545     
29546     setActivePanel : function(panel){
29547         panel = this.getPanel(panel);
29548         if(this.activePanel && this.activePanel != panel){
29549             this.activePanel.setActiveState(false);
29550             this.activePanel.getEl().setLeftTop(-10000,-10000);
29551         }
29552         this.activePanel = panel;
29553         panel.setActiveState(true);
29554         if(this.box){
29555             panel.setSize(this.box.width, this.box.height);
29556         }
29557         this.fireEvent("panelactivated", this, panel);
29558         this.fireEvent("invalidated");
29559     },
29560     
29561     /**
29562      * Show the specified panel.
29563      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
29564      * @return {Roo.ContentPanel} The shown panel or null
29565      */
29566     showPanel : function(panel){
29567         if(panel = this.getPanel(panel)){
29568             this.setActivePanel(panel);
29569         }
29570         return panel;
29571     },
29572     
29573     /**
29574      * Get the active panel for this region.
29575      * @return {Roo.ContentPanel} The active panel or null
29576      */
29577     getActivePanel : function(){
29578         return this.activePanel;
29579     },
29580     
29581     /**
29582      * Add the passed ContentPanel(s)
29583      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
29584      * @return {Roo.ContentPanel} The panel added (if only one was added)
29585      */
29586     add : function(panel){
29587         if(arguments.length > 1){
29588             for(var i = 0, len = arguments.length; i < len; i++) {
29589                 this.add(arguments[i]);
29590             }
29591             return null;
29592         }
29593         if(this.hasPanel(panel)){
29594             this.showPanel(panel);
29595             return panel;
29596         }
29597         var el = panel.getEl();
29598         if(el.dom.parentNode != this.mgr.el.dom){
29599             this.mgr.el.dom.appendChild(el.dom);
29600         }
29601         if(panel.setRegion){
29602             panel.setRegion(this);
29603         }
29604         this.panels.add(panel);
29605         el.setStyle("position", "absolute");
29606         if(!panel.background){
29607             this.setActivePanel(panel);
29608             if(this.config.initialSize && this.panels.getCount()==1){
29609                 this.resizeTo(this.config.initialSize);
29610             }
29611         }
29612         this.fireEvent("paneladded", this, panel);
29613         return panel;
29614     },
29615     
29616     /**
29617      * Returns true if the panel is in this region.
29618      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29619      * @return {Boolean}
29620      */
29621     hasPanel : function(panel){
29622         if(typeof panel == "object"){ // must be panel obj
29623             panel = panel.getId();
29624         }
29625         return this.getPanel(panel) ? true : false;
29626     },
29627     
29628     /**
29629      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
29630      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29631      * @param {Boolean} preservePanel Overrides the config preservePanel option
29632      * @return {Roo.ContentPanel} The panel that was removed
29633      */
29634     remove : function(panel, preservePanel){
29635         panel = this.getPanel(panel);
29636         if(!panel){
29637             return null;
29638         }
29639         var e = {};
29640         this.fireEvent("beforeremove", this, panel, e);
29641         if(e.cancel === true){
29642             return null;
29643         }
29644         var panelId = panel.getId();
29645         this.panels.removeKey(panelId);
29646         return panel;
29647     },
29648     
29649     /**
29650      * Returns the panel specified or null if it's not in this region.
29651      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29652      * @return {Roo.ContentPanel}
29653      */
29654     getPanel : function(id){
29655         if(typeof id == "object"){ // must be panel obj
29656             return id;
29657         }
29658         return this.panels.get(id);
29659     },
29660     
29661     /**
29662      * Returns this regions position (north/south/east/west/center).
29663      * @return {String} 
29664      */
29665     getPosition: function(){
29666         return this.position;    
29667     }
29668 });/*
29669  * Based on:
29670  * Ext JS Library 1.1.1
29671  * Copyright(c) 2006-2007, Ext JS, LLC.
29672  *
29673  * Originally Released Under LGPL - original licence link has changed is not relivant.
29674  *
29675  * Fork - LGPL
29676  * <script type="text/javascript">
29677  */
29678  
29679 /**
29680  * @class Roo.LayoutRegion
29681  * @extends Roo.BasicLayoutRegion
29682  * This class represents a region in a layout manager.
29683  * @cfg {Boolean} collapsible False to disable collapsing (defaults to true)
29684  * @cfg {Boolean} collapsed True to set the initial display to collapsed (defaults to false)
29685  * @cfg {Boolean} floatable False to disable floating (defaults to true)
29686  * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
29687  * @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})
29688  * @cfg {String} tabPosition "top" or "bottom" (defaults to "bottom")
29689  * @cfg {String} collapsedTitle Optional string message to display in the collapsed block of a north or south region
29690  * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
29691  * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
29692  * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
29693  * @cfg {String} title The title for the region (overrides panel titles)
29694  * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
29695  * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
29696  * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
29697  * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
29698  * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
29699  * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
29700  * the space available, similar to FireFox 1.5 tabs (defaults to false)
29701  * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
29702  * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
29703  * @cfg {Boolean} showPin True to show a pin button
29704 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
29705 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
29706 * @cfg {Boolean} disableTabTips True to disable tab tooltips
29707 * @cfg {Number} width  For East/West panels
29708 * @cfg {Number} height For North/South panels
29709 * @cfg {Boolean} split To show the splitter
29710  */
29711 Roo.LayoutRegion = function(mgr, config, pos){
29712     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
29713     var dh = Roo.DomHelper;
29714     /** This region's container element 
29715     * @type Roo.Element */
29716     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
29717     /** This region's title element 
29718     * @type Roo.Element */
29719
29720     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
29721         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
29722         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
29723     ]}, true);
29724     this.titleEl.enableDisplayMode();
29725     /** This region's title text element 
29726     * @type HTMLElement */
29727     this.titleTextEl = this.titleEl.dom.firstChild;
29728     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
29729     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
29730     this.closeBtn.enableDisplayMode();
29731     this.closeBtn.on("click", this.closeClicked, this);
29732     this.closeBtn.hide();
29733
29734     this.createBody(config);
29735     this.visible = true;
29736     this.collapsed = false;
29737
29738     if(config.hideWhenEmpty){
29739         this.hide();
29740         this.on("paneladded", this.validateVisibility, this);
29741         this.on("panelremoved", this.validateVisibility, this);
29742     }
29743     this.applyConfig(config);
29744 };
29745
29746 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
29747
29748     createBody : function(){
29749         /** This region's body element 
29750         * @type Roo.Element */
29751         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
29752     },
29753
29754     applyConfig : function(c){
29755         if(c.collapsible && this.position != "center" && !this.collapsedEl){
29756             var dh = Roo.DomHelper;
29757             if(c.titlebar !== false){
29758                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
29759                 this.collapseBtn.on("click", this.collapse, this);
29760                 this.collapseBtn.enableDisplayMode();
29761
29762                 if(c.showPin === true || this.showPin){
29763                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
29764                     this.stickBtn.enableDisplayMode();
29765                     this.stickBtn.on("click", this.expand, this);
29766                     this.stickBtn.hide();
29767                 }
29768             }
29769             /** This region's collapsed element
29770             * @type Roo.Element */
29771             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
29772                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
29773             ]}, true);
29774             if(c.floatable !== false){
29775                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
29776                this.collapsedEl.on("click", this.collapseClick, this);
29777             }
29778
29779             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
29780                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
29781                    id: "message", unselectable: "on", style:{"float":"left"}});
29782                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
29783              }
29784             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
29785             this.expandBtn.on("click", this.expand, this);
29786         }
29787         if(this.collapseBtn){
29788             this.collapseBtn.setVisible(c.collapsible == true);
29789         }
29790         this.cmargins = c.cmargins || this.cmargins ||
29791                          (this.position == "west" || this.position == "east" ?
29792                              {top: 0, left: 2, right:2, bottom: 0} :
29793                              {top: 2, left: 0, right:0, bottom: 2});
29794         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
29795         this.bottomTabs = c.tabPosition != "top";
29796         this.autoScroll = c.autoScroll || false;
29797         if(this.autoScroll){
29798             this.bodyEl.setStyle("overflow", "auto");
29799         }else{
29800             this.bodyEl.setStyle("overflow", "hidden");
29801         }
29802         //if(c.titlebar !== false){
29803             if((!c.titlebar && !c.title) || c.titlebar === false){
29804                 this.titleEl.hide();
29805             }else{
29806                 this.titleEl.show();
29807                 if(c.title){
29808                     this.titleTextEl.innerHTML = c.title;
29809                 }
29810             }
29811         //}
29812         this.duration = c.duration || .30;
29813         this.slideDuration = c.slideDuration || .45;
29814         this.config = c;
29815         if(c.collapsed){
29816             this.collapse(true);
29817         }
29818         if(c.hidden){
29819             this.hide();
29820         }
29821     },
29822     /**
29823      * Returns true if this region is currently visible.
29824      * @return {Boolean}
29825      */
29826     isVisible : function(){
29827         return this.visible;
29828     },
29829
29830     /**
29831      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
29832      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
29833      */
29834     setCollapsedTitle : function(title){
29835         title = title || "&#160;";
29836         if(this.collapsedTitleTextEl){
29837             this.collapsedTitleTextEl.innerHTML = title;
29838         }
29839     },
29840
29841     getBox : function(){
29842         var b;
29843         if(!this.collapsed){
29844             b = this.el.getBox(false, true);
29845         }else{
29846             b = this.collapsedEl.getBox(false, true);
29847         }
29848         return b;
29849     },
29850
29851     getMargins : function(){
29852         return this.collapsed ? this.cmargins : this.margins;
29853     },
29854
29855     highlight : function(){
29856         this.el.addClass("x-layout-panel-dragover");
29857     },
29858
29859     unhighlight : function(){
29860         this.el.removeClass("x-layout-panel-dragover");
29861     },
29862
29863     updateBox : function(box){
29864         this.box = box;
29865         if(!this.collapsed){
29866             this.el.dom.style.left = box.x + "px";
29867             this.el.dom.style.top = box.y + "px";
29868             this.updateBody(box.width, box.height);
29869         }else{
29870             this.collapsedEl.dom.style.left = box.x + "px";
29871             this.collapsedEl.dom.style.top = box.y + "px";
29872             this.collapsedEl.setSize(box.width, box.height);
29873         }
29874         if(this.tabs){
29875             this.tabs.autoSizeTabs();
29876         }
29877     },
29878
29879     updateBody : function(w, h){
29880         if(w !== null){
29881             this.el.setWidth(w);
29882             w -= this.el.getBorderWidth("rl");
29883             if(this.config.adjustments){
29884                 w += this.config.adjustments[0];
29885             }
29886         }
29887         if(h !== null){
29888             this.el.setHeight(h);
29889             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
29890             h -= this.el.getBorderWidth("tb");
29891             if(this.config.adjustments){
29892                 h += this.config.adjustments[1];
29893             }
29894             this.bodyEl.setHeight(h);
29895             if(this.tabs){
29896                 h = this.tabs.syncHeight(h);
29897             }
29898         }
29899         if(this.panelSize){
29900             w = w !== null ? w : this.panelSize.width;
29901             h = h !== null ? h : this.panelSize.height;
29902         }
29903         if(this.activePanel){
29904             var el = this.activePanel.getEl();
29905             w = w !== null ? w : el.getWidth();
29906             h = h !== null ? h : el.getHeight();
29907             this.panelSize = {width: w, height: h};
29908             this.activePanel.setSize(w, h);
29909         }
29910         if(Roo.isIE && this.tabs){
29911             this.tabs.el.repaint();
29912         }
29913     },
29914
29915     /**
29916      * Returns the container element for this region.
29917      * @return {Roo.Element}
29918      */
29919     getEl : function(){
29920         return this.el;
29921     },
29922
29923     /**
29924      * Hides this region.
29925      */
29926     hide : function(){
29927         if(!this.collapsed){
29928             this.el.dom.style.left = "-2000px";
29929             this.el.hide();
29930         }else{
29931             this.collapsedEl.dom.style.left = "-2000px";
29932             this.collapsedEl.hide();
29933         }
29934         this.visible = false;
29935         this.fireEvent("visibilitychange", this, false);
29936     },
29937
29938     /**
29939      * Shows this region if it was previously hidden.
29940      */
29941     show : function(){
29942         if(!this.collapsed){
29943             this.el.show();
29944         }else{
29945             this.collapsedEl.show();
29946         }
29947         this.visible = true;
29948         this.fireEvent("visibilitychange", this, true);
29949     },
29950
29951     closeClicked : function(){
29952         if(this.activePanel){
29953             this.remove(this.activePanel);
29954         }
29955     },
29956
29957     collapseClick : function(e){
29958         if(this.isSlid){
29959            e.stopPropagation();
29960            this.slideIn();
29961         }else{
29962            e.stopPropagation();
29963            this.slideOut();
29964         }
29965     },
29966
29967     /**
29968      * Collapses this region.
29969      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
29970      */
29971     collapse : function(skipAnim){
29972         if(this.collapsed) return;
29973         this.collapsed = true;
29974         if(this.split){
29975             this.split.el.hide();
29976         }
29977         if(this.config.animate && skipAnim !== true){
29978             this.fireEvent("invalidated", this);
29979             this.animateCollapse();
29980         }else{
29981             this.el.setLocation(-20000,-20000);
29982             this.el.hide();
29983             this.collapsedEl.show();
29984             this.fireEvent("collapsed", this);
29985             this.fireEvent("invalidated", this);
29986         }
29987     },
29988
29989     animateCollapse : function(){
29990         // overridden
29991     },
29992
29993     /**
29994      * Expands this region if it was previously collapsed.
29995      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
29996      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
29997      */
29998     expand : function(e, skipAnim){
29999         if(e) e.stopPropagation();
30000         if(!this.collapsed || this.el.hasActiveFx()) return;
30001         if(this.isSlid){
30002             this.afterSlideIn();
30003             skipAnim = true;
30004         }
30005         this.collapsed = false;
30006         if(this.config.animate && skipAnim !== true){
30007             this.animateExpand();
30008         }else{
30009             this.el.show();
30010             if(this.split){
30011                 this.split.el.show();
30012             }
30013             this.collapsedEl.setLocation(-2000,-2000);
30014             this.collapsedEl.hide();
30015             this.fireEvent("invalidated", this);
30016             this.fireEvent("expanded", this);
30017         }
30018     },
30019
30020     animateExpand : function(){
30021         // overridden
30022     },
30023
30024     initTabs : function(){
30025         this.bodyEl.setStyle("overflow", "hidden");
30026         var ts = new Roo.TabPanel(this.bodyEl.dom, {
30027             tabPosition: this.bottomTabs ? 'bottom' : 'top',
30028             disableTooltips: this.config.disableTabTips
30029         });
30030         if(this.config.hideTabs){
30031             ts.stripWrap.setDisplayed(false);
30032         }
30033         this.tabs = ts;
30034         ts.resizeTabs = this.config.resizeTabs === true;
30035         ts.minTabWidth = this.config.minTabWidth || 40;
30036         ts.maxTabWidth = this.config.maxTabWidth || 250;
30037         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
30038         ts.monitorResize = false;
30039         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30040         ts.bodyEl.addClass('x-layout-tabs-body');
30041         this.panels.each(this.initPanelAsTab, this);
30042     },
30043
30044     initPanelAsTab : function(panel){
30045         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
30046                     this.config.closeOnTab && panel.isClosable());
30047         if(panel.tabTip !== undefined){
30048             ti.setTooltip(panel.tabTip);
30049         }
30050         ti.on("activate", function(){
30051               this.setActivePanel(panel);
30052         }, this);
30053         if(this.config.closeOnTab){
30054             ti.on("beforeclose", function(t, e){
30055                 e.cancel = true;
30056                 this.remove(panel);
30057             }, this);
30058         }
30059         return ti;
30060     },
30061
30062     updatePanelTitle : function(panel, title){
30063         if(this.activePanel == panel){
30064             this.updateTitle(title);
30065         }
30066         if(this.tabs){
30067             var ti = this.tabs.getTab(panel.getEl().id);
30068             ti.setText(title);
30069             if(panel.tabTip !== undefined){
30070                 ti.setTooltip(panel.tabTip);
30071             }
30072         }
30073     },
30074
30075     updateTitle : function(title){
30076         if(this.titleTextEl && !this.config.title){
30077             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
30078         }
30079     },
30080
30081     setActivePanel : function(panel){
30082         panel = this.getPanel(panel);
30083         if(this.activePanel && this.activePanel != panel){
30084             this.activePanel.setActiveState(false);
30085         }
30086         this.activePanel = panel;
30087         panel.setActiveState(true);
30088         if(this.panelSize){
30089             panel.setSize(this.panelSize.width, this.panelSize.height);
30090         }
30091         if(this.closeBtn){
30092             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
30093         }
30094         this.updateTitle(panel.getTitle());
30095         if(this.tabs){
30096             this.fireEvent("invalidated", this);
30097         }
30098         this.fireEvent("panelactivated", this, panel);
30099     },
30100
30101     /**
30102      * Shows the specified panel.
30103      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
30104      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
30105      */
30106     showPanel : function(panel){
30107         if(panel = this.getPanel(panel)){
30108             if(this.tabs){
30109                 var tab = this.tabs.getTab(panel.getEl().id);
30110                 if(tab.isHidden()){
30111                     this.tabs.unhideTab(tab.id);
30112                 }
30113                 tab.activate();
30114             }else{
30115                 this.setActivePanel(panel);
30116             }
30117         }
30118         return panel;
30119     },
30120
30121     /**
30122      * Get the active panel for this region.
30123      * @return {Roo.ContentPanel} The active panel or null
30124      */
30125     getActivePanel : function(){
30126         return this.activePanel;
30127     },
30128
30129     validateVisibility : function(){
30130         if(this.panels.getCount() < 1){
30131             this.updateTitle("&#160;");
30132             this.closeBtn.hide();
30133             this.hide();
30134         }else{
30135             if(!this.isVisible()){
30136                 this.show();
30137             }
30138         }
30139     },
30140
30141     /**
30142      * Adds the passed ContentPanel(s) to this region.
30143      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
30144      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
30145      */
30146     add : function(panel){
30147         if(arguments.length > 1){
30148             for(var i = 0, len = arguments.length; i < len; i++) {
30149                 this.add(arguments[i]);
30150             }
30151             return null;
30152         }
30153         if(this.hasPanel(panel)){
30154             this.showPanel(panel);
30155             return panel;
30156         }
30157         panel.setRegion(this);
30158         this.panels.add(panel);
30159         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
30160             this.bodyEl.dom.appendChild(panel.getEl().dom);
30161             if(panel.background !== true){
30162                 this.setActivePanel(panel);
30163             }
30164             this.fireEvent("paneladded", this, panel);
30165             return panel;
30166         }
30167         if(!this.tabs){
30168             this.initTabs();
30169         }else{
30170             this.initPanelAsTab(panel);
30171         }
30172         if(panel.background !== true){
30173             this.tabs.activate(panel.getEl().id);
30174         }
30175         this.fireEvent("paneladded", this, panel);
30176         return panel;
30177     },
30178
30179     /**
30180      * Hides the tab for the specified panel.
30181      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30182      */
30183     hidePanel : function(panel){
30184         if(this.tabs && (panel = this.getPanel(panel))){
30185             this.tabs.hideTab(panel.getEl().id);
30186         }
30187     },
30188
30189     /**
30190      * Unhides the tab for a previously hidden panel.
30191      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30192      */
30193     unhidePanel : function(panel){
30194         if(this.tabs && (panel = this.getPanel(panel))){
30195             this.tabs.unhideTab(panel.getEl().id);
30196         }
30197     },
30198
30199     clearPanels : function(){
30200         while(this.panels.getCount() > 0){
30201              this.remove(this.panels.first());
30202         }
30203     },
30204
30205     /**
30206      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
30207      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30208      * @param {Boolean} preservePanel Overrides the config preservePanel option
30209      * @return {Roo.ContentPanel} The panel that was removed
30210      */
30211     remove : function(panel, preservePanel){
30212         panel = this.getPanel(panel);
30213         if(!panel){
30214             return null;
30215         }
30216         var e = {};
30217         this.fireEvent("beforeremove", this, panel, e);
30218         if(e.cancel === true){
30219             return null;
30220         }
30221         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
30222         var panelId = panel.getId();
30223         this.panels.removeKey(panelId);
30224         if(preservePanel){
30225             document.body.appendChild(panel.getEl().dom);
30226         }
30227         if(this.tabs){
30228             this.tabs.removeTab(panel.getEl().id);
30229         }else if (!preservePanel){
30230             this.bodyEl.dom.removeChild(panel.getEl().dom);
30231         }
30232         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
30233             var p = this.panels.first();
30234             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
30235             tempEl.appendChild(p.getEl().dom);
30236             this.bodyEl.update("");
30237             this.bodyEl.dom.appendChild(p.getEl().dom);
30238             tempEl = null;
30239             this.updateTitle(p.getTitle());
30240             this.tabs = null;
30241             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30242             this.setActivePanel(p);
30243         }
30244         panel.setRegion(null);
30245         if(this.activePanel == panel){
30246             this.activePanel = null;
30247         }
30248         if(this.config.autoDestroy !== false && preservePanel !== true){
30249             try{panel.destroy();}catch(e){}
30250         }
30251         this.fireEvent("panelremoved", this, panel);
30252         return panel;
30253     },
30254
30255     /**
30256      * Returns the TabPanel component used by this region
30257      * @return {Roo.TabPanel}
30258      */
30259     getTabs : function(){
30260         return this.tabs;
30261     },
30262
30263     createTool : function(parentEl, className){
30264         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
30265             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
30266         btn.addClassOnOver("x-layout-tools-button-over");
30267         return btn;
30268     }
30269 });/*
30270  * Based on:
30271  * Ext JS Library 1.1.1
30272  * Copyright(c) 2006-2007, Ext JS, LLC.
30273  *
30274  * Originally Released Under LGPL - original licence link has changed is not relivant.
30275  *
30276  * Fork - LGPL
30277  * <script type="text/javascript">
30278  */
30279  
30280
30281
30282 /**
30283  * @class Roo.SplitLayoutRegion
30284  * @extends Roo.LayoutRegion
30285  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
30286  */
30287 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
30288     this.cursor = cursor;
30289     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
30290 };
30291
30292 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
30293     splitTip : "Drag to resize.",
30294     collapsibleSplitTip : "Drag to resize. Double click to hide.",
30295     useSplitTips : false,
30296
30297     applyConfig : function(config){
30298         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
30299         if(config.split){
30300             if(!this.split){
30301                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
30302                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
30303                 /** The SplitBar for this region 
30304                 * @type Roo.SplitBar */
30305                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
30306                 this.split.on("moved", this.onSplitMove, this);
30307                 this.split.useShim = config.useShim === true;
30308                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
30309                 if(this.useSplitTips){
30310                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
30311                 }
30312                 if(config.collapsible){
30313                     this.split.el.on("dblclick", this.collapse,  this);
30314                 }
30315             }
30316             if(typeof config.minSize != "undefined"){
30317                 this.split.minSize = config.minSize;
30318             }
30319             if(typeof config.maxSize != "undefined"){
30320                 this.split.maxSize = config.maxSize;
30321             }
30322             if(config.hideWhenEmpty || config.hidden || config.collapsed){
30323                 this.hideSplitter();
30324             }
30325         }
30326     },
30327
30328     getHMaxSize : function(){
30329          var cmax = this.config.maxSize || 10000;
30330          var center = this.mgr.getRegion("center");
30331          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
30332     },
30333
30334     getVMaxSize : function(){
30335          var cmax = this.config.maxSize || 10000;
30336          var center = this.mgr.getRegion("center");
30337          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
30338     },
30339
30340     onSplitMove : function(split, newSize){
30341         this.fireEvent("resized", this, newSize);
30342     },
30343     
30344     /** 
30345      * Returns the {@link Roo.SplitBar} for this region.
30346      * @return {Roo.SplitBar}
30347      */
30348     getSplitBar : function(){
30349         return this.split;
30350     },
30351     
30352     hide : function(){
30353         this.hideSplitter();
30354         Roo.SplitLayoutRegion.superclass.hide.call(this);
30355     },
30356
30357     hideSplitter : function(){
30358         if(this.split){
30359             this.split.el.setLocation(-2000,-2000);
30360             this.split.el.hide();
30361         }
30362     },
30363
30364     show : function(){
30365         if(this.split){
30366             this.split.el.show();
30367         }
30368         Roo.SplitLayoutRegion.superclass.show.call(this);
30369     },
30370     
30371     beforeSlide: function(){
30372         if(Roo.isGecko){// firefox overflow auto bug workaround
30373             this.bodyEl.clip();
30374             if(this.tabs) this.tabs.bodyEl.clip();
30375             if(this.activePanel){
30376                 this.activePanel.getEl().clip();
30377                 
30378                 if(this.activePanel.beforeSlide){
30379                     this.activePanel.beforeSlide();
30380                 }
30381             }
30382         }
30383     },
30384     
30385     afterSlide : function(){
30386         if(Roo.isGecko){// firefox overflow auto bug workaround
30387             this.bodyEl.unclip();
30388             if(this.tabs) this.tabs.bodyEl.unclip();
30389             if(this.activePanel){
30390                 this.activePanel.getEl().unclip();
30391                 if(this.activePanel.afterSlide){
30392                     this.activePanel.afterSlide();
30393                 }
30394             }
30395         }
30396     },
30397
30398     initAutoHide : function(){
30399         if(this.autoHide !== false){
30400             if(!this.autoHideHd){
30401                 var st = new Roo.util.DelayedTask(this.slideIn, this);
30402                 this.autoHideHd = {
30403                     "mouseout": function(e){
30404                         if(!e.within(this.el, true)){
30405                             st.delay(500);
30406                         }
30407                     },
30408                     "mouseover" : function(e){
30409                         st.cancel();
30410                     },
30411                     scope : this
30412                 };
30413             }
30414             this.el.on(this.autoHideHd);
30415         }
30416     },
30417
30418     clearAutoHide : function(){
30419         if(this.autoHide !== false){
30420             this.el.un("mouseout", this.autoHideHd.mouseout);
30421             this.el.un("mouseover", this.autoHideHd.mouseover);
30422         }
30423     },
30424
30425     clearMonitor : function(){
30426         Roo.get(document).un("click", this.slideInIf, this);
30427     },
30428
30429     // these names are backwards but not changed for compat
30430     slideOut : function(){
30431         if(this.isSlid || this.el.hasActiveFx()){
30432             return;
30433         }
30434         this.isSlid = true;
30435         if(this.collapseBtn){
30436             this.collapseBtn.hide();
30437         }
30438         this.closeBtnState = this.closeBtn.getStyle('display');
30439         this.closeBtn.hide();
30440         if(this.stickBtn){
30441             this.stickBtn.show();
30442         }
30443         this.el.show();
30444         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
30445         this.beforeSlide();
30446         this.el.setStyle("z-index", 10001);
30447         this.el.slideIn(this.getSlideAnchor(), {
30448             callback: function(){
30449                 this.afterSlide();
30450                 this.initAutoHide();
30451                 Roo.get(document).on("click", this.slideInIf, this);
30452                 this.fireEvent("slideshow", this);
30453             },
30454             scope: this,
30455             block: true
30456         });
30457     },
30458
30459     afterSlideIn : function(){
30460         this.clearAutoHide();
30461         this.isSlid = false;
30462         this.clearMonitor();
30463         this.el.setStyle("z-index", "");
30464         if(this.collapseBtn){
30465             this.collapseBtn.show();
30466         }
30467         this.closeBtn.setStyle('display', this.closeBtnState);
30468         if(this.stickBtn){
30469             this.stickBtn.hide();
30470         }
30471         this.fireEvent("slidehide", this);
30472     },
30473
30474     slideIn : function(cb){
30475         if(!this.isSlid || this.el.hasActiveFx()){
30476             Roo.callback(cb);
30477             return;
30478         }
30479         this.isSlid = false;
30480         this.beforeSlide();
30481         this.el.slideOut(this.getSlideAnchor(), {
30482             callback: function(){
30483                 this.el.setLeftTop(-10000, -10000);
30484                 this.afterSlide();
30485                 this.afterSlideIn();
30486                 Roo.callback(cb);
30487             },
30488             scope: this,
30489             block: true
30490         });
30491     },
30492     
30493     slideInIf : function(e){
30494         if(!e.within(this.el)){
30495             this.slideIn();
30496         }
30497     },
30498
30499     animateCollapse : function(){
30500         this.beforeSlide();
30501         this.el.setStyle("z-index", 20000);
30502         var anchor = this.getSlideAnchor();
30503         this.el.slideOut(anchor, {
30504             callback : function(){
30505                 this.el.setStyle("z-index", "");
30506                 this.collapsedEl.slideIn(anchor, {duration:.3});
30507                 this.afterSlide();
30508                 this.el.setLocation(-10000,-10000);
30509                 this.el.hide();
30510                 this.fireEvent("collapsed", this);
30511             },
30512             scope: this,
30513             block: true
30514         });
30515     },
30516
30517     animateExpand : function(){
30518         this.beforeSlide();
30519         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
30520         this.el.setStyle("z-index", 20000);
30521         this.collapsedEl.hide({
30522             duration:.1
30523         });
30524         this.el.slideIn(this.getSlideAnchor(), {
30525             callback : function(){
30526                 this.el.setStyle("z-index", "");
30527                 this.afterSlide();
30528                 if(this.split){
30529                     this.split.el.show();
30530                 }
30531                 this.fireEvent("invalidated", this);
30532                 this.fireEvent("expanded", this);
30533             },
30534             scope: this,
30535             block: true
30536         });
30537     },
30538
30539     anchors : {
30540         "west" : "left",
30541         "east" : "right",
30542         "north" : "top",
30543         "south" : "bottom"
30544     },
30545
30546     sanchors : {
30547         "west" : "l",
30548         "east" : "r",
30549         "north" : "t",
30550         "south" : "b"
30551     },
30552
30553     canchors : {
30554         "west" : "tl-tr",
30555         "east" : "tr-tl",
30556         "north" : "tl-bl",
30557         "south" : "bl-tl"
30558     },
30559
30560     getAnchor : function(){
30561         return this.anchors[this.position];
30562     },
30563
30564     getCollapseAnchor : function(){
30565         return this.canchors[this.position];
30566     },
30567
30568     getSlideAnchor : function(){
30569         return this.sanchors[this.position];
30570     },
30571
30572     getAlignAdj : function(){
30573         var cm = this.cmargins;
30574         switch(this.position){
30575             case "west":
30576                 return [0, 0];
30577             break;
30578             case "east":
30579                 return [0, 0];
30580             break;
30581             case "north":
30582                 return [0, 0];
30583             break;
30584             case "south":
30585                 return [0, 0];
30586             break;
30587         }
30588     },
30589
30590     getExpandAdj : function(){
30591         var c = this.collapsedEl, cm = this.cmargins;
30592         switch(this.position){
30593             case "west":
30594                 return [-(cm.right+c.getWidth()+cm.left), 0];
30595             break;
30596             case "east":
30597                 return [cm.right+c.getWidth()+cm.left, 0];
30598             break;
30599             case "north":
30600                 return [0, -(cm.top+cm.bottom+c.getHeight())];
30601             break;
30602             case "south":
30603                 return [0, cm.top+cm.bottom+c.getHeight()];
30604             break;
30605         }
30606     }
30607 });/*
30608  * Based on:
30609  * Ext JS Library 1.1.1
30610  * Copyright(c) 2006-2007, Ext JS, LLC.
30611  *
30612  * Originally Released Under LGPL - original licence link has changed is not relivant.
30613  *
30614  * Fork - LGPL
30615  * <script type="text/javascript">
30616  */
30617 /*
30618  * These classes are private internal classes
30619  */
30620 Roo.CenterLayoutRegion = function(mgr, config){
30621     Roo.LayoutRegion.call(this, mgr, config, "center");
30622     this.visible = true;
30623     this.minWidth = config.minWidth || 20;
30624     this.minHeight = config.minHeight || 20;
30625 };
30626
30627 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
30628     hide : function(){
30629         // center panel can't be hidden
30630     },
30631     
30632     show : function(){
30633         // center panel can't be hidden
30634     },
30635     
30636     getMinWidth: function(){
30637         return this.minWidth;
30638     },
30639     
30640     getMinHeight: function(){
30641         return this.minHeight;
30642     }
30643 });
30644
30645
30646 Roo.NorthLayoutRegion = function(mgr, config){
30647     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
30648     if(this.split){
30649         this.split.placement = Roo.SplitBar.TOP;
30650         this.split.orientation = Roo.SplitBar.VERTICAL;
30651         this.split.el.addClass("x-layout-split-v");
30652     }
30653     var size = config.initialSize || config.height;
30654     if(typeof size != "undefined"){
30655         this.el.setHeight(size);
30656     }
30657 };
30658 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
30659     orientation: Roo.SplitBar.VERTICAL,
30660     getBox : function(){
30661         if(this.collapsed){
30662             return this.collapsedEl.getBox();
30663         }
30664         var box = this.el.getBox();
30665         if(this.split){
30666             box.height += this.split.el.getHeight();
30667         }
30668         return box;
30669     },
30670     
30671     updateBox : function(box){
30672         if(this.split && !this.collapsed){
30673             box.height -= this.split.el.getHeight();
30674             this.split.el.setLeft(box.x);
30675             this.split.el.setTop(box.y+box.height);
30676             this.split.el.setWidth(box.width);
30677         }
30678         if(this.collapsed){
30679             this.updateBody(box.width, null);
30680         }
30681         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30682     }
30683 });
30684
30685 Roo.SouthLayoutRegion = function(mgr, config){
30686     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
30687     if(this.split){
30688         this.split.placement = Roo.SplitBar.BOTTOM;
30689         this.split.orientation = Roo.SplitBar.VERTICAL;
30690         this.split.el.addClass("x-layout-split-v");
30691     }
30692     var size = config.initialSize || config.height;
30693     if(typeof size != "undefined"){
30694         this.el.setHeight(size);
30695     }
30696 };
30697 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
30698     orientation: Roo.SplitBar.VERTICAL,
30699     getBox : function(){
30700         if(this.collapsed){
30701             return this.collapsedEl.getBox();
30702         }
30703         var box = this.el.getBox();
30704         if(this.split){
30705             var sh = this.split.el.getHeight();
30706             box.height += sh;
30707             box.y -= sh;
30708         }
30709         return box;
30710     },
30711     
30712     updateBox : function(box){
30713         if(this.split && !this.collapsed){
30714             var sh = this.split.el.getHeight();
30715             box.height -= sh;
30716             box.y += sh;
30717             this.split.el.setLeft(box.x);
30718             this.split.el.setTop(box.y-sh);
30719             this.split.el.setWidth(box.width);
30720         }
30721         if(this.collapsed){
30722             this.updateBody(box.width, null);
30723         }
30724         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30725     }
30726 });
30727
30728 Roo.EastLayoutRegion = function(mgr, config){
30729     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
30730     if(this.split){
30731         this.split.placement = Roo.SplitBar.RIGHT;
30732         this.split.orientation = Roo.SplitBar.HORIZONTAL;
30733         this.split.el.addClass("x-layout-split-h");
30734     }
30735     var size = config.initialSize || config.width;
30736     if(typeof size != "undefined"){
30737         this.el.setWidth(size);
30738     }
30739 };
30740 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
30741     orientation: Roo.SplitBar.HORIZONTAL,
30742     getBox : function(){
30743         if(this.collapsed){
30744             return this.collapsedEl.getBox();
30745         }
30746         var box = this.el.getBox();
30747         if(this.split){
30748             var sw = this.split.el.getWidth();
30749             box.width += sw;
30750             box.x -= sw;
30751         }
30752         return box;
30753     },
30754
30755     updateBox : function(box){
30756         if(this.split && !this.collapsed){
30757             var sw = this.split.el.getWidth();
30758             box.width -= sw;
30759             this.split.el.setLeft(box.x);
30760             this.split.el.setTop(box.y);
30761             this.split.el.setHeight(box.height);
30762             box.x += sw;
30763         }
30764         if(this.collapsed){
30765             this.updateBody(null, box.height);
30766         }
30767         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30768     }
30769 });
30770
30771 Roo.WestLayoutRegion = function(mgr, config){
30772     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
30773     if(this.split){
30774         this.split.placement = Roo.SplitBar.LEFT;
30775         this.split.orientation = Roo.SplitBar.HORIZONTAL;
30776         this.split.el.addClass("x-layout-split-h");
30777     }
30778     var size = config.initialSize || config.width;
30779     if(typeof size != "undefined"){
30780         this.el.setWidth(size);
30781     }
30782 };
30783 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
30784     orientation: Roo.SplitBar.HORIZONTAL,
30785     getBox : function(){
30786         if(this.collapsed){
30787             return this.collapsedEl.getBox();
30788         }
30789         var box = this.el.getBox();
30790         if(this.split){
30791             box.width += this.split.el.getWidth();
30792         }
30793         return box;
30794     },
30795     
30796     updateBox : function(box){
30797         if(this.split && !this.collapsed){
30798             var sw = this.split.el.getWidth();
30799             box.width -= sw;
30800             this.split.el.setLeft(box.x+box.width);
30801             this.split.el.setTop(box.y);
30802             this.split.el.setHeight(box.height);
30803         }
30804         if(this.collapsed){
30805             this.updateBody(null, box.height);
30806         }
30807         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30808     }
30809 });
30810 /*
30811  * Based on:
30812  * Ext JS Library 1.1.1
30813  * Copyright(c) 2006-2007, Ext JS, LLC.
30814  *
30815  * Originally Released Under LGPL - original licence link has changed is not relivant.
30816  *
30817  * Fork - LGPL
30818  * <script type="text/javascript">
30819  */
30820  
30821  
30822 /*
30823  * Private internal class for reading and applying state
30824  */
30825 Roo.LayoutStateManager = function(layout){
30826      // default empty state
30827      this.state = {
30828         north: {},
30829         south: {},
30830         east: {},
30831         west: {}       
30832     };
30833 };
30834
30835 Roo.LayoutStateManager.prototype = {
30836     init : function(layout, provider){
30837         this.provider = provider;
30838         var state = provider.get(layout.id+"-layout-state");
30839         if(state){
30840             var wasUpdating = layout.isUpdating();
30841             if(!wasUpdating){
30842                 layout.beginUpdate();
30843             }
30844             for(var key in state){
30845                 if(typeof state[key] != "function"){
30846                     var rstate = state[key];
30847                     var r = layout.getRegion(key);
30848                     if(r && rstate){
30849                         if(rstate.size){
30850                             r.resizeTo(rstate.size);
30851                         }
30852                         if(rstate.collapsed == true){
30853                             r.collapse(true);
30854                         }else{
30855                             r.expand(null, true);
30856                         }
30857                     }
30858                 }
30859             }
30860             if(!wasUpdating){
30861                 layout.endUpdate();
30862             }
30863             this.state = state; 
30864         }
30865         this.layout = layout;
30866         layout.on("regionresized", this.onRegionResized, this);
30867         layout.on("regioncollapsed", this.onRegionCollapsed, this);
30868         layout.on("regionexpanded", this.onRegionExpanded, this);
30869     },
30870     
30871     storeState : function(){
30872         this.provider.set(this.layout.id+"-layout-state", this.state);
30873     },
30874     
30875     onRegionResized : function(region, newSize){
30876         this.state[region.getPosition()].size = newSize;
30877         this.storeState();
30878     },
30879     
30880     onRegionCollapsed : function(region){
30881         this.state[region.getPosition()].collapsed = true;
30882         this.storeState();
30883     },
30884     
30885     onRegionExpanded : function(region){
30886         this.state[region.getPosition()].collapsed = false;
30887         this.storeState();
30888     }
30889 };/*
30890  * Based on:
30891  * Ext JS Library 1.1.1
30892  * Copyright(c) 2006-2007, Ext JS, LLC.
30893  *
30894  * Originally Released Under LGPL - original licence link has changed is not relivant.
30895  *
30896  * Fork - LGPL
30897  * <script type="text/javascript">
30898  */
30899 /**
30900  * @class Roo.ContentPanel
30901  * @extends Roo.util.Observable
30902  * A basic ContentPanel element.
30903  * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes  (defaults to false)
30904  * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
30905  * @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
30906  * @cfg {Boolean} closable True if the panel can be closed/removed
30907  * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
30908  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
30909  * @cfg {Toolbar} toolbar A toolbar for this panel
30910  * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
30911  * @cfg {String} title The title for this panel
30912  * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
30913  * @cfg {String} url Calls {@link #setUrl} with this value
30914  * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
30915  * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
30916  * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
30917  * @constructor
30918  * Create a new ContentPanel.
30919  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
30920  * @param {String/Object} config A string to set only the title or a config object
30921  * @param {String} content (optional) Set the HTML content for this panel
30922  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
30923  */
30924 Roo.ContentPanel = function(el, config, content){
30925     
30926      
30927     /*
30928     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
30929         config = el;
30930         el = Roo.id();
30931     }
30932     if (config && config.parentLayout) { 
30933         el = config.parentLayout.el.createChild(); 
30934     }
30935     */
30936     if(el.autoCreate){ // xtype is available if this is called from factory
30937         config = el;
30938         el = Roo.id();
30939     }
30940     this.el = Roo.get(el);
30941     if(!this.el && config && config.autoCreate){
30942         if(typeof config.autoCreate == "object"){
30943             if(!config.autoCreate.id){
30944                 config.autoCreate.id = config.id||el;
30945             }
30946             this.el = Roo.DomHelper.append(document.body,
30947                         config.autoCreate, true);
30948         }else{
30949             this.el = Roo.DomHelper.append(document.body,
30950                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
30951         }
30952     }
30953     this.closable = false;
30954     this.loaded = false;
30955     this.active = false;
30956     if(typeof config == "string"){
30957         this.title = config;
30958     }else{
30959         Roo.apply(this, config);
30960     }
30961     
30962     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
30963         this.wrapEl = this.el.wrap();    
30964         this.toolbar = new Roo.Toolbar(this.el.insertSibling(false, 'before'), [] , this.toolbar);
30965         
30966     }
30967     
30968     
30969     
30970     if(this.resizeEl){
30971         this.resizeEl = Roo.get(this.resizeEl, true);
30972     }else{
30973         this.resizeEl = this.el;
30974     }
30975     this.addEvents({
30976         /**
30977          * @event activate
30978          * Fires when this panel is activated. 
30979          * @param {Roo.ContentPanel} this
30980          */
30981         "activate" : true,
30982         /**
30983          * @event deactivate
30984          * Fires when this panel is activated. 
30985          * @param {Roo.ContentPanel} this
30986          */
30987         "deactivate" : true,
30988
30989         /**
30990          * @event resize
30991          * Fires when this panel is resized if fitToFrame is true.
30992          * @param {Roo.ContentPanel} this
30993          * @param {Number} width The width after any component adjustments
30994          * @param {Number} height The height after any component adjustments
30995          */
30996         "resize" : true
30997     });
30998     if(this.autoScroll){
30999         this.resizeEl.setStyle("overflow", "auto");
31000     } else {
31001         // fix randome scrolling
31002         this.el.on('scroll', function() {
31003             this.scrollTo('top',0); 
31004         });
31005     }
31006     content = content || this.content;
31007     if(content){
31008         this.setContent(content);
31009     }
31010     if(config && config.url){
31011         this.setUrl(this.url, this.params, this.loadOnce);
31012     }
31013     
31014     
31015     
31016     Roo.ContentPanel.superclass.constructor.call(this);
31017 };
31018
31019 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
31020     tabTip:'',
31021     setRegion : function(region){
31022         this.region = region;
31023         if(region){
31024            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
31025         }else{
31026            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
31027         } 
31028     },
31029     
31030     /**
31031      * Returns the toolbar for this Panel if one was configured. 
31032      * @return {Roo.Toolbar} 
31033      */
31034     getToolbar : function(){
31035         return this.toolbar;
31036     },
31037     
31038     setActiveState : function(active){
31039         this.active = active;
31040         if(!active){
31041             this.fireEvent("deactivate", this);
31042         }else{
31043             this.fireEvent("activate", this);
31044         }
31045     },
31046     /**
31047      * Updates this panel's element
31048      * @param {String} content The new content
31049      * @param {Boolean} loadScripts (optional) true to look for and process scripts
31050     */
31051     setContent : function(content, loadScripts){
31052         this.el.update(content, loadScripts);
31053     },
31054
31055     ignoreResize : function(w, h){
31056         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
31057             return true;
31058         }else{
31059             this.lastSize = {width: w, height: h};
31060             return false;
31061         }
31062     },
31063     /**
31064      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
31065      * @return {Roo.UpdateManager} The UpdateManager
31066      */
31067     getUpdateManager : function(){
31068         return this.el.getUpdateManager();
31069     },
31070      /**
31071      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
31072      * @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:
31073 <pre><code>
31074 panel.load({
31075     url: "your-url.php",
31076     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
31077     callback: yourFunction,
31078     scope: yourObject, //(optional scope)
31079     discardUrl: false,
31080     nocache: false,
31081     text: "Loading...",
31082     timeout: 30,
31083     scripts: false
31084 });
31085 </code></pre>
31086      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
31087      * 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.
31088      * @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}
31089      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
31090      * @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.
31091      * @return {Roo.ContentPanel} this
31092      */
31093     load : function(){
31094         var um = this.el.getUpdateManager();
31095         um.update.apply(um, arguments);
31096         return this;
31097     },
31098
31099
31100     /**
31101      * 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.
31102      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
31103      * @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)
31104      * @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)
31105      * @return {Roo.UpdateManager} The UpdateManager
31106      */
31107     setUrl : function(url, params, loadOnce){
31108         if(this.refreshDelegate){
31109             this.removeListener("activate", this.refreshDelegate);
31110         }
31111         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
31112         this.on("activate", this.refreshDelegate);
31113         return this.el.getUpdateManager();
31114     },
31115     
31116     _handleRefresh : function(url, params, loadOnce){
31117         if(!loadOnce || !this.loaded){
31118             var updater = this.el.getUpdateManager();
31119             updater.update(url, params, this._setLoaded.createDelegate(this));
31120         }
31121     },
31122     
31123     _setLoaded : function(){
31124         this.loaded = true;
31125     }, 
31126     
31127     /**
31128      * Returns this panel's id
31129      * @return {String} 
31130      */
31131     getId : function(){
31132         return this.el.id;
31133     },
31134     
31135     /** 
31136      * Returns this panel's element - used by regiosn to add.
31137      * @return {Roo.Element} 
31138      */
31139     getEl : function(){
31140         return this.wrapEl || this.el;
31141     },
31142     
31143     adjustForComponents : function(width, height){
31144         if(this.resizeEl != this.el){
31145             width -= this.el.getFrameWidth('lr');
31146             height -= this.el.getFrameWidth('tb');
31147         }
31148         if(this.toolbar){
31149             var te = this.toolbar.getEl();
31150             height -= te.getHeight();
31151             te.setWidth(width);
31152         }
31153         if(this.adjustments){
31154             width += this.adjustments[0];
31155             height += this.adjustments[1];
31156         }
31157         return {"width": width, "height": height};
31158     },
31159     
31160     setSize : function(width, height){
31161         if(this.fitToFrame && !this.ignoreResize(width, height)){
31162             if(this.fitContainer && this.resizeEl != this.el){
31163                 this.el.setSize(width, height);
31164             }
31165             var size = this.adjustForComponents(width, height);
31166             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
31167             this.fireEvent('resize', this, size.width, size.height);
31168         }
31169     },
31170     
31171     /**
31172      * Returns this panel's title
31173      * @return {String} 
31174      */
31175     getTitle : function(){
31176         return this.title;
31177     },
31178     
31179     /**
31180      * Set this panel's title
31181      * @param {String} title
31182      */
31183     setTitle : function(title){
31184         this.title = title;
31185         if(this.region){
31186             this.region.updatePanelTitle(this, title);
31187         }
31188     },
31189     
31190     /**
31191      * Returns true is this panel was configured to be closable
31192      * @return {Boolean} 
31193      */
31194     isClosable : function(){
31195         return this.closable;
31196     },
31197     
31198     beforeSlide : function(){
31199         this.el.clip();
31200         this.resizeEl.clip();
31201     },
31202     
31203     afterSlide : function(){
31204         this.el.unclip();
31205         this.resizeEl.unclip();
31206     },
31207     
31208     /**
31209      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
31210      *   Will fail silently if the {@link #setUrl} method has not been called.
31211      *   This does not activate the panel, just updates its content.
31212      */
31213     refresh : function(){
31214         if(this.refreshDelegate){
31215            this.loaded = false;
31216            this.refreshDelegate();
31217         }
31218     },
31219     
31220     /**
31221      * Destroys this panel
31222      */
31223     destroy : function(){
31224         this.el.removeAllListeners();
31225         var tempEl = document.createElement("span");
31226         tempEl.appendChild(this.el.dom);
31227         tempEl.innerHTML = "";
31228         this.el.remove();
31229         this.el = null;
31230     },
31231     
31232       /**
31233      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
31234      * <pre><code>
31235
31236 layout.addxtype({
31237        xtype : 'Form',
31238        items: [ .... ]
31239    }
31240 );
31241
31242 </code></pre>
31243      * @param {Object} cfg Xtype definition of item to add.
31244      */
31245     
31246     addxtype : function(cfg) {
31247         // add form..
31248         if (cfg.xtype.match(/^Form$/)) {
31249             var el = this.el.createChild();
31250
31251             this.form = new  Roo.form.Form(cfg);
31252             
31253             
31254             if ( this.form.allItems.length) this.form.render(el.dom);
31255             return this.form;
31256         }
31257         if (['View', 'JsonView'].indexOf(cfg.xtype) > -1) {
31258             // views..
31259             cfg.el = this.el.appendChild(document.createElement("div"));
31260             // factory?
31261             var ret = new Roo[cfg.xtype](cfg);
31262             ret.render(false, ''); // render blank..
31263             return ret;
31264             
31265         }
31266         return false;
31267         
31268     }
31269 });
31270
31271 /**
31272  * @class Roo.GridPanel
31273  * @extends Roo.ContentPanel
31274  * @constructor
31275  * Create a new GridPanel.
31276  * @param {Roo.grid.Grid} grid The grid for this panel
31277  * @param {String/Object} config A string to set only the panel's title, or a config object
31278  */
31279 Roo.GridPanel = function(grid, config){
31280     
31281   
31282     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
31283         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
31284         
31285     this.wrapper.dom.appendChild(grid.getGridEl().dom);
31286     
31287     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
31288     
31289     if(this.toolbar){
31290         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
31291     }
31292     // xtype created footer. - not sure if will work as we normally have to render first..
31293     if (this.footer && !this.footer.el && this.footer.xtype) {
31294         
31295         this.footer.container = this.grid.getView().getFooterPanel(true);
31296         this.footer.dataSource = this.grid.dataSource;
31297         this.footer = Roo.factory(this.footer, Roo);
31298         
31299     }
31300     
31301     grid.monitorWindowResize = false; // turn off autosizing
31302     grid.autoHeight = false;
31303     grid.autoWidth = false;
31304     this.grid = grid;
31305     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
31306 };
31307
31308 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
31309     getId : function(){
31310         return this.grid.id;
31311     },
31312     
31313     /**
31314      * Returns the grid for this panel
31315      * @return {Roo.grid.Grid} 
31316      */
31317     getGrid : function(){
31318         return this.grid;    
31319     },
31320     
31321     setSize : function(width, height){
31322         if(!this.ignoreResize(width, height)){
31323             var grid = this.grid;
31324             var size = this.adjustForComponents(width, height);
31325             grid.getGridEl().setSize(size.width, size.height);
31326             grid.autoSize();
31327         }
31328     },
31329     
31330     beforeSlide : function(){
31331         this.grid.getView().scroller.clip();
31332     },
31333     
31334     afterSlide : function(){
31335         this.grid.getView().scroller.unclip();
31336     },
31337     
31338     destroy : function(){
31339         this.grid.destroy();
31340         delete this.grid;
31341         Roo.GridPanel.superclass.destroy.call(this); 
31342     }
31343 });
31344
31345
31346 /**
31347  * @class Roo.NestedLayoutPanel
31348  * @extends Roo.ContentPanel
31349  * @constructor
31350  * Create a new NestedLayoutPanel.
31351  * 
31352  * 
31353  * @param {Roo.BorderLayout} layout The layout for this panel
31354  * @param {String/Object} config A string to set only the title or a config object
31355  */
31356 Roo.NestedLayoutPanel = function(layout, config)
31357 {
31358     // construct with only one argument..
31359     /* FIXME - implement nicer consturctors
31360     if (layout.layout) {
31361         config = layout;
31362         layout = config.layout;
31363         delete config.layout;
31364     }
31365     if (layout.xtype && !layout.getEl) {
31366         // then layout needs constructing..
31367         layout = Roo.factory(layout, Roo);
31368     }
31369     */
31370     
31371     
31372     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
31373     
31374     layout.monitorWindowResize = false; // turn off autosizing
31375     this.layout = layout;
31376     this.layout.getEl().addClass("x-layout-nested-layout");
31377     
31378     
31379     
31380     
31381 };
31382
31383 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
31384
31385     setSize : function(width, height){
31386         if(!this.ignoreResize(width, height)){
31387             var size = this.adjustForComponents(width, height);
31388             var el = this.layout.getEl();
31389             el.setSize(size.width, size.height);
31390             var touch = el.dom.offsetWidth;
31391             this.layout.layout();
31392             // ie requires a double layout on the first pass
31393             if(Roo.isIE && !this.initialized){
31394                 this.initialized = true;
31395                 this.layout.layout();
31396             }
31397         }
31398     },
31399     
31400     // activate all subpanels if not currently active..
31401     
31402     setActiveState : function(active){
31403         this.active = active;
31404         if(!active){
31405             this.fireEvent("deactivate", this);
31406             return;
31407         }
31408         
31409         this.fireEvent("activate", this);
31410         // not sure if this should happen before or after..
31411         if (!this.layout) {
31412             return; // should not happen..
31413         }
31414         var reg = false;
31415         for (var r in this.layout.regions) {
31416             reg = this.layout.getRegion(r);
31417             if (reg.getActivePanel()) {
31418                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
31419                 reg.setActivePanel(reg.getActivePanel());
31420                 continue;
31421             }
31422             if (!reg.panels.length) {
31423                 continue;
31424             }
31425             reg.showPanel(reg.getPanel(0));
31426         }
31427         
31428         
31429         
31430         
31431     },
31432     
31433     /**
31434      * Returns the nested BorderLayout for this panel
31435      * @return {Roo.BorderLayout} 
31436      */
31437     getLayout : function(){
31438         return this.layout;
31439     },
31440     
31441      /**
31442      * Adds a xtype elements to the layout of the nested panel
31443      * <pre><code>
31444
31445 panel.addxtype({
31446        xtype : 'ContentPanel',
31447        region: 'west',
31448        items: [ .... ]
31449    }
31450 );
31451
31452 panel.addxtype({
31453         xtype : 'NestedLayoutPanel',
31454         region: 'west',
31455         layout: {
31456            center: { },
31457            west: { }   
31458         },
31459         items : [ ... list of content panels or nested layout panels.. ]
31460    }
31461 );
31462 </code></pre>
31463      * @param {Object} cfg Xtype definition of item to add.
31464      */
31465     addxtype : function(cfg) {
31466         return this.layout.addxtype(cfg);
31467     
31468     }
31469 });
31470
31471 Roo.ScrollPanel = function(el, config, content){
31472     config = config || {};
31473     config.fitToFrame = true;
31474     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
31475     
31476     this.el.dom.style.overflow = "hidden";
31477     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
31478     this.el.removeClass("x-layout-inactive-content");
31479     this.el.on("mousewheel", this.onWheel, this);
31480
31481     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
31482     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
31483     up.unselectable(); down.unselectable();
31484     up.on("click", this.scrollUp, this);
31485     down.on("click", this.scrollDown, this);
31486     up.addClassOnOver("x-scroller-btn-over");
31487     down.addClassOnOver("x-scroller-btn-over");
31488     up.addClassOnClick("x-scroller-btn-click");
31489     down.addClassOnClick("x-scroller-btn-click");
31490     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
31491
31492     this.resizeEl = this.el;
31493     this.el = wrap; this.up = up; this.down = down;
31494 };
31495
31496 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
31497     increment : 100,
31498     wheelIncrement : 5,
31499     scrollUp : function(){
31500         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
31501     },
31502
31503     scrollDown : function(){
31504         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
31505     },
31506
31507     afterScroll : function(){
31508         var el = this.resizeEl;
31509         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
31510         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31511         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31512     },
31513
31514     setSize : function(){
31515         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
31516         this.afterScroll();
31517     },
31518
31519     onWheel : function(e){
31520         var d = e.getWheelDelta();
31521         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
31522         this.afterScroll();
31523         e.stopEvent();
31524     },
31525
31526     setContent : function(content, loadScripts){
31527         this.resizeEl.update(content, loadScripts);
31528     }
31529
31530 });
31531
31532
31533
31534
31535
31536
31537
31538
31539
31540 /**
31541  * @class Roo.TreePanel
31542  * @extends Roo.ContentPanel
31543  * @constructor
31544  * Create a new TreePanel. - defaults to fit/scoll contents.
31545  * @param {String/Object} config A string to set only the panel's title, or a config object
31546  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
31547  */
31548 Roo.TreePanel = function(config){
31549     var el = config.el;
31550     var tree = config.tree;
31551     delete config.tree; 
31552     delete config.el; // hopefull!
31553     
31554     // wrapper for IE7 strict & safari scroll issue
31555     
31556     var treeEl = el.createChild();
31557     config.resizeEl = treeEl;
31558     
31559     
31560     
31561     Roo.TreePanel.superclass.constructor.call(this, el, config);
31562  
31563  
31564     this.tree = new Roo.tree.TreePanel(treeEl , tree);
31565     //console.log(tree);
31566     this.on('activate', function()
31567     {
31568         if (this.tree.rendered) {
31569             return;
31570         }
31571         //console.log('render tree');
31572         this.tree.render();
31573     });
31574     
31575     this.on('resize',  function (cp, w, h) {
31576             this.tree.innerCt.setWidth(w);
31577             this.tree.innerCt.setHeight(h);
31578             this.tree.innerCt.setStyle('overflow-y', 'auto');
31579     });
31580
31581         
31582     
31583 };
31584
31585 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
31586     fitToFrame : true,
31587     autoScroll : true
31588 });
31589
31590
31591
31592
31593
31594
31595
31596
31597
31598
31599
31600 /*
31601  * Based on:
31602  * Ext JS Library 1.1.1
31603  * Copyright(c) 2006-2007, Ext JS, LLC.
31604  *
31605  * Originally Released Under LGPL - original licence link has changed is not relivant.
31606  *
31607  * Fork - LGPL
31608  * <script type="text/javascript">
31609  */
31610  
31611
31612 /**
31613  * @class Roo.ReaderLayout
31614  * @extends Roo.BorderLayout
31615  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
31616  * center region containing two nested regions (a top one for a list view and one for item preview below),
31617  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
31618  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
31619  * expedites the setup of the overall layout and regions for this common application style.
31620  * Example:
31621  <pre><code>
31622 var reader = new Roo.ReaderLayout();
31623 var CP = Roo.ContentPanel;  // shortcut for adding
31624
31625 reader.beginUpdate();
31626 reader.add("north", new CP("north", "North"));
31627 reader.add("west", new CP("west", {title: "West"}));
31628 reader.add("east", new CP("east", {title: "East"}));
31629
31630 reader.regions.listView.add(new CP("listView", "List"));
31631 reader.regions.preview.add(new CP("preview", "Preview"));
31632 reader.endUpdate();
31633 </code></pre>
31634 * @constructor
31635 * Create a new ReaderLayout
31636 * @param {Object} config Configuration options
31637 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
31638 * document.body if omitted)
31639 */
31640 Roo.ReaderLayout = function(config, renderTo){
31641     var c = config || {size:{}};
31642     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
31643         north: c.north !== false ? Roo.apply({
31644             split:false,
31645             initialSize: 32,
31646             titlebar: false
31647         }, c.north) : false,
31648         west: c.west !== false ? Roo.apply({
31649             split:true,
31650             initialSize: 200,
31651             minSize: 175,
31652             maxSize: 400,
31653             titlebar: true,
31654             collapsible: true,
31655             animate: true,
31656             margins:{left:5,right:0,bottom:5,top:5},
31657             cmargins:{left:5,right:5,bottom:5,top:5}
31658         }, c.west) : false,
31659         east: c.east !== false ? Roo.apply({
31660             split:true,
31661             initialSize: 200,
31662             minSize: 175,
31663             maxSize: 400,
31664             titlebar: true,
31665             collapsible: true,
31666             animate: true,
31667             margins:{left:0,right:5,bottom:5,top:5},
31668             cmargins:{left:5,right:5,bottom:5,top:5}
31669         }, c.east) : false,
31670         center: Roo.apply({
31671             tabPosition: 'top',
31672             autoScroll:false,
31673             closeOnTab: true,
31674             titlebar:false,
31675             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
31676         }, c.center)
31677     });
31678
31679     this.el.addClass('x-reader');
31680
31681     this.beginUpdate();
31682
31683     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
31684         south: c.preview !== false ? Roo.apply({
31685             split:true,
31686             initialSize: 200,
31687             minSize: 100,
31688             autoScroll:true,
31689             collapsible:true,
31690             titlebar: true,
31691             cmargins:{top:5,left:0, right:0, bottom:0}
31692         }, c.preview) : false,
31693         center: Roo.apply({
31694             autoScroll:false,
31695             titlebar:false,
31696             minHeight:200
31697         }, c.listView)
31698     });
31699     this.add('center', new Roo.NestedLayoutPanel(inner,
31700             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
31701
31702     this.endUpdate();
31703
31704     this.regions.preview = inner.getRegion('south');
31705     this.regions.listView = inner.getRegion('center');
31706 };
31707
31708 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
31709  * Based on:
31710  * Ext JS Library 1.1.1
31711  * Copyright(c) 2006-2007, Ext JS, LLC.
31712  *
31713  * Originally Released Under LGPL - original licence link has changed is not relivant.
31714  *
31715  * Fork - LGPL
31716  * <script type="text/javascript">
31717  */
31718  
31719 /**
31720  * @class Roo.grid.Grid
31721  * @extends Roo.util.Observable
31722  * This class represents the primary interface of a component based grid control.
31723  * <br><br>Usage:<pre><code>
31724  var grid = new Roo.grid.Grid("my-container-id", {
31725      ds: myDataStore,
31726      cm: myColModel,
31727      selModel: mySelectionModel,
31728      autoSizeColumns: true,
31729      monitorWindowResize: false,
31730      trackMouseOver: true
31731  });
31732  // set any options
31733  grid.render();
31734  * </code></pre>
31735  * <b>Common Problems:</b><br/>
31736  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
31737  * element will correct this<br/>
31738  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
31739  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
31740  * are unpredictable.<br/>
31741  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
31742  * grid to calculate dimensions/offsets.<br/>
31743   * @constructor
31744  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
31745  * The container MUST have some type of size defined for the grid to fill. The container will be
31746  * automatically set to position relative if it isn't already.
31747  * @param {Object} config A config object that sets properties on this grid.
31748  */
31749 Roo.grid.Grid = function(container, config){
31750         // initialize the container
31751         this.container = Roo.get(container);
31752         this.container.update("");
31753         this.container.setStyle("overflow", "hidden");
31754     this.container.addClass('x-grid-container');
31755
31756     this.id = this.container.id;
31757
31758     Roo.apply(this, config);
31759     // check and correct shorthanded configs
31760     if(this.ds){
31761         this.dataSource = this.ds;
31762         delete this.ds;
31763     }
31764     if(this.cm){
31765         this.colModel = this.cm;
31766         delete this.cm;
31767     }
31768     if(this.sm){
31769         this.selModel = this.sm;
31770         delete this.sm;
31771     }
31772
31773     if (this.selModel) {
31774         this.selModel = Roo.factory(this.selModel, Roo.grid);
31775         this.sm = this.selModel;
31776         this.sm.xmodule = this.xmodule || false;
31777     }
31778     if (typeof(this.colModel.config) == 'undefined') {
31779         this.colModel = new Roo.grid.ColumnModel(this.colModel);
31780         this.cm = this.colModel;
31781         this.cm.xmodule = this.xmodule || false;
31782     }
31783     if (this.dataSource) {
31784         this.dataSource= Roo.factory(this.dataSource, Roo.data);
31785         this.ds = this.dataSource;
31786         this.ds.xmodule = this.xmodule || false;
31787         
31788     }
31789     
31790     
31791     
31792     if(this.width){
31793         this.container.setWidth(this.width);
31794     }
31795
31796     if(this.height){
31797         this.container.setHeight(this.height);
31798     }
31799     /** @private */
31800         this.addEvents({
31801             // raw events
31802             /**
31803              * @event click
31804              * The raw click event for the entire grid.
31805              * @param {Roo.EventObject} e
31806              */
31807             "click" : true,
31808             /**
31809              * @event dblclick
31810              * The raw dblclick event for the entire grid.
31811              * @param {Roo.EventObject} e
31812              */
31813             "dblclick" : true,
31814             /**
31815              * @event contextmenu
31816              * The raw contextmenu event for the entire grid.
31817              * @param {Roo.EventObject} e
31818              */
31819             "contextmenu" : true,
31820             /**
31821              * @event mousedown
31822              * The raw mousedown event for the entire grid.
31823              * @param {Roo.EventObject} e
31824              */
31825             "mousedown" : true,
31826             /**
31827              * @event mouseup
31828              * The raw mouseup event for the entire grid.
31829              * @param {Roo.EventObject} e
31830              */
31831             "mouseup" : true,
31832             /**
31833              * @event mouseover
31834              * The raw mouseover event for the entire grid.
31835              * @param {Roo.EventObject} e
31836              */
31837             "mouseover" : true,
31838             /**
31839              * @event mouseout
31840              * The raw mouseout event for the entire grid.
31841              * @param {Roo.EventObject} e
31842              */
31843             "mouseout" : true,
31844             /**
31845              * @event keypress
31846              * The raw keypress event for the entire grid.
31847              * @param {Roo.EventObject} e
31848              */
31849             "keypress" : true,
31850             /**
31851              * @event keydown
31852              * The raw keydown event for the entire grid.
31853              * @param {Roo.EventObject} e
31854              */
31855             "keydown" : true,
31856
31857             // custom events
31858
31859             /**
31860              * @event cellclick
31861              * Fires when a cell is clicked
31862              * @param {Grid} this
31863              * @param {Number} rowIndex
31864              * @param {Number} columnIndex
31865              * @param {Roo.EventObject} e
31866              */
31867             "cellclick" : true,
31868             /**
31869              * @event celldblclick
31870              * Fires when a cell is double clicked
31871              * @param {Grid} this
31872              * @param {Number} rowIndex
31873              * @param {Number} columnIndex
31874              * @param {Roo.EventObject} e
31875              */
31876             "celldblclick" : true,
31877             /**
31878              * @event rowclick
31879              * Fires when a row is clicked
31880              * @param {Grid} this
31881              * @param {Number} rowIndex
31882              * @param {Roo.EventObject} e
31883              */
31884             "rowclick" : true,
31885             /**
31886              * @event rowdblclick
31887              * Fires when a row is double clicked
31888              * @param {Grid} this
31889              * @param {Number} rowIndex
31890              * @param {Roo.EventObject} e
31891              */
31892             "rowdblclick" : true,
31893             /**
31894              * @event headerclick
31895              * Fires when a header is clicked
31896              * @param {Grid} this
31897              * @param {Number} columnIndex
31898              * @param {Roo.EventObject} e
31899              */
31900             "headerclick" : true,
31901             /**
31902              * @event headerdblclick
31903              * Fires when a header cell is double clicked
31904              * @param {Grid} this
31905              * @param {Number} columnIndex
31906              * @param {Roo.EventObject} e
31907              */
31908             "headerdblclick" : true,
31909             /**
31910              * @event rowcontextmenu
31911              * Fires when a row is right clicked
31912              * @param {Grid} this
31913              * @param {Number} rowIndex
31914              * @param {Roo.EventObject} e
31915              */
31916             "rowcontextmenu" : true,
31917             /**
31918          * @event cellcontextmenu
31919          * Fires when a cell is right clicked
31920          * @param {Grid} this
31921          * @param {Number} rowIndex
31922          * @param {Number} cellIndex
31923          * @param {Roo.EventObject} e
31924          */
31925          "cellcontextmenu" : true,
31926             /**
31927              * @event headercontextmenu
31928              * Fires when a header is right clicked
31929              * @param {Grid} this
31930              * @param {Number} columnIndex
31931              * @param {Roo.EventObject} e
31932              */
31933             "headercontextmenu" : true,
31934             /**
31935              * @event bodyscroll
31936              * Fires when the body element is scrolled
31937              * @param {Number} scrollLeft
31938              * @param {Number} scrollTop
31939              */
31940             "bodyscroll" : true,
31941             /**
31942              * @event columnresize
31943              * Fires when the user resizes a column
31944              * @param {Number} columnIndex
31945              * @param {Number} newSize
31946              */
31947             "columnresize" : true,
31948             /**
31949              * @event columnmove
31950              * Fires when the user moves a column
31951              * @param {Number} oldIndex
31952              * @param {Number} newIndex
31953              */
31954             "columnmove" : true,
31955             /**
31956              * @event startdrag
31957              * Fires when row(s) start being dragged
31958              * @param {Grid} this
31959              * @param {Roo.GridDD} dd The drag drop object
31960              * @param {event} e The raw browser event
31961              */
31962             "startdrag" : true,
31963             /**
31964              * @event enddrag
31965              * Fires when a drag operation is complete
31966              * @param {Grid} this
31967              * @param {Roo.GridDD} dd The drag drop object
31968              * @param {event} e The raw browser event
31969              */
31970             "enddrag" : true,
31971             /**
31972              * @event dragdrop
31973              * Fires when dragged row(s) are dropped on a valid DD target
31974              * @param {Grid} this
31975              * @param {Roo.GridDD} dd The drag drop object
31976              * @param {String} targetId The target drag drop object
31977              * @param {event} e The raw browser event
31978              */
31979             "dragdrop" : true,
31980             /**
31981              * @event dragover
31982              * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
31983              * @param {Grid} this
31984              * @param {Roo.GridDD} dd The drag drop object
31985              * @param {String} targetId The target drag drop object
31986              * @param {event} e The raw browser event
31987              */
31988             "dragover" : true,
31989             /**
31990              * @event dragenter
31991              *  Fires when the dragged row(s) first cross another DD target while being dragged
31992              * @param {Grid} this
31993              * @param {Roo.GridDD} dd The drag drop object
31994              * @param {String} targetId The target drag drop object
31995              * @param {event} e The raw browser event
31996              */
31997             "dragenter" : true,
31998             /**
31999              * @event dragout
32000              * Fires when the dragged row(s) leave another DD target while being dragged
32001              * @param {Grid} this
32002              * @param {Roo.GridDD} dd The drag drop object
32003              * @param {String} targetId The target drag drop object
32004              * @param {event} e The raw browser event
32005              */
32006             "dragout" : true,
32007         /**
32008          * @event render
32009          * Fires when the grid is rendered
32010          * @param {Grid} grid
32011          */
32012         render : true
32013     });
32014
32015     Roo.grid.Grid.superclass.constructor.call(this);
32016 };
32017 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
32018     
32019     /**
32020      * @cfg {String} ddGroup - drag drop group.
32021          */
32022     
32023     /**
32024      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
32025          */
32026         minColumnWidth : 25,
32027
32028     /**
32029          * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
32030          * <b>on initial render.</b> It is more efficient to explicitly size the columns
32031          * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
32032          */
32033         autoSizeColumns : false,
32034
32035         /**
32036          * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
32037          */
32038         autoSizeHeaders : true,
32039
32040         /**
32041          * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
32042          */
32043         monitorWindowResize : true,
32044
32045         /**
32046          * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
32047          * rows measured to get a columns size. Default is 0 (all rows).
32048          */
32049         maxRowsToMeasure : 0,
32050
32051         /**
32052          * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
32053          */
32054         trackMouseOver : true,
32055
32056     /**
32057          * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
32058          */
32059     
32060         /**
32061          * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
32062          */
32063         enableDragDrop : false,
32064
32065         /**
32066          * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
32067          */
32068         enableColumnMove : true,
32069
32070         /**
32071          * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
32072          */
32073         enableColumnHide : true,
32074
32075         /**
32076          * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
32077          */
32078         enableRowHeightSync : false,
32079
32080         /**
32081          * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
32082          */
32083         stripeRows : true,
32084
32085         /**
32086          * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
32087          */
32088         autoHeight : false,
32089
32090     /**
32091      * @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.
32092      */
32093     autoExpandColumn : false,
32094
32095     /**
32096     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
32097     * Default is 50.
32098     */
32099     autoExpandMin : 50,
32100
32101     /**
32102     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
32103     */
32104     autoExpandMax : 1000,
32105
32106     /**
32107          * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
32108          */
32109         view : null,
32110
32111         /**
32112      * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
32113          */
32114         loadMask : false,
32115     /**
32116      * @cfg {Roo.dd.DropTarget} dragTarget An {@link Roo.dd.DragTarget} config
32117          */
32118         dropTarget: false,
32119     // private
32120     rendered : false,
32121
32122     /**
32123     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
32124     * of a fixed width. Default is false.
32125     */
32126     /**
32127     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
32128     */
32129     /**
32130      * Called once after all setup has been completed and the grid is ready to be rendered.
32131      * @return {Roo.grid.Grid} this
32132      */
32133     render : function(){
32134         var c = this.container;
32135         // try to detect autoHeight/width mode
32136         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
32137             this.autoHeight = true;
32138         }
32139         var view = this.getView();
32140         view.init(this);
32141
32142         c.on("click", this.onClick, this);
32143         c.on("dblclick", this.onDblClick, this);
32144         c.on("contextmenu", this.onContextMenu, this);
32145         c.on("keydown", this.onKeyDown, this);
32146
32147         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
32148
32149         this.getSelectionModel().init(this);
32150
32151         view.render();
32152
32153         if(this.loadMask){
32154             this.loadMask = new Roo.LoadMask(this.container,
32155                     Roo.apply({store:this.dataSource}, this.loadMask));
32156         }
32157         
32158         
32159         if (this.toolbar && this.toolbar.xtype) {
32160             this.toolbar.container = this.getView().getHeaderPanel(true);
32161             this.toolbar = new Ext.Toolbar(this.toolbar);
32162         }
32163         if (this.footer && this.footer.xtype) {
32164             this.footer.dataSource = this.getDataSource();
32165             this.footer.container = this.getView().getFooterPanel(true);
32166             this.footer = Roo.factory(this.footer, Roo);
32167         }
32168         if (this.dropTarget && this.dropTarget.xtype) {
32169             delete this.dropTarget.xtype;
32170             this.dropTarget =  new Ext.dd.DropTarget(this.getView().mainBody, this.dropTarget);
32171         }
32172         
32173         
32174         this.rendered = true;
32175         this.fireEvent('render', this);
32176         return this;
32177     },
32178
32179         /**
32180          * Reconfigures the grid to use a different Store and Column Model.
32181          * The View will be bound to the new objects and refreshed.
32182          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
32183          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
32184          */
32185     reconfigure : function(dataSource, colModel){
32186         if(this.loadMask){
32187             this.loadMask.destroy();
32188             this.loadMask = new Roo.LoadMask(this.container,
32189                     Roo.apply({store:dataSource}, this.loadMask));
32190         }
32191         this.view.bind(dataSource, colModel);
32192         this.dataSource = dataSource;
32193         this.colModel = colModel;
32194         this.view.refresh(true);
32195     },
32196
32197     // private
32198     onKeyDown : function(e){
32199         this.fireEvent("keydown", e);
32200     },
32201
32202     /**
32203      * Destroy this grid.
32204      * @param {Boolean} removeEl True to remove the element
32205      */
32206     destroy : function(removeEl, keepListeners){
32207         if(this.loadMask){
32208             this.loadMask.destroy();
32209         }
32210         var c = this.container;
32211         c.removeAllListeners();
32212         this.view.destroy();
32213         this.colModel.purgeListeners();
32214         if(!keepListeners){
32215             this.purgeListeners();
32216         }
32217         c.update("");
32218         if(removeEl === true){
32219             c.remove();
32220         }
32221     },
32222
32223     // private
32224     processEvent : function(name, e){
32225         this.fireEvent(name, e);
32226         var t = e.getTarget();
32227         var v = this.view;
32228         var header = v.findHeaderIndex(t);
32229         if(header !== false){
32230             this.fireEvent("header" + name, this, header, e);
32231         }else{
32232             var row = v.findRowIndex(t);
32233             var cell = v.findCellIndex(t);
32234             if(row !== false){
32235                 this.fireEvent("row" + name, this, row, e);
32236                 if(cell !== false){
32237                     this.fireEvent("cell" + name, this, row, cell, e);
32238                 }
32239             }
32240         }
32241     },
32242
32243     // private
32244     onClick : function(e){
32245         this.processEvent("click", e);
32246     },
32247
32248     // private
32249     onContextMenu : function(e, t){
32250         this.processEvent("contextmenu", e);
32251     },
32252
32253     // private
32254     onDblClick : function(e){
32255         this.processEvent("dblclick", e);
32256     },
32257
32258     // private
32259     walkCells : function(row, col, step, fn, scope){
32260         var cm = this.colModel, clen = cm.getColumnCount();
32261         var ds = this.dataSource, rlen = ds.getCount(), first = true;
32262         if(step < 0){
32263             if(col < 0){
32264                 row--;
32265                 first = false;
32266             }
32267             while(row >= 0){
32268                 if(!first){
32269                     col = clen-1;
32270                 }
32271                 first = false;
32272                 while(col >= 0){
32273                     if(fn.call(scope || this, row, col, cm) === true){
32274                         return [row, col];
32275                     }
32276                     col--;
32277                 }
32278                 row--;
32279             }
32280         } else {
32281             if(col >= clen){
32282                 row++;
32283                 first = false;
32284             }
32285             while(row < rlen){
32286                 if(!first){
32287                     col = 0;
32288                 }
32289                 first = false;
32290                 while(col < clen){
32291                     if(fn.call(scope || this, row, col, cm) === true){
32292                         return [row, col];
32293                     }
32294                     col++;
32295                 }
32296                 row++;
32297             }
32298         }
32299         return null;
32300     },
32301
32302     // private
32303     getSelections : function(){
32304         return this.selModel.getSelections();
32305     },
32306
32307     /**
32308      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
32309      * but if manual update is required this method will initiate it.
32310      */
32311     autoSize : function(){
32312         if(this.rendered){
32313             this.view.layout();
32314             if(this.view.adjustForScroll){
32315                 this.view.adjustForScroll();
32316             }
32317         }
32318     },
32319
32320     /**
32321      * Returns the grid's underlying element.
32322      * @return {Element} The element
32323      */
32324     getGridEl : function(){
32325         return this.container;
32326     },
32327
32328     // private for compatibility, overridden by editor grid
32329     stopEditing : function(){},
32330
32331     /**
32332      * Returns the grid's SelectionModel.
32333      * @return {SelectionModel}
32334      */
32335     getSelectionModel : function(){
32336         if(!this.selModel){
32337             this.selModel = new Roo.grid.RowSelectionModel();
32338         }
32339         return this.selModel;
32340     },
32341
32342     /**
32343      * Returns the grid's DataSource.
32344      * @return {DataSource}
32345      */
32346     getDataSource : function(){
32347         return this.dataSource;
32348     },
32349
32350     /**
32351      * Returns the grid's ColumnModel.
32352      * @return {ColumnModel}
32353      */
32354     getColumnModel : function(){
32355         return this.colModel;
32356     },
32357
32358     /**
32359      * Returns the grid's GridView object.
32360      * @return {GridView}
32361      */
32362     getView : function(){
32363         if(!this.view){
32364             this.view = new Roo.grid.GridView(this.viewConfig);
32365         }
32366         return this.view;
32367     },
32368     /**
32369      * Called to get grid's drag proxy text, by default returns this.ddText.
32370      * @return {String}
32371      */
32372     getDragDropText : function(){
32373         var count = this.selModel.getCount();
32374         return String.format(this.ddText, count, count == 1 ? '' : 's');
32375     }
32376 });
32377 /**
32378  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
32379  * %0 is replaced with the number of selected rows.
32380  * @type String
32381  */
32382 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
32383  * Based on:
32384  * Ext JS Library 1.1.1
32385  * Copyright(c) 2006-2007, Ext JS, LLC.
32386  *
32387  * Originally Released Under LGPL - original licence link has changed is not relivant.
32388  *
32389  * Fork - LGPL
32390  * <script type="text/javascript">
32391  */
32392  
32393 Roo.grid.AbstractGridView = function(){
32394         this.grid = null;
32395         
32396         this.events = {
32397             "beforerowremoved" : true,
32398             "beforerowsinserted" : true,
32399             "beforerefresh" : true,
32400             "rowremoved" : true,
32401             "rowsinserted" : true,
32402             "rowupdated" : true,
32403             "refresh" : true
32404         };
32405     Roo.grid.AbstractGridView.superclass.constructor.call(this);
32406 };
32407
32408 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
32409     rowClass : "x-grid-row",
32410     cellClass : "x-grid-cell",
32411     tdClass : "x-grid-td",
32412     hdClass : "x-grid-hd",
32413     splitClass : "x-grid-hd-split",
32414     
32415         init: function(grid){
32416         this.grid = grid;
32417                 var cid = this.grid.getGridEl().id;
32418         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
32419         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
32420         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
32421         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
32422         },
32423         
32424         getColumnRenderers : function(){
32425         var renderers = [];
32426         var cm = this.grid.colModel;
32427         var colCount = cm.getColumnCount();
32428         for(var i = 0; i < colCount; i++){
32429             renderers[i] = cm.getRenderer(i);
32430         }
32431         return renderers;
32432     },
32433     
32434     getColumnIds : function(){
32435         var ids = [];
32436         var cm = this.grid.colModel;
32437         var colCount = cm.getColumnCount();
32438         for(var i = 0; i < colCount; i++){
32439             ids[i] = cm.getColumnId(i);
32440         }
32441         return ids;
32442     },
32443     
32444     getDataIndexes : function(){
32445         if(!this.indexMap){
32446             this.indexMap = this.buildIndexMap();
32447         }
32448         return this.indexMap.colToData;
32449     },
32450     
32451     getColumnIndexByDataIndex : function(dataIndex){
32452         if(!this.indexMap){
32453             this.indexMap = this.buildIndexMap();
32454         }
32455         return this.indexMap.dataToCol[dataIndex];
32456     },
32457     
32458     /**
32459      * Set a css style for a column dynamically. 
32460      * @param {Number} colIndex The index of the column
32461      * @param {String} name The css property name
32462      * @param {String} value The css value
32463      */
32464     setCSSStyle : function(colIndex, name, value){
32465         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
32466         Roo.util.CSS.updateRule(selector, name, value);
32467     },
32468     
32469     generateRules : function(cm){
32470         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
32471         Roo.util.CSS.removeStyleSheet(rulesId);
32472         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
32473             var cid = cm.getColumnId(i);
32474             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
32475                          this.tdSelector, cid, " {\n}\n",
32476                          this.hdSelector, cid, " {\n}\n",
32477                          this.splitSelector, cid, " {\n}\n");
32478         }
32479         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
32480     }
32481 });/*
32482  * Based on:
32483  * Ext JS Library 1.1.1
32484  * Copyright(c) 2006-2007, Ext JS, LLC.
32485  *
32486  * Originally Released Under LGPL - original licence link has changed is not relivant.
32487  *
32488  * Fork - LGPL
32489  * <script type="text/javascript">
32490  */
32491
32492 // private
32493 // This is a support class used internally by the Grid components
32494 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
32495     this.grid = grid;
32496     this.view = grid.getView();
32497     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
32498     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
32499     if(hd2){
32500         this.setHandleElId(Roo.id(hd));
32501         this.setOuterHandleElId(Roo.id(hd2));
32502     }
32503     this.scroll = false;
32504 };
32505 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
32506     maxDragWidth: 120,
32507     getDragData : function(e){
32508         var t = Roo.lib.Event.getTarget(e);
32509         var h = this.view.findHeaderCell(t);
32510         if(h){
32511             return {ddel: h.firstChild, header:h};
32512         }
32513         return false;
32514     },
32515
32516     onInitDrag : function(e){
32517         this.view.headersDisabled = true;
32518         var clone = this.dragData.ddel.cloneNode(true);
32519         clone.id = Roo.id();
32520         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
32521         this.proxy.update(clone);
32522         return true;
32523     },
32524
32525     afterValidDrop : function(){
32526         var v = this.view;
32527         setTimeout(function(){
32528             v.headersDisabled = false;
32529         }, 50);
32530     },
32531
32532     afterInvalidDrop : function(){
32533         var v = this.view;
32534         setTimeout(function(){
32535             v.headersDisabled = false;
32536         }, 50);
32537     }
32538 });
32539 /*
32540  * Based on:
32541  * Ext JS Library 1.1.1
32542  * Copyright(c) 2006-2007, Ext JS, LLC.
32543  *
32544  * Originally Released Under LGPL - original licence link has changed is not relivant.
32545  *
32546  * Fork - LGPL
32547  * <script type="text/javascript">
32548  */
32549 // private
32550 // This is a support class used internally by the Grid components
32551 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
32552     this.grid = grid;
32553     this.view = grid.getView();
32554     // split the proxies so they don't interfere with mouse events
32555     this.proxyTop = Roo.DomHelper.append(document.body, {
32556         cls:"col-move-top", html:"&#160;"
32557     }, true);
32558     this.proxyBottom = Roo.DomHelper.append(document.body, {
32559         cls:"col-move-bottom", html:"&#160;"
32560     }, true);
32561     this.proxyTop.hide = this.proxyBottom.hide = function(){
32562         this.setLeftTop(-100,-100);
32563         this.setStyle("visibility", "hidden");
32564     };
32565     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
32566     // temporarily disabled
32567     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
32568     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
32569 };
32570 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
32571     proxyOffsets : [-4, -9],
32572     fly: Roo.Element.fly,
32573
32574     getTargetFromEvent : function(e){
32575         var t = Roo.lib.Event.getTarget(e);
32576         var cindex = this.view.findCellIndex(t);
32577         if(cindex !== false){
32578             return this.view.getHeaderCell(cindex);
32579         }
32580     },
32581
32582     nextVisible : function(h){
32583         var v = this.view, cm = this.grid.colModel;
32584         h = h.nextSibling;
32585         while(h){
32586             if(!cm.isHidden(v.getCellIndex(h))){
32587                 return h;
32588             }
32589             h = h.nextSibling;
32590         }
32591         return null;
32592     },
32593
32594     prevVisible : function(h){
32595         var v = this.view, cm = this.grid.colModel;
32596         h = h.prevSibling;
32597         while(h){
32598             if(!cm.isHidden(v.getCellIndex(h))){
32599                 return h;
32600             }
32601             h = h.prevSibling;
32602         }
32603         return null;
32604     },
32605
32606     positionIndicator : function(h, n, e){
32607         var x = Roo.lib.Event.getPageX(e);
32608         var r = Roo.lib.Dom.getRegion(n.firstChild);
32609         var px, pt, py = r.top + this.proxyOffsets[1];
32610         if((r.right - x) <= (r.right-r.left)/2){
32611             px = r.right+this.view.borderWidth;
32612             pt = "after";
32613         }else{
32614             px = r.left;
32615             pt = "before";
32616         }
32617         var oldIndex = this.view.getCellIndex(h);
32618         var newIndex = this.view.getCellIndex(n);
32619
32620         if(this.grid.colModel.isFixed(newIndex)){
32621             return false;
32622         }
32623
32624         var locked = this.grid.colModel.isLocked(newIndex);
32625
32626         if(pt == "after"){
32627             newIndex++;
32628         }
32629         if(oldIndex < newIndex){
32630             newIndex--;
32631         }
32632         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
32633             return false;
32634         }
32635         px +=  this.proxyOffsets[0];
32636         this.proxyTop.setLeftTop(px, py);
32637         this.proxyTop.show();
32638         if(!this.bottomOffset){
32639             this.bottomOffset = this.view.mainHd.getHeight();
32640         }
32641         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
32642         this.proxyBottom.show();
32643         return pt;
32644     },
32645
32646     onNodeEnter : function(n, dd, e, data){
32647         if(data.header != n){
32648             this.positionIndicator(data.header, n, e);
32649         }
32650     },
32651
32652     onNodeOver : function(n, dd, e, data){
32653         var result = false;
32654         if(data.header != n){
32655             result = this.positionIndicator(data.header, n, e);
32656         }
32657         if(!result){
32658             this.proxyTop.hide();
32659             this.proxyBottom.hide();
32660         }
32661         return result ? this.dropAllowed : this.dropNotAllowed;
32662     },
32663
32664     onNodeOut : function(n, dd, e, data){
32665         this.proxyTop.hide();
32666         this.proxyBottom.hide();
32667     },
32668
32669     onNodeDrop : function(n, dd, e, data){
32670         var h = data.header;
32671         if(h != n){
32672             var cm = this.grid.colModel;
32673             var x = Roo.lib.Event.getPageX(e);
32674             var r = Roo.lib.Dom.getRegion(n.firstChild);
32675             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
32676             var oldIndex = this.view.getCellIndex(h);
32677             var newIndex = this.view.getCellIndex(n);
32678             var locked = cm.isLocked(newIndex);
32679             if(pt == "after"){
32680                 newIndex++;
32681             }
32682             if(oldIndex < newIndex){
32683                 newIndex--;
32684             }
32685             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
32686                 return false;
32687             }
32688             cm.setLocked(oldIndex, locked, true);
32689             cm.moveColumn(oldIndex, newIndex);
32690             this.grid.fireEvent("columnmove", oldIndex, newIndex);
32691             return true;
32692         }
32693         return false;
32694     }
32695 });
32696 /*
32697  * Based on:
32698  * Ext JS Library 1.1.1
32699  * Copyright(c) 2006-2007, Ext JS, LLC.
32700  *
32701  * Originally Released Under LGPL - original licence link has changed is not relivant.
32702  *
32703  * Fork - LGPL
32704  * <script type="text/javascript">
32705  */
32706   
32707 /**
32708  * @class Roo.grid.GridView
32709  * @extends Roo.util.Observable
32710  *
32711  * @constructor
32712  * @param {Object} config
32713  */
32714 Roo.grid.GridView = function(config){
32715     Roo.grid.GridView.superclass.constructor.call(this);
32716     this.el = null;
32717
32718     Roo.apply(this, config);
32719 };
32720
32721 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
32722
32723     /**
32724      * Override this function to apply custom css classes to rows during rendering
32725      * @param {Record} record The record
32726      * @param {Number} index
32727      * @method getRowClass
32728      */
32729     rowClass : "x-grid-row",
32730
32731     cellClass : "x-grid-col",
32732
32733     tdClass : "x-grid-td",
32734
32735     hdClass : "x-grid-hd",
32736
32737     splitClass : "x-grid-split",
32738
32739     sortClasses : ["sort-asc", "sort-desc"],
32740
32741     enableMoveAnim : false,
32742
32743     hlColor: "C3DAF9",
32744
32745     dh : Roo.DomHelper,
32746
32747     fly : Roo.Element.fly,
32748
32749     css : Roo.util.CSS,
32750
32751     borderWidth: 1,
32752
32753     splitOffset: 3,
32754
32755     scrollIncrement : 22,
32756
32757     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
32758
32759     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
32760
32761     bind : function(ds, cm){
32762         if(this.ds){
32763             this.ds.un("load", this.onLoad, this);
32764             this.ds.un("datachanged", this.onDataChange, this);
32765             this.ds.un("add", this.onAdd, this);
32766             this.ds.un("remove", this.onRemove, this);
32767             this.ds.un("update", this.onUpdate, this);
32768             this.ds.un("clear", this.onClear, this);
32769         }
32770         if(ds){
32771             ds.on("load", this.onLoad, this);
32772             ds.on("datachanged", this.onDataChange, this);
32773             ds.on("add", this.onAdd, this);
32774             ds.on("remove", this.onRemove, this);
32775             ds.on("update", this.onUpdate, this);
32776             ds.on("clear", this.onClear, this);
32777         }
32778         this.ds = ds;
32779
32780         if(this.cm){
32781             this.cm.un("widthchange", this.onColWidthChange, this);
32782             this.cm.un("headerchange", this.onHeaderChange, this);
32783             this.cm.un("hiddenchange", this.onHiddenChange, this);
32784             this.cm.un("columnmoved", this.onColumnMove, this);
32785             this.cm.un("columnlockchange", this.onColumnLock, this);
32786         }
32787         if(cm){
32788             this.generateRules(cm);
32789             cm.on("widthchange", this.onColWidthChange, this);
32790             cm.on("headerchange", this.onHeaderChange, this);
32791             cm.on("hiddenchange", this.onHiddenChange, this);
32792             cm.on("columnmoved", this.onColumnMove, this);
32793             cm.on("columnlockchange", this.onColumnLock, this);
32794         }
32795         this.cm = cm;
32796     },
32797
32798     init: function(grid){
32799                 Roo.grid.GridView.superclass.init.call(this, grid);
32800
32801                 this.bind(grid.dataSource, grid.colModel);
32802
32803             grid.on("headerclick", this.handleHeaderClick, this);
32804
32805         if(grid.trackMouseOver){
32806             grid.on("mouseover", this.onRowOver, this);
32807                 grid.on("mouseout", this.onRowOut, this);
32808             }
32809             grid.cancelTextSelection = function(){};
32810                 this.gridId = grid.id;
32811
32812                 var tpls = this.templates || {};
32813
32814                 if(!tpls.master){
32815                     tpls.master = new Roo.Template(
32816                        '<div class="x-grid" hidefocus="true">',
32817                           '<div class="x-grid-topbar"></div>',
32818                           '<div class="x-grid-scroller"><div></div></div>',
32819                           '<div class="x-grid-locked">',
32820                               '<div class="x-grid-header">{lockedHeader}</div>',
32821                               '<div class="x-grid-body">{lockedBody}</div>',
32822                           "</div>",
32823                           '<div class="x-grid-viewport">',
32824                               '<div class="x-grid-header">{header}</div>',
32825                               '<div class="x-grid-body">{body}</div>',
32826                           "</div>",
32827                           '<div class="x-grid-bottombar"></div>',
32828                           '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
32829                           '<div class="x-grid-resize-proxy">&#160;</div>',
32830                        "</div>"
32831                     );
32832                     tpls.master.disableformats = true;
32833                 }
32834
32835                 if(!tpls.header){
32836                     tpls.header = new Roo.Template(
32837                        '<table border="0" cellspacing="0" cellpadding="0">',
32838                        '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
32839                        "</table>{splits}"
32840                     );
32841                     tpls.header.disableformats = true;
32842                 }
32843                 tpls.header.compile();
32844
32845                 if(!tpls.hcell){
32846                     tpls.hcell = new Roo.Template(
32847                         '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
32848                         '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
32849                         "</div></td>"
32850                      );
32851                      tpls.hcell.disableFormats = true;
32852                 }
32853                 tpls.hcell.compile();
32854
32855                 if(!tpls.hsplit){
32856                     tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
32857                     tpls.hsplit.disableFormats = true;
32858                 }
32859                 tpls.hsplit.compile();
32860
32861                 if(!tpls.body){
32862                     tpls.body = new Roo.Template(
32863                        '<table border="0" cellspacing="0" cellpadding="0">',
32864                        "<tbody>{rows}</tbody>",
32865                        "</table>"
32866                     );
32867                     tpls.body.disableFormats = true;
32868                 }
32869                 tpls.body.compile();
32870
32871                 if(!tpls.row){
32872                     tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
32873                     tpls.row.disableFormats = true;
32874                 }
32875                 tpls.row.compile();
32876
32877                 if(!tpls.cell){
32878                     tpls.cell = new Roo.Template(
32879                         '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
32880                         '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
32881                         "</td>"
32882                     );
32883             tpls.cell.disableFormats = true;
32884         }
32885                 tpls.cell.compile();
32886
32887                 this.templates = tpls;
32888         },
32889
32890         // remap these for backwards compat
32891     onColWidthChange : function(){
32892         this.updateColumns.apply(this, arguments);
32893     },
32894     onHeaderChange : function(){
32895         this.updateHeaders.apply(this, arguments);
32896     }, 
32897     onHiddenChange : function(){
32898         this.handleHiddenChange.apply(this, arguments);
32899     },
32900     onColumnMove : function(){
32901         this.handleColumnMove.apply(this, arguments);
32902     },
32903     onColumnLock : function(){
32904         this.handleLockChange.apply(this, arguments);
32905     },
32906
32907     onDataChange : function(){
32908         this.refresh();
32909         this.updateHeaderSortState();
32910     },
32911
32912         onClear : function(){
32913         this.refresh();
32914     },
32915
32916         onUpdate : function(ds, record){
32917         this.refreshRow(record);
32918     },
32919
32920     refreshRow : function(record){
32921         var ds = this.ds, index;
32922         if(typeof record == 'number'){
32923             index = record;
32924             record = ds.getAt(index);
32925         }else{
32926             index = ds.indexOf(record);
32927         }
32928         this.insertRows(ds, index, index, true);
32929         this.onRemove(ds, record, index+1, true);
32930         this.syncRowHeights(index, index);
32931         this.layout();
32932         this.fireEvent("rowupdated", this, index, record);
32933     },
32934
32935     onAdd : function(ds, records, index){
32936         this.insertRows(ds, index, index + (records.length-1));
32937     },
32938
32939     onRemove : function(ds, record, index, isUpdate){
32940         if(isUpdate !== true){
32941             this.fireEvent("beforerowremoved", this, index, record);
32942         }
32943         var bt = this.getBodyTable(), lt = this.getLockedTable();
32944         if(bt.rows[index]){
32945             bt.firstChild.removeChild(bt.rows[index]);
32946         }
32947         if(lt.rows[index]){
32948             lt.firstChild.removeChild(lt.rows[index]);
32949         }
32950         if(isUpdate !== true){
32951             this.stripeRows(index);
32952             this.syncRowHeights(index, index);
32953             this.layout();
32954             this.fireEvent("rowremoved", this, index, record);
32955         }
32956     },
32957
32958     onLoad : function(){
32959         this.scrollToTop();
32960     },
32961
32962     /**
32963      * Scrolls the grid to the top
32964      */
32965     scrollToTop : function(){
32966         if(this.scroller){
32967             this.scroller.dom.scrollTop = 0;
32968             this.syncScroll();
32969         }
32970     },
32971
32972     /**
32973      * Gets a panel in the header of the grid that can be used for toolbars etc.
32974      * After modifying the contents of this panel a call to grid.autoSize() may be
32975      * required to register any changes in size.
32976      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
32977      * @return Roo.Element
32978      */
32979     getHeaderPanel : function(doShow){
32980         if(doShow){
32981             this.headerPanel.show();
32982         }
32983         return this.headerPanel;
32984         },
32985
32986         /**
32987      * Gets a panel in the footer of the grid that can be used for toolbars etc.
32988      * After modifying the contents of this panel a call to grid.autoSize() may be
32989      * required to register any changes in size.
32990      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
32991      * @return Roo.Element
32992      */
32993     getFooterPanel : function(doShow){
32994         if(doShow){
32995             this.footerPanel.show();
32996         }
32997         return this.footerPanel;
32998         },
32999
33000         initElements : function(){
33001             var E = Roo.Element;
33002             var el = this.grid.getGridEl().dom.firstChild;
33003             var cs = el.childNodes;
33004
33005             this.el = new E(el);
33006             this.headerPanel = new E(el.firstChild);
33007             this.headerPanel.enableDisplayMode("block");
33008
33009         this.scroller = new E(cs[1]);
33010             this.scrollSizer = new E(this.scroller.dom.firstChild);
33011
33012             this.lockedWrap = new E(cs[2]);
33013             this.lockedHd = new E(this.lockedWrap.dom.firstChild);
33014             this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
33015
33016             this.mainWrap = new E(cs[3]);
33017             this.mainHd = new E(this.mainWrap.dom.firstChild);
33018             this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
33019
33020             this.footerPanel = new E(cs[4]);
33021             this.footerPanel.enableDisplayMode("block");
33022
33023         this.focusEl = new E(cs[5]);
33024         this.focusEl.swallowEvent("click", true);
33025         this.resizeProxy = new E(cs[6]);
33026
33027             this.headerSelector = String.format(
33028                '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
33029                this.lockedHd.id, this.mainHd.id
33030             );
33031
33032             this.splitterSelector = String.format(
33033                '#{0} div.x-grid-split, #{1} div.x-grid-split',
33034                this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
33035             );
33036     },
33037     idToCssName : function(s)
33038     {
33039         return s.replace(/[^a-z0-9]+/ig, '-');
33040     },
33041
33042         getHeaderCell : function(index){
33043             return Roo.DomQuery.select(this.headerSelector)[index];
33044         },
33045
33046         getHeaderCellMeasure : function(index){
33047             return this.getHeaderCell(index).firstChild;
33048         },
33049
33050         getHeaderCellText : function(index){
33051             return this.getHeaderCell(index).firstChild.firstChild;
33052         },
33053
33054         getLockedTable : function(){
33055             return this.lockedBody.dom.firstChild;
33056         },
33057
33058         getBodyTable : function(){
33059             return this.mainBody.dom.firstChild;
33060         },
33061
33062         getLockedRow : function(index){
33063             return this.getLockedTable().rows[index];
33064         },
33065
33066         getRow : function(index){
33067             return this.getBodyTable().rows[index];
33068         },
33069
33070         getRowComposite : function(index){
33071             if(!this.rowEl){
33072                 this.rowEl = new Roo.CompositeElementLite();
33073             }
33074         var els = [], lrow, mrow;
33075         if(lrow = this.getLockedRow(index)){
33076             els.push(lrow);
33077         }
33078         if(mrow = this.getRow(index)){
33079             els.push(mrow);
33080         }
33081         this.rowEl.elements = els;
33082             return this.rowEl;
33083         },
33084
33085         getCell : function(rowIndex, colIndex){
33086             var locked = this.cm.getLockedCount();
33087             var source;
33088             if(colIndex < locked){
33089                 source = this.lockedBody.dom.firstChild;
33090             }else{
33091                 source = this.mainBody.dom.firstChild;
33092                 colIndex -= locked;
33093             }
33094         return source.rows[rowIndex].childNodes[colIndex];
33095         },
33096
33097         getCellText : function(rowIndex, colIndex){
33098             return this.getCell(rowIndex, colIndex).firstChild.firstChild;
33099         },
33100
33101         getCellBox : function(cell){
33102             var b = this.fly(cell).getBox();
33103         if(Roo.isOpera){ // opera fails to report the Y
33104             b.y = cell.offsetTop + this.mainBody.getY();
33105         }
33106         return b;
33107     },
33108
33109     getCellIndex : function(cell){
33110         var id = String(cell.className).match(this.cellRE);
33111         if(id){
33112             return parseInt(id[1], 10);
33113         }
33114         return 0;
33115     },
33116
33117     findHeaderIndex : function(n){
33118         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33119         return r ? this.getCellIndex(r) : false;
33120     },
33121
33122     findHeaderCell : function(n){
33123         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33124         return r ? r : false;
33125     },
33126
33127     findRowIndex : function(n){
33128         if(!n){
33129             return false;
33130         }
33131         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
33132         return r ? r.rowIndex : false;
33133     },
33134
33135     findCellIndex : function(node){
33136         var stop = this.el.dom;
33137         while(node && node != stop){
33138             if(this.findRE.test(node.className)){
33139                 return this.getCellIndex(node);
33140             }
33141             node = node.parentNode;
33142         }
33143         return false;
33144     },
33145
33146     getColumnId : function(index){
33147             return this.cm.getColumnId(index);
33148         },
33149
33150         getSplitters : function(){
33151             if(this.splitterSelector){
33152                return Roo.DomQuery.select(this.splitterSelector);
33153             }else{
33154                 return null;
33155             }
33156         },
33157
33158         getSplitter : function(index){
33159             return this.getSplitters()[index];
33160         },
33161
33162     onRowOver : function(e, t){
33163         var row;
33164         if((row = this.findRowIndex(t)) !== false){
33165             this.getRowComposite(row).addClass("x-grid-row-over");
33166         }
33167     },
33168
33169     onRowOut : function(e, t){
33170         var row;
33171         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
33172             this.getRowComposite(row).removeClass("x-grid-row-over");
33173         }
33174     },
33175
33176     renderHeaders : function(){
33177             var cm = this.cm;
33178         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
33179         var cb = [], lb = [], sb = [], lsb = [], p = {};
33180         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33181             p.cellId = "x-grid-hd-0-" + i;
33182             p.splitId = "x-grid-csplit-0-" + i;
33183             p.id = cm.getColumnId(i);
33184             p.title = cm.getColumnTooltip(i) || "";
33185             p.value = cm.getColumnHeader(i) || "";
33186             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
33187             if(!cm.isLocked(i)){
33188                 cb[cb.length] = ct.apply(p);
33189                 sb[sb.length] = st.apply(p);
33190             }else{
33191                 lb[lb.length] = ct.apply(p);
33192                 lsb[lsb.length] = st.apply(p);
33193             }
33194         }
33195         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
33196                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
33197         },
33198
33199         updateHeaders : function(){
33200         var html = this.renderHeaders();
33201         this.lockedHd.update(html[0]);
33202         this.mainHd.update(html[1]);
33203     },
33204
33205     /**
33206      * Focuses the specified row.
33207      * @param {Number} row The row index
33208      */
33209     focusRow : function(row){
33210         var x = this.scroller.dom.scrollLeft;
33211         this.focusCell(row, 0, false);
33212         this.scroller.dom.scrollLeft = x;
33213     },
33214
33215     /**
33216      * Focuses the specified cell.
33217      * @param {Number} row The row index
33218      * @param {Number} col The column index
33219      * @param {Boolean} hscroll false to disable horizontal scrolling
33220      */
33221     focusCell : function(row, col, hscroll){
33222         var el = this.ensureVisible(row, col, hscroll);
33223         this.focusEl.alignTo(el, "tl-tl");
33224         if(Roo.isGecko){
33225             this.focusEl.focus();
33226         }else{
33227             this.focusEl.focus.defer(1, this.focusEl);
33228         }
33229     },
33230
33231     /**
33232      * Scrolls the specified cell into view
33233      * @param {Number} row The row index
33234      * @param {Number} col The column index
33235      * @param {Boolean} hscroll false to disable horizontal scrolling
33236      */
33237     ensureVisible : function(row, col, hscroll){
33238         if(typeof row != "number"){
33239             row = row.rowIndex;
33240         }
33241         if(row < 0 && row >= this.ds.getCount()){
33242             return;
33243         }
33244         col = (col !== undefined ? col : 0);
33245         var cm = this.grid.colModel;
33246         while(cm.isHidden(col)){
33247             col++;
33248         }
33249
33250         var el = this.getCell(row, col);
33251         if(!el){
33252             return;
33253         }
33254         var c = this.scroller.dom;
33255
33256         var ctop = parseInt(el.offsetTop, 10);
33257         var cleft = parseInt(el.offsetLeft, 10);
33258         var cbot = ctop + el.offsetHeight;
33259         var cright = cleft + el.offsetWidth;
33260
33261         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
33262         var stop = parseInt(c.scrollTop, 10);
33263         var sleft = parseInt(c.scrollLeft, 10);
33264         var sbot = stop + ch;
33265         var sright = sleft + c.clientWidth;
33266
33267         if(ctop < stop){
33268                 c.scrollTop = ctop;
33269         }else if(cbot > sbot){
33270             c.scrollTop = cbot-ch;
33271         }
33272
33273         if(hscroll !== false){
33274             if(cleft < sleft){
33275                 c.scrollLeft = cleft;
33276             }else if(cright > sright){
33277                 c.scrollLeft = cright-c.clientWidth;
33278             }
33279         }
33280         return el;
33281     },
33282
33283     updateColumns : function(){
33284         this.grid.stopEditing();
33285         var cm = this.grid.colModel, colIds = this.getColumnIds();
33286         //var totalWidth = cm.getTotalWidth();
33287         var pos = 0;
33288         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33289             //if(cm.isHidden(i)) continue;
33290             var w = cm.getColumnWidth(i);
33291             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33292             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33293         }
33294         this.updateSplitters();
33295     },
33296
33297     generateRules : function(cm){
33298         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
33299         Roo.util.CSS.removeStyleSheet(rulesId);
33300         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33301             var cid = cm.getColumnId(i);
33302             var align = '';
33303             if(cm.config[i].align){
33304                 align = 'text-align:'+cm.config[i].align+';';
33305             }
33306             var hidden = '';
33307             if(cm.isHidden(i)){
33308                 hidden = 'display:none;';
33309             }
33310             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
33311             ruleBuf.push(
33312                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
33313                     this.hdSelector, cid, " {\n", align, width, "}\n",
33314                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
33315                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
33316         }
33317         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
33318     },
33319
33320     updateSplitters : function(){
33321         var cm = this.cm, s = this.getSplitters();
33322         if(s){ // splitters not created yet
33323             var pos = 0, locked = true;
33324             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33325                 if(cm.isHidden(i)) continue;
33326                 var w = cm.getColumnWidth(i);
33327                 if(!cm.isLocked(i) && locked){
33328                     pos = 0;
33329                     locked = false;
33330                 }
33331                 pos += w;
33332                 s[i].style.left = (pos-this.splitOffset) + "px";
33333             }
33334         }
33335     },
33336
33337     handleHiddenChange : function(colModel, colIndex, hidden){
33338         if(hidden){
33339             this.hideColumn(colIndex);
33340         }else{
33341             this.unhideColumn(colIndex);
33342         }
33343     },
33344
33345     hideColumn : function(colIndex){
33346         var cid = this.getColumnId(colIndex);
33347         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
33348         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
33349         if(Roo.isSafari){
33350             this.updateHeaders();
33351         }
33352         this.updateSplitters();
33353         this.layout();
33354     },
33355
33356     unhideColumn : function(colIndex){
33357         var cid = this.getColumnId(colIndex);
33358         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
33359         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
33360
33361         if(Roo.isSafari){
33362             this.updateHeaders();
33363         }
33364         this.updateSplitters();
33365         this.layout();
33366     },
33367
33368     insertRows : function(dm, firstRow, lastRow, isUpdate){
33369         if(firstRow == 0 && lastRow == dm.getCount()-1){
33370             this.refresh();
33371         }else{
33372             if(!isUpdate){
33373                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
33374             }
33375             var s = this.getScrollState();
33376             var markup = this.renderRows(firstRow, lastRow);
33377             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
33378             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
33379             this.restoreScroll(s);
33380             if(!isUpdate){
33381                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
33382                 this.syncRowHeights(firstRow, lastRow);
33383                 this.stripeRows(firstRow);
33384                 this.layout();
33385             }
33386         }
33387     },
33388
33389     bufferRows : function(markup, target, index){
33390         var before = null, trows = target.rows, tbody = target.tBodies[0];
33391         if(index < trows.length){
33392             before = trows[index];
33393         }
33394         var b = document.createElement("div");
33395         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
33396         var rows = b.firstChild.rows;
33397         for(var i = 0, len = rows.length; i < len; i++){
33398             if(before){
33399                 tbody.insertBefore(rows[0], before);
33400             }else{
33401                 tbody.appendChild(rows[0]);
33402             }
33403         }
33404         b.innerHTML = "";
33405         b = null;
33406     },
33407
33408     deleteRows : function(dm, firstRow, lastRow){
33409         if(dm.getRowCount()<1){
33410             this.fireEvent("beforerefresh", this);
33411             this.mainBody.update("");
33412             this.lockedBody.update("");
33413             this.fireEvent("refresh", this);
33414         }else{
33415             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
33416             var bt = this.getBodyTable();
33417             var tbody = bt.firstChild;
33418             var rows = bt.rows;
33419             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
33420                 tbody.removeChild(rows[firstRow]);
33421             }
33422             this.stripeRows(firstRow);
33423             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
33424         }
33425     },
33426
33427     updateRows : function(dataSource, firstRow, lastRow){
33428         var s = this.getScrollState();
33429         this.refresh();
33430         this.restoreScroll(s);
33431     },
33432
33433     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
33434         if(!noRefresh){
33435            this.refresh();
33436         }
33437         this.updateHeaderSortState();
33438     },
33439
33440     getScrollState : function(){
33441         var sb = this.scroller.dom;
33442         return {left: sb.scrollLeft, top: sb.scrollTop};
33443     },
33444
33445     stripeRows : function(startRow){
33446         if(!this.grid.stripeRows || this.ds.getCount() < 1){
33447             return;
33448         }
33449         startRow = startRow || 0;
33450         var rows = this.getBodyTable().rows;
33451         var lrows = this.getLockedTable().rows;
33452         var cls = ' x-grid-row-alt ';
33453         for(var i = startRow, len = rows.length; i < len; i++){
33454             var row = rows[i], lrow = lrows[i];
33455             var isAlt = ((i+1) % 2 == 0);
33456             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
33457             if(isAlt == hasAlt){
33458                 continue;
33459             }
33460             if(isAlt){
33461                 row.className += " x-grid-row-alt";
33462             }else{
33463                 row.className = row.className.replace("x-grid-row-alt", "");
33464             }
33465             if(lrow){
33466                 lrow.className = row.className;
33467             }
33468         }
33469     },
33470
33471     restoreScroll : function(state){
33472         var sb = this.scroller.dom;
33473         sb.scrollLeft = state.left;
33474         sb.scrollTop = state.top;
33475         this.syncScroll();
33476     },
33477
33478     syncScroll : function(){
33479         var sb = this.scroller.dom;
33480         var sh = this.mainHd.dom;
33481         var bs = this.mainBody.dom;
33482         var lv = this.lockedBody.dom;
33483         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
33484         lv.scrollTop = bs.scrollTop = sb.scrollTop;
33485     },
33486
33487     handleScroll : function(e){
33488         this.syncScroll();
33489         var sb = this.scroller.dom;
33490         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
33491         e.stopEvent();
33492     },
33493
33494     handleWheel : function(e){
33495         var d = e.getWheelDelta();
33496         this.scroller.dom.scrollTop -= d*22;
33497         // set this here to prevent jumpy scrolling on large tables
33498         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
33499         e.stopEvent();
33500     },
33501
33502     renderRows : function(startRow, endRow){
33503         // pull in all the crap needed to render rows
33504         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
33505         var colCount = cm.getColumnCount();
33506
33507         if(ds.getCount() < 1){
33508             return ["", ""];
33509         }
33510
33511         // build a map for all the columns
33512         var cs = [];
33513         for(var i = 0; i < colCount; i++){
33514             var name = cm.getDataIndex(i);
33515             cs[i] = {
33516                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
33517                 renderer : cm.getRenderer(i),
33518                 id : cm.getColumnId(i),
33519                 locked : cm.isLocked(i)
33520             };
33521         }
33522
33523         startRow = startRow || 0;
33524         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
33525
33526         // records to render
33527         var rs = ds.getRange(startRow, endRow);
33528
33529         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
33530     },
33531
33532     // As much as I hate to duplicate code, this was branched because FireFox really hates
33533     // [].join("") on strings. The performance difference was substantial enough to
33534     // branch this function
33535     doRender : Roo.isGecko ?
33536             function(cs, rs, ds, startRow, colCount, stripe){
33537                 var ts = this.templates, ct = ts.cell, rt = ts.row;
33538                 // buffers
33539                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
33540                 for(var j = 0, len = rs.length; j < len; j++){
33541                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
33542                     for(var i = 0; i < colCount; i++){
33543                         c = cs[i];
33544                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
33545                         p.id = c.id;
33546                         p.css = p.attr = "";
33547                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
33548                         if(p.value == undefined || p.value === "") p.value = "&#160;";
33549                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
33550                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
33551                         }
33552                         var markup = ct.apply(p);
33553                         if(!c.locked){
33554                             cb+= markup;
33555                         }else{
33556                             lcb+= markup;
33557                         }
33558                     }
33559                     var alt = [];
33560                     if(stripe && ((rowIndex+1) % 2 == 0)){
33561                         alt[0] = "x-grid-row-alt";
33562                     }
33563                     if(r.dirty){
33564                         alt[1] = " x-grid-dirty-row";
33565                     }
33566                     rp.cells = lcb;
33567                     if(this.getRowClass){
33568                         alt[2] = this.getRowClass(r, rowIndex);
33569                     }
33570                     rp.alt = alt.join(" ");
33571                     lbuf+= rt.apply(rp);
33572                     rp.cells = cb;
33573                     buf+=  rt.apply(rp);
33574                 }
33575                 return [lbuf, buf];
33576             } :
33577             function(cs, rs, ds, startRow, colCount, stripe){
33578                 var ts = this.templates, ct = ts.cell, rt = ts.row;
33579                 // buffers
33580                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
33581                 for(var j = 0, len = rs.length; j < len; j++){
33582                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
33583                     for(var i = 0; i < colCount; i++){
33584                         c = cs[i];
33585                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
33586                         p.id = c.id;
33587                         p.css = p.attr = "";
33588                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
33589                         if(p.value == undefined || p.value === "") p.value = "&#160;";
33590                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
33591                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
33592                         }
33593                         var markup = ct.apply(p);
33594                         if(!c.locked){
33595                             cb[cb.length] = markup;
33596                         }else{
33597                             lcb[lcb.length] = markup;
33598                         }
33599                     }
33600                     var alt = [];
33601                     if(stripe && ((rowIndex+1) % 2 == 0)){
33602                         alt[0] = "x-grid-row-alt";
33603                     }
33604                     if(r.dirty){
33605                         alt[1] = " x-grid-dirty-row";
33606                     }
33607                     rp.cells = lcb;
33608                     if(this.getRowClass){
33609                         alt[2] = this.getRowClass(r, rowIndex);
33610                     }
33611                     rp.alt = alt.join(" ");
33612                     rp.cells = lcb.join("");
33613                     lbuf[lbuf.length] = rt.apply(rp);
33614                     rp.cells = cb.join("");
33615                     buf[buf.length] =  rt.apply(rp);
33616                 }
33617                 return [lbuf.join(""), buf.join("")];
33618             },
33619
33620     renderBody : function(){
33621         var markup = this.renderRows();
33622         var bt = this.templates.body;
33623         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
33624     },
33625
33626     /**
33627      * Refreshes the grid
33628      * @param {Boolean} headersToo
33629      */
33630     refresh : function(headersToo){
33631         this.fireEvent("beforerefresh", this);
33632         this.grid.stopEditing();
33633         var result = this.renderBody();
33634         this.lockedBody.update(result[0]);
33635         this.mainBody.update(result[1]);
33636         if(headersToo === true){
33637             this.updateHeaders();
33638             this.updateColumns();
33639             this.updateSplitters();
33640             this.updateHeaderSortState();
33641         }
33642         this.syncRowHeights();
33643         this.layout();
33644         this.fireEvent("refresh", this);
33645     },
33646
33647     handleColumnMove : function(cm, oldIndex, newIndex){
33648         this.indexMap = null;
33649         var s = this.getScrollState();
33650         this.refresh(true);
33651         this.restoreScroll(s);
33652         this.afterMove(newIndex);
33653     },
33654
33655     afterMove : function(colIndex){
33656         if(this.enableMoveAnim && Roo.enableFx){
33657             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
33658         }
33659     },
33660
33661     updateCell : function(dm, rowIndex, dataIndex){
33662         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
33663         if(typeof colIndex == "undefined"){ // not present in grid
33664             return;
33665         }
33666         var cm = this.grid.colModel;
33667         var cell = this.getCell(rowIndex, colIndex);
33668         var cellText = this.getCellText(rowIndex, colIndex);
33669
33670         var p = {
33671             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
33672             id : cm.getColumnId(colIndex),
33673             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
33674         };
33675         var renderer = cm.getRenderer(colIndex);
33676         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
33677         if(typeof val == "undefined" || val === "") val = "&#160;";
33678         cellText.innerHTML = val;
33679         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
33680         this.syncRowHeights(rowIndex, rowIndex);
33681     },
33682
33683     calcColumnWidth : function(colIndex, maxRowsToMeasure){
33684         var maxWidth = 0;
33685         if(this.grid.autoSizeHeaders){
33686             var h = this.getHeaderCellMeasure(colIndex);
33687             maxWidth = Math.max(maxWidth, h.scrollWidth);
33688         }
33689         var tb, index;
33690         if(this.cm.isLocked(colIndex)){
33691             tb = this.getLockedTable();
33692             index = colIndex;
33693         }else{
33694             tb = this.getBodyTable();
33695             index = colIndex - this.cm.getLockedCount();
33696         }
33697         if(tb && tb.rows){
33698             var rows = tb.rows;
33699             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
33700             for(var i = 0; i < stopIndex; i++){
33701                 var cell = rows[i].childNodes[index].firstChild;
33702                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
33703             }
33704         }
33705         return maxWidth + /*margin for error in IE*/ 5;
33706     },
33707     /**
33708      * Autofit a column to its content.
33709      * @param {Number} colIndex
33710      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
33711      */
33712      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
33713          if(this.cm.isHidden(colIndex)){
33714              return; // can't calc a hidden column
33715          }
33716         if(forceMinSize){
33717             var cid = this.cm.getColumnId(colIndex);
33718             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
33719            if(this.grid.autoSizeHeaders){
33720                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
33721            }
33722         }
33723         var newWidth = this.calcColumnWidth(colIndex);
33724         this.cm.setColumnWidth(colIndex,
33725             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
33726         if(!suppressEvent){
33727             this.grid.fireEvent("columnresize", colIndex, newWidth);
33728         }
33729     },
33730
33731     /**
33732      * Autofits all columns to their content and then expands to fit any extra space in the grid
33733      */
33734      autoSizeColumns : function(){
33735         var cm = this.grid.colModel;
33736         var colCount = cm.getColumnCount();
33737         for(var i = 0; i < colCount; i++){
33738             this.autoSizeColumn(i, true, true);
33739         }
33740         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
33741             this.fitColumns();
33742         }else{
33743             this.updateColumns();
33744             this.layout();
33745         }
33746     },
33747
33748     /**
33749      * Autofits all columns to the grid's width proportionate with their current size
33750      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
33751      */
33752     fitColumns : function(reserveScrollSpace){
33753         var cm = this.grid.colModel;
33754         var colCount = cm.getColumnCount();
33755         var cols = [];
33756         var width = 0;
33757         var i, w;
33758         for (i = 0; i < colCount; i++){
33759             if(!cm.isHidden(i) && !cm.isFixed(i)){
33760                 w = cm.getColumnWidth(i);
33761                 cols.push(i);
33762                 cols.push(w);
33763                 width += w;
33764             }
33765         }
33766         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
33767         if(reserveScrollSpace){
33768             avail -= 17;
33769         }
33770         var frac = (avail - cm.getTotalWidth())/width;
33771         while (cols.length){
33772             w = cols.pop();
33773             i = cols.pop();
33774             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
33775         }
33776         this.updateColumns();
33777         this.layout();
33778     },
33779
33780     onRowSelect : function(rowIndex){
33781         var row = this.getRowComposite(rowIndex);
33782         row.addClass("x-grid-row-selected");
33783     },
33784
33785     onRowDeselect : function(rowIndex){
33786         var row = this.getRowComposite(rowIndex);
33787         row.removeClass("x-grid-row-selected");
33788     },
33789
33790     onCellSelect : function(row, col){
33791         var cell = this.getCell(row, col);
33792         if(cell){
33793             Roo.fly(cell).addClass("x-grid-cell-selected");
33794         }
33795     },
33796
33797     onCellDeselect : function(row, col){
33798         var cell = this.getCell(row, col);
33799         if(cell){
33800             Roo.fly(cell).removeClass("x-grid-cell-selected");
33801         }
33802     },
33803
33804     updateHeaderSortState : function(){
33805         var state = this.ds.getSortState();
33806         if(!state){
33807             return;
33808         }
33809         this.sortState = state;
33810         var sortColumn = this.cm.findColumnIndex(state.field);
33811         if(sortColumn != -1){
33812             var sortDir = state.direction;
33813             var sc = this.sortClasses;
33814             var hds = this.el.select(this.headerSelector).removeClass(sc);
33815             hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
33816         }
33817     },
33818
33819     handleHeaderClick : function(g, index){
33820         if(this.headersDisabled){
33821             return;
33822         }
33823         var dm = g.dataSource, cm = g.colModel;
33824             if(!cm.isSortable(index)){
33825             return;
33826         }
33827             g.stopEditing();
33828         dm.sort(cm.getDataIndex(index));
33829     },
33830
33831
33832     destroy : function(){
33833         if(this.colMenu){
33834             this.colMenu.removeAll();
33835             Roo.menu.MenuMgr.unregister(this.colMenu);
33836             this.colMenu.getEl().remove();
33837             delete this.colMenu;
33838         }
33839         if(this.hmenu){
33840             this.hmenu.removeAll();
33841             Roo.menu.MenuMgr.unregister(this.hmenu);
33842             this.hmenu.getEl().remove();
33843             delete this.hmenu;
33844         }
33845         if(this.grid.enableColumnMove){
33846             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
33847             if(dds){
33848                 for(var dd in dds){
33849                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
33850                         var elid = dds[dd].dragElId;
33851                         dds[dd].unreg();
33852                         Roo.get(elid).remove();
33853                     } else if(dds[dd].config.isTarget){
33854                         dds[dd].proxyTop.remove();
33855                         dds[dd].proxyBottom.remove();
33856                         dds[dd].unreg();
33857                     }
33858                     if(Roo.dd.DDM.locationCache[dd]){
33859                         delete Roo.dd.DDM.locationCache[dd];
33860                     }
33861                 }
33862                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
33863             }
33864         }
33865         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
33866         this.bind(null, null);
33867         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
33868     },
33869
33870     handleLockChange : function(){
33871         this.refresh(true);
33872     },
33873
33874     onDenyColumnLock : function(){
33875
33876     },
33877
33878     onDenyColumnHide : function(){
33879
33880     },
33881
33882     handleHdMenuClick : function(item){
33883         var index = this.hdCtxIndex;
33884         var cm = this.cm, ds = this.ds;
33885         switch(item.id){
33886             case "asc":
33887                 ds.sort(cm.getDataIndex(index), "ASC");
33888                 break;
33889             case "desc":
33890                 ds.sort(cm.getDataIndex(index), "DESC");
33891                 break;
33892             case "lock":
33893                 var lc = cm.getLockedCount();
33894                 if(cm.getColumnCount(true) <= lc+1){
33895                     this.onDenyColumnLock();
33896                     return;
33897                 }
33898                 if(lc != index){
33899                     cm.setLocked(index, true, true);
33900                     cm.moveColumn(index, lc);
33901                     this.grid.fireEvent("columnmove", index, lc);
33902                 }else{
33903                     cm.setLocked(index, true);
33904                 }
33905             break;
33906             case "unlock":
33907                 var lc = cm.getLockedCount();
33908                 if((lc-1) != index){
33909                     cm.setLocked(index, false, true);
33910                     cm.moveColumn(index, lc-1);
33911                     this.grid.fireEvent("columnmove", index, lc-1);
33912                 }else{
33913                     cm.setLocked(index, false);
33914                 }
33915             break;
33916             default:
33917                 index = cm.getIndexById(item.id.substr(4));
33918                 if(index != -1){
33919                     if(item.checked && cm.getColumnCount(true) <= 1){
33920                         this.onDenyColumnHide();
33921                         return false;
33922                     }
33923                     cm.setHidden(index, item.checked);
33924                 }
33925         }
33926         return true;
33927     },
33928
33929     beforeColMenuShow : function(){
33930         var cm = this.cm,  colCount = cm.getColumnCount();
33931         this.colMenu.removeAll();
33932         for(var i = 0; i < colCount; i++){
33933             this.colMenu.add(new Roo.menu.CheckItem({
33934                 id: "col-"+cm.getColumnId(i),
33935                 text: cm.getColumnHeader(i),
33936                 checked: !cm.isHidden(i),
33937                 hideOnClick:false
33938             }));
33939         }
33940     },
33941
33942     handleHdCtx : function(g, index, e){
33943         e.stopEvent();
33944         var hd = this.getHeaderCell(index);
33945         this.hdCtxIndex = index;
33946         var ms = this.hmenu.items, cm = this.cm;
33947         ms.get("asc").setDisabled(!cm.isSortable(index));
33948         ms.get("desc").setDisabled(!cm.isSortable(index));
33949         if(this.grid.enableColLock !== false){
33950             ms.get("lock").setDisabled(cm.isLocked(index));
33951             ms.get("unlock").setDisabled(!cm.isLocked(index));
33952         }
33953         this.hmenu.show(hd, "tl-bl");
33954     },
33955
33956     handleHdOver : function(e){
33957         var hd = this.findHeaderCell(e.getTarget());
33958         if(hd && !this.headersDisabled){
33959             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
33960                this.fly(hd).addClass("x-grid-hd-over");
33961             }
33962         }
33963     },
33964
33965     handleHdOut : function(e){
33966         var hd = this.findHeaderCell(e.getTarget());
33967         if(hd){
33968             this.fly(hd).removeClass("x-grid-hd-over");
33969         }
33970     },
33971
33972     handleSplitDblClick : function(e, t){
33973         var i = this.getCellIndex(t);
33974         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
33975             this.autoSizeColumn(i, true);
33976             this.layout();
33977         }
33978     },
33979
33980     render : function(){
33981
33982         var cm = this.cm;
33983         var colCount = cm.getColumnCount();
33984
33985         if(this.grid.monitorWindowResize === true){
33986             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
33987         }
33988         var header = this.renderHeaders();
33989         var body = this.templates.body.apply({rows:""});
33990         var html = this.templates.master.apply({
33991             lockedBody: body,
33992             body: body,
33993             lockedHeader: header[0],
33994             header: header[1]
33995         });
33996
33997         //this.updateColumns();
33998
33999         this.grid.getGridEl().dom.innerHTML = html;
34000
34001         this.initElements();
34002         
34003         // a kludge to fix the random scolling effect in webkit
34004         this.el.on("scroll", function() {
34005             this.el.dom.scrollTop=0; // hopefully not recursive..
34006         },this);
34007
34008         this.scroller.on("scroll", this.handleScroll, this);
34009         this.lockedBody.on("mousewheel", this.handleWheel, this);
34010         this.mainBody.on("mousewheel", this.handleWheel, this);
34011
34012         this.mainHd.on("mouseover", this.handleHdOver, this);
34013         this.mainHd.on("mouseout", this.handleHdOut, this);
34014         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
34015                 {delegate: "."+this.splitClass});
34016
34017         this.lockedHd.on("mouseover", this.handleHdOver, this);
34018         this.lockedHd.on("mouseout", this.handleHdOut, this);
34019         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
34020                 {delegate: "."+this.splitClass});
34021
34022         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
34023             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34024         }
34025
34026         this.updateSplitters();
34027
34028         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
34029             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34030             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34031         }
34032
34033         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
34034             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
34035             this.hmenu.add(
34036                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
34037                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
34038             );
34039             if(this.grid.enableColLock !== false){
34040                 this.hmenu.add('-',
34041                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
34042                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
34043                 );
34044             }
34045             if(this.grid.enableColumnHide !== false){
34046
34047                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
34048                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
34049                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
34050
34051                 this.hmenu.add('-',
34052                     {id:"columns", text: this.columnsText, menu: this.colMenu}
34053                 );
34054             }
34055             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
34056
34057             this.grid.on("headercontextmenu", this.handleHdCtx, this);
34058         }
34059
34060         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
34061             this.dd = new Roo.grid.GridDragZone(this.grid, {
34062                 ddGroup : this.grid.ddGroup || 'GridDD'
34063             });
34064         }
34065
34066         /*
34067         for(var i = 0; i < colCount; i++){
34068             if(cm.isHidden(i)){
34069                 this.hideColumn(i);
34070             }
34071             if(cm.config[i].align){
34072                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
34073                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
34074             }
34075         }*/
34076         
34077         this.updateHeaderSortState();
34078
34079         this.beforeInitialResize();
34080         this.layout(true);
34081
34082         // two part rendering gives faster view to the user
34083         this.renderPhase2.defer(1, this);
34084     },
34085
34086     renderPhase2 : function(){
34087         // render the rows now
34088         this.refresh();
34089         if(this.grid.autoSizeColumns){
34090             this.autoSizeColumns();
34091         }
34092     },
34093
34094     beforeInitialResize : function(){
34095
34096     },
34097
34098     onColumnSplitterMoved : function(i, w){
34099         this.userResized = true;
34100         var cm = this.grid.colModel;
34101         cm.setColumnWidth(i, w, true);
34102         var cid = cm.getColumnId(i);
34103         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34104         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34105         this.updateSplitters();
34106         this.layout();
34107         this.grid.fireEvent("columnresize", i, w);
34108     },
34109
34110     syncRowHeights : function(startIndex, endIndex){
34111         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
34112             startIndex = startIndex || 0;
34113             var mrows = this.getBodyTable().rows;
34114             var lrows = this.getLockedTable().rows;
34115             var len = mrows.length-1;
34116             endIndex = Math.min(endIndex || len, len);
34117             for(var i = startIndex; i <= endIndex; i++){
34118                 var m = mrows[i], l = lrows[i];
34119                 var h = Math.max(m.offsetHeight, l.offsetHeight);
34120                 m.style.height = l.style.height = h + "px";
34121             }
34122         }
34123     },
34124
34125     layout : function(initialRender, is2ndPass){
34126         var g = this.grid;
34127         var auto = g.autoHeight;
34128         var scrollOffset = 16;
34129         var c = g.getGridEl(), cm = this.cm,
34130                 expandCol = g.autoExpandColumn,
34131                 gv = this;
34132         //c.beginMeasure();
34133
34134         if(!c.dom.offsetWidth){ // display:none?
34135             if(initialRender){
34136                 this.lockedWrap.show();
34137                 this.mainWrap.show();
34138             }
34139             return;
34140         }
34141
34142         var hasLock = this.cm.isLocked(0);
34143
34144         var tbh = this.headerPanel.getHeight();
34145         var bbh = this.footerPanel.getHeight();
34146
34147         if(auto){
34148             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
34149             var newHeight = ch + c.getBorderWidth("tb");
34150             if(g.maxHeight){
34151                 newHeight = Math.min(g.maxHeight, newHeight);
34152             }
34153             c.setHeight(newHeight);
34154         }
34155
34156         if(g.autoWidth){
34157             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
34158         }
34159
34160         var s = this.scroller;
34161
34162         var csize = c.getSize(true);
34163
34164         this.el.setSize(csize.width, csize.height);
34165
34166         this.headerPanel.setWidth(csize.width);
34167         this.footerPanel.setWidth(csize.width);
34168
34169         var hdHeight = this.mainHd.getHeight();
34170         var vw = csize.width;
34171         var vh = csize.height - (tbh + bbh);
34172
34173         s.setSize(vw, vh);
34174
34175         var bt = this.getBodyTable();
34176         var ltWidth = hasLock ?
34177                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
34178
34179         var scrollHeight = bt.offsetHeight;
34180         var scrollWidth = ltWidth + bt.offsetWidth;
34181         var vscroll = false, hscroll = false;
34182
34183         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
34184
34185         var lw = this.lockedWrap, mw = this.mainWrap;
34186         var lb = this.lockedBody, mb = this.mainBody;
34187
34188         setTimeout(function(){
34189             var t = s.dom.offsetTop;
34190             var w = s.dom.clientWidth,
34191                 h = s.dom.clientHeight;
34192
34193             lw.setTop(t);
34194             lw.setSize(ltWidth, h);
34195
34196             mw.setLeftTop(ltWidth, t);
34197             mw.setSize(w-ltWidth, h);
34198
34199             lb.setHeight(h-hdHeight);
34200             mb.setHeight(h-hdHeight);
34201
34202             if(is2ndPass !== true && !gv.userResized && expandCol){
34203                 // high speed resize without full column calculation
34204                 
34205                 var ci = cm.getIndexById(expandCol);
34206                 if (ci < 0) {
34207                     ci = cm.findColumnIndex(expandCol);
34208                 }
34209                 ci = Math.max(0, ci); // make sure it's got at least the first col.
34210                 var expandId = cm.getColumnId(ci);
34211                 var  tw = cm.getTotalWidth(false);
34212                 var currentWidth = cm.getColumnWidth(ci);
34213                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
34214                 if(currentWidth != cw){
34215                     cm.setColumnWidth(ci, cw, true);
34216                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34217                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34218                     gv.updateSplitters();
34219                     gv.layout(false, true);
34220                 }
34221             }
34222
34223             if(initialRender){
34224                 lw.show();
34225                 mw.show();
34226             }
34227             //c.endMeasure();
34228         }, 10);
34229     },
34230
34231     onWindowResize : function(){
34232         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
34233             return;
34234         }
34235         this.layout();
34236     },
34237
34238     appendFooter : function(parentEl){
34239         return null;
34240     },
34241
34242     sortAscText : "Sort Ascending",
34243     sortDescText : "Sort Descending",
34244     lockText : "Lock Column",
34245     unlockText : "Unlock Column",
34246     columnsText : "Columns"
34247 });
34248
34249
34250 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
34251     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
34252     this.proxy.el.addClass('x-grid3-col-dd');
34253 };
34254
34255 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
34256     handleMouseDown : function(e){
34257
34258     },
34259
34260     callHandleMouseDown : function(e){
34261         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
34262     }
34263 });
34264 /*
34265  * Based on:
34266  * Ext JS Library 1.1.1
34267  * Copyright(c) 2006-2007, Ext JS, LLC.
34268  *
34269  * Originally Released Under LGPL - original licence link has changed is not relivant.
34270  *
34271  * Fork - LGPL
34272  * <script type="text/javascript">
34273  */
34274  
34275 // private
34276 // This is a support class used internally by the Grid components
34277 Roo.grid.SplitDragZone = function(grid, hd, hd2){
34278     this.grid = grid;
34279     this.view = grid.getView();
34280     this.proxy = this.view.resizeProxy;
34281     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
34282         "gridSplitters" + this.grid.getGridEl().id, {
34283         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
34284     });
34285     this.setHandleElId(Roo.id(hd));
34286     this.setOuterHandleElId(Roo.id(hd2));
34287     this.scroll = false;
34288 };
34289 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
34290     fly: Roo.Element.fly,
34291
34292     b4StartDrag : function(x, y){
34293         this.view.headersDisabled = true;
34294         this.proxy.setHeight(this.view.mainWrap.getHeight());
34295         var w = this.cm.getColumnWidth(this.cellIndex);
34296         var minw = Math.max(w-this.grid.minColumnWidth, 0);
34297         this.resetConstraints();
34298         this.setXConstraint(minw, 1000);
34299         this.setYConstraint(0, 0);
34300         this.minX = x - minw;
34301         this.maxX = x + 1000;
34302         this.startPos = x;
34303         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
34304     },
34305
34306
34307     handleMouseDown : function(e){
34308         ev = Roo.EventObject.setEvent(e);
34309         var t = this.fly(ev.getTarget());
34310         if(t.hasClass("x-grid-split")){
34311             this.cellIndex = this.view.getCellIndex(t.dom);
34312             this.split = t.dom;
34313             this.cm = this.grid.colModel;
34314             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
34315                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
34316             }
34317         }
34318     },
34319
34320     endDrag : function(e){
34321         this.view.headersDisabled = false;
34322         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
34323         var diff = endX - this.startPos;
34324         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
34325     },
34326
34327     autoOffset : function(){
34328         this.setDelta(0,0);
34329     }
34330 });/*
34331  * Based on:
34332  * Ext JS Library 1.1.1
34333  * Copyright(c) 2006-2007, Ext JS, LLC.
34334  *
34335  * Originally Released Under LGPL - original licence link has changed is not relivant.
34336  *
34337  * Fork - LGPL
34338  * <script type="text/javascript">
34339  */
34340  
34341 // private
34342 // This is a support class used internally by the Grid components
34343 Roo.grid.GridDragZone = function(grid, config){
34344     this.view = grid.getView();
34345     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
34346     if(this.view.lockedBody){
34347         this.setHandleElId(Roo.id(this.view.mainBody.dom));
34348         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
34349     }
34350     this.scroll = false;
34351     this.grid = grid;
34352     this.ddel = document.createElement('div');
34353     this.ddel.className = 'x-grid-dd-wrap';
34354 };
34355
34356 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
34357     ddGroup : "GridDD",
34358
34359     getDragData : function(e){
34360         var t = Roo.lib.Event.getTarget(e);
34361         var rowIndex = this.view.findRowIndex(t);
34362         if(rowIndex !== false){
34363             var sm = this.grid.selModel;
34364             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
34365               //  sm.mouseDown(e, t);
34366             //}
34367             if (e.hasModifier()){
34368                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
34369             }
34370             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
34371         }
34372         return false;
34373     },
34374
34375     onInitDrag : function(e){
34376         var data = this.dragData;
34377         this.ddel.innerHTML = this.grid.getDragDropText();
34378         this.proxy.update(this.ddel);
34379         // fire start drag?
34380     },
34381
34382     afterRepair : function(){
34383         this.dragging = false;
34384     },
34385
34386     getRepairXY : function(e, data){
34387         return false;
34388     },
34389
34390     onEndDrag : function(data, e){
34391         // fire end drag?
34392     },
34393
34394     onValidDrop : function(dd, e, id){
34395         // fire drag drop?
34396         this.hideProxy();
34397     },
34398
34399     beforeInvalidDrop : function(e, id){
34400
34401     }
34402 });/*
34403  * Based on:
34404  * Ext JS Library 1.1.1
34405  * Copyright(c) 2006-2007, Ext JS, LLC.
34406  *
34407  * Originally Released Under LGPL - original licence link has changed is not relivant.
34408  *
34409  * Fork - LGPL
34410  * <script type="text/javascript">
34411  */
34412  
34413
34414 /**
34415  * @class Roo.grid.ColumnModel
34416  * @extends Roo.util.Observable
34417  * This is the default implementation of a ColumnModel used by the Grid. It defines
34418  * the columns in the grid.
34419  * <br>Usage:<br>
34420  <pre><code>
34421  var colModel = new Roo.grid.ColumnModel([
34422         {header: "Ticker", width: 60, sortable: true, locked: true},
34423         {header: "Company Name", width: 150, sortable: true},
34424         {header: "Market Cap.", width: 100, sortable: true},
34425         {header: "$ Sales", width: 100, sortable: true, renderer: money},
34426         {header: "Employees", width: 100, sortable: true, resizable: false}
34427  ]);
34428  </code></pre>
34429  * <p>
34430  
34431  * The config options listed for this class are options which may appear in each
34432  * individual column definition.
34433  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
34434  * @constructor
34435  * @param {Object} config An Array of column config objects. See this class's
34436  * config objects for details.
34437 */
34438 Roo.grid.ColumnModel = function(config){
34439         /**
34440      * The config passed into the constructor
34441      */
34442     this.config = config;
34443     this.lookup = {};
34444
34445     // if no id, create one
34446     // if the column does not have a dataIndex mapping,
34447     // map it to the order it is in the config
34448     for(var i = 0, len = config.length; i < len; i++){
34449         var c = config[i];
34450         if(typeof c.dataIndex == "undefined"){
34451             c.dataIndex = i;
34452         }
34453         if(typeof c.renderer == "string"){
34454             c.renderer = Roo.util.Format[c.renderer];
34455         }
34456         if(typeof c.id == "undefined"){
34457             c.id = Roo.id();
34458         }
34459         if(c.editor && c.editor.xtype){
34460             c.editor  = Roo.factory(c.editor, Roo.grid);
34461         }
34462         if(c.editor && c.editor.isFormField){
34463             c.editor = new Roo.grid.GridEditor(c.editor);
34464         }
34465         this.lookup[c.id] = c;
34466     }
34467
34468     /**
34469      * The width of columns which have no width specified (defaults to 100)
34470      * @type Number
34471      */
34472     this.defaultWidth = 100;
34473
34474     /**
34475      * Default sortable of columns which have no sortable specified (defaults to false)
34476      * @type Boolean
34477      */
34478     this.defaultSortable = false;
34479
34480     this.addEvents({
34481         /**
34482              * @event widthchange
34483              * Fires when the width of a column changes.
34484              * @param {ColumnModel} this
34485              * @param {Number} columnIndex The column index
34486              * @param {Number} newWidth The new width
34487              */
34488             "widthchange": true,
34489         /**
34490              * @event headerchange
34491              * Fires when the text of a header changes.
34492              * @param {ColumnModel} this
34493              * @param {Number} columnIndex The column index
34494              * @param {Number} newText The new header text
34495              */
34496             "headerchange": true,
34497         /**
34498              * @event hiddenchange
34499              * Fires when a column is hidden or "unhidden".
34500              * @param {ColumnModel} this
34501              * @param {Number} columnIndex The column index
34502              * @param {Boolean} hidden true if hidden, false otherwise
34503              */
34504             "hiddenchange": true,
34505             /**
34506          * @event columnmoved
34507          * Fires when a column is moved.
34508          * @param {ColumnModel} this
34509          * @param {Number} oldIndex
34510          * @param {Number} newIndex
34511          */
34512         "columnmoved" : true,
34513         /**
34514          * @event columlockchange
34515          * Fires when a column's locked state is changed
34516          * @param {ColumnModel} this
34517          * @param {Number} colIndex
34518          * @param {Boolean} locked true if locked
34519          */
34520         "columnlockchange" : true
34521     });
34522     Roo.grid.ColumnModel.superclass.constructor.call(this);
34523 };
34524 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
34525     /**
34526      * @cfg {String} header The header text to display in the Grid view.
34527      */
34528     /**
34529      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
34530      * {@link Roo.data.Record} definition from which to draw the column's value. If not
34531      * specified, the column's index is used as an index into the Record's data Array.
34532      */
34533     /**
34534      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
34535      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
34536      */
34537     /**
34538      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
34539      * Defaults to the value of the {@link #defaultSortable} property.
34540      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
34541      */
34542     /**
34543      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
34544      */
34545     /**
34546      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
34547      */
34548     /**
34549      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
34550      */
34551     /**
34552      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
34553      */
34554     /**
34555      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
34556      * given the cell's data value. See {@link #setRenderer}. If not specified, the
34557      * default renderer uses the raw data value.
34558      */
34559        /**
34560      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
34561      */
34562     /**
34563      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
34564      */
34565
34566     /**
34567      * Returns the id of the column at the specified index.
34568      * @param {Number} index The column index
34569      * @return {String} the id
34570      */
34571     getColumnId : function(index){
34572         return this.config[index].id;
34573     },
34574
34575     /**
34576      * Returns the column for a specified id.
34577      * @param {String} id The column id
34578      * @return {Object} the column
34579      */
34580     getColumnById : function(id){
34581         return this.lookup[id];
34582     },
34583
34584     
34585     /**
34586      * Returns the column for a specified dataIndex.
34587      * @param {String} dataIndex The column dataIndex
34588      * @return {Object|Boolean} the column or false if not found
34589      */
34590     getColumnByDataIndex: function(dataIndex){
34591         var index = this.findColumnIndex(dataIndex);
34592         return index > -1 ? this.config[index] : false;
34593     },
34594     
34595     /**
34596      * Returns the index for a specified column id.
34597      * @param {String} id The column id
34598      * @return {Number} the index, or -1 if not found
34599      */
34600     getIndexById : function(id){
34601         for(var i = 0, len = this.config.length; i < len; i++){
34602             if(this.config[i].id == id){
34603                 return i;
34604             }
34605         }
34606         return -1;
34607     },
34608     
34609     /**
34610      * Returns the index for a specified column dataIndex.
34611      * @param {String} dataIndex The column dataIndex
34612      * @return {Number} the index, or -1 if not found
34613      */
34614     
34615     findColumnIndex : function(dataIndex){
34616         for(var i = 0, len = this.config.length; i < len; i++){
34617             if(this.config[i].dataIndex == dataIndex){
34618                 return i;
34619             }
34620         }
34621         return -1;
34622     },
34623     
34624     
34625     moveColumn : function(oldIndex, newIndex){
34626         var c = this.config[oldIndex];
34627         this.config.splice(oldIndex, 1);
34628         this.config.splice(newIndex, 0, c);
34629         this.dataMap = null;
34630         this.fireEvent("columnmoved", this, oldIndex, newIndex);
34631     },
34632
34633     isLocked : function(colIndex){
34634         return this.config[colIndex].locked === true;
34635     },
34636
34637     setLocked : function(colIndex, value, suppressEvent){
34638         if(this.isLocked(colIndex) == value){
34639             return;
34640         }
34641         this.config[colIndex].locked = value;
34642         if(!suppressEvent){
34643             this.fireEvent("columnlockchange", this, colIndex, value);
34644         }
34645     },
34646
34647     getTotalLockedWidth : function(){
34648         var totalWidth = 0;
34649         for(var i = 0; i < this.config.length; i++){
34650             if(this.isLocked(i) && !this.isHidden(i)){
34651                 this.totalWidth += this.getColumnWidth(i);
34652             }
34653         }
34654         return totalWidth;
34655     },
34656
34657     getLockedCount : function(){
34658         for(var i = 0, len = this.config.length; i < len; i++){
34659             if(!this.isLocked(i)){
34660                 return i;
34661             }
34662         }
34663     },
34664
34665     /**
34666      * Returns the number of columns.
34667      * @return {Number}
34668      */
34669     getColumnCount : function(visibleOnly){
34670         if(visibleOnly === true){
34671             var c = 0;
34672             for(var i = 0, len = this.config.length; i < len; i++){
34673                 if(!this.isHidden(i)){
34674                     c++;
34675                 }
34676             }
34677             return c;
34678         }
34679         return this.config.length;
34680     },
34681
34682     /**
34683      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
34684      * @param {Function} fn
34685      * @param {Object} scope (optional)
34686      * @return {Array} result
34687      */
34688     getColumnsBy : function(fn, scope){
34689         var r = [];
34690         for(var i = 0, len = this.config.length; i < len; i++){
34691             var c = this.config[i];
34692             if(fn.call(scope||this, c, i) === true){
34693                 r[r.length] = c;
34694             }
34695         }
34696         return r;
34697     },
34698
34699     /**
34700      * Returns true if the specified column is sortable.
34701      * @param {Number} col The column index
34702      * @return {Boolean}
34703      */
34704     isSortable : function(col){
34705         if(typeof this.config[col].sortable == "undefined"){
34706             return this.defaultSortable;
34707         }
34708         return this.config[col].sortable;
34709     },
34710
34711     /**
34712      * Returns the rendering (formatting) function defined for the column.
34713      * @param {Number} col The column index.
34714      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
34715      */
34716     getRenderer : function(col){
34717         if(!this.config[col].renderer){
34718             return Roo.grid.ColumnModel.defaultRenderer;
34719         }
34720         return this.config[col].renderer;
34721     },
34722
34723     /**
34724      * Sets the rendering (formatting) function for a column.
34725      * @param {Number} col The column index
34726      * @param {Function} fn The function to use to process the cell's raw data
34727      * to return HTML markup for the grid view. The render function is called with
34728      * the following parameters:<ul>
34729      * <li>Data value.</li>
34730      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
34731      * <li>css A CSS style string to apply to the table cell.</li>
34732      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
34733      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
34734      * <li>Row index</li>
34735      * <li>Column index</li>
34736      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
34737      */
34738     setRenderer : function(col, fn){
34739         this.config[col].renderer = fn;
34740     },
34741
34742     /**
34743      * Returns the width for the specified column.
34744      * @param {Number} col The column index
34745      * @return {Number}
34746      */
34747     getColumnWidth : function(col){
34748         return this.config[col].width || this.defaultWidth;
34749     },
34750
34751     /**
34752      * Sets the width for a column.
34753      * @param {Number} col The column index
34754      * @param {Number} width The new width
34755      */
34756     setColumnWidth : function(col, width, suppressEvent){
34757         this.config[col].width = width;
34758         this.totalWidth = null;
34759         if(!suppressEvent){
34760              this.fireEvent("widthchange", this, col, width);
34761         }
34762     },
34763
34764     /**
34765      * Returns the total width of all columns.
34766      * @param {Boolean} includeHidden True to include hidden column widths
34767      * @return {Number}
34768      */
34769     getTotalWidth : function(includeHidden){
34770         if(!this.totalWidth){
34771             this.totalWidth = 0;
34772             for(var i = 0, len = this.config.length; i < len; i++){
34773                 if(includeHidden || !this.isHidden(i)){
34774                     this.totalWidth += this.getColumnWidth(i);
34775                 }
34776             }
34777         }
34778         return this.totalWidth;
34779     },
34780
34781     /**
34782      * Returns the header for the specified column.
34783      * @param {Number} col The column index
34784      * @return {String}
34785      */
34786     getColumnHeader : function(col){
34787         return this.config[col].header;
34788     },
34789
34790     /**
34791      * Sets the header for a column.
34792      * @param {Number} col The column index
34793      * @param {String} header The new header
34794      */
34795     setColumnHeader : function(col, header){
34796         this.config[col].header = header;
34797         this.fireEvent("headerchange", this, col, header);
34798     },
34799
34800     /**
34801      * Returns the tooltip for the specified column.
34802      * @param {Number} col The column index
34803      * @return {String}
34804      */
34805     getColumnTooltip : function(col){
34806             return this.config[col].tooltip;
34807     },
34808     /**
34809      * Sets the tooltip for a column.
34810      * @param {Number} col The column index
34811      * @param {String} tooltip The new tooltip
34812      */
34813     setColumnTooltip : function(col, tooltip){
34814             this.config[col].tooltip = tooltip;
34815     },
34816
34817     /**
34818      * Returns the dataIndex for the specified column.
34819      * @param {Number} col The column index
34820      * @return {Number}
34821      */
34822     getDataIndex : function(col){
34823         return this.config[col].dataIndex;
34824     },
34825
34826     /**
34827      * Sets the dataIndex for a column.
34828      * @param {Number} col The column index
34829      * @param {Number} dataIndex The new dataIndex
34830      */
34831     setDataIndex : function(col, dataIndex){
34832         this.config[col].dataIndex = dataIndex;
34833     },
34834
34835     
34836     
34837     /**
34838      * Returns true if the cell is editable.
34839      * @param {Number} colIndex The column index
34840      * @param {Number} rowIndex The row index
34841      * @return {Boolean}
34842      */
34843     isCellEditable : function(colIndex, rowIndex){
34844         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
34845     },
34846
34847     /**
34848      * Returns the editor defined for the cell/column.
34849      * return false or null to disable editing.
34850      * @param {Number} colIndex The column index
34851      * @param {Number} rowIndex The row index
34852      * @return {Object}
34853      */
34854     getCellEditor : function(colIndex, rowIndex){
34855         return this.config[colIndex].editor;
34856     },
34857
34858     /**
34859      * Sets if a column is editable.
34860      * @param {Number} col The column index
34861      * @param {Boolean} editable True if the column is editable
34862      */
34863     setEditable : function(col, editable){
34864         this.config[col].editable = editable;
34865     },
34866
34867
34868     /**
34869      * Returns true if the column is hidden.
34870      * @param {Number} colIndex The column index
34871      * @return {Boolean}
34872      */
34873     isHidden : function(colIndex){
34874         return this.config[colIndex].hidden;
34875     },
34876
34877
34878     /**
34879      * Returns true if the column width cannot be changed
34880      */
34881     isFixed : function(colIndex){
34882         return this.config[colIndex].fixed;
34883     },
34884
34885     /**
34886      * Returns true if the column can be resized
34887      * @return {Boolean}
34888      */
34889     isResizable : function(colIndex){
34890         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
34891     },
34892     /**
34893      * Sets if a column is hidden.
34894      * @param {Number} colIndex The column index
34895      * @param {Boolean} hidden True if the column is hidden
34896      */
34897     setHidden : function(colIndex, hidden){
34898         this.config[colIndex].hidden = hidden;
34899         this.totalWidth = null;
34900         this.fireEvent("hiddenchange", this, colIndex, hidden);
34901     },
34902
34903     /**
34904      * Sets the editor for a column.
34905      * @param {Number} col The column index
34906      * @param {Object} editor The editor object
34907      */
34908     setEditor : function(col, editor){
34909         this.config[col].editor = editor;
34910     }
34911 });
34912
34913 Roo.grid.ColumnModel.defaultRenderer = function(value){
34914         if(typeof value == "string" && value.length < 1){
34915             return "&#160;";
34916         }
34917         return value;
34918 };
34919
34920 // Alias for backwards compatibility
34921 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
34922 /*
34923  * Based on:
34924  * Ext JS Library 1.1.1
34925  * Copyright(c) 2006-2007, Ext JS, LLC.
34926  *
34927  * Originally Released Under LGPL - original licence link has changed is not relivant.
34928  *
34929  * Fork - LGPL
34930  * <script type="text/javascript">
34931  */
34932
34933 /**
34934  * @class Roo.grid.AbstractSelectionModel
34935  * @extends Roo.util.Observable
34936  * Abstract base class for grid SelectionModels.  It provides the interface that should be
34937  * implemented by descendant classes.  This class should not be directly instantiated.
34938  * @constructor
34939  */
34940 Roo.grid.AbstractSelectionModel = function(){
34941     this.locked = false;
34942     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
34943 };
34944
34945 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
34946     /** @ignore Called by the grid automatically. Do not call directly. */
34947     init : function(grid){
34948         this.grid = grid;
34949         this.initEvents();
34950     },
34951
34952     /**
34953      * Locks the selections.
34954      */
34955     lock : function(){
34956         this.locked = true;
34957     },
34958
34959     /**
34960      * Unlocks the selections.
34961      */
34962     unlock : function(){
34963         this.locked = false;
34964     },
34965
34966     /**
34967      * Returns true if the selections are locked.
34968      * @return {Boolean}
34969      */
34970     isLocked : function(){
34971         return this.locked;
34972     }
34973 });/*
34974  * Based on:
34975  * Ext JS Library 1.1.1
34976  * Copyright(c) 2006-2007, Ext JS, LLC.
34977  *
34978  * Originally Released Under LGPL - original licence link has changed is not relivant.
34979  *
34980  * Fork - LGPL
34981  * <script type="text/javascript">
34982  */
34983 /**
34984  * @extends Roo.grid.AbstractSelectionModel
34985  * @class Roo.grid.RowSelectionModel
34986  * The default SelectionModel used by {@link Roo.grid.Grid}.
34987  * It supports multiple selections and keyboard selection/navigation. 
34988  * @constructor
34989  * @param {Object} config
34990  */
34991 Roo.grid.RowSelectionModel = function(config){
34992     Roo.apply(this, config);
34993     this.selections = new Roo.util.MixedCollection(false, function(o){
34994         return o.id;
34995     });
34996
34997     this.last = false;
34998     this.lastActive = false;
34999
35000     this.addEvents({
35001         /**
35002              * @event selectionchange
35003              * Fires when the selection changes
35004              * @param {SelectionModel} this
35005              */
35006             "selectionchange" : true,
35007         /**
35008              * @event afterselectionchange
35009              * Fires after the selection changes (eg. by key press or clicking)
35010              * @param {SelectionModel} this
35011              */
35012             "afterselectionchange" : true,
35013         /**
35014              * @event beforerowselect
35015              * Fires when a row is selected being selected, return false to cancel.
35016              * @param {SelectionModel} this
35017              * @param {Number} rowIndex The selected index
35018              * @param {Boolean} keepExisting False if other selections will be cleared
35019              */
35020             "beforerowselect" : true,
35021         /**
35022              * @event rowselect
35023              * Fires when a row is selected.
35024              * @param {SelectionModel} this
35025              * @param {Number} rowIndex The selected index
35026              * @param {Roo.data.Record} r The record
35027              */
35028             "rowselect" : true,
35029         /**
35030              * @event rowdeselect
35031              * Fires when a row is deselected.
35032              * @param {SelectionModel} this
35033              * @param {Number} rowIndex The selected index
35034              */
35035         "rowdeselect" : true
35036     });
35037     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
35038     this.locked = false;
35039 };
35040
35041 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
35042     /**
35043      * @cfg {Boolean} singleSelect
35044      * True to allow selection of only one row at a time (defaults to false)
35045      */
35046     singleSelect : false,
35047
35048     // private
35049     initEvents : function(){
35050
35051         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
35052             this.grid.on("mousedown", this.handleMouseDown, this);
35053         }else{ // allow click to work like normal
35054             this.grid.on("rowclick", this.handleDragableRowClick, this);
35055         }
35056
35057         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
35058             "up" : function(e){
35059                 if(!e.shiftKey){
35060                     this.selectPrevious(e.shiftKey);
35061                 }else if(this.last !== false && this.lastActive !== false){
35062                     var last = this.last;
35063                     this.selectRange(this.last,  this.lastActive-1);
35064                     this.grid.getView().focusRow(this.lastActive);
35065                     if(last !== false){
35066                         this.last = last;
35067                     }
35068                 }else{
35069                     this.selectFirstRow();
35070                 }
35071                 this.fireEvent("afterselectionchange", this);
35072             },
35073             "down" : function(e){
35074                 if(!e.shiftKey){
35075                     this.selectNext(e.shiftKey);
35076                 }else if(this.last !== false && this.lastActive !== false){
35077                     var last = this.last;
35078                     this.selectRange(this.last,  this.lastActive+1);
35079                     this.grid.getView().focusRow(this.lastActive);
35080                     if(last !== false){
35081                         this.last = last;
35082                     }
35083                 }else{
35084                     this.selectFirstRow();
35085                 }
35086                 this.fireEvent("afterselectionchange", this);
35087             },
35088             scope: this
35089         });
35090
35091         var view = this.grid.view;
35092         view.on("refresh", this.onRefresh, this);
35093         view.on("rowupdated", this.onRowUpdated, this);
35094         view.on("rowremoved", this.onRemove, this);
35095     },
35096
35097     // private
35098     onRefresh : function(){
35099         var ds = this.grid.dataSource, i, v = this.grid.view;
35100         var s = this.selections;
35101         s.each(function(r){
35102             if((i = ds.indexOfId(r.id)) != -1){
35103                 v.onRowSelect(i);
35104             }else{
35105                 s.remove(r);
35106             }
35107         });
35108     },
35109
35110     // private
35111     onRemove : function(v, index, r){
35112         this.selections.remove(r);
35113     },
35114
35115     // private
35116     onRowUpdated : function(v, index, r){
35117         if(this.isSelected(r)){
35118             v.onRowSelect(index);
35119         }
35120     },
35121
35122     /**
35123      * Select records.
35124      * @param {Array} records The records to select
35125      * @param {Boolean} keepExisting (optional) True to keep existing selections
35126      */
35127     selectRecords : function(records, keepExisting){
35128         if(!keepExisting){
35129             this.clearSelections();
35130         }
35131         var ds = this.grid.dataSource;
35132         for(var i = 0, len = records.length; i < len; i++){
35133             this.selectRow(ds.indexOf(records[i]), true);
35134         }
35135     },
35136
35137     /**
35138      * Gets the number of selected rows.
35139      * @return {Number}
35140      */
35141     getCount : function(){
35142         return this.selections.length;
35143     },
35144
35145     /**
35146      * Selects the first row in the grid.
35147      */
35148     selectFirstRow : function(){
35149         this.selectRow(0);
35150     },
35151
35152     /**
35153      * Select the last row.
35154      * @param {Boolean} keepExisting (optional) True to keep existing selections
35155      */
35156     selectLastRow : function(keepExisting){
35157         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
35158     },
35159
35160     /**
35161      * Selects the row immediately following the last selected row.
35162      * @param {Boolean} keepExisting (optional) True to keep existing selections
35163      */
35164     selectNext : function(keepExisting){
35165         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
35166             this.selectRow(this.last+1, keepExisting);
35167             this.grid.getView().focusRow(this.last);
35168         }
35169     },
35170
35171     /**
35172      * Selects the row that precedes the last selected row.
35173      * @param {Boolean} keepExisting (optional) True to keep existing selections
35174      */
35175     selectPrevious : function(keepExisting){
35176         if(this.last){
35177             this.selectRow(this.last-1, keepExisting);
35178             this.grid.getView().focusRow(this.last);
35179         }
35180     },
35181
35182     /**
35183      * Returns the selected records
35184      * @return {Array} Array of selected records
35185      */
35186     getSelections : function(){
35187         return [].concat(this.selections.items);
35188     },
35189
35190     /**
35191      * Returns the first selected record.
35192      * @return {Record}
35193      */
35194     getSelected : function(){
35195         return this.selections.itemAt(0);
35196     },
35197
35198
35199     /**
35200      * Clears all selections.
35201      */
35202     clearSelections : function(fast){
35203         if(this.locked) return;
35204         if(fast !== true){
35205             var ds = this.grid.dataSource;
35206             var s = this.selections;
35207             s.each(function(r){
35208                 this.deselectRow(ds.indexOfId(r.id));
35209             }, this);
35210             s.clear();
35211         }else{
35212             this.selections.clear();
35213         }
35214         this.last = false;
35215     },
35216
35217
35218     /**
35219      * Selects all rows.
35220      */
35221     selectAll : function(){
35222         if(this.locked) return;
35223         this.selections.clear();
35224         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
35225             this.selectRow(i, true);
35226         }
35227     },
35228
35229     /**
35230      * Returns True if there is a selection.
35231      * @return {Boolean}
35232      */
35233     hasSelection : function(){
35234         return this.selections.length > 0;
35235     },
35236
35237     /**
35238      * Returns True if the specified row is selected.
35239      * @param {Number/Record} record The record or index of the record to check
35240      * @return {Boolean}
35241      */
35242     isSelected : function(index){
35243         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
35244         return (r && this.selections.key(r.id) ? true : false);
35245     },
35246
35247     /**
35248      * Returns True if the specified record id is selected.
35249      * @param {String} id The id of record to check
35250      * @return {Boolean}
35251      */
35252     isIdSelected : function(id){
35253         return (this.selections.key(id) ? true : false);
35254     },
35255
35256     // private
35257     handleMouseDown : function(e, t){
35258         var view = this.grid.getView(), rowIndex;
35259         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
35260             return;
35261         };
35262         if(e.shiftKey && this.last !== false){
35263             var last = this.last;
35264             this.selectRange(last, rowIndex, e.ctrlKey);
35265             this.last = last; // reset the last
35266             view.focusRow(rowIndex);
35267         }else{
35268             var isSelected = this.isSelected(rowIndex);
35269             if(e.button !== 0 && isSelected){
35270                 view.focusRow(rowIndex);
35271             }else if(e.ctrlKey && isSelected){
35272                 this.deselectRow(rowIndex);
35273             }else if(!isSelected){
35274                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
35275                 view.focusRow(rowIndex);
35276             }
35277         }
35278         this.fireEvent("afterselectionchange", this);
35279     },
35280     // private
35281     handleDragableRowClick :  function(grid, rowIndex, e) 
35282     {
35283         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
35284             this.selectRow(rowIndex, false);
35285             grid.view.focusRow(rowIndex);
35286              this.fireEvent("afterselectionchange", this);
35287         }
35288     },
35289     
35290     /**
35291      * Selects multiple rows.
35292      * @param {Array} rows Array of the indexes of the row to select
35293      * @param {Boolean} keepExisting (optional) True to keep existing selections
35294      */
35295     selectRows : function(rows, keepExisting){
35296         if(!keepExisting){
35297             this.clearSelections();
35298         }
35299         for(var i = 0, len = rows.length; i < len; i++){
35300             this.selectRow(rows[i], true);
35301         }
35302     },
35303
35304     /**
35305      * Selects a range of rows. All rows in between startRow and endRow are also selected.
35306      * @param {Number} startRow The index of the first row in the range
35307      * @param {Number} endRow The index of the last row in the range
35308      * @param {Boolean} keepExisting (optional) True to retain existing selections
35309      */
35310     selectRange : function(startRow, endRow, keepExisting){
35311         if(this.locked) return;
35312         if(!keepExisting){
35313             this.clearSelections();
35314         }
35315         if(startRow <= endRow){
35316             for(var i = startRow; i <= endRow; i++){
35317                 this.selectRow(i, true);
35318             }
35319         }else{
35320             for(var i = startRow; i >= endRow; i--){
35321                 this.selectRow(i, true);
35322             }
35323         }
35324     },
35325
35326     /**
35327      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
35328      * @param {Number} startRow The index of the first row in the range
35329      * @param {Number} endRow The index of the last row in the range
35330      */
35331     deselectRange : function(startRow, endRow, preventViewNotify){
35332         if(this.locked) return;
35333         for(var i = startRow; i <= endRow; i++){
35334             this.deselectRow(i, preventViewNotify);
35335         }
35336     },
35337
35338     /**
35339      * Selects a row.
35340      * @param {Number} row The index of the row to select
35341      * @param {Boolean} keepExisting (optional) True to keep existing selections
35342      */
35343     selectRow : function(index, keepExisting, preventViewNotify){
35344         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
35345         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
35346             if(!keepExisting || this.singleSelect){
35347                 this.clearSelections();
35348             }
35349             var r = this.grid.dataSource.getAt(index);
35350             this.selections.add(r);
35351             this.last = this.lastActive = index;
35352             if(!preventViewNotify){
35353                 this.grid.getView().onRowSelect(index);
35354             }
35355             this.fireEvent("rowselect", this, index, r);
35356             this.fireEvent("selectionchange", this);
35357         }
35358     },
35359
35360     /**
35361      * Deselects a row.
35362      * @param {Number} row The index of the row to deselect
35363      */
35364     deselectRow : function(index, preventViewNotify){
35365         if(this.locked) return;
35366         if(this.last == index){
35367             this.last = false;
35368         }
35369         if(this.lastActive == index){
35370             this.lastActive = false;
35371         }
35372         var r = this.grid.dataSource.getAt(index);
35373         this.selections.remove(r);
35374         if(!preventViewNotify){
35375             this.grid.getView().onRowDeselect(index);
35376         }
35377         this.fireEvent("rowdeselect", this, index);
35378         this.fireEvent("selectionchange", this);
35379     },
35380
35381     // private
35382     restoreLast : function(){
35383         if(this._last){
35384             this.last = this._last;
35385         }
35386     },
35387
35388     // private
35389     acceptsNav : function(row, col, cm){
35390         return !cm.isHidden(col) && cm.isCellEditable(col, row);
35391     },
35392
35393     // private
35394     onEditorKey : function(field, e){
35395         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
35396         if(k == e.TAB){
35397             e.stopEvent();
35398             ed.completeEdit();
35399             if(e.shiftKey){
35400                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
35401             }else{
35402                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
35403             }
35404         }else if(k == e.ENTER && !e.ctrlKey){
35405             e.stopEvent();
35406             ed.completeEdit();
35407             if(e.shiftKey){
35408                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
35409             }else{
35410                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
35411             }
35412         }else if(k == e.ESC){
35413             ed.cancelEdit();
35414         }
35415         if(newCell){
35416             g.startEditing(newCell[0], newCell[1]);
35417         }
35418     }
35419 });/*
35420  * Based on:
35421  * Ext JS Library 1.1.1
35422  * Copyright(c) 2006-2007, Ext JS, LLC.
35423  *
35424  * Originally Released Under LGPL - original licence link has changed is not relivant.
35425  *
35426  * Fork - LGPL
35427  * <script type="text/javascript">
35428  */
35429 /**
35430  * @class Roo.grid.CellSelectionModel
35431  * @extends Roo.grid.AbstractSelectionModel
35432  * This class provides the basic implementation for cell selection in a grid.
35433  * @constructor
35434  * @param {Object} config The object containing the configuration of this model.
35435  */
35436 Roo.grid.CellSelectionModel = function(config){
35437     Roo.apply(this, config);
35438
35439     this.selection = null;
35440
35441     this.addEvents({
35442         /**
35443              * @event beforerowselect
35444              * Fires before a cell is selected.
35445              * @param {SelectionModel} this
35446              * @param {Number} rowIndex The selected row index
35447              * @param {Number} colIndex The selected cell index
35448              */
35449             "beforecellselect" : true,
35450         /**
35451              * @event cellselect
35452              * Fires when a cell is selected.
35453              * @param {SelectionModel} this
35454              * @param {Number} rowIndex The selected row index
35455              * @param {Number} colIndex The selected cell index
35456              */
35457             "cellselect" : true,
35458         /**
35459              * @event selectionchange
35460              * Fires when the active selection changes.
35461              * @param {SelectionModel} this
35462              * @param {Object} selection null for no selection or an object (o) with two properties
35463                 <ul>
35464                 <li>o.record: the record object for the row the selection is in</li>
35465                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
35466                 </ul>
35467              */
35468             "selectionchange" : true
35469     });
35470     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
35471 };
35472
35473 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
35474
35475     /** @ignore */
35476     initEvents : function(){
35477         this.grid.on("mousedown", this.handleMouseDown, this);
35478         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
35479         var view = this.grid.view;
35480         view.on("refresh", this.onViewChange, this);
35481         view.on("rowupdated", this.onRowUpdated, this);
35482         view.on("beforerowremoved", this.clearSelections, this);
35483         view.on("beforerowsinserted", this.clearSelections, this);
35484         if(this.grid.isEditor){
35485             this.grid.on("beforeedit", this.beforeEdit,  this);
35486         }
35487     },
35488
35489         //private
35490     beforeEdit : function(e){
35491         this.select(e.row, e.column, false, true, e.record);
35492     },
35493
35494         //private
35495     onRowUpdated : function(v, index, r){
35496         if(this.selection && this.selection.record == r){
35497             v.onCellSelect(index, this.selection.cell[1]);
35498         }
35499     },
35500
35501         //private
35502     onViewChange : function(){
35503         this.clearSelections(true);
35504     },
35505
35506         /**
35507          * Returns the currently selected cell,.
35508          * @return {Array} The selected cell (row, column) or null if none selected.
35509          */
35510     getSelectedCell : function(){
35511         return this.selection ? this.selection.cell : null;
35512     },
35513
35514     /**
35515      * Clears all selections.
35516      * @param {Boolean} true to prevent the gridview from being notified about the change.
35517      */
35518     clearSelections : function(preventNotify){
35519         var s = this.selection;
35520         if(s){
35521             if(preventNotify !== true){
35522                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
35523             }
35524             this.selection = null;
35525             this.fireEvent("selectionchange", this, null);
35526         }
35527     },
35528
35529     /**
35530      * Returns true if there is a selection.
35531      * @return {Boolean}
35532      */
35533     hasSelection : function(){
35534         return this.selection ? true : false;
35535     },
35536
35537     /** @ignore */
35538     handleMouseDown : function(e, t){
35539         var v = this.grid.getView();
35540         if(this.isLocked()){
35541             return;
35542         };
35543         var row = v.findRowIndex(t);
35544         var cell = v.findCellIndex(t);
35545         if(row !== false && cell !== false){
35546             this.select(row, cell);
35547         }
35548     },
35549
35550     /**
35551      * Selects a cell.
35552      * @param {Number} rowIndex
35553      * @param {Number} collIndex
35554      */
35555     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
35556         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
35557             this.clearSelections();
35558             r = r || this.grid.dataSource.getAt(rowIndex);
35559             this.selection = {
35560                 record : r,
35561                 cell : [rowIndex, colIndex]
35562             };
35563             if(!preventViewNotify){
35564                 var v = this.grid.getView();
35565                 v.onCellSelect(rowIndex, colIndex);
35566                 if(preventFocus !== true){
35567                     v.focusCell(rowIndex, colIndex);
35568                 }
35569             }
35570             this.fireEvent("cellselect", this, rowIndex, colIndex);
35571             this.fireEvent("selectionchange", this, this.selection);
35572         }
35573     },
35574
35575         //private
35576     isSelectable : function(rowIndex, colIndex, cm){
35577         return !cm.isHidden(colIndex);
35578     },
35579
35580     /** @ignore */
35581     handleKeyDown : function(e){
35582         Roo.log('Cell Sel Model handleKeyDown');
35583         if(!e.isNavKeyPress()){
35584             return;
35585         }
35586         var g = this.grid, s = this.selection;
35587         if(!s){
35588             e.stopEvent();
35589             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
35590             if(cell){
35591                 this.select(cell[0], cell[1]);
35592             }
35593             return;
35594         }
35595         var sm = this;
35596         var walk = function(row, col, step){
35597             return g.walkCells(row, col, step, sm.isSelectable,  sm);
35598         };
35599         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
35600         var newCell;
35601
35602         switch(k){
35603             case e.TAB:
35604                 // handled by onEditorKey
35605                 if (g.isEditor && g.editing) {
35606                     return;
35607                 }
35608                 if(e.shiftKey){
35609                      newCell = walk(r, c-1, -1);
35610                 }else{
35611                      newCell = walk(r, c+1, 1);
35612                 }
35613              break;
35614              case e.DOWN:
35615                  newCell = walk(r+1, c, 1);
35616              break;
35617              case e.UP:
35618                  newCell = walk(r-1, c, -1);
35619              break;
35620              case e.RIGHT:
35621                  newCell = walk(r, c+1, 1);
35622              break;
35623              case e.LEFT:
35624                  newCell = walk(r, c-1, -1);
35625              break;
35626              case e.ENTER:
35627                  if(g.isEditor && !g.editing){
35628                     g.startEditing(r, c);
35629                     e.stopEvent();
35630                     return;
35631                 }
35632              break;
35633         };
35634         if(newCell){
35635             this.select(newCell[0], newCell[1]);
35636             e.stopEvent();
35637         }
35638     },
35639
35640     acceptsNav : function(row, col, cm){
35641         return !cm.isHidden(col) && cm.isCellEditable(col, row);
35642     },
35643
35644     onEditorKey : function(field, e){
35645         
35646         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
35647         ///Roo.log('onEditorKey' + k);
35648         
35649         if(k == e.TAB){
35650             if(e.shiftKey){
35651                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
35652             }else{
35653                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
35654             }
35655             e.stopEvent();
35656         }else if(k == e.ENTER && !e.ctrlKey){
35657             ed.completeEdit();
35658             e.stopEvent();
35659             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
35660         }else if(k == e.ESC){
35661             ed.cancelEdit();
35662         }
35663         
35664         
35665         if(newCell){
35666             //Roo.log('next cell after edit');
35667             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
35668         }
35669     }
35670 });/*
35671  * Based on:
35672  * Ext JS Library 1.1.1
35673  * Copyright(c) 2006-2007, Ext JS, LLC.
35674  *
35675  * Originally Released Under LGPL - original licence link has changed is not relivant.
35676  *
35677  * Fork - LGPL
35678  * <script type="text/javascript">
35679  */
35680  
35681 /**
35682  * @class Roo.grid.EditorGrid
35683  * @extends Roo.grid.Grid
35684  * Class for creating and editable grid.
35685  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
35686  * The container MUST have some type of size defined for the grid to fill. The container will be 
35687  * automatically set to position relative if it isn't already.
35688  * @param {Object} dataSource The data model to bind to
35689  * @param {Object} colModel The column model with info about this grid's columns
35690  */
35691 Roo.grid.EditorGrid = function(container, config){
35692     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
35693     this.getGridEl().addClass("xedit-grid");
35694
35695     if(!this.selModel){
35696         this.selModel = new Roo.grid.CellSelectionModel();
35697     }
35698
35699     this.activeEditor = null;
35700
35701         this.addEvents({
35702             /**
35703              * @event beforeedit
35704              * Fires before cell editing is triggered. The edit event object has the following properties <br />
35705              * <ul style="padding:5px;padding-left:16px;">
35706              * <li>grid - This grid</li>
35707              * <li>record - The record being edited</li>
35708              * <li>field - The field name being edited</li>
35709              * <li>value - The value for the field being edited.</li>
35710              * <li>row - The grid row index</li>
35711              * <li>column - The grid column index</li>
35712              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
35713              * </ul>
35714              * @param {Object} e An edit event (see above for description)
35715              */
35716             "beforeedit" : true,
35717             /**
35718              * @event afteredit
35719              * Fires after a cell is edited. <br />
35720              * <ul style="padding:5px;padding-left:16px;">
35721              * <li>grid - This grid</li>
35722              * <li>record - The record being edited</li>
35723              * <li>field - The field name being edited</li>
35724              * <li>value - The value being set</li>
35725              * <li>originalValue - The original value for the field, before the edit.</li>
35726              * <li>row - The grid row index</li>
35727              * <li>column - The grid column index</li>
35728              * </ul>
35729              * @param {Object} e An edit event (see above for description)
35730              */
35731             "afteredit" : true,
35732             /**
35733              * @event validateedit
35734              * Fires after a cell is edited, but before the value is set in the record. 
35735          * You can use this to modify the value being set in the field, Return false
35736              * to cancel the change. The edit event object has the following properties <br />
35737              * <ul style="padding:5px;padding-left:16px;">
35738          * <li>editor - This editor</li>
35739              * <li>grid - This grid</li>
35740              * <li>record - The record being edited</li>
35741              * <li>field - The field name being edited</li>
35742              * <li>value - The value being set</li>
35743              * <li>originalValue - The original value for the field, before the edit.</li>
35744              * <li>row - The grid row index</li>
35745              * <li>column - The grid column index</li>
35746              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
35747              * </ul>
35748              * @param {Object} e An edit event (see above for description)
35749              */
35750             "validateedit" : true
35751         });
35752     this.on("bodyscroll", this.stopEditing,  this);
35753     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
35754 };
35755
35756 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
35757     /**
35758      * @cfg {Number} clicksToEdit
35759      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
35760      */
35761     clicksToEdit: 2,
35762
35763     // private
35764     isEditor : true,
35765     // private
35766     trackMouseOver: false, // causes very odd FF errors
35767
35768     onCellDblClick : function(g, row, col){
35769         this.startEditing(row, col);
35770     },
35771
35772     onEditComplete : function(ed, value, startValue){
35773         this.editing = false;
35774         this.activeEditor = null;
35775         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
35776         var r = ed.record;
35777         var field = this.colModel.getDataIndex(ed.col);
35778         var e = {
35779             grid: this,
35780             record: r,
35781             field: field,
35782             originalValue: startValue,
35783             value: value,
35784             row: ed.row,
35785             column: ed.col,
35786             cancel:false,
35787             editor: ed
35788         };
35789         if(String(value) !== String(startValue)){
35790             
35791             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
35792                 r.set(field, e.value);
35793                 // if we are dealing with a combo box..
35794                 // then we also set the 'name' colum to be the displayField
35795                 if (ed.field.displayField && ed.field.name) {
35796                     r.set(ed.field.name, ed.field.el.dom.value);
35797                 }
35798                 
35799                 delete e.cancel; //?? why!!!
35800                 this.fireEvent("afteredit", e);
35801             }
35802         } else {
35803             this.fireEvent("afteredit", e); // always fire it!
35804         }
35805         this.view.focusCell(ed.row, ed.col);
35806     },
35807
35808     /**
35809      * Starts editing the specified for the specified row/column
35810      * @param {Number} rowIndex
35811      * @param {Number} colIndex
35812      */
35813     startEditing : function(row, col){
35814         this.stopEditing();
35815         if(this.colModel.isCellEditable(col, row)){
35816             this.view.ensureVisible(row, col, true);
35817             var r = this.dataSource.getAt(row);
35818             var field = this.colModel.getDataIndex(col);
35819             var e = {
35820                 grid: this,
35821                 record: r,
35822                 field: field,
35823                 value: r.data[field],
35824                 row: row,
35825                 column: col,
35826                 cancel:false
35827             };
35828             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
35829                 this.editing = true;
35830                 var ed = this.colModel.getCellEditor(col, row);
35831                 
35832                 if (!ed) {
35833                     return;
35834                 }
35835                 if(!ed.rendered){
35836                     ed.render(ed.parentEl || document.body);
35837                 }
35838                 ed.field.reset();
35839                 (function(){ // complex but required for focus issues in safari, ie and opera
35840                     ed.row = row;
35841                     ed.col = col;
35842                     ed.record = r;
35843                     ed.on("complete", this.onEditComplete, this, {single: true});
35844                     ed.on("specialkey", this.selModel.onEditorKey, this.selModel);
35845                     this.activeEditor = ed;
35846                     var v = r.data[field];
35847                     ed.startEdit(this.view.getCell(row, col), v);
35848                     // combo's with 'displayField and name set
35849                     if (ed.field.displayField && ed.field.name) {
35850                         ed.field.el.dom.value = r.data[ed.field.name];
35851                     }
35852                     
35853                     
35854                 }).defer(50, this);
35855             }
35856         }
35857     },
35858         
35859     /**
35860      * Stops any active editing
35861      */
35862     stopEditing : function(){
35863         if(this.activeEditor){
35864             this.activeEditor.completeEdit();
35865         }
35866         this.activeEditor = null;
35867     }
35868 });/*
35869  * Based on:
35870  * Ext JS Library 1.1.1
35871  * Copyright(c) 2006-2007, Ext JS, LLC.
35872  *
35873  * Originally Released Under LGPL - original licence link has changed is not relivant.
35874  *
35875  * Fork - LGPL
35876  * <script type="text/javascript">
35877  */
35878
35879 // private - not really -- you end up using it !
35880 // This is a support class used internally by the Grid components
35881
35882 /**
35883  * @class Roo.grid.GridEditor
35884  * @extends Roo.Editor
35885  * Class for creating and editable grid elements.
35886  * @param {Object} config any settings (must include field)
35887  */
35888 Roo.grid.GridEditor = function(field, config){
35889     if (!config && field.field) {
35890         config = field;
35891         field = Roo.factory(config.field, Roo.form);
35892     }
35893     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
35894     field.monitorTab = false;
35895 };
35896
35897 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
35898     
35899     /**
35900      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
35901      */
35902     
35903     alignment: "tl-tl",
35904     autoSize: "width",
35905     hideEl : false,
35906     cls: "x-small-editor x-grid-editor",
35907     shim:false,
35908     shadow:"frame"
35909 });/*
35910  * Based on:
35911  * Ext JS Library 1.1.1
35912  * Copyright(c) 2006-2007, Ext JS, LLC.
35913  *
35914  * Originally Released Under LGPL - original licence link has changed is not relivant.
35915  *
35916  * Fork - LGPL
35917  * <script type="text/javascript">
35918  */
35919   
35920
35921   
35922 Roo.grid.PropertyRecord = Roo.data.Record.create([
35923     {name:'name',type:'string'},  'value'
35924 ]);
35925
35926
35927 Roo.grid.PropertyStore = function(grid, source){
35928     this.grid = grid;
35929     this.store = new Roo.data.Store({
35930         recordType : Roo.grid.PropertyRecord
35931     });
35932     this.store.on('update', this.onUpdate,  this);
35933     if(source){
35934         this.setSource(source);
35935     }
35936     Roo.grid.PropertyStore.superclass.constructor.call(this);
35937 };
35938
35939
35940
35941 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
35942     setSource : function(o){
35943         this.source = o;
35944         this.store.removeAll();
35945         var data = [];
35946         for(var k in o){
35947             if(this.isEditableValue(o[k])){
35948                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
35949             }
35950         }
35951         this.store.loadRecords({records: data}, {}, true);
35952     },
35953
35954     onUpdate : function(ds, record, type){
35955         if(type == Roo.data.Record.EDIT){
35956             var v = record.data['value'];
35957             var oldValue = record.modified['value'];
35958             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
35959                 this.source[record.id] = v;
35960                 record.commit();
35961                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
35962             }else{
35963                 record.reject();
35964             }
35965         }
35966     },
35967
35968     getProperty : function(row){
35969        return this.store.getAt(row);
35970     },
35971
35972     isEditableValue: function(val){
35973         if(val && val instanceof Date){
35974             return true;
35975         }else if(typeof val == 'object' || typeof val == 'function'){
35976             return false;
35977         }
35978         return true;
35979     },
35980
35981     setValue : function(prop, value){
35982         this.source[prop] = value;
35983         this.store.getById(prop).set('value', value);
35984     },
35985
35986     getSource : function(){
35987         return this.source;
35988     }
35989 });
35990
35991 Roo.grid.PropertyColumnModel = function(grid, store){
35992     this.grid = grid;
35993     var g = Roo.grid;
35994     g.PropertyColumnModel.superclass.constructor.call(this, [
35995         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
35996         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
35997     ]);
35998     this.store = store;
35999     this.bselect = Roo.DomHelper.append(document.body, {
36000         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
36001             {tag: 'option', value: 'true', html: 'true'},
36002             {tag: 'option', value: 'false', html: 'false'}
36003         ]
36004     });
36005     Roo.id(this.bselect);
36006     var f = Roo.form;
36007     this.editors = {
36008         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
36009         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
36010         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
36011         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
36012         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
36013     };
36014     this.renderCellDelegate = this.renderCell.createDelegate(this);
36015     this.renderPropDelegate = this.renderProp.createDelegate(this);
36016 };
36017
36018 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
36019     
36020     
36021     nameText : 'Name',
36022     valueText : 'Value',
36023     
36024     dateFormat : 'm/j/Y',
36025     
36026     
36027     renderDate : function(dateVal){
36028         return dateVal.dateFormat(this.dateFormat);
36029     },
36030
36031     renderBool : function(bVal){
36032         return bVal ? 'true' : 'false';
36033     },
36034
36035     isCellEditable : function(colIndex, rowIndex){
36036         return colIndex == 1;
36037     },
36038
36039     getRenderer : function(col){
36040         return col == 1 ?
36041             this.renderCellDelegate : this.renderPropDelegate;
36042     },
36043
36044     renderProp : function(v){
36045         return this.getPropertyName(v);
36046     },
36047
36048     renderCell : function(val){
36049         var rv = val;
36050         if(val instanceof Date){
36051             rv = this.renderDate(val);
36052         }else if(typeof val == 'boolean'){
36053             rv = this.renderBool(val);
36054         }
36055         return Roo.util.Format.htmlEncode(rv);
36056     },
36057
36058     getPropertyName : function(name){
36059         var pn = this.grid.propertyNames;
36060         return pn && pn[name] ? pn[name] : name;
36061     },
36062
36063     getCellEditor : function(colIndex, rowIndex){
36064         var p = this.store.getProperty(rowIndex);
36065         var n = p.data['name'], val = p.data['value'];
36066         
36067         if(typeof(this.grid.customEditors[n]) == 'string'){
36068             return this.editors[this.grid.customEditors[n]];
36069         }
36070         if(typeof(this.grid.customEditors[n]) != 'undefined'){
36071             return this.grid.customEditors[n];
36072         }
36073         if(val instanceof Date){
36074             return this.editors['date'];
36075         }else if(typeof val == 'number'){
36076             return this.editors['number'];
36077         }else if(typeof val == 'boolean'){
36078             return this.editors['boolean'];
36079         }else{
36080             return this.editors['string'];
36081         }
36082     }
36083 });
36084
36085 /**
36086  * @class Roo.grid.PropertyGrid
36087  * @extends Roo.grid.EditorGrid
36088  * This class represents the  interface of a component based property grid control.
36089  * <br><br>Usage:<pre><code>
36090  var grid = new Roo.grid.PropertyGrid("my-container-id", {
36091       
36092  });
36093  // set any options
36094  grid.render();
36095  * </code></pre>
36096   
36097  * @constructor
36098  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
36099  * The container MUST have some type of size defined for the grid to fill. The container will be
36100  * automatically set to position relative if it isn't already.
36101  * @param {Object} config A config object that sets properties on this grid.
36102  */
36103 Roo.grid.PropertyGrid = function(container, config){
36104     config = config || {};
36105     var store = new Roo.grid.PropertyStore(this);
36106     this.store = store;
36107     var cm = new Roo.grid.PropertyColumnModel(this, store);
36108     store.store.sort('name', 'ASC');
36109     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
36110         ds: store.store,
36111         cm: cm,
36112         enableColLock:false,
36113         enableColumnMove:false,
36114         stripeRows:false,
36115         trackMouseOver: false,
36116         clicksToEdit:1
36117     }, config));
36118     this.getGridEl().addClass('x-props-grid');
36119     this.lastEditRow = null;
36120     this.on('columnresize', this.onColumnResize, this);
36121     this.addEvents({
36122          /**
36123              * @event beforepropertychange
36124              * Fires before a property changes (return false to stop?)
36125              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
36126              * @param {String} id Record Id
36127              * @param {String} newval New Value
36128          * @param {String} oldval Old Value
36129              */
36130         "beforepropertychange": true,
36131         /**
36132              * @event propertychange
36133              * Fires after a property changes
36134              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
36135              * @param {String} id Record Id
36136              * @param {String} newval New Value
36137          * @param {String} oldval Old Value
36138              */
36139         "propertychange": true
36140     });
36141     this.customEditors = this.customEditors || {};
36142 };
36143 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
36144     
36145      /**
36146      * @cfg {Object} customEditors map of colnames=> custom editors.
36147      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
36148      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
36149      * false disables editing of the field.
36150          */
36151     
36152       /**
36153      * @cfg {Object} propertyNames map of property Names to their displayed value
36154          */
36155     
36156     render : function(){
36157         Roo.grid.PropertyGrid.superclass.render.call(this);
36158         this.autoSize.defer(100, this);
36159     },
36160
36161     autoSize : function(){
36162         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
36163         if(this.view){
36164             this.view.fitColumns();
36165         }
36166     },
36167
36168     onColumnResize : function(){
36169         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
36170         this.autoSize();
36171     },
36172     /**
36173      * Sets the data for the Grid
36174      * accepts a Key => Value object of all the elements avaiable.
36175      * @param {Object} data  to appear in grid.
36176      */
36177     setSource : function(source){
36178         this.store.setSource(source);
36179         //this.autoSize();
36180     },
36181     /**
36182      * Gets all the data from the grid.
36183      * @return {Object} data  data stored in grid
36184      */
36185     getSource : function(){
36186         return this.store.getSource();
36187     }
36188 });/*
36189  * Based on:
36190  * Ext JS Library 1.1.1
36191  * Copyright(c) 2006-2007, Ext JS, LLC.
36192  *
36193  * Originally Released Under LGPL - original licence link has changed is not relivant.
36194  *
36195  * Fork - LGPL
36196  * <script type="text/javascript">
36197  */
36198  
36199 /**
36200  * @class Roo.LoadMask
36201  * A simple utility class for generically masking elements while loading data.  If the element being masked has
36202  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
36203  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
36204  * element's UpdateManager load indicator and will be destroyed after the initial load.
36205  * @constructor
36206  * Create a new LoadMask
36207  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
36208  * @param {Object} config The config object
36209  */
36210 Roo.LoadMask = function(el, config){
36211     this.el = Roo.get(el);
36212     Roo.apply(this, config);
36213     if(this.store){
36214         this.store.on('beforeload', this.onBeforeLoad, this);
36215         this.store.on('load', this.onLoad, this);
36216         this.store.on('loadexception', this.onLoad, this);
36217         this.removeMask = false;
36218     }else{
36219         var um = this.el.getUpdateManager();
36220         um.showLoadIndicator = false; // disable the default indicator
36221         um.on('beforeupdate', this.onBeforeLoad, this);
36222         um.on('update', this.onLoad, this);
36223         um.on('failure', this.onLoad, this);
36224         this.removeMask = true;
36225     }
36226 };
36227
36228 Roo.LoadMask.prototype = {
36229     /**
36230      * @cfg {Boolean} removeMask
36231      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
36232      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
36233      */
36234     /**
36235      * @cfg {String} msg
36236      * The text to display in a centered loading message box (defaults to 'Loading...')
36237      */
36238     msg : 'Loading...',
36239     /**
36240      * @cfg {String} msgCls
36241      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
36242      */
36243     msgCls : 'x-mask-loading',
36244
36245     /**
36246      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
36247      * @type Boolean
36248      */
36249     disabled: false,
36250
36251     /**
36252      * Disables the mask to prevent it from being displayed
36253      */
36254     disable : function(){
36255        this.disabled = true;
36256     },
36257
36258     /**
36259      * Enables the mask so that it can be displayed
36260      */
36261     enable : function(){
36262         this.disabled = false;
36263     },
36264
36265     // private
36266     onLoad : function(){
36267         this.el.unmask(this.removeMask);
36268     },
36269
36270     // private
36271     onBeforeLoad : function(){
36272         if(!this.disabled){
36273             this.el.mask(this.msg, this.msgCls);
36274         }
36275     },
36276
36277     // private
36278     destroy : function(){
36279         if(this.store){
36280             this.store.un('beforeload', this.onBeforeLoad, this);
36281             this.store.un('load', this.onLoad, this);
36282             this.store.un('loadexception', this.onLoad, this);
36283         }else{
36284             var um = this.el.getUpdateManager();
36285             um.un('beforeupdate', this.onBeforeLoad, this);
36286             um.un('update', this.onLoad, this);
36287             um.un('failure', this.onLoad, this);
36288         }
36289     }
36290 };/*
36291  * Based on:
36292  * Ext JS Library 1.1.1
36293  * Copyright(c) 2006-2007, Ext JS, LLC.
36294  *
36295  * Originally Released Under LGPL - original licence link has changed is not relivant.
36296  *
36297  * Fork - LGPL
36298  * <script type="text/javascript">
36299  */
36300 Roo.XTemplate = function(){
36301     Roo.XTemplate.superclass.constructor.apply(this, arguments);
36302     var s = this.html;
36303
36304     s = ['<tpl>', s, '</tpl>'].join('');
36305
36306     var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/;
36307
36308     var nameRe = /^<tpl\b[^>]*?for="(.*?)"/;
36309     var ifRe = /^<tpl\b[^>]*?if="(.*?)"/;
36310     var execRe = /^<tpl\b[^>]*?exec="(.*?)"/;
36311     var m, id = 0;
36312     var tpls = [];
36313
36314     while(m = s.match(re)){
36315        var m2 = m[0].match(nameRe);
36316        var m3 = m[0].match(ifRe);
36317        var m4 = m[0].match(execRe);
36318        var exp = null, fn = null, exec = null;
36319        var name = m2 && m2[1] ? m2[1] : '';
36320        if(m3){
36321            exp = m3 && m3[1] ? m3[1] : null;
36322            if(exp){
36323                fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
36324            }
36325        }
36326        if(m4){
36327            exp = m4 && m4[1] ? m4[1] : null;
36328            if(exp){
36329                exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
36330            }
36331        }
36332        if(name){
36333            switch(name){
36334                case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
36335                case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
36336                default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
36337            }
36338        }
36339        tpls.push({
36340             id: id,
36341             target: name,
36342             exec: exec,
36343             test: fn,
36344             body: m[1]||''
36345         });
36346        s = s.replace(m[0], '{xtpl'+ id + '}');
36347        ++id;
36348     }
36349     for(var i = tpls.length-1; i >= 0; --i){
36350         this.compileTpl(tpls[i]);
36351     }
36352     this.master = tpls[tpls.length-1];
36353     this.tpls = tpls;
36354 };
36355 Roo.extend(Roo.XTemplate, Roo.Template, {
36356
36357     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
36358
36359     applySubTemplate : function(id, values, parent){
36360         var t = this.tpls[id];
36361         if(t.test && !t.test.call(this, values, parent)){
36362             return '';
36363         }
36364         if(t.exec && t.exec.call(this, values, parent)){
36365             return '';
36366         }
36367         var vs = t.target ? t.target.call(this, values, parent) : values;
36368         parent = t.target ? values : parent;
36369         if(t.target && vs instanceof Array){
36370             var buf = [];
36371             for(var i = 0, len = vs.length; i < len; i++){
36372                 buf[buf.length] = t.compiled.call(this, vs[i], parent);
36373             }
36374             return buf.join('');
36375         }
36376         return t.compiled.call(this, vs, parent);
36377     },
36378
36379     compileTpl : function(tpl){
36380         var fm = Roo.util.Format;
36381         var useF = this.disableFormats !== true;
36382         var sep = Roo.isGecko ? "+" : ",";
36383         var fn = function(m, name, format, args){
36384             if(name.substr(0, 4) == 'xtpl'){
36385                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
36386             }
36387             var v;
36388             if(name.indexOf('.') != -1){
36389                 v = name;
36390             }else{
36391                 v = "values['" + name + "']";
36392             }
36393             if(format && useF){
36394                 args = args ? ',' + args : "";
36395                 if(format.substr(0, 5) != "this."){
36396                     format = "fm." + format + '(';
36397                 }else{
36398                     format = 'this.call("'+ format.substr(5) + '", ';
36399                     args = ", values";
36400                 }
36401             }else{
36402                 args= ''; format = "("+v+" === undefined ? '' : ";
36403             }
36404             return "'"+ sep + format + v + args + ")"+sep+"'";
36405         };
36406         var body;
36407         // branched to use + in gecko and [].join() in others
36408         if(Roo.isGecko){
36409             body = "tpl.compiled = function(values, parent){ return '" +
36410                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
36411                     "';};";
36412         }else{
36413             body = ["tpl.compiled = function(values, parent){ return ['"];
36414             body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
36415             body.push("'].join('');};");
36416             body = body.join('');
36417         }
36418         /** eval:var:zzzzzzz */
36419         eval(body);
36420         return this;
36421     },
36422
36423     applyTemplate : function(values){
36424         return this.master.compiled.call(this, values, {});
36425         var s = this.subs;
36426     },
36427
36428     apply : function(){
36429         return this.applyTemplate.apply(this, arguments);
36430     },
36431
36432     compile : function(){return this;}
36433 });
36434
36435 Roo.XTemplate.from = function(el){
36436     el = Roo.getDom(el);
36437     return new Roo.XTemplate(el.value || el.innerHTML);
36438 };/*
36439  * Original code for Roojs - LGPL
36440  * <script type="text/javascript">
36441  */
36442  
36443 /**
36444  * @class Roo.XComponent
36445  * A delayed Element creator...
36446  * 
36447  * Mypart.xyx = new Roo.XComponent({
36448
36449     parent : 'Mypart.xyz', // empty == document.element.!!
36450     order : '001',
36451     name : 'xxxx'
36452     region : 'xxxx'
36453     disabled : function() {} 
36454      
36455     tree : function() { // return an tree of xtype declared components
36456         var MODULE = this;
36457         return 
36458         {
36459             xtype : 'NestedLayoutPanel',
36460             // technicall
36461         }
36462      ]
36463  *})
36464  * @extends Roo.util.Observable
36465  * @constructor
36466  * @param cfg {Object} configuration of component
36467  * 
36468  */
36469 Roo.XComponent = function(cfg) {
36470     Roo.apply(this, cfg);
36471     this.addEvents({ 
36472         /**
36473              * @event built
36474              * Fires when this the componnt is built
36475              * @param {Roo.XComponent} c the component
36476              */
36477         'built' : true,
36478         /**
36479              * @event buildcomplete
36480              * Fires on the top level element when all elements have been built
36481              * @param {Roo.XComponent} c the top level component.
36482          */
36483         'buildcomplete' : true
36484         
36485     });
36486     
36487     Roo.XComponent.register(this);
36488     this.modules = false;
36489     this.el = false; // where the layout goes..
36490     
36491     
36492 }
36493 Roo.extend(Roo.XComponent, Roo.util.Observable, {
36494     /**
36495      * @property el
36496      * The created element (with Roo.factory())
36497      * @type {Roo.Layout}
36498      */
36499     el  : false,
36500     
36501     /**
36502      * @property el
36503      * for BC  - use el in new code
36504      * @type {Roo.Layout}
36505      */
36506     panel : false,
36507     
36508     /**
36509      * @property layout
36510      * for BC  - use el in new code
36511      * @type {Roo.Layout}
36512      */
36513     layout : false,
36514     
36515      /**
36516      * @cfg {Function|boolean} disabled
36517      * If this module is disabled by some rule, return true from the funtion
36518      */
36519     disabled : false,
36520     
36521     /**
36522      * @cfg {String} parent 
36523      * Name of parent element which it get xtype added to..
36524      */
36525     parent: false,
36526     
36527     /**
36528      * @cfg {String} order
36529      * Used to set the order in which elements are created (usefull for multiple tabs)
36530      */
36531     
36532     order : false,
36533     /**
36534      * @cfg {String} name
36535      * String to display while loading.
36536      */
36537     name : false,
36538     /**
36539      * @cfg {Array} items
36540      * A single item array - the first element is the root of the tree..
36541      * It's done this way to stay compatible with the Xtype system...
36542      */
36543     items : false
36544      
36545      
36546     
36547 });
36548
36549 Roo.apply(Roo.XComponent, {
36550     
36551     /**
36552      * @property  buildCompleted
36553      * True when the builder has completed building the interface.
36554      * @type Boolean
36555      */
36556     buildCompleted : false,
36557      
36558     /**
36559      * @property  topModule
36560      * the upper most module - uses document.element as it's constructor.
36561      * @type Object
36562      */
36563      
36564     topModule  : false,
36565       
36566     /**
36567      * @property  modules
36568      * array of modules to be created by registration system.
36569      * @type Roo.XComponent
36570      */
36571     
36572     modules : [],
36573       
36574     
36575     /**
36576      * Register components to be built later.
36577      *
36578      * This solves the following issues
36579      * - Building is not done on page load, but after an authentication process has occured.
36580      * - Interface elements are registered on page load
36581      * - Parent Interface elements may not be loaded before child, so this handles that..
36582      * 
36583      *
36584      * example:
36585      * 
36586      * MyApp.register({
36587           order : '000001',
36588           module : 'Pman.Tab.projectMgr',
36589           region : 'center',
36590           parent : 'Pman.layout',
36591           disabled : false,  // or use a function..
36592         })
36593      
36594      * * @param {Object} details about module
36595      */
36596     register : function(obj) {
36597         this.modules.push(obj);
36598          
36599     },
36600     /**
36601      * convert a string to an object..
36602      * 
36603      */
36604     
36605     toObject : function(str)
36606     {
36607         if (!str || typeof(str) == 'object') {
36608             return str;
36609         }
36610         var ar = str.split('.');
36611         var rt, o;
36612         rt = ar.shift();
36613             /** eval:var:o */
36614         eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
36615         if (o === false) {
36616             throw "Module not found : " + str;
36617         }
36618         Roo.each(ar, function(e) {
36619             if (typeof(o[e]) == 'undefined') {
36620                 throw "Module not found : " + str;
36621             }
36622             o = o[e];
36623         });
36624         return o;
36625         
36626     },
36627     
36628     
36629     /**
36630      * move modules into their correct place in the tree..
36631      * 
36632      */
36633     preBuild : function ()
36634     {
36635         
36636         Roo.each(this.modules , function (obj)
36637         {
36638             obj.parent = this.toObject(obj.parent);
36639             
36640             if (!obj.parent) {
36641                 this.topModule = obj;
36642                 return;
36643             }
36644             
36645             if (!obj.parent.modules) {
36646                 obj.parent.modules = new Roo.util.MixedCollection(false, 
36647                     function(o) { return o.order + '' }
36648                 );
36649             }
36650             
36651             obj.parent.modules.add(obj);
36652         }, this);
36653     },
36654     
36655      /**
36656      * make a list of modules to build.
36657      * @return {Array} list of modules. 
36658      */ 
36659     
36660     buildOrder : function()
36661     {
36662         var _this = this;
36663         var cmp = function(a,b) {   
36664             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
36665         };
36666         
36667         if (!this.topModule || !this.topModule.modules) {
36668             throw "No top level modules to build";
36669         }
36670        
36671         // make a flat list in order of modules to build.
36672         var mods = [ this.topModule ];
36673         
36674         
36675         // add modules to their parents..
36676         var addMod = function(m) {
36677            // Roo.debug && Roo.log(m.modKey);
36678             
36679             mods.push(m);
36680             if (m.modules) {
36681                 m.modules.keySort('ASC',  cmp );
36682                 m.modules.each(addMod);
36683             }
36684             // not sure if this is used any more..
36685             if (m.finalize) {
36686                 m.finalize.name = m.name + " (clean up) ";
36687                 mods.push(m.finalize);
36688             }
36689             
36690         }
36691         this.topModule.modules.keySort('ASC',  cmp );
36692         this.topModule.modules.each(addMod);
36693         return mods;
36694     },
36695     
36696      /**
36697      * Build the registered modules.
36698      * @param {Object} parent element.
36699      * @param {Function} optional method to call after module has been added.
36700      * 
36701      */ 
36702    
36703     build : function() 
36704     {
36705         
36706         this.preBuild();
36707         var mods = this.buildOrder();
36708       
36709         //this.allmods = mods;
36710         //Roo.debug && Roo.log(mods);
36711         //return;
36712         if (!mods.length) { // should not happen
36713             throw "NO modules!!!";
36714         }
36715         
36716         
36717         
36718         // flash it up as modal - so we store the mask!?
36719         Roo.MessageBox.show({ title: 'loading' });
36720         Roo.MessageBox.show({
36721            title: "Please wait...",
36722            msg: "Building Interface...",
36723            width:450,
36724            progress:true,
36725            closable:false,
36726            modal: false
36727           
36728         });
36729         var total = mods.length;
36730         
36731         var _this = this;
36732         var progressRun = function() {
36733             if (!mods.length) {
36734                 Roo.debug && Roo.log('hide?');
36735                 Roo.MessageBox.hide();
36736                 _this.topModule.fireEvent('buildcomplete', _this.topModule);
36737                 return;    
36738             }
36739             
36740             var m = mods.shift();
36741             Roo.debug && Roo.log(m);
36742             if (typeof(m) == 'function') { // not sure if this is supported any more..
36743                 m.call(this);
36744                 return progressRun.defer(10, _this);
36745             } 
36746             
36747             Roo.MessageBox.updateProgress(
36748                 (total  - mods.length)/total,  "Building Interface " + (total  - mods.length) + 
36749                     " of " + total + 
36750                     (m.name ? (' - ' + m.name) : '')
36751                     );
36752             
36753          
36754             
36755             var disabled = (typeof(m.disabled) == 'function') ?
36756                 m.disabled.call(m.module.disabled) : m.disabled;    
36757             
36758             
36759             if (disabled) {
36760                 return progressRun(); // we do not update the display!
36761             }
36762             
36763             if (!m.parent) {
36764                 // it's a top level one..
36765                 var layoutbase = new Ext.BorderLayout(document.body, {
36766                
36767                     center: {
36768                          titlebar: false,
36769                          autoScroll:false,
36770                          closeOnTab: true,
36771                          tabPosition: 'top',
36772                          //resizeTabs: true,
36773                          alwaysShowTabs: true,
36774                          minTabWidth: 140
36775                     }
36776                 });
36777                 var tree = m.tree();
36778                 tree.region = 'center';
36779                 m.el = layoutbase.addxtype(tree);
36780                 m.panel = m.el;
36781                 m.layout = m.panel.layout;    
36782                 return progressRun.defer(10, _this);
36783             }
36784             
36785             var tree = m.tree();
36786             tree.region = tree.region || m.region;
36787             m.el = m.parent.el.addxtype(tree);
36788             m.fireEvent('built', m);
36789             m.panel = m.el;
36790             m.layout = m.panel.layout;    
36791             progressRun.defer(10, _this); 
36792             
36793         }
36794         progressRun.defer(1, _this);
36795      
36796         
36797         
36798     }
36799      
36800    
36801     
36802     
36803 });
36804  //<script type="text/javascript">
36805
36806
36807 /**
36808  * @class Roo.Login
36809  * @extends Roo.LayoutDialog
36810  * A generic Login Dialog..... - only one needed in theory!?!?
36811  *
36812  * Fires XComponent builder on success...
36813  * 
36814  * Sends 
36815  *    username,password, lang = for login actions.
36816  *    check = 1 for periodic checking that sesion is valid.
36817  *    passwordRequest = email request password
36818  *    logout = 1 = to logout
36819  * 
36820  * Affects: (this id="????" elements)
36821  *   loading  (removed) (used to indicate application is loading)
36822  *   loading-mask (hides) (used to hide application when it's building loading)
36823  *   
36824  * 
36825  * Usage: 
36826  *    
36827  * 
36828  * Myapp.login = Roo.Login({
36829      url: xxxx,
36830    
36831      realm : 'Myapp', 
36832      
36833      
36834      method : 'POST',
36835      
36836      
36837      * 
36838  })
36839  * 
36840  * 
36841  * 
36842  **/
36843  
36844 Roo.Login = function(cfg)
36845 {
36846     this.addEvents({
36847         'refreshed' : true
36848     });
36849     
36850     Roo.apply(this,cfg);
36851     
36852     Roo.onReady(function() {
36853         this.onLoad();
36854     }, this);
36855     // call parent..
36856     
36857    
36858     Roo.Login.superclass.constructor.call(this, this);
36859     //this.addxtype(this.items[0]);
36860     
36861     
36862 }
36863
36864
36865 Roo.extend(Roo.Login, Roo.LayoutDialog, {
36866     
36867     /**
36868      * @cfg {String} method
36869      * Method used to query for login details.
36870      */
36871     
36872     method : 'POST',
36873     /**
36874      * @cfg {String} url
36875      * URL to query login data. - eg. baseURL + '/Login.php'
36876      */
36877     url : '',
36878     
36879     /**
36880      * @property user
36881      * The user data - if user.id < 0 then login will be bypassed. (used for inital setup situation.
36882      * @type {Object} 
36883      */
36884     user : false,
36885     /**
36886      * @property checkFails
36887      * Number of times we have attempted to get authentication check, and failed.
36888      * @type {Number} 
36889      */
36890     checkFails : 0,
36891       /**
36892      * @property intervalID
36893      * The window interval that does the constant login checking.
36894      * @type {Number} 
36895      */
36896     intervalID : 0,
36897     
36898     
36899     onLoad : function() // called on page load...
36900     {
36901         // load 
36902          
36903         if (Roo.get('loading')) { // clear any loading indicator..
36904             Roo.get('loading').remove();
36905         }
36906         
36907         //this.switchLang('en'); // set the language to english..
36908        
36909         this.check({
36910             success:  function(response, opts)  {  // check successfull...
36911             
36912                 var res = this.processResponse(response);
36913                 this.checkFails =0;
36914                 if (!res.success) { // error!
36915                     this.checkFails = 5;
36916                     //console.log('call failure');
36917                     return this.failure(response,opts);
36918                 }
36919                 
36920                 if (!res.data.id) { // id=0 == login failure.
36921                     return this.show();
36922                 }
36923                 
36924                               
36925                         //console.log(success);
36926                 this.fillAuth(res.data);   
36927                 this.checkFails =0;
36928                 Roo.XComponent.build();
36929             },
36930             failure : this.show
36931         });
36932         
36933     }, 
36934     
36935     
36936     check: function(cfg) // called every so often to refresh cookie etc..
36937     {
36938         if (cfg.again) { // could be undefined..
36939             this.checkFails++;
36940         } else {
36941             this.checkFails = 0;
36942         }
36943         var _this = this;
36944         if (this.sending) {
36945             if ( this.checkFails > 4) {
36946                 Roo.MessageBox.alert("Error",  
36947                     "Error getting authentication status. - try reloading, or wait a while", function() {
36948                         _this.sending = false;
36949                     }); 
36950                 return;
36951             }
36952             cfg.again = true;
36953             _this.check.defer(10000, _this, [ cfg ]); // check in 10 secs.
36954             return;
36955         }
36956         this.sending = true;
36957         
36958         Roo.Ajax.request({  
36959             url: this.url,
36960             params: {
36961                 getAuthUser: true
36962             },  
36963             method: this.method,
36964             success:  cfg.success || this.success,
36965             failure : cfg.failure || this.failure,
36966             scope : this,
36967             callCfg : cfg
36968               
36969         });  
36970     }, 
36971     
36972     
36973     logout: function()
36974     {
36975         window.onbeforeunload = function() { }; // false does not work for IE..
36976         this.user = false;
36977         var _this = this;
36978         
36979         Roo.Ajax.request({  
36980             url: this.url,
36981             params: {
36982                 logout: 1
36983             },  
36984             method: 'GET',
36985             failure : function() {
36986                 Roo.MessageBox.alert("Error", "Error logging out. - continuing anyway.", function() {
36987                     document.location = document.location.toString() + '?ts=' + Math.random();
36988                 });
36989                 
36990             },
36991             success : function() {
36992                 _this.user = false;
36993                 this.checkFails =0;
36994                 // fixme..
36995                 document.location = document.location.toString() + '?ts=' + Math.random();
36996             }
36997               
36998               
36999         }); 
37000     },
37001     
37002     processResponse : function (response)
37003     {
37004         var res = '';
37005         try {
37006             res = Roo.decode(response.responseText);
37007             // oops...
37008             if (typeof(res) != 'object') {
37009                 res = { success : false, errorMsg : res, errors : true };
37010             }
37011             if (typeof(res.success) == 'undefined') {
37012                 res.success = false;
37013             }
37014             
37015         } catch(e) {
37016             res = { success : false,  errorMsg : response.responseText, errors : true };
37017         }
37018         return res;
37019     },
37020     
37021     success : function(response, opts)  // check successfull...
37022     {  
37023         this.sending = false;
37024         var res = this.processResponse(response);
37025         if (!res.success) {
37026             return this.failure(response, opts);
37027         }
37028         if (!res.data || !res.data.id) {
37029             return this.failure(response,opts);
37030         }
37031         //console.log(res);
37032         this.fillAuth(res.data);
37033         
37034         this.checkFails =0;
37035         
37036     },
37037     
37038     
37039     failure : function (response, opts) // called if login 'check' fails.. (causes re-check)
37040     {
37041         this.authUser = -1;
37042         this.sending = false;
37043         var res = this.processResponse(response);
37044         //console.log(res);
37045         if ( this.checkFails > 2) {
37046         
37047             Roo.MessageBox.alert("Error", res.errorMsg ? res.errorMsg : 
37048                 "Error getting authentication status. - try reloading"); 
37049             return;
37050         }
37051         opts.callCfg.again = true;
37052         this.check.defer(1000, this, [ opts.callCfg ]);
37053         return;  
37054     },
37055     
37056     
37057     
37058     fillAuth: function(au) {
37059         this.startAuthCheck();
37060         this.authUserId = au.id;
37061         this.authUser = au;
37062         this.lastChecked = new Date();
37063         this.fireEvent('refreshed', au);
37064         //Pman.Tab.FaxQueue.newMaxId(au.faxMax);
37065         //Pman.Tab.FaxTab.setTitle(au.faxNumPending);
37066         au.lang = au.lang || 'en';
37067         //this.switchLang(Roo.state.Manager.get('Pman.Login.lang', 'en'));
37068         Roo.state.Manager.set( this.realm + 'lang' , au.lang);
37069         this.switchLang(au.lang );
37070         
37071      
37072         // open system... - -on setyp..
37073         if (this.authUserId  < 0) {
37074             Roo.MessageBox.alert("Warning", 
37075                 "This is an open system - please set up a admin user with a password.");  
37076         }
37077          
37078         //Pman.onload(); // which should do nothing if it's a re-auth result...
37079         
37080              
37081     },
37082     
37083     startAuthCheck : function() // starter for timeout checking..
37084     {
37085         if (this.intervalID) { // timer already in place...
37086             return false;
37087         }
37088         var _this = this;
37089         this.intervalID =  window.setInterval(function() {
37090               _this.check(false);
37091             }, 120000); // every 120 secs = 2mins..
37092         
37093         
37094     },
37095          
37096     
37097     switchLang : function (lang) 
37098     {
37099         _T = typeof(_T) == 'undefined' ? false : _T;
37100           if (!_T || !lang.length) {
37101             return;
37102         }
37103         
37104         if (!_T && lang != 'en') {
37105             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
37106             return;
37107         }
37108         
37109         if (typeof(_T.en) == 'undefined') {
37110             _T.en = {};
37111             Roo.apply(_T.en, _T);
37112         }
37113         
37114         if (typeof(_T[lang]) == 'undefined') {
37115             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
37116             return;
37117         }
37118         
37119         
37120         Roo.apply(_T, _T[lang]);
37121         // just need to set the text values for everything...
37122         var _this = this;
37123         /* this will not work ...
37124         if (this.form) { 
37125             
37126                
37127             function formLabel(name, val) {
37128                 _this.form.findField(name).fieldEl.child('label').dom.innerHTML  = val;
37129             }
37130             
37131             formLabel('password', "Password"+':');
37132             formLabel('username', "Email Address"+':');
37133             formLabel('lang', "Language"+':');
37134             this.dialog.setTitle("Login");
37135             this.dialog.buttons[0].setText("Forgot Password");
37136             this.dialog.buttons[1].setText("Login");
37137         }
37138         */
37139         
37140         
37141     },
37142     
37143     
37144     title: "Login",
37145     modal: true,
37146     width:  350,
37147     //height: 230,
37148     height: 180,
37149     shadow: true,
37150     minWidth:200,
37151     minHeight:180,
37152     //proxyDrag: true,
37153     closable: false,
37154     draggable: false,
37155     collapsible: false,
37156     resizable: false,
37157     center: {  // needed??
37158         autoScroll:false,
37159         titlebar: false,
37160        // tabPosition: 'top',
37161         hideTabs: true,
37162         closeOnTab: true,
37163         alwaysShowTabs: false
37164     } ,
37165     listeners : {
37166         
37167         show  : function(dlg)
37168         {
37169             //console.log(this);
37170             this.form = this.layout.getRegion('center').activePanel.form;
37171             this.form.dialog = dlg;
37172             this.buttons[0].form = this.form;
37173             this.buttons[0].dialog = dlg;
37174             this.buttons[1].form = this.form;
37175             this.buttons[1].dialog = dlg;
37176            
37177            //this.resizeToLogo.defer(1000,this);
37178             // this is all related to resizing for logos..
37179             //var sz = Roo.get(Pman.Login.form.el.query('img')[0]).getSize();
37180            //// if (!sz) {
37181              //   this.resizeToLogo.defer(1000,this);
37182              //   return;
37183            // }
37184             //var w = Ext.lib.Dom.getViewWidth() - 100;
37185             //var h = Ext.lib.Dom.getViewHeight() - 100;
37186             //this.resizeTo(Math.max(350, Math.min(sz.width + 30, w)),Math.min(sz.height+200, h));
37187             //this.center();
37188             if (this.disabled) {
37189                 this.hide();
37190                 return;
37191             }
37192             
37193             if (this.user.id < 0) { // used for inital setup situations.
37194                 return;
37195             }
37196             
37197             if (this.intervalID) {
37198                 // remove the timer
37199                 window.clearInterval(this.intervalID);
37200                 this.intervalID = false;
37201             }
37202             
37203             
37204             if (Roo.get('loading')) {
37205                 Roo.get('loading').remove();
37206             }
37207             if (Roo.get('loading-mask')) {
37208                 Roo.get('loading-mask').hide();
37209             }
37210             
37211             //incomming._node = tnode;
37212             this.form.reset();
37213             //this.dialog.modal = !modal;
37214             //this.dialog.show();
37215             this.el.unmask(); 
37216             
37217             
37218             this.form.setValues({
37219                 'username' : Roo.state.Manager.get(this.realm + '.username', ''),
37220                 'lang' : Roo.state.Manager.get(this.realm + '.lang', 'en')
37221             });
37222             
37223             this.switchLang(Roo.state.Manager.get(this.realm + '.lang', 'en'));
37224             if (this.form.findField('username').getValue().length > 0 ){
37225                 this.form.findField('password').focus();
37226             } else {
37227                this.form.findField('username').focus();
37228             }
37229     
37230         }
37231     },
37232     items : [
37233          {
37234        
37235             xtype : 'ContentPanel',
37236             xns : Roo,
37237             region: 'center',
37238             fitToFrame : true,
37239             
37240             items : [
37241     
37242                 {
37243                
37244                     xtype : 'Form',
37245                     xns : Roo.form,
37246                     labelWidth: 100,
37247                     style : 'margin: 10px;',
37248                     
37249                     listeners : {
37250                         actionfailed : function(f, act) {
37251                             // form can return { errors: .... }
37252                                 
37253                             //act.result.errors // invalid form element list...
37254                             //act.result.errorMsg// invalid form element list...
37255                             
37256                             this.dialog.el.unmask();
37257                             Roo.MessageBox.alert("Error", act.result.errorMsg ? act.result.errorMsg : 
37258                                         "Login failed - communication error - try again.");
37259                                       
37260                         },
37261                         actioncomplete: function(re, act) {
37262                              
37263                             Roo.state.Manager.set(
37264                                 this.dialog.realm + '.username',  
37265                                     this.findField('username').getValue()
37266                             );
37267                             Roo.state.Manager.set(
37268                                 this.dialog.realm + '.lang',  
37269                                 this.findField('lang').getValue() 
37270                             );
37271                             
37272                             this.dialog.fillAuth(act.result.data);
37273                               
37274                             this.dialog.hide();
37275                             
37276                             if (Roo.get('loading-mask')) {
37277                                 Roo.get('loading-mask').show();
37278                             }
37279                             Roo.XComponent.build();
37280                             
37281                              
37282                             
37283                         }
37284                     },
37285                     items : [
37286                         {
37287                             xtype : 'TextField',
37288                             xns : Roo.form,
37289                             fieldLabel: "Email Address",
37290                             name: 'username',
37291                             width:200,
37292                             autoCreate : {tag: "input", type: "text", size: "20"}
37293                         },
37294                         {
37295                             xtype : 'TextField',
37296                             xns : Roo.form,
37297                             fieldLabel: "Password",
37298                             inputType: 'password',
37299                             name: 'password',
37300                             width:200,
37301                             autoCreate : {tag: "input", type: "text", size: "20"},
37302                             listeners : {
37303                                 specialkey : function(e,ev) {
37304                                     if (ev.keyCode == 13) {
37305                                         this.form.dialog.el.mask("Logging in");
37306                                         this.form.doAction('submit', {
37307                                             url: this.form.dialog.url,
37308                                             method: this.form.dialog.method
37309                                         });
37310                                     }
37311                                 }
37312                             }  
37313                         },
37314                         {
37315                             xtype : 'ComboBox',
37316                             xns : Roo.form,
37317                             fieldLabel: "Language",
37318                             name : 'langdisp',
37319                             store: {
37320                                 xtype : 'SimpleStore',
37321                                 fields: ['lang', 'ldisp'],
37322                                 data : [
37323                                     [ 'en', 'English' ],
37324                                     [ 'zh_HK' , '\u7E41\u4E2D' ],
37325                                     [ 'zh_CN', '\u7C21\u4E2D' ]
37326                                 ]
37327                             },
37328                             
37329                             valueField : 'lang',
37330                             hiddenName:  'lang',
37331                             width: 200,
37332                             displayField:'ldisp',
37333                             typeAhead: false,
37334                             editable: false,
37335                             mode: 'local',
37336                             triggerAction: 'all',
37337                             emptyText:'Select a Language...',
37338                             selectOnFocus:true,
37339                             listeners : {
37340                                 select :  function(cb, rec, ix) {
37341                                     this.form.switchLang(rec.data.lang);
37342                                 }
37343                             }
37344                         
37345                         }
37346                     ]
37347                 }
37348                   
37349                 
37350             ]
37351         }
37352     ],
37353     buttons : [
37354         {
37355             xtype : 'Button',
37356             xns : 'Roo',
37357             text : "Forgot Password",
37358             listeners : {
37359                 click : function() {
37360                     //console.log(this);
37361                     var n = this.form.findField('username').getValue();
37362                     if (!n.length) {
37363                         Roo.MessageBox.alert("Error", "Fill in your email address");
37364                         return;
37365                     }
37366                     Roo.Ajax.request({
37367                         url: this.dialog.url,
37368                         params: {
37369                             passwordRequest: n
37370                         },
37371                         method: this.dialog.method,
37372                         success:  function(response, opts)  {  // check successfull...
37373                         
37374                             var res = this.dialog.processResponse(response);
37375                             if (!res.success) { // error!
37376                                Roo.MessageBox.alert("Error" ,
37377                                     res.errorMsg ? res.errorMsg  : "Problem Requesting Password Reset");
37378                                return;
37379                             }
37380                             Roo.MessageBox.alert("Notice" ,
37381                                 "Please check you email for the Password Reset message");
37382                         },
37383                         failure : function() {
37384                             Roo.MessageBox.alert("Error" , "Problem Requesting Password Reset");
37385                         }
37386                         
37387                     });
37388                 }
37389             }
37390         },
37391         {
37392             xtype : 'Button',
37393             xns : 'Roo',
37394             text : "Login",
37395             listeners : {
37396                 
37397                 click : function () {
37398                         
37399                     this.dialog.el.mask("Logging in");
37400                     this.form.doAction('submit', {
37401                             url: this.dialog.url,
37402                             method: this.dialog.method
37403                     });
37404                 }
37405             }
37406         }
37407     ]
37408   
37409   
37410 })
37411  
37412
37413
37414