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} dropAllowed
3978      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3979      */
3980     dropAllowed : "x-dd-drop-ok",
3981     /**
3982      * @cfg {String} dropNotAllowed
3983      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3984      */
3985     dropNotAllowed : "x-dd-drop-nodrop",
3986     /**
3987      * @cfg {boolean} success
3988      * set this after drop listener.. 
3989      */
3990     success : false,
3991     /**
3992      * @cfg {boolean} valid
3993      * if the drop point is valid for over/enter..
3994      */
3995     valid : false,
3996     // private
3997     isTarget : true,
3998
3999     // private
4000     isNotifyTarget : true,
4001     
4002     // private
4003
4004     notifyEnter : function(dd, e, data){
4005         this.valid = true;
4006         this.fireEvent('enter', this, dd, e, data);
4007         if(this.overClass){
4008             this.el.addClass(this.overClass);
4009         }
4010         return this.valid ? this.dropAllowed : this.dropNotAllowed;
4011     },
4012
4013     // private
4014
4015     notifyOver : function(dd, e, data){
4016         this.valid = true;
4017         this.fireEvent('over', this, dd, e, data);
4018         return this.valid ? this.dropAllowed : this.dropNotAllowed;
4019     },
4020
4021     
4022     notifyOut : function(dd, e, data){
4023         this.fireEvent('out', this, dd, e, data);
4024         if(this.overClass){
4025             this.el.removeClass(this.overClass);
4026         }
4027     },
4028
4029     
4030     notifyDrop : function(dd, e, data){
4031         this.success = false;
4032         this.fireEvent('drop', this, dd, e, data);
4033         return this.success;
4034     }
4035 });/*
4036  * Based on:
4037  * Ext JS Library 1.1.1
4038  * Copyright(c) 2006-2007, Ext JS, LLC.
4039  *
4040  * Originally Released Under LGPL - original licence link has changed is not relivant.
4041  *
4042  * Fork - LGPL
4043  * <script type="text/javascript">
4044  */
4045
4046
4047 /**
4048  * @class Roo.dd.DragZone
4049  * @extends Roo.dd.DragSource
4050  * This class provides a container DD instance that proxies for multiple child node sources.<br />
4051  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
4052  * @constructor
4053  * @param {String/HTMLElement/Element} el The container element
4054  * @param {Object} config
4055  */
4056 Roo.dd.DragZone = function(el, config){
4057     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
4058     if(this.containerScroll){
4059         Roo.dd.ScrollManager.register(this.el);
4060     }
4061 };
4062
4063 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
4064     /**
4065      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
4066      * for auto scrolling during drag operations.
4067      */
4068     /**
4069      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
4070      * method after a failed drop (defaults to "c3daf9" - light blue)
4071      */
4072
4073     /**
4074      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
4075      * for a valid target to drag based on the mouse down. Override this method
4076      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
4077      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
4078      * @param {EventObject} e The mouse down event
4079      * @return {Object} The dragData
4080      */
4081     getDragData : function(e){
4082         return Roo.dd.Registry.getHandleFromEvent(e);
4083     },
4084     
4085     /**
4086      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
4087      * this.dragData.ddel
4088      * @param {Number} x The x position of the click on the dragged object
4089      * @param {Number} y The y position of the click on the dragged object
4090      * @return {Boolean} true to continue the drag, false to cancel
4091      */
4092     onInitDrag : function(x, y){
4093         this.proxy.update(this.dragData.ddel.cloneNode(true));
4094         this.onStartDrag(x, y);
4095         return true;
4096     },
4097     
4098     /**
4099      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
4100      */
4101     afterRepair : function(){
4102         if(Roo.enableFx){
4103             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
4104         }
4105         this.dragging = false;
4106     },
4107
4108     /**
4109      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
4110      * the XY of this.dragData.ddel
4111      * @param {EventObject} e The mouse up event
4112      * @return {Array} The xy location (e.g. [100, 200])
4113      */
4114     getRepairXY : function(e){
4115         return Roo.Element.fly(this.dragData.ddel).getXY();  
4116     }
4117 });/*
4118  * Based on:
4119  * Ext JS Library 1.1.1
4120  * Copyright(c) 2006-2007, Ext JS, LLC.
4121  *
4122  * Originally Released Under LGPL - original licence link has changed is not relivant.
4123  *
4124  * Fork - LGPL
4125  * <script type="text/javascript">
4126  */
4127 /**
4128  * @class Roo.dd.DropZone
4129  * @extends Roo.dd.DropTarget
4130  * This class provides a container DD instance that proxies for multiple child node targets.<br />
4131  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
4132  * @constructor
4133  * @param {String/HTMLElement/Element} el The container element
4134  * @param {Object} config
4135  */
4136 Roo.dd.DropZone = function(el, config){
4137     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
4138 };
4139
4140 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
4141     /**
4142      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
4143      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
4144      * provide your own custom lookup.
4145      * @param {Event} e The event
4146      * @return {Object} data The custom data
4147      */
4148     getTargetFromEvent : function(e){
4149         return Roo.dd.Registry.getTargetFromEvent(e);
4150     },
4151
4152     /**
4153      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
4154      * that it has registered.  This method has no default implementation and should be overridden to provide
4155      * node-specific processing if necessary.
4156      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
4157      * {@link #getTargetFromEvent} for this node)
4158      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4159      * @param {Event} e The event
4160      * @param {Object} data An object containing arbitrary data supplied by the drag source
4161      */
4162     onNodeEnter : function(n, dd, e, data){
4163         
4164     },
4165
4166     /**
4167      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
4168      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
4169      * overridden to provide the proper feedback.
4170      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4171      * {@link #getTargetFromEvent} for this node)
4172      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4173      * @param {Event} e The event
4174      * @param {Object} data An object containing arbitrary data supplied by the drag source
4175      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4176      * underlying {@link Roo.dd.StatusProxy} can be updated
4177      */
4178     onNodeOver : function(n, dd, e, data){
4179         return this.dropAllowed;
4180     },
4181
4182     /**
4183      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
4184      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
4185      * node-specific processing if necessary.
4186      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4187      * {@link #getTargetFromEvent} for this node)
4188      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4189      * @param {Event} e The event
4190      * @param {Object} data An object containing arbitrary data supplied by the drag source
4191      */
4192     onNodeOut : function(n, dd, e, data){
4193         
4194     },
4195
4196     /**
4197      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
4198      * the drop node.  The default implementation returns false, so it should be overridden to provide the
4199      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
4200      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4201      * {@link #getTargetFromEvent} for this node)
4202      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4203      * @param {Event} e The event
4204      * @param {Object} data An object containing arbitrary data supplied by the drag source
4205      * @return {Boolean} True if the drop was valid, else false
4206      */
4207     onNodeDrop : function(n, dd, e, data){
4208         return false;
4209     },
4210
4211     /**
4212      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
4213      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
4214      * it should be overridden to provide the proper feedback if necessary.
4215      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4216      * @param {Event} e The event
4217      * @param {Object} data An object containing arbitrary data supplied by the drag source
4218      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4219      * underlying {@link Roo.dd.StatusProxy} can be updated
4220      */
4221     onContainerOver : function(dd, e, data){
4222         return this.dropNotAllowed;
4223     },
4224
4225     /**
4226      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
4227      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
4228      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
4229      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
4230      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4231      * @param {Event} e The event
4232      * @param {Object} data An object containing arbitrary data supplied by the drag source
4233      * @return {Boolean} True if the drop was valid, else false
4234      */
4235     onContainerDrop : function(dd, e, data){
4236         return false;
4237     },
4238
4239     /**
4240      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
4241      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
4242      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
4243      * you should override this method and provide a custom implementation.
4244      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4245      * @param {Event} e The event
4246      * @param {Object} data An object containing arbitrary data supplied by the drag source
4247      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4248      * underlying {@link Roo.dd.StatusProxy} can be updated
4249      */
4250     notifyEnter : function(dd, e, data){
4251         return this.dropNotAllowed;
4252     },
4253
4254     /**
4255      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
4256      * This method will be called on every mouse movement while the drag source is over the drop zone.
4257      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
4258      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
4259      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
4260      * registered node, it will call {@link #onContainerOver}.
4261      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4262      * @param {Event} e The event
4263      * @param {Object} data An object containing arbitrary data supplied by the drag source
4264      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4265      * underlying {@link Roo.dd.StatusProxy} can be updated
4266      */
4267     notifyOver : function(dd, e, data){
4268         var n = this.getTargetFromEvent(e);
4269         if(!n){ // not over valid drop target
4270             if(this.lastOverNode){
4271                 this.onNodeOut(this.lastOverNode, dd, e, data);
4272                 this.lastOverNode = null;
4273             }
4274             return this.onContainerOver(dd, e, data);
4275         }
4276         if(this.lastOverNode != n){
4277             if(this.lastOverNode){
4278                 this.onNodeOut(this.lastOverNode, dd, e, data);
4279             }
4280             this.onNodeEnter(n, dd, e, data);
4281             this.lastOverNode = n;
4282         }
4283         return this.onNodeOver(n, dd, e, data);
4284     },
4285
4286     /**
4287      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
4288      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
4289      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
4290      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
4291      * @param {Event} e The event
4292      * @param {Object} data An object containing arbitrary data supplied by the drag zone
4293      */
4294     notifyOut : function(dd, e, data){
4295         if(this.lastOverNode){
4296             this.onNodeOut(this.lastOverNode, dd, e, data);
4297             this.lastOverNode = null;
4298         }
4299     },
4300
4301     /**
4302      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
4303      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
4304      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
4305      * otherwise it will call {@link #onContainerDrop}.
4306      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4307      * @param {Event} e The event
4308      * @param {Object} data An object containing arbitrary data supplied by the drag source
4309      * @return {Boolean} True if the drop was valid, else false
4310      */
4311     notifyDrop : function(dd, e, data){
4312         if(this.lastOverNode){
4313             this.onNodeOut(this.lastOverNode, dd, e, data);
4314             this.lastOverNode = null;
4315         }
4316         var n = this.getTargetFromEvent(e);
4317         return n ?
4318             this.onNodeDrop(n, dd, e, data) :
4319             this.onContainerDrop(dd, e, data);
4320     },
4321
4322     // private
4323     triggerCacheRefresh : function(){
4324         Roo.dd.DDM.refreshCache(this.groups);
4325     }  
4326 });/*
4327  * Based on:
4328  * Ext JS Library 1.1.1
4329  * Copyright(c) 2006-2007, Ext JS, LLC.
4330  *
4331  * Originally Released Under LGPL - original licence link has changed is not relivant.
4332  *
4333  * Fork - LGPL
4334  * <script type="text/javascript">
4335  */
4336
4337
4338 /**
4339  * @class Roo.data.SortTypes
4340  * @singleton
4341  * Defines the default sorting (casting?) comparison functions used when sorting data.
4342  */
4343 Roo.data.SortTypes = {
4344     /**
4345      * Default sort that does nothing
4346      * @param {Mixed} s The value being converted
4347      * @return {Mixed} The comparison value
4348      */
4349     none : function(s){
4350         return s;
4351     },
4352     
4353     /**
4354      * The regular expression used to strip tags
4355      * @type {RegExp}
4356      * @property
4357      */
4358     stripTagsRE : /<\/?[^>]+>/gi,
4359     
4360     /**
4361      * Strips all HTML tags to sort on text only
4362      * @param {Mixed} s The value being converted
4363      * @return {String} The comparison value
4364      */
4365     asText : function(s){
4366         return String(s).replace(this.stripTagsRE, "");
4367     },
4368     
4369     /**
4370      * Strips all HTML tags to sort on text only - Case insensitive
4371      * @param {Mixed} s The value being converted
4372      * @return {String} The comparison value
4373      */
4374     asUCText : function(s){
4375         return String(s).toUpperCase().replace(this.stripTagsRE, "");
4376     },
4377     
4378     /**
4379      * Case insensitive string
4380      * @param {Mixed} s The value being converted
4381      * @return {String} The comparison value
4382      */
4383     asUCString : function(s) {
4384         return String(s).toUpperCase();
4385     },
4386     
4387     /**
4388      * Date sorting
4389      * @param {Mixed} s The value being converted
4390      * @return {Number} The comparison value
4391      */
4392     asDate : function(s) {
4393         if(!s){
4394             return 0;
4395         }
4396         if(s instanceof Date){
4397             return s.getTime();
4398         }
4399         return Date.parse(String(s));
4400     },
4401     
4402     /**
4403      * Float sorting
4404      * @param {Mixed} s The value being converted
4405      * @return {Float} The comparison value
4406      */
4407     asFloat : function(s) {
4408         var val = parseFloat(String(s).replace(/,/g, ""));
4409         if(isNaN(val)) val = 0;
4410         return val;
4411     },
4412     
4413     /**
4414      * Integer sorting
4415      * @param {Mixed} s The value being converted
4416      * @return {Number} The comparison value
4417      */
4418     asInt : function(s) {
4419         var val = parseInt(String(s).replace(/,/g, ""));
4420         if(isNaN(val)) val = 0;
4421         return val;
4422     }
4423 };/*
4424  * Based on:
4425  * Ext JS Library 1.1.1
4426  * Copyright(c) 2006-2007, Ext JS, LLC.
4427  *
4428  * Originally Released Under LGPL - original licence link has changed is not relivant.
4429  *
4430  * Fork - LGPL
4431  * <script type="text/javascript">
4432  */
4433
4434 /**
4435 * @class Roo.data.Record
4436  * Instances of this class encapsulate both record <em>definition</em> information, and record
4437  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
4438  * to access Records cached in an {@link Roo.data.Store} object.<br>
4439  * <p>
4440  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
4441  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
4442  * objects.<br>
4443  * <p>
4444  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
4445  * @constructor
4446  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
4447  * {@link #create}. The parameters are the same.
4448  * @param {Array} data An associative Array of data values keyed by the field name.
4449  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
4450  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
4451  * not specified an integer id is generated.
4452  */
4453 Roo.data.Record = function(data, id){
4454     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
4455     this.data = data;
4456 };
4457
4458 /**
4459  * Generate a constructor for a specific record layout.
4460  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
4461  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
4462  * Each field definition object may contain the following properties: <ul>
4463  * <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,
4464  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
4465  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
4466  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
4467  * is being used, then this is a string containing the javascript expression to reference the data relative to 
4468  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
4469  * to the data item relative to the record element. If the mapping expression is the same as the field name,
4470  * this may be omitted.</p></li>
4471  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
4472  * <ul><li>auto (Default, implies no conversion)</li>
4473  * <li>string</li>
4474  * <li>int</li>
4475  * <li>float</li>
4476  * <li>boolean</li>
4477  * <li>date</li></ul></p></li>
4478  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
4479  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
4480  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
4481  * by the Reader into an object that will be stored in the Record. It is passed the
4482  * following parameters:<ul>
4483  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
4484  * </ul></p></li>
4485  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
4486  * </ul>
4487  * <br>usage:<br><pre><code>
4488 var TopicRecord = Roo.data.Record.create(
4489     {name: 'title', mapping: 'topic_title'},
4490     {name: 'author', mapping: 'username'},
4491     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
4492     {name: 'lastPost', mapping: 'post_time', type: 'date'},
4493     {name: 'lastPoster', mapping: 'user2'},
4494     {name: 'excerpt', mapping: 'post_text'}
4495 );
4496
4497 var myNewRecord = new TopicRecord({
4498     title: 'Do my job please',
4499     author: 'noobie',
4500     totalPosts: 1,
4501     lastPost: new Date(),
4502     lastPoster: 'Animal',
4503     excerpt: 'No way dude!'
4504 });
4505 myStore.add(myNewRecord);
4506 </code></pre>
4507  * @method create
4508  * @static
4509  */
4510 Roo.data.Record.create = function(o){
4511     var f = function(){
4512         f.superclass.constructor.apply(this, arguments);
4513     };
4514     Roo.extend(f, Roo.data.Record);
4515     var p = f.prototype;
4516     p.fields = new Roo.util.MixedCollection(false, function(field){
4517         return field.name;
4518     });
4519     for(var i = 0, len = o.length; i < len; i++){
4520         p.fields.add(new Roo.data.Field(o[i]));
4521     }
4522     f.getField = function(name){
4523         return p.fields.get(name);  
4524     };
4525     return f;
4526 };
4527
4528 Roo.data.Record.AUTO_ID = 1000;
4529 Roo.data.Record.EDIT = 'edit';
4530 Roo.data.Record.REJECT = 'reject';
4531 Roo.data.Record.COMMIT = 'commit';
4532
4533 Roo.data.Record.prototype = {
4534     /**
4535      * Readonly flag - true if this record has been modified.
4536      * @type Boolean
4537      */
4538     dirty : false,
4539     editing : false,
4540     error: null,
4541     modified: null,
4542
4543     // private
4544     join : function(store){
4545         this.store = store;
4546     },
4547
4548     /**
4549      * Set the named field to the specified value.
4550      * @param {String} name The name of the field to set.
4551      * @param {Object} value The value to set the field to.
4552      */
4553     set : function(name, value){
4554         if(this.data[name] == value){
4555             return;
4556         }
4557         this.dirty = true;
4558         if(!this.modified){
4559             this.modified = {};
4560         }
4561         if(typeof this.modified[name] == 'undefined'){
4562             this.modified[name] = this.data[name];
4563         }
4564         this.data[name] = value;
4565         if(!this.editing){
4566             this.store.afterEdit(this);
4567         }       
4568     },
4569
4570     /**
4571      * Get the value of the named field.
4572      * @param {String} name The name of the field to get the value of.
4573      * @return {Object} The value of the field.
4574      */
4575     get : function(name){
4576         return this.data[name]; 
4577     },
4578
4579     // private
4580     beginEdit : function(){
4581         this.editing = true;
4582         this.modified = {}; 
4583     },
4584
4585     // private
4586     cancelEdit : function(){
4587         this.editing = false;
4588         delete this.modified;
4589     },
4590
4591     // private
4592     endEdit : function(){
4593         this.editing = false;
4594         if(this.dirty && this.store){
4595             this.store.afterEdit(this);
4596         }
4597     },
4598
4599     /**
4600      * Usually called by the {@link Roo.data.Store} which owns the Record.
4601      * Rejects all changes made to the Record since either creation, or the last commit operation.
4602      * Modified fields are reverted to their original values.
4603      * <p>
4604      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4605      * of reject operations.
4606      */
4607     reject : function(){
4608         var m = this.modified;
4609         for(var n in m){
4610             if(typeof m[n] != "function"){
4611                 this.data[n] = m[n];
4612             }
4613         }
4614         this.dirty = false;
4615         delete this.modified;
4616         this.editing = false;
4617         if(this.store){
4618             this.store.afterReject(this);
4619         }
4620     },
4621
4622     /**
4623      * Usually called by the {@link Roo.data.Store} which owns the Record.
4624      * Commits all changes made to the Record since either creation, or the last commit operation.
4625      * <p>
4626      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4627      * of commit operations.
4628      */
4629     commit : function(){
4630         this.dirty = false;
4631         delete this.modified;
4632         this.editing = false;
4633         if(this.store){
4634             this.store.afterCommit(this);
4635         }
4636     },
4637
4638     // private
4639     hasError : function(){
4640         return this.error != null;
4641     },
4642
4643     // private
4644     clearError : function(){
4645         this.error = null;
4646     },
4647
4648     /**
4649      * Creates a copy of this record.
4650      * @param {String} id (optional) A new record id if you don't want to use this record's id
4651      * @return {Record}
4652      */
4653     copy : function(newId) {
4654         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
4655     }
4656 };/*
4657  * Based on:
4658  * Ext JS Library 1.1.1
4659  * Copyright(c) 2006-2007, Ext JS, LLC.
4660  *
4661  * Originally Released Under LGPL - original licence link has changed is not relivant.
4662  *
4663  * Fork - LGPL
4664  * <script type="text/javascript">
4665  */
4666
4667
4668
4669 /**
4670  * @class Roo.data.Store
4671  * @extends Roo.util.Observable
4672  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
4673  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
4674  * <p>
4675  * 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
4676  * has no knowledge of the format of the data returned by the Proxy.<br>
4677  * <p>
4678  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
4679  * instances from the data object. These records are cached and made available through accessor functions.
4680  * @constructor
4681  * Creates a new Store.
4682  * @param {Object} config A config object containing the objects needed for the Store to access data,
4683  * and read the data into Records.
4684  */
4685 Roo.data.Store = function(config){
4686     this.data = new Roo.util.MixedCollection(false);
4687     this.data.getKey = function(o){
4688         return o.id;
4689     };
4690     this.baseParams = {};
4691     // private
4692     this.paramNames = {
4693         "start" : "start",
4694         "limit" : "limit",
4695         "sort" : "sort",
4696         "dir" : "dir"
4697     };
4698
4699     if(config && config.data){
4700         this.inlineData = config.data;
4701         delete config.data;
4702     }
4703
4704     Roo.apply(this, config);
4705     
4706     if(this.reader){ // reader passed
4707         this.reader = Roo.factory(this.reader, Roo.data);
4708         this.reader.xmodule = this.xmodule || false;
4709         if(!this.recordType){
4710             this.recordType = this.reader.recordType;
4711         }
4712         if(this.reader.onMetaChange){
4713             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
4714         }
4715     }
4716
4717     if(this.recordType){
4718         this.fields = this.recordType.prototype.fields;
4719     }
4720     this.modified = [];
4721
4722     this.addEvents({
4723         /**
4724          * @event datachanged
4725          * Fires when the data cache has changed, and a widget which is using this Store
4726          * as a Record cache should refresh its view.
4727          * @param {Store} this
4728          */
4729         datachanged : true,
4730         /**
4731          * @event metachange
4732          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
4733          * @param {Store} this
4734          * @param {Object} meta The JSON metadata
4735          */
4736         metachange : true,
4737         /**
4738          * @event add
4739          * Fires when Records have been added to the Store
4740          * @param {Store} this
4741          * @param {Roo.data.Record[]} records The array of Records added
4742          * @param {Number} index The index at which the record(s) were added
4743          */
4744         add : true,
4745         /**
4746          * @event remove
4747          * Fires when a Record has been removed from the Store
4748          * @param {Store} this
4749          * @param {Roo.data.Record} record The Record that was removed
4750          * @param {Number} index The index at which the record was removed
4751          */
4752         remove : true,
4753         /**
4754          * @event update
4755          * Fires when a Record has been updated
4756          * @param {Store} this
4757          * @param {Roo.data.Record} record The Record that was updated
4758          * @param {String} operation The update operation being performed.  Value may be one of:
4759          * <pre><code>
4760  Roo.data.Record.EDIT
4761  Roo.data.Record.REJECT
4762  Roo.data.Record.COMMIT
4763          * </code></pre>
4764          */
4765         update : true,
4766         /**
4767          * @event clear
4768          * Fires when the data cache has been cleared.
4769          * @param {Store} this
4770          */
4771         clear : true,
4772         /**
4773          * @event beforeload
4774          * Fires before a request is made for a new data object.  If the beforeload handler returns false
4775          * the load action will be canceled.
4776          * @param {Store} this
4777          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4778          */
4779         beforeload : true,
4780         /**
4781          * @event load
4782          * Fires after a new set of Records has been loaded.
4783          * @param {Store} this
4784          * @param {Roo.data.Record[]} records The Records that were loaded
4785          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4786          */
4787         load : true,
4788         /**
4789          * @event loadexception
4790          * Fires if an exception occurs in the Proxy during loading.
4791          * Called with the signature of the Proxy's "loadexception" event.
4792          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
4793          * 
4794          * @param {Proxy} 
4795          * @param {Object} return from JsonData.reader() - success, totalRecords, records
4796          * @param {Object} load options 
4797          * @param {Object} jsonData from your request (normally this contains the Exception)
4798          */
4799         loadexception : true
4800     });
4801     
4802     if(this.proxy){
4803         this.proxy = Roo.factory(this.proxy, Roo.data);
4804         this.proxy.xmodule = this.xmodule || false;
4805         this.relayEvents(this.proxy,  ["loadexception"]);
4806     }
4807     this.sortToggle = {};
4808
4809     Roo.data.Store.superclass.constructor.call(this);
4810
4811     if(this.inlineData){
4812         this.loadData(this.inlineData);
4813         delete this.inlineData;
4814     }
4815 };
4816 Roo.extend(Roo.data.Store, Roo.util.Observable, {
4817      /**
4818     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
4819     * without a remote query - used by combo/forms at present.
4820     */
4821     
4822     /**
4823     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
4824     */
4825     /**
4826     * @cfg {Array} data Inline data to be loaded when the store is initialized.
4827     */
4828     /**
4829     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
4830     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
4831     */
4832     /**
4833     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
4834     * on any HTTP request
4835     */
4836     /**
4837     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
4838     */
4839     /**
4840     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
4841     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
4842     */
4843     remoteSort : false,
4844
4845     /**
4846     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
4847      * loaded or when a record is removed. (defaults to false).
4848     */
4849     pruneModifiedRecords : false,
4850
4851     // private
4852     lastOptions : null,
4853
4854     /**
4855      * Add Records to the Store and fires the add event.
4856      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4857      */
4858     add : function(records){
4859         records = [].concat(records);
4860         for(var i = 0, len = records.length; i < len; i++){
4861             records[i].join(this);
4862         }
4863         var index = this.data.length;
4864         this.data.addAll(records);
4865         this.fireEvent("add", this, records, index);
4866     },
4867
4868     /**
4869      * Remove a Record from the Store and fires the remove event.
4870      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
4871      */
4872     remove : function(record){
4873         var index = this.data.indexOf(record);
4874         this.data.removeAt(index);
4875         if(this.pruneModifiedRecords){
4876             this.modified.remove(record);
4877         }
4878         this.fireEvent("remove", this, record, index);
4879     },
4880
4881     /**
4882      * Remove all Records from the Store and fires the clear event.
4883      */
4884     removeAll : function(){
4885         this.data.clear();
4886         if(this.pruneModifiedRecords){
4887             this.modified = [];
4888         }
4889         this.fireEvent("clear", this);
4890     },
4891
4892     /**
4893      * Inserts Records to the Store at the given index and fires the add event.
4894      * @param {Number} index The start index at which to insert the passed Records.
4895      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4896      */
4897     insert : function(index, records){
4898         records = [].concat(records);
4899         for(var i = 0, len = records.length; i < len; i++){
4900             this.data.insert(index, records[i]);
4901             records[i].join(this);
4902         }
4903         this.fireEvent("add", this, records, index);
4904     },
4905
4906     /**
4907      * Get the index within the cache of the passed Record.
4908      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
4909      * @return {Number} The index of the passed Record. Returns -1 if not found.
4910      */
4911     indexOf : function(record){
4912         return this.data.indexOf(record);
4913     },
4914
4915     /**
4916      * Get the index within the cache of the Record with the passed id.
4917      * @param {String} id The id of the Record to find.
4918      * @return {Number} The index of the Record. Returns -1 if not found.
4919      */
4920     indexOfId : function(id){
4921         return this.data.indexOfKey(id);
4922     },
4923
4924     /**
4925      * Get the Record with the specified id.
4926      * @param {String} id The id of the Record to find.
4927      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
4928      */
4929     getById : function(id){
4930         return this.data.key(id);
4931     },
4932
4933     /**
4934      * Get the Record at the specified index.
4935      * @param {Number} index The index of the Record to find.
4936      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
4937      */
4938     getAt : function(index){
4939         return this.data.itemAt(index);
4940     },
4941
4942     /**
4943      * Returns a range of Records between specified indices.
4944      * @param {Number} startIndex (optional) The starting index (defaults to 0)
4945      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
4946      * @return {Roo.data.Record[]} An array of Records
4947      */
4948     getRange : function(start, end){
4949         return this.data.getRange(start, end);
4950     },
4951
4952     // private
4953     storeOptions : function(o){
4954         o = Roo.apply({}, o);
4955         delete o.callback;
4956         delete o.scope;
4957         this.lastOptions = o;
4958     },
4959
4960     /**
4961      * Loads the Record cache from the configured Proxy using the configured Reader.
4962      * <p>
4963      * If using remote paging, then the first load call must specify the <em>start</em>
4964      * and <em>limit</em> properties in the options.params property to establish the initial
4965      * position within the dataset, and the number of Records to cache on each read from the Proxy.
4966      * <p>
4967      * <strong>It is important to note that for remote data sources, loading is asynchronous,
4968      * and this call will return before the new data has been loaded. Perform any post-processing
4969      * in a callback function, or in a "load" event handler.</strong>
4970      * <p>
4971      * @param {Object} options An object containing properties which control loading options:<ul>
4972      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
4973      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
4974      * passed the following arguments:<ul>
4975      * <li>r : Roo.data.Record[]</li>
4976      * <li>options: Options object from the load call</li>
4977      * <li>success: Boolean success indicator</li></ul></li>
4978      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
4979      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
4980      * </ul>
4981      */
4982     load : function(options){
4983         options = options || {};
4984         if(this.fireEvent("beforeload", this, options) !== false){
4985             this.storeOptions(options);
4986             var p = Roo.apply(options.params || {}, this.baseParams);
4987             // if meta was not loaded from remote source.. try requesting it.
4988             if (!this.reader.metaFromRemote) {
4989                 p._requestMeta = 1;
4990             }
4991             if(this.sortInfo && this.remoteSort){
4992                 var pn = this.paramNames;
4993                 p[pn["sort"]] = this.sortInfo.field;
4994                 p[pn["dir"]] = this.sortInfo.direction;
4995             }
4996             this.proxy.load(p, this.reader, this.loadRecords, this, options);
4997         }
4998     },
4999
5000     /**
5001      * Reloads the Record cache from the configured Proxy using the configured Reader and
5002      * the options from the last load operation performed.
5003      * @param {Object} options (optional) An object containing properties which may override the options
5004      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
5005      * the most recently used options are reused).
5006      */
5007     reload : function(options){
5008         this.load(Roo.applyIf(options||{}, this.lastOptions));
5009     },
5010
5011     // private
5012     // Called as a callback by the Reader during a load operation.
5013     loadRecords : function(o, options, success){
5014         if(!o || success === false){
5015             if(success !== false){
5016                 this.fireEvent("load", this, [], options);
5017             }
5018             if(options.callback){
5019                 options.callback.call(options.scope || this, [], options, false);
5020             }
5021             return;
5022         }
5023         // if data returned failure - throw an exception.
5024         if (o.success === false) {
5025             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
5026             return;
5027         }
5028         var r = o.records, t = o.totalRecords || r.length;
5029         if(!options || options.add !== true){
5030             if(this.pruneModifiedRecords){
5031                 this.modified = [];
5032             }
5033             for(var i = 0, len = r.length; i < len; i++){
5034                 r[i].join(this);
5035             }
5036             if(this.snapshot){
5037                 this.data = this.snapshot;
5038                 delete this.snapshot;
5039             }
5040             this.data.clear();
5041             this.data.addAll(r);
5042             this.totalLength = t;
5043             this.applySort();
5044             this.fireEvent("datachanged", this);
5045         }else{
5046             this.totalLength = Math.max(t, this.data.length+r.length);
5047             this.add(r);
5048         }
5049         this.fireEvent("load", this, r, options);
5050         if(options.callback){
5051             options.callback.call(options.scope || this, r, options, true);
5052         }
5053     },
5054
5055     /**
5056      * Loads data from a passed data block. A Reader which understands the format of the data
5057      * must have been configured in the constructor.
5058      * @param {Object} data The data block from which to read the Records.  The format of the data expected
5059      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
5060      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
5061      */
5062     loadData : function(o, append){
5063         var r = this.reader.readRecords(o);
5064         this.loadRecords(r, {add: append}, true);
5065     },
5066
5067     /**
5068      * Gets the number of cached records.
5069      * <p>
5070      * <em>If using paging, this may not be the total size of the dataset. If the data object
5071      * used by the Reader contains the dataset size, then the getTotalCount() function returns
5072      * the data set size</em>
5073      */
5074     getCount : function(){
5075         return this.data.length || 0;
5076     },
5077
5078     /**
5079      * Gets the total number of records in the dataset as returned by the server.
5080      * <p>
5081      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
5082      * the dataset size</em>
5083      */
5084     getTotalCount : function(){
5085         return this.totalLength || 0;
5086     },
5087
5088     /**
5089      * Returns the sort state of the Store as an object with two properties:
5090      * <pre><code>
5091  field {String} The name of the field by which the Records are sorted
5092  direction {String} The sort order, "ASC" or "DESC"
5093      * </code></pre>
5094      */
5095     getSortState : function(){
5096         return this.sortInfo;
5097     },
5098
5099     // private
5100     applySort : function(){
5101         if(this.sortInfo && !this.remoteSort){
5102             var s = this.sortInfo, f = s.field;
5103             var st = this.fields.get(f).sortType;
5104             var fn = function(r1, r2){
5105                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
5106                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
5107             };
5108             this.data.sort(s.direction, fn);
5109             if(this.snapshot && this.snapshot != this.data){
5110                 this.snapshot.sort(s.direction, fn);
5111             }
5112         }
5113     },
5114
5115     /**
5116      * Sets the default sort column and order to be used by the next load operation.
5117      * @param {String} fieldName The name of the field to sort by.
5118      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5119      */
5120     setDefaultSort : function(field, dir){
5121         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
5122     },
5123
5124     /**
5125      * Sort the Records.
5126      * If remote sorting is used, the sort is performed on the server, and the cache is
5127      * reloaded. If local sorting is used, the cache is sorted internally.
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     sort : function(fieldName, dir){
5132         var f = this.fields.get(fieldName);
5133         if(!dir){
5134             if(this.sortInfo && this.sortInfo.field == f.name){ // toggle sort dir
5135                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
5136             }else{
5137                 dir = f.sortDir;
5138             }
5139         }
5140         this.sortToggle[f.name] = dir;
5141         this.sortInfo = {field: f.name, direction: dir};
5142         if(!this.remoteSort){
5143             this.applySort();
5144             this.fireEvent("datachanged", this);
5145         }else{
5146             this.load(this.lastOptions);
5147         }
5148     },
5149
5150     /**
5151      * Calls the specified function for each of the Records in the cache.
5152      * @param {Function} fn The function to call. The Record is passed as the first parameter.
5153      * Returning <em>false</em> aborts and exits the iteration.
5154      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
5155      */
5156     each : function(fn, scope){
5157         this.data.each(fn, scope);
5158     },
5159
5160     /**
5161      * Gets all records modified since the last commit.  Modified records are persisted across load operations
5162      * (e.g., during paging).
5163      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
5164      */
5165     getModifiedRecords : function(){
5166         return this.modified;
5167     },
5168
5169     // private
5170     createFilterFn : function(property, value, anyMatch){
5171         if(!value.exec){ // not a regex
5172             value = String(value);
5173             if(value.length == 0){
5174                 return false;
5175             }
5176             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
5177         }
5178         return function(r){
5179             return value.test(r.data[property]);
5180         };
5181     },
5182
5183     /**
5184      * Sums the value of <i>property</i> for each record between start and end and returns the result.
5185      * @param {String} property A field on your records
5186      * @param {Number} start The record index to start at (defaults to 0)
5187      * @param {Number} end The last record index to include (defaults to length - 1)
5188      * @return {Number} The sum
5189      */
5190     sum : function(property, start, end){
5191         var rs = this.data.items, v = 0;
5192         start = start || 0;
5193         end = (end || end === 0) ? end : rs.length-1;
5194
5195         for(var i = start; i <= end; i++){
5196             v += (rs[i].data[property] || 0);
5197         }
5198         return v;
5199     },
5200
5201     /**
5202      * Filter the records by a specified property.
5203      * @param {String} field A field on your records
5204      * @param {String/RegExp} value Either a string that the field
5205      * should start with or a RegExp to test against the field
5206      * @param {Boolean} anyMatch True to match any part not just the beginning
5207      */
5208     filter : function(property, value, anyMatch){
5209         var fn = this.createFilterFn(property, value, anyMatch);
5210         return fn ? this.filterBy(fn) : this.clearFilter();
5211     },
5212
5213     /**
5214      * Filter by a function. The specified function will be called with each
5215      * record in this data source. If the function returns true the record is included,
5216      * otherwise it is filtered.
5217      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5218      * @param {Object} scope (optional) The scope of the function (defaults to this)
5219      */
5220     filterBy : function(fn, scope){
5221         this.snapshot = this.snapshot || this.data;
5222         this.data = this.queryBy(fn, scope||this);
5223         this.fireEvent("datachanged", this);
5224     },
5225
5226     /**
5227      * Query the records by a specified property.
5228      * @param {String} field A field on your records
5229      * @param {String/RegExp} value Either a string that the field
5230      * should start with or a RegExp to test against the field
5231      * @param {Boolean} anyMatch True to match any part not just the beginning
5232      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5233      */
5234     query : function(property, value, anyMatch){
5235         var fn = this.createFilterFn(property, value, anyMatch);
5236         return fn ? this.queryBy(fn) : this.data.clone();
5237     },
5238
5239     /**
5240      * Query by a function. The specified function will be called with each
5241      * record in this data source. If the function returns true the record is included
5242      * in the results.
5243      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5244      * @param {Object} scope (optional) The scope of the function (defaults to this)
5245       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5246      **/
5247     queryBy : function(fn, scope){
5248         var data = this.snapshot || this.data;
5249         return data.filterBy(fn, scope||this);
5250     },
5251
5252     /**
5253      * Collects unique values for a particular dataIndex from this store.
5254      * @param {String} dataIndex The property to collect
5255      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
5256      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
5257      * @return {Array} An array of the unique values
5258      **/
5259     collect : function(dataIndex, allowNull, bypassFilter){
5260         var d = (bypassFilter === true && this.snapshot) ?
5261                 this.snapshot.items : this.data.items;
5262         var v, sv, r = [], l = {};
5263         for(var i = 0, len = d.length; i < len; i++){
5264             v = d[i].data[dataIndex];
5265             sv = String(v);
5266             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
5267                 l[sv] = true;
5268                 r[r.length] = v;
5269             }
5270         }
5271         return r;
5272     },
5273
5274     /**
5275      * Revert to a view of the Record cache with no filtering applied.
5276      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
5277      */
5278     clearFilter : function(suppressEvent){
5279         if(this.snapshot && this.snapshot != this.data){
5280             this.data = this.snapshot;
5281             delete this.snapshot;
5282             if(suppressEvent !== true){
5283                 this.fireEvent("datachanged", this);
5284             }
5285         }
5286     },
5287
5288     // private
5289     afterEdit : function(record){
5290         if(this.modified.indexOf(record) == -1){
5291             this.modified.push(record);
5292         }
5293         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
5294     },
5295
5296     // private
5297     afterReject : function(record){
5298         this.modified.remove(record);
5299         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
5300     },
5301
5302     // private
5303     afterCommit : function(record){
5304         this.modified.remove(record);
5305         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
5306     },
5307
5308     /**
5309      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
5310      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
5311      */
5312     commitChanges : function(){
5313         var m = this.modified.slice(0);
5314         this.modified = [];
5315         for(var i = 0, len = m.length; i < len; i++){
5316             m[i].commit();
5317         }
5318     },
5319
5320     /**
5321      * Cancel outstanding changes on all changed records.
5322      */
5323     rejectChanges : 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].reject();
5328         }
5329     },
5330
5331     onMetaChange : function(meta, rtype, o){
5332         this.recordType = rtype;
5333         this.fields = rtype.prototype.fields;
5334         delete this.snapshot;
5335         this.sortInfo = meta.sortInfo || this.sortInfo;
5336         this.modified = [];
5337         this.fireEvent('metachange', this, this.reader.meta);
5338     }
5339 });/*
5340  * Based on:
5341  * Ext JS Library 1.1.1
5342  * Copyright(c) 2006-2007, Ext JS, LLC.
5343  *
5344  * Originally Released Under LGPL - original licence link has changed is not relivant.
5345  *
5346  * Fork - LGPL
5347  * <script type="text/javascript">
5348  */
5349
5350 /**
5351  * @class Roo.data.SimpleStore
5352  * @extends Roo.data.Store
5353  * Small helper class to make creating Stores from Array data easier.
5354  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
5355  * @cfg {Array} fields An array of field definition objects, or field name strings.
5356  * @cfg {Array} data The multi-dimensional array of data
5357  * @constructor
5358  * @param {Object} config
5359  */
5360 Roo.data.SimpleStore = function(config){
5361     Roo.data.SimpleStore.superclass.constructor.call(this, {
5362         isLocal : true,
5363         reader: new Roo.data.ArrayReader({
5364                 id: config.id
5365             },
5366             Roo.data.Record.create(config.fields)
5367         ),
5368         proxy : new Roo.data.MemoryProxy(config.data)
5369     });
5370     this.load();
5371 };
5372 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
5373  * Based on:
5374  * Ext JS Library 1.1.1
5375  * Copyright(c) 2006-2007, Ext JS, LLC.
5376  *
5377  * Originally Released Under LGPL - original licence link has changed is not relivant.
5378  *
5379  * Fork - LGPL
5380  * <script type="text/javascript">
5381  */
5382
5383 /**
5384 /**
5385  * @extends Roo.data.Store
5386  * @class Roo.data.JsonStore
5387  * Small helper class to make creating Stores for JSON data easier. <br/>
5388 <pre><code>
5389 var store = new Roo.data.JsonStore({
5390     url: 'get-images.php',
5391     root: 'images',
5392     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
5393 });
5394 </code></pre>
5395  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
5396  * JsonReader and HttpProxy (unless inline data is provided).</b>
5397  * @cfg {Array} fields An array of field definition objects, or field name strings.
5398  * @constructor
5399  * @param {Object} config
5400  */
5401 Roo.data.JsonStore = function(c){
5402     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
5403         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
5404         reader: new Roo.data.JsonReader(c, c.fields)
5405     }));
5406 };
5407 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
5408  * Based on:
5409  * Ext JS Library 1.1.1
5410  * Copyright(c) 2006-2007, Ext JS, LLC.
5411  *
5412  * Originally Released Under LGPL - original licence link has changed is not relivant.
5413  *
5414  * Fork - LGPL
5415  * <script type="text/javascript">
5416  */
5417
5418  
5419 Roo.data.Field = function(config){
5420     if(typeof config == "string"){
5421         config = {name: config};
5422     }
5423     Roo.apply(this, config);
5424     
5425     if(!this.type){
5426         this.type = "auto";
5427     }
5428     
5429     var st = Roo.data.SortTypes;
5430     // named sortTypes are supported, here we look them up
5431     if(typeof this.sortType == "string"){
5432         this.sortType = st[this.sortType];
5433     }
5434     
5435     // set default sortType for strings and dates
5436     if(!this.sortType){
5437         switch(this.type){
5438             case "string":
5439                 this.sortType = st.asUCString;
5440                 break;
5441             case "date":
5442                 this.sortType = st.asDate;
5443                 break;
5444             default:
5445                 this.sortType = st.none;
5446         }
5447     }
5448
5449     // define once
5450     var stripRe = /[\$,%]/g;
5451
5452     // prebuilt conversion function for this field, instead of
5453     // switching every time we're reading a value
5454     if(!this.convert){
5455         var cv, dateFormat = this.dateFormat;
5456         switch(this.type){
5457             case "":
5458             case "auto":
5459             case undefined:
5460                 cv = function(v){ return v; };
5461                 break;
5462             case "string":
5463                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
5464                 break;
5465             case "int":
5466                 cv = function(v){
5467                     return v !== undefined && v !== null && v !== '' ?
5468                            parseInt(String(v).replace(stripRe, ""), 10) : '';
5469                     };
5470                 break;
5471             case "float":
5472                 cv = function(v){
5473                     return v !== undefined && v !== null && v !== '' ?
5474                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
5475                     };
5476                 break;
5477             case "bool":
5478             case "boolean":
5479                 cv = function(v){ return v === true || v === "true" || v == 1; };
5480                 break;
5481             case "date":
5482                 cv = function(v){
5483                     if(!v){
5484                         return '';
5485                     }
5486                     if(v instanceof Date){
5487                         return v;
5488                     }
5489                     if(dateFormat){
5490                         if(dateFormat == "timestamp"){
5491                             return new Date(v*1000);
5492                         }
5493                         return Date.parseDate(v, dateFormat);
5494                     }
5495                     var parsed = Date.parse(v);
5496                     return parsed ? new Date(parsed) : null;
5497                 };
5498              break;
5499             
5500         }
5501         this.convert = cv;
5502     }
5503 };
5504
5505 Roo.data.Field.prototype = {
5506     dateFormat: null,
5507     defaultValue: "",
5508     mapping: null,
5509     sortType : null,
5510     sortDir : "ASC"
5511 };/*
5512  * Based on:
5513  * Ext JS Library 1.1.1
5514  * Copyright(c) 2006-2007, Ext JS, LLC.
5515  *
5516  * Originally Released Under LGPL - original licence link has changed is not relivant.
5517  *
5518  * Fork - LGPL
5519  * <script type="text/javascript">
5520  */
5521  
5522 // Base class for reading structured data from a data source.  This class is intended to be
5523 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
5524
5525 /**
5526  * @class Roo.data.DataReader
5527  * Base class for reading structured data from a data source.  This class is intended to be
5528  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
5529  */
5530
5531 Roo.data.DataReader = function(meta, recordType){
5532     
5533     this.meta = meta;
5534     
5535     this.recordType = recordType instanceof Array ? 
5536         Roo.data.Record.create(recordType) : recordType;
5537 };
5538
5539 Roo.data.DataReader.prototype = {
5540      /**
5541      * Create an empty record
5542      * @param {Object} data (optional) - overlay some values
5543      * @return {Roo.data.Record} record created.
5544      */
5545     newRow :  function(d) {
5546         var da =  {};
5547         this.recordType.prototype.fields.each(function(c) {
5548             switch( c.type) {
5549                 case 'int' : da[c.name] = 0; break;
5550                 case 'date' : da[c.name] = new Date(); break;
5551                 case 'float' : da[c.name] = 0.0; break;
5552                 case 'boolean' : da[c.name] = false; break;
5553                 default : da[c.name] = ""; break;
5554             }
5555             
5556         });
5557         return new this.recordType(Roo.apply(da, d));
5558     }
5559     
5560 };/*
5561  * Based on:
5562  * Ext JS Library 1.1.1
5563  * Copyright(c) 2006-2007, Ext JS, LLC.
5564  *
5565  * Originally Released Under LGPL - original licence link has changed is not relivant.
5566  *
5567  * Fork - LGPL
5568  * <script type="text/javascript">
5569  */
5570
5571 /**
5572  * @class Roo.data.DataProxy
5573  * @extends Roo.data.Observable
5574  * This class is an abstract base class for implementations which provide retrieval of
5575  * unformatted data objects.<br>
5576  * <p>
5577  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
5578  * (of the appropriate type which knows how to parse the data object) to provide a block of
5579  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
5580  * <p>
5581  * Custom implementations must implement the load method as described in
5582  * {@link Roo.data.HttpProxy#load}.
5583  */
5584 Roo.data.DataProxy = function(){
5585     this.addEvents({
5586         /**
5587          * @event beforeload
5588          * Fires before a network request is made to retrieve a data object.
5589          * @param {Object} This DataProxy object.
5590          * @param {Object} params The params parameter to the load function.
5591          */
5592         beforeload : true,
5593         /**
5594          * @event load
5595          * Fires before the load method's callback is called.
5596          * @param {Object} This DataProxy object.
5597          * @param {Object} o The data object.
5598          * @param {Object} arg The callback argument object passed to the load function.
5599          */
5600         load : true,
5601         /**
5602          * @event loadexception
5603          * Fires if an Exception occurs during data retrieval.
5604          * @param {Object} This DataProxy object.
5605          * @param {Object} o The data object.
5606          * @param {Object} arg The callback argument object passed to the load function.
5607          * @param {Object} e The Exception.
5608          */
5609         loadexception : true
5610     });
5611     Roo.data.DataProxy.superclass.constructor.call(this);
5612 };
5613
5614 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
5615
5616     /**
5617      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
5618      */
5619 /*
5620  * Based on:
5621  * Ext JS Library 1.1.1
5622  * Copyright(c) 2006-2007, Ext JS, LLC.
5623  *
5624  * Originally Released Under LGPL - original licence link has changed is not relivant.
5625  *
5626  * Fork - LGPL
5627  * <script type="text/javascript">
5628  */
5629 /**
5630  * @class Roo.data.MemoryProxy
5631  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
5632  * to the Reader when its load method is called.
5633  * @constructor
5634  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
5635  */
5636 Roo.data.MemoryProxy = function(data){
5637     if (data.data) {
5638         data = data.data;
5639     }
5640     Roo.data.MemoryProxy.superclass.constructor.call(this);
5641     this.data = data;
5642 };
5643
5644 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
5645     /**
5646      * Load data from the requested source (in this case an in-memory
5647      * data object passed to the constructor), read the data object into
5648      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5649      * process that block using the passed callback.
5650      * @param {Object} params This parameter is not used by the MemoryProxy class.
5651      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5652      * object into a block of Roo.data.Records.
5653      * @param {Function} callback The function into which to pass the block of Roo.data.records.
5654      * The function must be passed <ul>
5655      * <li>The Record block object</li>
5656      * <li>The "arg" argument from the load function</li>
5657      * <li>A boolean success indicator</li>
5658      * </ul>
5659      * @param {Object} scope The scope in which to call the callback
5660      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5661      */
5662     load : function(params, reader, callback, scope, arg){
5663         params = params || {};
5664         var result;
5665         try {
5666             result = reader.readRecords(this.data);
5667         }catch(e){
5668             this.fireEvent("loadexception", this, arg, null, e);
5669             callback.call(scope, null, arg, false);
5670             return;
5671         }
5672         callback.call(scope, result, arg, true);
5673     },
5674     
5675     // private
5676     update : function(params, records){
5677         
5678     }
5679 });/*
5680  * Based on:
5681  * Ext JS Library 1.1.1
5682  * Copyright(c) 2006-2007, Ext JS, LLC.
5683  *
5684  * Originally Released Under LGPL - original licence link has changed is not relivant.
5685  *
5686  * Fork - LGPL
5687  * <script type="text/javascript">
5688  */
5689 /**
5690  * @class Roo.data.HttpProxy
5691  * @extends Roo.data.DataProxy
5692  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
5693  * configured to reference a certain URL.<br><br>
5694  * <p>
5695  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
5696  * from which the running page was served.<br><br>
5697  * <p>
5698  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
5699  * <p>
5700  * Be aware that to enable the browser to parse an XML document, the server must set
5701  * the Content-Type header in the HTTP response to "text/xml".
5702  * @constructor
5703  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
5704  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
5705  * will be used to make the request.
5706  */
5707 Roo.data.HttpProxy = function(conn){
5708     Roo.data.HttpProxy.superclass.constructor.call(this);
5709     // is conn a conn config or a real conn?
5710     this.conn = conn;
5711     this.useAjax = !conn || !conn.events;
5712   
5713 };
5714
5715 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
5716     // thse are take from connection...
5717     
5718     /**
5719      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
5720      */
5721     /**
5722      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
5723      * extra parameters to each request made by this object. (defaults to undefined)
5724      */
5725     /**
5726      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
5727      *  to each request made by this object. (defaults to undefined)
5728      */
5729     /**
5730      * @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)
5731      */
5732     /**
5733      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
5734      */
5735      /**
5736      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
5737      * @type Boolean
5738      */
5739   
5740
5741     /**
5742      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
5743      * @type Boolean
5744      */
5745     /**
5746      * Return the {@link Roo.data.Connection} object being used by this Proxy.
5747      * @return {Connection} The Connection object. This object may be used to subscribe to events on
5748      * a finer-grained basis than the DataProxy events.
5749      */
5750     getConnection : function(){
5751         return this.useAjax ? Roo.Ajax : this.conn;
5752     },
5753
5754     /**
5755      * Load data from the configured {@link Roo.data.Connection}, read the data object into
5756      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
5757      * process that block using the passed callback.
5758      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5759      * for the request to the remote server.
5760      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5761      * object into a block of Roo.data.Records.
5762      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5763      * The function must be passed <ul>
5764      * <li>The Record block object</li>
5765      * <li>The "arg" argument from the load function</li>
5766      * <li>A boolean success indicator</li>
5767      * </ul>
5768      * @param {Object} scope The scope in which to call the callback
5769      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5770      */
5771     load : function(params, reader, callback, scope, arg){
5772         if(this.fireEvent("beforeload", this, params) !== false){
5773             var  o = {
5774                 params : params || {},
5775                 request: {
5776                     callback : callback,
5777                     scope : scope,
5778                     arg : arg
5779                 },
5780                 reader: reader,
5781                 callback : this.loadResponse,
5782                 scope: this
5783             };
5784             if(this.useAjax){
5785                 Roo.applyIf(o, this.conn);
5786                 if(this.activeRequest){
5787                     Roo.Ajax.abort(this.activeRequest);
5788                 }
5789                 this.activeRequest = Roo.Ajax.request(o);
5790             }else{
5791                 this.conn.request(o);
5792             }
5793         }else{
5794             callback.call(scope||this, null, arg, false);
5795         }
5796     },
5797
5798     // private
5799     loadResponse : function(o, success, response){
5800         delete this.activeRequest;
5801         if(!success){
5802             this.fireEvent("loadexception", this, o, response);
5803             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5804             return;
5805         }
5806         var result;
5807         try {
5808             result = o.reader.read(response);
5809         }catch(e){
5810             this.fireEvent("loadexception", this, o, response, e);
5811             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5812             return;
5813         }
5814         
5815         this.fireEvent("load", this, o, o.request.arg);
5816         o.request.callback.call(o.request.scope, result, o.request.arg, true);
5817     },
5818
5819     // private
5820     update : function(dataSet){
5821
5822     },
5823
5824     // private
5825     updateResponse : function(dataSet){
5826
5827     }
5828 });/*
5829  * Based on:
5830  * Ext JS Library 1.1.1
5831  * Copyright(c) 2006-2007, Ext JS, LLC.
5832  *
5833  * Originally Released Under LGPL - original licence link has changed is not relivant.
5834  *
5835  * Fork - LGPL
5836  * <script type="text/javascript">
5837  */
5838
5839 /**
5840  * @class Roo.data.ScriptTagProxy
5841  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
5842  * other than the originating domain of the running page.<br><br>
5843  * <p>
5844  * <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
5845  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
5846  * <p>
5847  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
5848  * source code that is used as the source inside a &lt;script> tag.<br><br>
5849  * <p>
5850  * In order for the browser to process the returned data, the server must wrap the data object
5851  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
5852  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
5853  * depending on whether the callback name was passed:
5854  * <p>
5855  * <pre><code>
5856 boolean scriptTag = false;
5857 String cb = request.getParameter("callback");
5858 if (cb != null) {
5859     scriptTag = true;
5860     response.setContentType("text/javascript");
5861 } else {
5862     response.setContentType("application/x-json");
5863 }
5864 Writer out = response.getWriter();
5865 if (scriptTag) {
5866     out.write(cb + "(");
5867 }
5868 out.print(dataBlock.toJsonString());
5869 if (scriptTag) {
5870     out.write(");");
5871 }
5872 </pre></code>
5873  *
5874  * @constructor
5875  * @param {Object} config A configuration object.
5876  */
5877 Roo.data.ScriptTagProxy = function(config){
5878     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
5879     Roo.apply(this, config);
5880     this.head = document.getElementsByTagName("head")[0];
5881 };
5882
5883 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
5884
5885 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
5886     /**
5887      * @cfg {String} url The URL from which to request the data object.
5888      */
5889     /**
5890      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
5891      */
5892     timeout : 30000,
5893     /**
5894      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
5895      * the server the name of the callback function set up by the load call to process the returned data object.
5896      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
5897      * javascript output which calls this named function passing the data object as its only parameter.
5898      */
5899     callbackParam : "callback",
5900     /**
5901      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
5902      * name to the request.
5903      */
5904     nocache : true,
5905
5906     /**
5907      * Load data from the configured URL, read the data object into
5908      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5909      * process that block using the passed callback.
5910      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5911      * for the request to the remote server.
5912      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5913      * object into a block of Roo.data.Records.
5914      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5915      * The function must be passed <ul>
5916      * <li>The Record block object</li>
5917      * <li>The "arg" argument from the load function</li>
5918      * <li>A boolean success indicator</li>
5919      * </ul>
5920      * @param {Object} scope The scope in which to call the callback
5921      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5922      */
5923     load : function(params, reader, callback, scope, arg){
5924         if(this.fireEvent("beforeload", this, params) !== false){
5925
5926             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
5927
5928             var url = this.url;
5929             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
5930             if(this.nocache){
5931                 url += "&_dc=" + (new Date().getTime());
5932             }
5933             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
5934             var trans = {
5935                 id : transId,
5936                 cb : "stcCallback"+transId,
5937                 scriptId : "stcScript"+transId,
5938                 params : params,
5939                 arg : arg,
5940                 url : url,
5941                 callback : callback,
5942                 scope : scope,
5943                 reader : reader
5944             };
5945             var conn = this;
5946
5947             window[trans.cb] = function(o){
5948                 conn.handleResponse(o, trans);
5949             };
5950
5951             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
5952
5953             if(this.autoAbort !== false){
5954                 this.abort();
5955             }
5956
5957             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
5958
5959             var script = document.createElement("script");
5960             script.setAttribute("src", url);
5961             script.setAttribute("type", "text/javascript");
5962             script.setAttribute("id", trans.scriptId);
5963             this.head.appendChild(script);
5964
5965             this.trans = trans;
5966         }else{
5967             callback.call(scope||this, null, arg, false);
5968         }
5969     },
5970
5971     // private
5972     isLoading : function(){
5973         return this.trans ? true : false;
5974     },
5975
5976     /**
5977      * Abort the current server request.
5978      */
5979     abort : function(){
5980         if(this.isLoading()){
5981             this.destroyTrans(this.trans);
5982         }
5983     },
5984
5985     // private
5986     destroyTrans : function(trans, isLoaded){
5987         this.head.removeChild(document.getElementById(trans.scriptId));
5988         clearTimeout(trans.timeoutId);
5989         if(isLoaded){
5990             window[trans.cb] = undefined;
5991             try{
5992                 delete window[trans.cb];
5993             }catch(e){}
5994         }else{
5995             // if hasn't been loaded, wait for load to remove it to prevent script error
5996             window[trans.cb] = function(){
5997                 window[trans.cb] = undefined;
5998                 try{
5999                     delete window[trans.cb];
6000                 }catch(e){}
6001             };
6002         }
6003     },
6004
6005     // private
6006     handleResponse : function(o, trans){
6007         this.trans = false;
6008         this.destroyTrans(trans, true);
6009         var result;
6010         try {
6011             result = trans.reader.readRecords(o);
6012         }catch(e){
6013             this.fireEvent("loadexception", this, o, trans.arg, e);
6014             trans.callback.call(trans.scope||window, null, trans.arg, false);
6015             return;
6016         }
6017         this.fireEvent("load", this, o, trans.arg);
6018         trans.callback.call(trans.scope||window, result, trans.arg, true);
6019     },
6020
6021     // private
6022     handleFailure : function(trans){
6023         this.trans = false;
6024         this.destroyTrans(trans, false);
6025         this.fireEvent("loadexception", this, null, trans.arg);
6026         trans.callback.call(trans.scope||window, null, trans.arg, false);
6027     }
6028 });/*
6029  * Based on:
6030  * Ext JS Library 1.1.1
6031  * Copyright(c) 2006-2007, Ext JS, LLC.
6032  *
6033  * Originally Released Under LGPL - original licence link has changed is not relivant.
6034  *
6035  * Fork - LGPL
6036  * <script type="text/javascript">
6037  */
6038
6039 /**
6040  * @class Roo.data.JsonReader
6041  * @extends Roo.data.DataReader
6042  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
6043  * based on mappings in a provided Roo.data.Record constructor.
6044  * 
6045  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
6046  * in the reply previously. 
6047  * 
6048  * <p>
6049  * Example code:
6050  * <pre><code>
6051 var RecordDef = Roo.data.Record.create([
6052     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6053     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6054 ]);
6055 var myReader = new Roo.data.JsonReader({
6056     totalProperty: "results",    // The property which contains the total dataset size (optional)
6057     root: "rows",                // The property which contains an Array of row objects
6058     id: "id"                     // The property within each row object that provides an ID for the record (optional)
6059 }, RecordDef);
6060 </code></pre>
6061  * <p>
6062  * This would consume a JSON file like this:
6063  * <pre><code>
6064 { 'results': 2, 'rows': [
6065     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
6066     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
6067 }
6068 </code></pre>
6069  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
6070  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6071  * paged from the remote server.
6072  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
6073  * @cfg {String} root name of the property which contains the Array of row objects.
6074  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
6075  * @constructor
6076  * Create a new JsonReader
6077  * @param {Object} meta Metadata configuration options
6078  * @param {Object} recordType Either an Array of field definition objects,
6079  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
6080  */
6081 Roo.data.JsonReader = function(meta, recordType){
6082     
6083     meta = meta || {};
6084     // set some defaults:
6085     Roo.applyIf(meta, {
6086         totalProperty: 'total',
6087         successProperty : 'success',
6088         root : 'data',
6089         id : 'id'
6090     });
6091     
6092     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6093 };
6094 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
6095     
6096     /**
6097      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
6098      * Used by Store query builder to append _requestMeta to params.
6099      * 
6100      */
6101     metaFromRemote : false,
6102     /**
6103      * This method is only used by a DataProxy which has retrieved data from a remote server.
6104      * @param {Object} response The XHR object which contains the JSON data in its responseText.
6105      * @return {Object} data A data block which is used by an Roo.data.Store object as
6106      * a cache of Roo.data.Records.
6107      */
6108     read : function(response){
6109         var json = response.responseText;
6110        
6111         var o = /* eval:var:o */ eval("("+json+")");
6112         if(!o) {
6113             throw {message: "JsonReader.read: Json object not found"};
6114         }
6115         
6116         if(o.metaData){
6117             
6118             delete this.ef;
6119             this.metaFromRemote = true;
6120             this.meta = o.metaData;
6121             this.recordType = Roo.data.Record.create(o.metaData.fields);
6122             this.onMetaChange(this.meta, this.recordType, o);
6123         }
6124         return this.readRecords(o);
6125     },
6126
6127     // private function a store will implement
6128     onMetaChange : function(meta, recordType, o){
6129
6130     },
6131
6132     /**
6133          * @ignore
6134          */
6135     simpleAccess: function(obj, subsc) {
6136         return obj[subsc];
6137     },
6138
6139         /**
6140          * @ignore
6141          */
6142     getJsonAccessor: function(){
6143         var re = /[\[\.]/;
6144         return function(expr) {
6145             try {
6146                 return(re.test(expr))
6147                     ? new Function("obj", "return obj." + expr)
6148                     : function(obj){
6149                         return obj[expr];
6150                     };
6151             } catch(e){}
6152             return Roo.emptyFn;
6153         };
6154     }(),
6155
6156     /**
6157      * Create a data block containing Roo.data.Records from an XML document.
6158      * @param {Object} o An object which contains an Array of row objects in the property specified
6159      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
6160      * which contains the total size of the dataset.
6161      * @return {Object} data A data block which is used by an Roo.data.Store object as
6162      * a cache of Roo.data.Records.
6163      */
6164     readRecords : function(o){
6165         /**
6166          * After any data loads, the raw JSON data is available for further custom processing.
6167          * @type Object
6168          */
6169         this.jsonData = o;
6170         var s = this.meta, Record = this.recordType,
6171             f = Record.prototype.fields, fi = f.items, fl = f.length;
6172
6173 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
6174         if (!this.ef) {
6175             if(s.totalProperty) {
6176                     this.getTotal = this.getJsonAccessor(s.totalProperty);
6177                 }
6178                 if(s.successProperty) {
6179                     this.getSuccess = this.getJsonAccessor(s.successProperty);
6180                 }
6181                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
6182                 if (s.id) {
6183                         var g = this.getJsonAccessor(s.id);
6184                         this.getId = function(rec) {
6185                                 var r = g(rec);
6186                                 return (r === undefined || r === "") ? null : r;
6187                         };
6188                 } else {
6189                         this.getId = function(){return null;};
6190                 }
6191             this.ef = [];
6192             for(var jj = 0; jj < fl; jj++){
6193                 f = fi[jj];
6194                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
6195                 this.ef[jj] = this.getJsonAccessor(map);
6196             }
6197         }
6198
6199         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
6200         if(s.totalProperty){
6201             var vt = parseInt(this.getTotal(o), 10);
6202             if(!isNaN(vt)){
6203                 totalRecords = vt;
6204             }
6205         }
6206         if(s.successProperty){
6207             var vs = this.getSuccess(o);
6208             if(vs === false || vs === 'false'){
6209                 success = false;
6210             }
6211         }
6212         var records = [];
6213             for(var i = 0; i < c; i++){
6214                     var n = root[i];
6215                 var values = {};
6216                 var id = this.getId(n);
6217                 for(var j = 0; j < fl; j++){
6218                     f = fi[j];
6219                 var v = this.ef[j](n);
6220                 if (!f.convert) {
6221                     Roo.log('missing convert for ' + f.name);
6222                     Roo.log(f);
6223                     continue;
6224                 }
6225                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
6226                 }
6227                 var record = new Record(values, id);
6228                 record.json = n;
6229                 records[i] = record;
6230             }
6231             return {
6232                 success : success,
6233                 records : records,
6234                 totalRecords : totalRecords
6235             };
6236     }
6237 });/*
6238  * Based on:
6239  * Ext JS Library 1.1.1
6240  * Copyright(c) 2006-2007, Ext JS, LLC.
6241  *
6242  * Originally Released Under LGPL - original licence link has changed is not relivant.
6243  *
6244  * Fork - LGPL
6245  * <script type="text/javascript">
6246  */
6247
6248 /**
6249  * @class Roo.data.XmlReader
6250  * @extends Roo.data.DataReader
6251  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
6252  * based on mappings in a provided Roo.data.Record constructor.<br><br>
6253  * <p>
6254  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
6255  * header in the HTTP response must be set to "text/xml".</em>
6256  * <p>
6257  * Example code:
6258  * <pre><code>
6259 var RecordDef = Roo.data.Record.create([
6260    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6261    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6262 ]);
6263 var myReader = new Roo.data.XmlReader({
6264    totalRecords: "results", // The element which contains the total dataset size (optional)
6265    record: "row",           // The repeated element which contains row information
6266    id: "id"                 // The element within the row that provides an ID for the record (optional)
6267 }, RecordDef);
6268 </code></pre>
6269  * <p>
6270  * This would consume an XML file like this:
6271  * <pre><code>
6272 &lt;?xml?>
6273 &lt;dataset>
6274  &lt;results>2&lt;/results>
6275  &lt;row>
6276    &lt;id>1&lt;/id>
6277    &lt;name>Bill&lt;/name>
6278    &lt;occupation>Gardener&lt;/occupation>
6279  &lt;/row>
6280  &lt;row>
6281    &lt;id>2&lt;/id>
6282    &lt;name>Ben&lt;/name>
6283    &lt;occupation>Horticulturalist&lt;/occupation>
6284  &lt;/row>
6285 &lt;/dataset>
6286 </code></pre>
6287  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
6288  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6289  * paged from the remote server.
6290  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
6291  * @cfg {String} success The DomQuery path to the success attribute used by forms.
6292  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
6293  * a record identifier value.
6294  * @constructor
6295  * Create a new XmlReader
6296  * @param {Object} meta Metadata configuration options
6297  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
6298  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
6299  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
6300  */
6301 Roo.data.XmlReader = function(meta, recordType){
6302     meta = meta || {};
6303     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6304 };
6305 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
6306     /**
6307      * This method is only used by a DataProxy which has retrieved data from a remote server.
6308          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
6309          * to contain a method called 'responseXML' that returns an XML document object.
6310      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6311      * a cache of Roo.data.Records.
6312      */
6313     read : function(response){
6314         var doc = response.responseXML;
6315         if(!doc) {
6316             throw {message: "XmlReader.read: XML Document not available"};
6317         }
6318         return this.readRecords(doc);
6319     },
6320
6321     /**
6322      * Create a data block containing Roo.data.Records from an XML document.
6323          * @param {Object} doc A parsed XML document.
6324      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6325      * a cache of Roo.data.Records.
6326      */
6327     readRecords : function(doc){
6328         /**
6329          * After any data loads/reads, the raw XML Document is available for further custom processing.
6330          * @type XMLDocument
6331          */
6332         this.xmlData = doc;
6333         var root = doc.documentElement || doc;
6334         var q = Roo.DomQuery;
6335         var recordType = this.recordType, fields = recordType.prototype.fields;
6336         var sid = this.meta.id;
6337         var totalRecords = 0, success = true;
6338         if(this.meta.totalRecords){
6339             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
6340         }
6341         
6342         if(this.meta.success){
6343             var sv = q.selectValue(this.meta.success, root, true);
6344             success = sv !== false && sv !== 'false';
6345         }
6346         var records = [];
6347         var ns = q.select(this.meta.record, root);
6348         for(var i = 0, len = ns.length; i < len; i++) {
6349                 var n = ns[i];
6350                 var values = {};
6351                 var id = sid ? q.selectValue(sid, n) : undefined;
6352                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6353                     var f = fields.items[j];
6354                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
6355                     v = f.convert(v);
6356                     values[f.name] = v;
6357                 }
6358                 var record = new recordType(values, id);
6359                 record.node = n;
6360                 records[records.length] = record;
6361             }
6362
6363             return {
6364                 success : success,
6365                 records : records,
6366                 totalRecords : totalRecords || records.length
6367             };
6368     }
6369 });/*
6370  * Based on:
6371  * Ext JS Library 1.1.1
6372  * Copyright(c) 2006-2007, Ext JS, LLC.
6373  *
6374  * Originally Released Under LGPL - original licence link has changed is not relivant.
6375  *
6376  * Fork - LGPL
6377  * <script type="text/javascript">
6378  */
6379
6380 /**
6381  * @class Roo.data.ArrayReader
6382  * @extends Roo.data.DataReader
6383  * Data reader class to create an Array of Roo.data.Record objects from an Array.
6384  * Each element of that Array represents a row of data fields. The
6385  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
6386  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
6387  * <p>
6388  * Example code:.
6389  * <pre><code>
6390 var RecordDef = Roo.data.Record.create([
6391     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
6392     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
6393 ]);
6394 var myReader = new Roo.data.ArrayReader({
6395     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
6396 }, RecordDef);
6397 </code></pre>
6398  * <p>
6399  * This would consume an Array like this:
6400  * <pre><code>
6401 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
6402   </code></pre>
6403  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
6404  * @constructor
6405  * Create a new JsonReader
6406  * @param {Object} meta Metadata configuration options.
6407  * @param {Object} recordType Either an Array of field definition objects
6408  * as specified to {@link Roo.data.Record#create},
6409  * or an {@link Roo.data.Record} object
6410  * created using {@link Roo.data.Record#create}.
6411  */
6412 Roo.data.ArrayReader = function(meta, recordType){
6413     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
6414 };
6415
6416 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
6417     /**
6418      * Create a data block containing Roo.data.Records from an XML document.
6419      * @param {Object} o An Array of row objects which represents the dataset.
6420      * @return {Object} data A data block which is used by an Roo.data.Store object as
6421      * a cache of Roo.data.Records.
6422      */
6423     readRecords : function(o){
6424         var sid = this.meta ? this.meta.id : null;
6425         var recordType = this.recordType, fields = recordType.prototype.fields;
6426         var records = [];
6427         var root = o;
6428             for(var i = 0; i < root.length; i++){
6429                     var n = root[i];
6430                 var values = {};
6431                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
6432                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6433                 var f = fields.items[j];
6434                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
6435                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
6436                 v = f.convert(v);
6437                 values[f.name] = v;
6438             }
6439                 var record = new recordType(values, id);
6440                 record.json = n;
6441                 records[records.length] = record;
6442             }
6443             return {
6444                 records : records,
6445                 totalRecords : records.length
6446             };
6447     }
6448 });/*
6449  * Based on:
6450  * Ext JS Library 1.1.1
6451  * Copyright(c) 2006-2007, Ext JS, LLC.
6452  *
6453  * Originally Released Under LGPL - original licence link has changed is not relivant.
6454  *
6455  * Fork - LGPL
6456  * <script type="text/javascript">
6457  */
6458
6459
6460 /**
6461  * @class Roo.data.Tree
6462  * @extends Roo.util.Observable
6463  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
6464  * in the tree have most standard DOM functionality.
6465  * @constructor
6466  * @param {Node} root (optional) The root node
6467  */
6468 Roo.data.Tree = function(root){
6469    this.nodeHash = {};
6470    /**
6471     * The root node for this tree
6472     * @type Node
6473     */
6474    this.root = null;
6475    if(root){
6476        this.setRootNode(root);
6477    }
6478    this.addEvents({
6479        /**
6480         * @event append
6481         * Fires when a new child node is appended to a node in this tree.
6482         * @param {Tree} tree The owner tree
6483         * @param {Node} parent The parent node
6484         * @param {Node} node The newly appended node
6485         * @param {Number} index The index of the newly appended node
6486         */
6487        "append" : true,
6488        /**
6489         * @event remove
6490         * Fires when a child node is removed from a node in this tree.
6491         * @param {Tree} tree The owner tree
6492         * @param {Node} parent The parent node
6493         * @param {Node} node The child node removed
6494         */
6495        "remove" : true,
6496        /**
6497         * @event move
6498         * Fires when a node is moved to a new location in the tree
6499         * @param {Tree} tree The owner tree
6500         * @param {Node} node The node moved
6501         * @param {Node} oldParent The old parent of this node
6502         * @param {Node} newParent The new parent of this node
6503         * @param {Number} index The index it was moved to
6504         */
6505        "move" : true,
6506        /**
6507         * @event insert
6508         * Fires when a new child node is inserted in a node in this tree.
6509         * @param {Tree} tree The owner tree
6510         * @param {Node} parent The parent node
6511         * @param {Node} node The child node inserted
6512         * @param {Node} refNode The child node the node was inserted before
6513         */
6514        "insert" : true,
6515        /**
6516         * @event beforeappend
6517         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
6518         * @param {Tree} tree The owner tree
6519         * @param {Node} parent The parent node
6520         * @param {Node} node The child node to be appended
6521         */
6522        "beforeappend" : true,
6523        /**
6524         * @event beforeremove
6525         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
6526         * @param {Tree} tree The owner tree
6527         * @param {Node} parent The parent node
6528         * @param {Node} node The child node to be removed
6529         */
6530        "beforeremove" : true,
6531        /**
6532         * @event beforemove
6533         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
6534         * @param {Tree} tree The owner tree
6535         * @param {Node} node The node being moved
6536         * @param {Node} oldParent The parent of the node
6537         * @param {Node} newParent The new parent the node is moving to
6538         * @param {Number} index The index it is being moved to
6539         */
6540        "beforemove" : true,
6541        /**
6542         * @event beforeinsert
6543         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
6544         * @param {Tree} tree The owner tree
6545         * @param {Node} parent The parent node
6546         * @param {Node} node The child node to be inserted
6547         * @param {Node} refNode The child node the node is being inserted before
6548         */
6549        "beforeinsert" : true
6550    });
6551
6552     Roo.data.Tree.superclass.constructor.call(this);
6553 };
6554
6555 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
6556     pathSeparator: "/",
6557
6558     proxyNodeEvent : function(){
6559         return this.fireEvent.apply(this, arguments);
6560     },
6561
6562     /**
6563      * Returns the root node for this tree.
6564      * @return {Node}
6565      */
6566     getRootNode : function(){
6567         return this.root;
6568     },
6569
6570     /**
6571      * Sets the root node for this tree.
6572      * @param {Node} node
6573      * @return {Node}
6574      */
6575     setRootNode : function(node){
6576         this.root = node;
6577         node.ownerTree = this;
6578         node.isRoot = true;
6579         this.registerNode(node);
6580         return node;
6581     },
6582
6583     /**
6584      * Gets a node in this tree by its id.
6585      * @param {String} id
6586      * @return {Node}
6587      */
6588     getNodeById : function(id){
6589         return this.nodeHash[id];
6590     },
6591
6592     registerNode : function(node){
6593         this.nodeHash[node.id] = node;
6594     },
6595
6596     unregisterNode : function(node){
6597         delete this.nodeHash[node.id];
6598     },
6599
6600     toString : function(){
6601         return "[Tree"+(this.id?" "+this.id:"")+"]";
6602     }
6603 });
6604
6605 /**
6606  * @class Roo.data.Node
6607  * @extends Roo.util.Observable
6608  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
6609  * @cfg {String} id The id for this node. If one is not specified, one is generated.
6610  * @constructor
6611  * @param {Object} attributes The attributes/config for the node
6612  */
6613 Roo.data.Node = function(attributes){
6614     /**
6615      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
6616      * @type {Object}
6617      */
6618     this.attributes = attributes || {};
6619     this.leaf = this.attributes.leaf;
6620     /**
6621      * The node id. @type String
6622      */
6623     this.id = this.attributes.id;
6624     if(!this.id){
6625         this.id = Roo.id(null, "ynode-");
6626         this.attributes.id = this.id;
6627     }
6628     /**
6629      * All child nodes of this node. @type Array
6630      */
6631     this.childNodes = [];
6632     if(!this.childNodes.indexOf){ // indexOf is a must
6633         this.childNodes.indexOf = function(o){
6634             for(var i = 0, len = this.length; i < len; i++){
6635                 if(this[i] == o) {
6636                     return i;
6637                 }
6638             }
6639             return -1;
6640         };
6641     }
6642     /**
6643      * The parent node for this node. @type Node
6644      */
6645     this.parentNode = null;
6646     /**
6647      * The first direct child node of this node, or null if this node has no child nodes. @type Node
6648      */
6649     this.firstChild = null;
6650     /**
6651      * The last direct child node of this node, or null if this node has no child nodes. @type Node
6652      */
6653     this.lastChild = null;
6654     /**
6655      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
6656      */
6657     this.previousSibling = null;
6658     /**
6659      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
6660      */
6661     this.nextSibling = null;
6662
6663     this.addEvents({
6664        /**
6665         * @event append
6666         * Fires when a new child node is appended
6667         * @param {Tree} tree The owner tree
6668         * @param {Node} this This node
6669         * @param {Node} node The newly appended node
6670         * @param {Number} index The index of the newly appended node
6671         */
6672        "append" : true,
6673        /**
6674         * @event remove
6675         * Fires when a child node is removed
6676         * @param {Tree} tree The owner tree
6677         * @param {Node} this This node
6678         * @param {Node} node The removed node
6679         */
6680        "remove" : true,
6681        /**
6682         * @event move
6683         * Fires when this node is moved to a new location in the tree
6684         * @param {Tree} tree The owner tree
6685         * @param {Node} this This node
6686         * @param {Node} oldParent The old parent of this node
6687         * @param {Node} newParent The new parent of this node
6688         * @param {Number} index The index it was moved to
6689         */
6690        "move" : true,
6691        /**
6692         * @event insert
6693         * Fires when a new child node is inserted.
6694         * @param {Tree} tree The owner tree
6695         * @param {Node} this This node
6696         * @param {Node} node The child node inserted
6697         * @param {Node} refNode The child node the node was inserted before
6698         */
6699        "insert" : true,
6700        /**
6701         * @event beforeappend
6702         * Fires before a new child is appended, return false to cancel the append.
6703         * @param {Tree} tree The owner tree
6704         * @param {Node} this This node
6705         * @param {Node} node The child node to be appended
6706         */
6707        "beforeappend" : true,
6708        /**
6709         * @event beforeremove
6710         * Fires before a child is removed, return false to cancel the remove.
6711         * @param {Tree} tree The owner tree
6712         * @param {Node} this This node
6713         * @param {Node} node The child node to be removed
6714         */
6715        "beforeremove" : true,
6716        /**
6717         * @event beforemove
6718         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
6719         * @param {Tree} tree The owner tree
6720         * @param {Node} this This node
6721         * @param {Node} oldParent The parent of this node
6722         * @param {Node} newParent The new parent this node is moving to
6723         * @param {Number} index The index it is being moved to
6724         */
6725        "beforemove" : true,
6726        /**
6727         * @event beforeinsert
6728         * Fires before a new child is inserted, return false to cancel the insert.
6729         * @param {Tree} tree The owner tree
6730         * @param {Node} this This node
6731         * @param {Node} node The child node to be inserted
6732         * @param {Node} refNode The child node the node is being inserted before
6733         */
6734        "beforeinsert" : true
6735    });
6736     this.listeners = this.attributes.listeners;
6737     Roo.data.Node.superclass.constructor.call(this);
6738 };
6739
6740 Roo.extend(Roo.data.Node, Roo.util.Observable, {
6741     fireEvent : function(evtName){
6742         // first do standard event for this node
6743         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
6744             return false;
6745         }
6746         // then bubble it up to the tree if the event wasn't cancelled
6747         var ot = this.getOwnerTree();
6748         if(ot){
6749             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
6750                 return false;
6751             }
6752         }
6753         return true;
6754     },
6755
6756     /**
6757      * Returns true if this node is a leaf
6758      * @return {Boolean}
6759      */
6760     isLeaf : function(){
6761         return this.leaf === true;
6762     },
6763
6764     // private
6765     setFirstChild : function(node){
6766         this.firstChild = node;
6767     },
6768
6769     //private
6770     setLastChild : function(node){
6771         this.lastChild = node;
6772     },
6773
6774
6775     /**
6776      * Returns true if this node is the last child of its parent
6777      * @return {Boolean}
6778      */
6779     isLast : function(){
6780        return (!this.parentNode ? true : this.parentNode.lastChild == this);
6781     },
6782
6783     /**
6784      * Returns true if this node is the first child of its parent
6785      * @return {Boolean}
6786      */
6787     isFirst : function(){
6788        return (!this.parentNode ? true : this.parentNode.firstChild == this);
6789     },
6790
6791     hasChildNodes : function(){
6792         return !this.isLeaf() && this.childNodes.length > 0;
6793     },
6794
6795     /**
6796      * Insert node(s) as the last child node of this node.
6797      * @param {Node/Array} node The node or Array of nodes to append
6798      * @return {Node} The appended node if single append, or null if an array was passed
6799      */
6800     appendChild : function(node){
6801         var multi = false;
6802         if(node instanceof Array){
6803             multi = node;
6804         }else if(arguments.length > 1){
6805             multi = arguments;
6806         }
6807         // if passed an array or multiple args do them one by one
6808         if(multi){
6809             for(var i = 0, len = multi.length; i < len; i++) {
6810                 this.appendChild(multi[i]);
6811             }
6812         }else{
6813             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
6814                 return false;
6815             }
6816             var index = this.childNodes.length;
6817             var oldParent = node.parentNode;
6818             // it's a move, make sure we move it cleanly
6819             if(oldParent){
6820                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
6821                     return false;
6822                 }
6823                 oldParent.removeChild(node);
6824             }
6825             index = this.childNodes.length;
6826             if(index == 0){
6827                 this.setFirstChild(node);
6828             }
6829             this.childNodes.push(node);
6830             node.parentNode = this;
6831             var ps = this.childNodes[index-1];
6832             if(ps){
6833                 node.previousSibling = ps;
6834                 ps.nextSibling = node;
6835             }else{
6836                 node.previousSibling = null;
6837             }
6838             node.nextSibling = null;
6839             this.setLastChild(node);
6840             node.setOwnerTree(this.getOwnerTree());
6841             this.fireEvent("append", this.ownerTree, this, node, index);
6842             if(oldParent){
6843                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
6844             }
6845             return node;
6846         }
6847     },
6848
6849     /**
6850      * Removes a child node from this node.
6851      * @param {Node} node The node to remove
6852      * @return {Node} The removed node
6853      */
6854     removeChild : function(node){
6855         var index = this.childNodes.indexOf(node);
6856         if(index == -1){
6857             return false;
6858         }
6859         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
6860             return false;
6861         }
6862
6863         // remove it from childNodes collection
6864         this.childNodes.splice(index, 1);
6865
6866         // update siblings
6867         if(node.previousSibling){
6868             node.previousSibling.nextSibling = node.nextSibling;
6869         }
6870         if(node.nextSibling){
6871             node.nextSibling.previousSibling = node.previousSibling;
6872         }
6873
6874         // update child refs
6875         if(this.firstChild == node){
6876             this.setFirstChild(node.nextSibling);
6877         }
6878         if(this.lastChild == node){
6879             this.setLastChild(node.previousSibling);
6880         }
6881
6882         node.setOwnerTree(null);
6883         // clear any references from the node
6884         node.parentNode = null;
6885         node.previousSibling = null;
6886         node.nextSibling = null;
6887         this.fireEvent("remove", this.ownerTree, this, node);
6888         return node;
6889     },
6890
6891     /**
6892      * Inserts the first node before the second node in this nodes childNodes collection.
6893      * @param {Node} node The node to insert
6894      * @param {Node} refNode The node to insert before (if null the node is appended)
6895      * @return {Node} The inserted node
6896      */
6897     insertBefore : function(node, refNode){
6898         if(!refNode){ // like standard Dom, refNode can be null for append
6899             return this.appendChild(node);
6900         }
6901         // nothing to do
6902         if(node == refNode){
6903             return false;
6904         }
6905
6906         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
6907             return false;
6908         }
6909         var index = this.childNodes.indexOf(refNode);
6910         var oldParent = node.parentNode;
6911         var refIndex = index;
6912
6913         // when moving internally, indexes will change after remove
6914         if(oldParent == this && this.childNodes.indexOf(node) < index){
6915             refIndex--;
6916         }
6917
6918         // it's a move, make sure we move it cleanly
6919         if(oldParent){
6920             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
6921                 return false;
6922             }
6923             oldParent.removeChild(node);
6924         }
6925         if(refIndex == 0){
6926             this.setFirstChild(node);
6927         }
6928         this.childNodes.splice(refIndex, 0, node);
6929         node.parentNode = this;
6930         var ps = this.childNodes[refIndex-1];
6931         if(ps){
6932             node.previousSibling = ps;
6933             ps.nextSibling = node;
6934         }else{
6935             node.previousSibling = null;
6936         }
6937         node.nextSibling = refNode;
6938         refNode.previousSibling = node;
6939         node.setOwnerTree(this.getOwnerTree());
6940         this.fireEvent("insert", this.ownerTree, this, node, refNode);
6941         if(oldParent){
6942             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
6943         }
6944         return node;
6945     },
6946
6947     /**
6948      * Returns the child node at the specified index.
6949      * @param {Number} index
6950      * @return {Node}
6951      */
6952     item : function(index){
6953         return this.childNodes[index];
6954     },
6955
6956     /**
6957      * Replaces one child node in this node with another.
6958      * @param {Node} newChild The replacement node
6959      * @param {Node} oldChild The node to replace
6960      * @return {Node} The replaced node
6961      */
6962     replaceChild : function(newChild, oldChild){
6963         this.insertBefore(newChild, oldChild);
6964         this.removeChild(oldChild);
6965         return oldChild;
6966     },
6967
6968     /**
6969      * Returns the index of a child node
6970      * @param {Node} node
6971      * @return {Number} The index of the node or -1 if it was not found
6972      */
6973     indexOf : function(child){
6974         return this.childNodes.indexOf(child);
6975     },
6976
6977     /**
6978      * Returns the tree this node is in.
6979      * @return {Tree}
6980      */
6981     getOwnerTree : function(){
6982         // if it doesn't have one, look for one
6983         if(!this.ownerTree){
6984             var p = this;
6985             while(p){
6986                 if(p.ownerTree){
6987                     this.ownerTree = p.ownerTree;
6988                     break;
6989                 }
6990                 p = p.parentNode;
6991             }
6992         }
6993         return this.ownerTree;
6994     },
6995
6996     /**
6997      * Returns depth of this node (the root node has a depth of 0)
6998      * @return {Number}
6999      */
7000     getDepth : function(){
7001         var depth = 0;
7002         var p = this;
7003         while(p.parentNode){
7004             ++depth;
7005             p = p.parentNode;
7006         }
7007         return depth;
7008     },
7009
7010     // private
7011     setOwnerTree : function(tree){
7012         // if it's move, we need to update everyone
7013         if(tree != this.ownerTree){
7014             if(this.ownerTree){
7015                 this.ownerTree.unregisterNode(this);
7016             }
7017             this.ownerTree = tree;
7018             var cs = this.childNodes;
7019             for(var i = 0, len = cs.length; i < len; i++) {
7020                 cs[i].setOwnerTree(tree);
7021             }
7022             if(tree){
7023                 tree.registerNode(this);
7024             }
7025         }
7026     },
7027
7028     /**
7029      * Returns the path for this node. The path can be used to expand or select this node programmatically.
7030      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
7031      * @return {String} The path
7032      */
7033     getPath : function(attr){
7034         attr = attr || "id";
7035         var p = this.parentNode;
7036         var b = [this.attributes[attr]];
7037         while(p){
7038             b.unshift(p.attributes[attr]);
7039             p = p.parentNode;
7040         }
7041         var sep = this.getOwnerTree().pathSeparator;
7042         return sep + b.join(sep);
7043     },
7044
7045     /**
7046      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7047      * function call will be the scope provided or the current node. The arguments to the function
7048      * will be the args provided or the current node. If the function returns false at any point,
7049      * the bubble is stopped.
7050      * @param {Function} fn The function to call
7051      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7052      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7053      */
7054     bubble : function(fn, scope, args){
7055         var p = this;
7056         while(p){
7057             if(fn.call(scope || p, args || p) === false){
7058                 break;
7059             }
7060             p = p.parentNode;
7061         }
7062     },
7063
7064     /**
7065      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7066      * function call will be the scope provided or the current node. The arguments to the function
7067      * will be the args provided or the current node. If the function returns false at any point,
7068      * the cascade is stopped on that branch.
7069      * @param {Function} fn The function to call
7070      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7071      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7072      */
7073     cascade : function(fn, scope, args){
7074         if(fn.call(scope || this, args || this) !== false){
7075             var cs = this.childNodes;
7076             for(var i = 0, len = cs.length; i < len; i++) {
7077                 cs[i].cascade(fn, scope, args);
7078             }
7079         }
7080     },
7081
7082     /**
7083      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
7084      * function call will be the scope provided or the current node. The arguments to the function
7085      * will be the args provided or the current node. If the function returns false at any point,
7086      * the iteration stops.
7087      * @param {Function} fn The function to call
7088      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7089      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7090      */
7091     eachChild : function(fn, scope, args){
7092         var cs = this.childNodes;
7093         for(var i = 0, len = cs.length; i < len; i++) {
7094                 if(fn.call(scope || this, args || cs[i]) === false){
7095                     break;
7096                 }
7097         }
7098     },
7099
7100     /**
7101      * Finds the first child that has the attribute with the specified value.
7102      * @param {String} attribute The attribute name
7103      * @param {Mixed} value The value to search for
7104      * @return {Node} The found child or null if none was found
7105      */
7106     findChild : function(attribute, value){
7107         var cs = this.childNodes;
7108         for(var i = 0, len = cs.length; i < len; i++) {
7109                 if(cs[i].attributes[attribute] == value){
7110                     return cs[i];
7111                 }
7112         }
7113         return null;
7114     },
7115
7116     /**
7117      * Finds the first child by a custom function. The child matches if the function passed
7118      * returns true.
7119      * @param {Function} fn
7120      * @param {Object} scope (optional)
7121      * @return {Node} The found child or null if none was found
7122      */
7123     findChildBy : function(fn, scope){
7124         var cs = this.childNodes;
7125         for(var i = 0, len = cs.length; i < len; i++) {
7126                 if(fn.call(scope||cs[i], cs[i]) === true){
7127                     return cs[i];
7128                 }
7129         }
7130         return null;
7131     },
7132
7133     /**
7134      * Sorts this nodes children using the supplied sort function
7135      * @param {Function} fn
7136      * @param {Object} scope (optional)
7137      */
7138     sort : function(fn, scope){
7139         var cs = this.childNodes;
7140         var len = cs.length;
7141         if(len > 0){
7142             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
7143             cs.sort(sortFn);
7144             for(var i = 0; i < len; i++){
7145                 var n = cs[i];
7146                 n.previousSibling = cs[i-1];
7147                 n.nextSibling = cs[i+1];
7148                 if(i == 0){
7149                     this.setFirstChild(n);
7150                 }
7151                 if(i == len-1){
7152                     this.setLastChild(n);
7153                 }
7154             }
7155         }
7156     },
7157
7158     /**
7159      * Returns true if this node is an ancestor (at any point) of the passed node.
7160      * @param {Node} node
7161      * @return {Boolean}
7162      */
7163     contains : function(node){
7164         return node.isAncestor(this);
7165     },
7166
7167     /**
7168      * Returns true if the passed node is an ancestor (at any point) of this node.
7169      * @param {Node} node
7170      * @return {Boolean}
7171      */
7172     isAncestor : function(node){
7173         var p = this.parentNode;
7174         while(p){
7175             if(p == node){
7176                 return true;
7177             }
7178             p = p.parentNode;
7179         }
7180         return false;
7181     },
7182
7183     toString : function(){
7184         return "[Node"+(this.id?" "+this.id:"")+"]";
7185     }
7186 });/*
7187  * Based on:
7188  * Ext JS Library 1.1.1
7189  * Copyright(c) 2006-2007, Ext JS, LLC.
7190  *
7191  * Originally Released Under LGPL - original licence link has changed is not relivant.
7192  *
7193  * Fork - LGPL
7194  * <script type="text/javascript">
7195  */
7196  
7197
7198 /**
7199  * @class Roo.ComponentMgr
7200  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
7201  * @singleton
7202  */
7203 Roo.ComponentMgr = function(){
7204     var all = new Roo.util.MixedCollection();
7205
7206     return {
7207         /**
7208          * Registers a component.
7209          * @param {Roo.Component} c The component
7210          */
7211         register : function(c){
7212             all.add(c);
7213         },
7214
7215         /**
7216          * Unregisters a component.
7217          * @param {Roo.Component} c The component
7218          */
7219         unregister : function(c){
7220             all.remove(c);
7221         },
7222
7223         /**
7224          * Returns a component by id
7225          * @param {String} id The component id
7226          */
7227         get : function(id){
7228             return all.get(id);
7229         },
7230
7231         /**
7232          * Registers a function that will be called when a specified component is added to ComponentMgr
7233          * @param {String} id The component id
7234          * @param {Funtction} fn The callback function
7235          * @param {Object} scope The scope of the callback
7236          */
7237         onAvailable : function(id, fn, scope){
7238             all.on("add", function(index, o){
7239                 if(o.id == id){
7240                     fn.call(scope || o, o);
7241                     all.un("add", fn, scope);
7242                 }
7243             });
7244         }
7245     };
7246 }();/*
7247  * Based on:
7248  * Ext JS Library 1.1.1
7249  * Copyright(c) 2006-2007, Ext JS, LLC.
7250  *
7251  * Originally Released Under LGPL - original licence link has changed is not relivant.
7252  *
7253  * Fork - LGPL
7254  * <script type="text/javascript">
7255  */
7256  
7257 /**
7258  * @class Roo.Component
7259  * @extends Roo.util.Observable
7260  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
7261  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
7262  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
7263  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
7264  * All visual components (widgets) that require rendering into a layout should subclass Component.
7265  * @constructor
7266  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
7267  * 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
7268  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
7269  */
7270 Roo.Component = function(config){
7271     config = config || {};
7272     if(config.tagName || config.dom || typeof config == "string"){ // element object
7273         config = {el: config, id: config.id || config};
7274     }
7275     this.initialConfig = config;
7276
7277     Roo.apply(this, config);
7278     this.addEvents({
7279         /**
7280          * @event disable
7281          * Fires after the component is disabled.
7282              * @param {Roo.Component} this
7283              */
7284         disable : true,
7285         /**
7286          * @event enable
7287          * Fires after the component is enabled.
7288              * @param {Roo.Component} this
7289              */
7290         enable : true,
7291         /**
7292          * @event beforeshow
7293          * Fires before the component is shown.  Return false to stop the show.
7294              * @param {Roo.Component} this
7295              */
7296         beforeshow : true,
7297         /**
7298          * @event show
7299          * Fires after the component is shown.
7300              * @param {Roo.Component} this
7301              */
7302         show : true,
7303         /**
7304          * @event beforehide
7305          * Fires before the component is hidden. Return false to stop the hide.
7306              * @param {Roo.Component} this
7307              */
7308         beforehide : true,
7309         /**
7310          * @event hide
7311          * Fires after the component is hidden.
7312              * @param {Roo.Component} this
7313              */
7314         hide : true,
7315         /**
7316          * @event beforerender
7317          * Fires before the component is rendered. Return false to stop the render.
7318              * @param {Roo.Component} this
7319              */
7320         beforerender : true,
7321         /**
7322          * @event render
7323          * Fires after the component is rendered.
7324              * @param {Roo.Component} this
7325              */
7326         render : true,
7327         /**
7328          * @event beforedestroy
7329          * Fires before the component is destroyed. Return false to stop the destroy.
7330              * @param {Roo.Component} this
7331              */
7332         beforedestroy : true,
7333         /**
7334          * @event destroy
7335          * Fires after the component is destroyed.
7336              * @param {Roo.Component} this
7337              */
7338         destroy : true
7339     });
7340     if(!this.id){
7341         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
7342     }
7343     Roo.ComponentMgr.register(this);
7344     Roo.Component.superclass.constructor.call(this);
7345     this.initComponent();
7346     if(this.renderTo){ // not supported by all components yet. use at your own risk!
7347         this.render(this.renderTo);
7348         delete this.renderTo;
7349     }
7350 };
7351
7352 // private
7353 Roo.Component.AUTO_ID = 1000;
7354
7355 Roo.extend(Roo.Component, Roo.util.Observable, {
7356     /**
7357      * @property {Boolean} hidden
7358      * true if this component is hidden. Read-only.
7359      */
7360     hidden : false,
7361     /**
7362      * true if this component is disabled. Read-only.
7363      */
7364     disabled : false,
7365     /**
7366      * true if this component has been rendered. Read-only.
7367      */
7368     rendered : false,
7369     
7370     /** @cfg {String} disableClass
7371      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
7372      */
7373     disabledClass : "x-item-disabled",
7374         /** @cfg {Boolean} allowDomMove
7375          * Whether the component can move the Dom node when rendering (defaults to true).
7376          */
7377     allowDomMove : true,
7378     /** @cfg {String} hideMode
7379      * How this component should hidden. Supported values are
7380      * "visibility" (css visibility), "offsets" (negative offset position) and
7381      * "display" (css display) - defaults to "display".
7382      */
7383     hideMode: 'display',
7384
7385     // private
7386     ctype : "Roo.Component",
7387
7388     /** @cfg {String} actionMode 
7389      * which property holds the element that used for  hide() / show() / disable() / enable()
7390      * default is 'el' 
7391      */
7392     actionMode : "el",
7393
7394     // private
7395     getActionEl : function(){
7396         return this[this.actionMode];
7397     },
7398
7399     initComponent : Roo.emptyFn,
7400     /**
7401      * If this is a lazy rendering component, render it to its container element.
7402      * @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.
7403      */
7404     render : function(container, position){
7405         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
7406             if(!container && this.el){
7407                 this.el = Roo.get(this.el);
7408                 container = this.el.dom.parentNode;
7409                 this.allowDomMove = false;
7410             }
7411             this.container = Roo.get(container);
7412             this.rendered = true;
7413             if(position !== undefined){
7414                 if(typeof position == 'number'){
7415                     position = this.container.dom.childNodes[position];
7416                 }else{
7417                     position = Roo.getDom(position);
7418                 }
7419             }
7420             this.onRender(this.container, position || null);
7421             if(this.cls){
7422                 this.el.addClass(this.cls);
7423                 delete this.cls;
7424             }
7425             if(this.style){
7426                 this.el.applyStyles(this.style);
7427                 delete this.style;
7428             }
7429             this.fireEvent("render", this);
7430             this.afterRender(this.container);
7431             if(this.hidden){
7432                 this.hide();
7433             }
7434             if(this.disabled){
7435                 this.disable();
7436             }
7437         }
7438         return this;
7439     },
7440
7441     // private
7442     // default function is not really useful
7443     onRender : function(ct, position){
7444         if(this.el){
7445             this.el = Roo.get(this.el);
7446             if(this.allowDomMove !== false){
7447                 ct.dom.insertBefore(this.el.dom, position);
7448             }
7449         }
7450     },
7451
7452     // private
7453     getAutoCreate : function(){
7454         var cfg = typeof this.autoCreate == "object" ?
7455                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
7456         if(this.id && !cfg.id){
7457             cfg.id = this.id;
7458         }
7459         return cfg;
7460     },
7461
7462     // private
7463     afterRender : Roo.emptyFn,
7464
7465     /**
7466      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
7467      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
7468      */
7469     destroy : function(){
7470         if(this.fireEvent("beforedestroy", this) !== false){
7471             this.purgeListeners();
7472             this.beforeDestroy();
7473             if(this.rendered){
7474                 this.el.removeAllListeners();
7475                 this.el.remove();
7476                 if(this.actionMode == "container"){
7477                     this.container.remove();
7478                 }
7479             }
7480             this.onDestroy();
7481             Roo.ComponentMgr.unregister(this);
7482             this.fireEvent("destroy", this);
7483         }
7484     },
7485
7486         // private
7487     beforeDestroy : function(){
7488
7489     },
7490
7491         // private
7492         onDestroy : function(){
7493
7494     },
7495
7496     /**
7497      * Returns the underlying {@link Roo.Element}.
7498      * @return {Roo.Element} The element
7499      */
7500     getEl : function(){
7501         return this.el;
7502     },
7503
7504     /**
7505      * Returns the id of this component.
7506      * @return {String}
7507      */
7508     getId : function(){
7509         return this.id;
7510     },
7511
7512     /**
7513      * Try to focus this component.
7514      * @param {Boolean} selectText True to also select the text in this component (if applicable)
7515      * @return {Roo.Component} this
7516      */
7517     focus : function(selectText){
7518         if(this.rendered){
7519             this.el.focus();
7520             if(selectText === true){
7521                 this.el.dom.select();
7522             }
7523         }
7524         return this;
7525     },
7526
7527     // private
7528     blur : function(){
7529         if(this.rendered){
7530             this.el.blur();
7531         }
7532         return this;
7533     },
7534
7535     /**
7536      * Disable this component.
7537      * @return {Roo.Component} this
7538      */
7539     disable : function(){
7540         if(this.rendered){
7541             this.onDisable();
7542         }
7543         this.disabled = true;
7544         this.fireEvent("disable", this);
7545         return this;
7546     },
7547
7548         // private
7549     onDisable : function(){
7550         this.getActionEl().addClass(this.disabledClass);
7551         this.el.dom.disabled = true;
7552     },
7553
7554     /**
7555      * Enable this component.
7556      * @return {Roo.Component} this
7557      */
7558     enable : function(){
7559         if(this.rendered){
7560             this.onEnable();
7561         }
7562         this.disabled = false;
7563         this.fireEvent("enable", this);
7564         return this;
7565     },
7566
7567         // private
7568     onEnable : function(){
7569         this.getActionEl().removeClass(this.disabledClass);
7570         this.el.dom.disabled = false;
7571     },
7572
7573     /**
7574      * Convenience function for setting disabled/enabled by boolean.
7575      * @param {Boolean} disabled
7576      */
7577     setDisabled : function(disabled){
7578         this[disabled ? "disable" : "enable"]();
7579     },
7580
7581     /**
7582      * Show this component.
7583      * @return {Roo.Component} this
7584      */
7585     show: function(){
7586         if(this.fireEvent("beforeshow", this) !== false){
7587             this.hidden = false;
7588             if(this.rendered){
7589                 this.onShow();
7590             }
7591             this.fireEvent("show", this);
7592         }
7593         return this;
7594     },
7595
7596     // private
7597     onShow : function(){
7598         var ae = this.getActionEl();
7599         if(this.hideMode == 'visibility'){
7600             ae.dom.style.visibility = "visible";
7601         }else if(this.hideMode == 'offsets'){
7602             ae.removeClass('x-hidden');
7603         }else{
7604             ae.dom.style.display = "";
7605         }
7606     },
7607
7608     /**
7609      * Hide this component.
7610      * @return {Roo.Component} this
7611      */
7612     hide: function(){
7613         if(this.fireEvent("beforehide", this) !== false){
7614             this.hidden = true;
7615             if(this.rendered){
7616                 this.onHide();
7617             }
7618             this.fireEvent("hide", this);
7619         }
7620         return this;
7621     },
7622
7623     // private
7624     onHide : function(){
7625         var ae = this.getActionEl();
7626         if(this.hideMode == 'visibility'){
7627             ae.dom.style.visibility = "hidden";
7628         }else if(this.hideMode == 'offsets'){
7629             ae.addClass('x-hidden');
7630         }else{
7631             ae.dom.style.display = "none";
7632         }
7633     },
7634
7635     /**
7636      * Convenience function to hide or show this component by boolean.
7637      * @param {Boolean} visible True to show, false to hide
7638      * @return {Roo.Component} this
7639      */
7640     setVisible: function(visible){
7641         if(visible) {
7642             this.show();
7643         }else{
7644             this.hide();
7645         }
7646         return this;
7647     },
7648
7649     /**
7650      * Returns true if this component is visible.
7651      */
7652     isVisible : function(){
7653         return this.getActionEl().isVisible();
7654     },
7655
7656     cloneConfig : function(overrides){
7657         overrides = overrides || {};
7658         var id = overrides.id || Roo.id();
7659         var cfg = Roo.applyIf(overrides, this.initialConfig);
7660         cfg.id = id; // prevent dup id
7661         return new this.constructor(cfg);
7662     }
7663 });/*
7664  * Based on:
7665  * Ext JS Library 1.1.1
7666  * Copyright(c) 2006-2007, Ext JS, LLC.
7667  *
7668  * Originally Released Under LGPL - original licence link has changed is not relivant.
7669  *
7670  * Fork - LGPL
7671  * <script type="text/javascript">
7672  */
7673  (function(){ 
7674 /**
7675  * @class Roo.Layer
7676  * @extends Roo.Element
7677  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
7678  * automatic maintaining of shadow/shim positions.
7679  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
7680  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
7681  * you can pass a string with a CSS class name. False turns off the shadow.
7682  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
7683  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
7684  * @cfg {String} cls CSS class to add to the element
7685  * @cfg {Number} zindex Starting z-index (defaults to 11000)
7686  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
7687  * @constructor
7688  * @param {Object} config An object with config options.
7689  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
7690  */
7691
7692 Roo.Layer = function(config, existingEl){
7693     config = config || {};
7694     var dh = Roo.DomHelper;
7695     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
7696     if(existingEl){
7697         this.dom = Roo.getDom(existingEl);
7698     }
7699     if(!this.dom){
7700         var o = config.dh || {tag: "div", cls: "x-layer"};
7701         this.dom = dh.append(pel, o);
7702     }
7703     if(config.cls){
7704         this.addClass(config.cls);
7705     }
7706     this.constrain = config.constrain !== false;
7707     this.visibilityMode = Roo.Element.VISIBILITY;
7708     if(config.id){
7709         this.id = this.dom.id = config.id;
7710     }else{
7711         this.id = Roo.id(this.dom);
7712     }
7713     this.zindex = config.zindex || this.getZIndex();
7714     this.position("absolute", this.zindex);
7715     if(config.shadow){
7716         this.shadowOffset = config.shadowOffset || 4;
7717         this.shadow = new Roo.Shadow({
7718             offset : this.shadowOffset,
7719             mode : config.shadow
7720         });
7721     }else{
7722         this.shadowOffset = 0;
7723     }
7724     this.useShim = config.shim !== false && Roo.useShims;
7725     this.useDisplay = config.useDisplay;
7726     this.hide();
7727 };
7728
7729 var supr = Roo.Element.prototype;
7730
7731 // shims are shared among layer to keep from having 100 iframes
7732 var shims = [];
7733
7734 Roo.extend(Roo.Layer, Roo.Element, {
7735
7736     getZIndex : function(){
7737         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
7738     },
7739
7740     getShim : function(){
7741         if(!this.useShim){
7742             return null;
7743         }
7744         if(this.shim){
7745             return this.shim;
7746         }
7747         var shim = shims.shift();
7748         if(!shim){
7749             shim = this.createShim();
7750             shim.enableDisplayMode('block');
7751             shim.dom.style.display = 'none';
7752             shim.dom.style.visibility = 'visible';
7753         }
7754         var pn = this.dom.parentNode;
7755         if(shim.dom.parentNode != pn){
7756             pn.insertBefore(shim.dom, this.dom);
7757         }
7758         shim.setStyle('z-index', this.getZIndex()-2);
7759         this.shim = shim;
7760         return shim;
7761     },
7762
7763     hideShim : function(){
7764         if(this.shim){
7765             this.shim.setDisplayed(false);
7766             shims.push(this.shim);
7767             delete this.shim;
7768         }
7769     },
7770
7771     disableShadow : function(){
7772         if(this.shadow){
7773             this.shadowDisabled = true;
7774             this.shadow.hide();
7775             this.lastShadowOffset = this.shadowOffset;
7776             this.shadowOffset = 0;
7777         }
7778     },
7779
7780     enableShadow : function(show){
7781         if(this.shadow){
7782             this.shadowDisabled = false;
7783             this.shadowOffset = this.lastShadowOffset;
7784             delete this.lastShadowOffset;
7785             if(show){
7786                 this.sync(true);
7787             }
7788         }
7789     },
7790
7791     // private
7792     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
7793     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
7794     sync : function(doShow){
7795         var sw = this.shadow;
7796         if(!this.updating && this.isVisible() && (sw || this.useShim)){
7797             var sh = this.getShim();
7798
7799             var w = this.getWidth(),
7800                 h = this.getHeight();
7801
7802             var l = this.getLeft(true),
7803                 t = this.getTop(true);
7804
7805             if(sw && !this.shadowDisabled){
7806                 if(doShow && !sw.isVisible()){
7807                     sw.show(this);
7808                 }else{
7809                     sw.realign(l, t, w, h);
7810                 }
7811                 if(sh){
7812                     if(doShow){
7813                        sh.show();
7814                     }
7815                     // fit the shim behind the shadow, so it is shimmed too
7816                     var a = sw.adjusts, s = sh.dom.style;
7817                     s.left = (Math.min(l, l+a.l))+"px";
7818                     s.top = (Math.min(t, t+a.t))+"px";
7819                     s.width = (w+a.w)+"px";
7820                     s.height = (h+a.h)+"px";
7821                 }
7822             }else if(sh){
7823                 if(doShow){
7824                    sh.show();
7825                 }
7826                 sh.setSize(w, h);
7827                 sh.setLeftTop(l, t);
7828             }
7829             
7830         }
7831     },
7832
7833     // private
7834     destroy : function(){
7835         this.hideShim();
7836         if(this.shadow){
7837             this.shadow.hide();
7838         }
7839         this.removeAllListeners();
7840         var pn = this.dom.parentNode;
7841         if(pn){
7842             pn.removeChild(this.dom);
7843         }
7844         Roo.Element.uncache(this.id);
7845     },
7846
7847     remove : function(){
7848         this.destroy();
7849     },
7850
7851     // private
7852     beginUpdate : function(){
7853         this.updating = true;
7854     },
7855
7856     // private
7857     endUpdate : function(){
7858         this.updating = false;
7859         this.sync(true);
7860     },
7861
7862     // private
7863     hideUnders : function(negOffset){
7864         if(this.shadow){
7865             this.shadow.hide();
7866         }
7867         this.hideShim();
7868     },
7869
7870     // private
7871     constrainXY : function(){
7872         if(this.constrain){
7873             var vw = Roo.lib.Dom.getViewWidth(),
7874                 vh = Roo.lib.Dom.getViewHeight();
7875             var s = Roo.get(document).getScroll();
7876
7877             var xy = this.getXY();
7878             var x = xy[0], y = xy[1];   
7879             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
7880             // only move it if it needs it
7881             var moved = false;
7882             // first validate right/bottom
7883             if((x + w) > vw+s.left){
7884                 x = vw - w - this.shadowOffset;
7885                 moved = true;
7886             }
7887             if((y + h) > vh+s.top){
7888                 y = vh - h - this.shadowOffset;
7889                 moved = true;
7890             }
7891             // then make sure top/left isn't negative
7892             if(x < s.left){
7893                 x = s.left;
7894                 moved = true;
7895             }
7896             if(y < s.top){
7897                 y = s.top;
7898                 moved = true;
7899             }
7900             if(moved){
7901                 if(this.avoidY){
7902                     var ay = this.avoidY;
7903                     if(y <= ay && (y+h) >= ay){
7904                         y = ay-h-5;   
7905                     }
7906                 }
7907                 xy = [x, y];
7908                 this.storeXY(xy);
7909                 supr.setXY.call(this, xy);
7910                 this.sync();
7911             }
7912         }
7913     },
7914
7915     isVisible : function(){
7916         return this.visible;    
7917     },
7918
7919     // private
7920     showAction : function(){
7921         this.visible = true; // track visibility to prevent getStyle calls
7922         if(this.useDisplay === true){
7923             this.setDisplayed("");
7924         }else if(this.lastXY){
7925             supr.setXY.call(this, this.lastXY);
7926         }else if(this.lastLT){
7927             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
7928         }
7929     },
7930
7931     // private
7932     hideAction : function(){
7933         this.visible = false;
7934         if(this.useDisplay === true){
7935             this.setDisplayed(false);
7936         }else{
7937             this.setLeftTop(-10000,-10000);
7938         }
7939     },
7940
7941     // overridden Element method
7942     setVisible : function(v, a, d, c, e){
7943         if(v){
7944             this.showAction();
7945         }
7946         if(a && v){
7947             var cb = function(){
7948                 this.sync(true);
7949                 if(c){
7950                     c();
7951                 }
7952             }.createDelegate(this);
7953             supr.setVisible.call(this, true, true, d, cb, e);
7954         }else{
7955             if(!v){
7956                 this.hideUnders(true);
7957             }
7958             var cb = c;
7959             if(a){
7960                 cb = function(){
7961                     this.hideAction();
7962                     if(c){
7963                         c();
7964                     }
7965                 }.createDelegate(this);
7966             }
7967             supr.setVisible.call(this, v, a, d, cb, e);
7968             if(v){
7969                 this.sync(true);
7970             }else if(!a){
7971                 this.hideAction();
7972             }
7973         }
7974     },
7975
7976     storeXY : function(xy){
7977         delete this.lastLT;
7978         this.lastXY = xy;
7979     },
7980
7981     storeLeftTop : function(left, top){
7982         delete this.lastXY;
7983         this.lastLT = [left, top];
7984     },
7985
7986     // private
7987     beforeFx : function(){
7988         this.beforeAction();
7989         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
7990     },
7991
7992     // private
7993     afterFx : function(){
7994         Roo.Layer.superclass.afterFx.apply(this, arguments);
7995         this.sync(this.isVisible());
7996     },
7997
7998     // private
7999     beforeAction : function(){
8000         if(!this.updating && this.shadow){
8001             this.shadow.hide();
8002         }
8003     },
8004
8005     // overridden Element method
8006     setLeft : function(left){
8007         this.storeLeftTop(left, this.getTop(true));
8008         supr.setLeft.apply(this, arguments);
8009         this.sync();
8010     },
8011
8012     setTop : function(top){
8013         this.storeLeftTop(this.getLeft(true), top);
8014         supr.setTop.apply(this, arguments);
8015         this.sync();
8016     },
8017
8018     setLeftTop : function(left, top){
8019         this.storeLeftTop(left, top);
8020         supr.setLeftTop.apply(this, arguments);
8021         this.sync();
8022     },
8023
8024     setXY : function(xy, a, d, c, e){
8025         this.fixDisplay();
8026         this.beforeAction();
8027         this.storeXY(xy);
8028         var cb = this.createCB(c);
8029         supr.setXY.call(this, xy, a, d, cb, e);
8030         if(!a){
8031             cb();
8032         }
8033     },
8034
8035     // private
8036     createCB : function(c){
8037         var el = this;
8038         return function(){
8039             el.constrainXY();
8040             el.sync(true);
8041             if(c){
8042                 c();
8043             }
8044         };
8045     },
8046
8047     // overridden Element method
8048     setX : function(x, a, d, c, e){
8049         this.setXY([x, this.getY()], a, d, c, e);
8050     },
8051
8052     // overridden Element method
8053     setY : function(y, a, d, c, e){
8054         this.setXY([this.getX(), y], a, d, c, e);
8055     },
8056
8057     // overridden Element method
8058     setSize : function(w, h, a, d, c, e){
8059         this.beforeAction();
8060         var cb = this.createCB(c);
8061         supr.setSize.call(this, w, h, a, d, cb, e);
8062         if(!a){
8063             cb();
8064         }
8065     },
8066
8067     // overridden Element method
8068     setWidth : function(w, a, d, c, e){
8069         this.beforeAction();
8070         var cb = this.createCB(c);
8071         supr.setWidth.call(this, w, a, d, cb, e);
8072         if(!a){
8073             cb();
8074         }
8075     },
8076
8077     // overridden Element method
8078     setHeight : function(h, a, d, c, e){
8079         this.beforeAction();
8080         var cb = this.createCB(c);
8081         supr.setHeight.call(this, h, a, d, cb, e);
8082         if(!a){
8083             cb();
8084         }
8085     },
8086
8087     // overridden Element method
8088     setBounds : function(x, y, w, h, a, d, c, e){
8089         this.beforeAction();
8090         var cb = this.createCB(c);
8091         if(!a){
8092             this.storeXY([x, y]);
8093             supr.setXY.call(this, [x, y]);
8094             supr.setSize.call(this, w, h, a, d, cb, e);
8095             cb();
8096         }else{
8097             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
8098         }
8099         return this;
8100     },
8101     
8102     /**
8103      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
8104      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
8105      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
8106      * @param {Number} zindex The new z-index to set
8107      * @return {this} The Layer
8108      */
8109     setZIndex : function(zindex){
8110         this.zindex = zindex;
8111         this.setStyle("z-index", zindex + 2);
8112         if(this.shadow){
8113             this.shadow.setZIndex(zindex + 1);
8114         }
8115         if(this.shim){
8116             this.shim.setStyle("z-index", zindex);
8117         }
8118     }
8119 });
8120 })();/*
8121  * Based on:
8122  * Ext JS Library 1.1.1
8123  * Copyright(c) 2006-2007, Ext JS, LLC.
8124  *
8125  * Originally Released Under LGPL - original licence link has changed is not relivant.
8126  *
8127  * Fork - LGPL
8128  * <script type="text/javascript">
8129  */
8130
8131
8132 /**
8133  * @class Roo.Shadow
8134  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
8135  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
8136  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
8137  * @constructor
8138  * Create a new Shadow
8139  * @param {Object} config The config object
8140  */
8141 Roo.Shadow = function(config){
8142     Roo.apply(this, config);
8143     if(typeof this.mode != "string"){
8144         this.mode = this.defaultMode;
8145     }
8146     var o = this.offset, a = {h: 0};
8147     var rad = Math.floor(this.offset/2);
8148     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
8149         case "drop":
8150             a.w = 0;
8151             a.l = a.t = o;
8152             a.t -= 1;
8153             if(Roo.isIE){
8154                 a.l -= this.offset + rad;
8155                 a.t -= this.offset + rad;
8156                 a.w -= rad;
8157                 a.h -= rad;
8158                 a.t += 1;
8159             }
8160         break;
8161         case "sides":
8162             a.w = (o*2);
8163             a.l = -o;
8164             a.t = o-1;
8165             if(Roo.isIE){
8166                 a.l -= (this.offset - rad);
8167                 a.t -= this.offset + rad;
8168                 a.l += 1;
8169                 a.w -= (this.offset - rad)*2;
8170                 a.w -= rad + 1;
8171                 a.h -= 1;
8172             }
8173         break;
8174         case "frame":
8175             a.w = a.h = (o*2);
8176             a.l = a.t = -o;
8177             a.t += 1;
8178             a.h -= 2;
8179             if(Roo.isIE){
8180                 a.l -= (this.offset - rad);
8181                 a.t -= (this.offset - rad);
8182                 a.l += 1;
8183                 a.w -= (this.offset + rad + 1);
8184                 a.h -= (this.offset + rad);
8185                 a.h += 1;
8186             }
8187         break;
8188     };
8189
8190     this.adjusts = a;
8191 };
8192
8193 Roo.Shadow.prototype = {
8194     /**
8195      * @cfg {String} mode
8196      * The shadow display mode.  Supports the following options:<br />
8197      * sides: Shadow displays on both sides and bottom only<br />
8198      * frame: Shadow displays equally on all four sides<br />
8199      * drop: Traditional bottom-right drop shadow (default)
8200      */
8201     /**
8202      * @cfg {String} offset
8203      * The number of pixels to offset the shadow from the element (defaults to 4)
8204      */
8205     offset: 4,
8206
8207     // private
8208     defaultMode: "drop",
8209
8210     /**
8211      * Displays the shadow under the target element
8212      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
8213      */
8214     show : function(target){
8215         target = Roo.get(target);
8216         if(!this.el){
8217             this.el = Roo.Shadow.Pool.pull();
8218             if(this.el.dom.nextSibling != target.dom){
8219                 this.el.insertBefore(target);
8220             }
8221         }
8222         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
8223         if(Roo.isIE){
8224             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
8225         }
8226         this.realign(
8227             target.getLeft(true),
8228             target.getTop(true),
8229             target.getWidth(),
8230             target.getHeight()
8231         );
8232         this.el.dom.style.display = "block";
8233     },
8234
8235     /**
8236      * Returns true if the shadow is visible, else false
8237      */
8238     isVisible : function(){
8239         return this.el ? true : false;  
8240     },
8241
8242     /**
8243      * Direct alignment when values are already available. Show must be called at least once before
8244      * calling this method to ensure it is initialized.
8245      * @param {Number} left The target element left position
8246      * @param {Number} top The target element top position
8247      * @param {Number} width The target element width
8248      * @param {Number} height The target element height
8249      */
8250     realign : function(l, t, w, h){
8251         if(!this.el){
8252             return;
8253         }
8254         var a = this.adjusts, d = this.el.dom, s = d.style;
8255         var iea = 0;
8256         s.left = (l+a.l)+"px";
8257         s.top = (t+a.t)+"px";
8258         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
8259  
8260         if(s.width != sws || s.height != shs){
8261             s.width = sws;
8262             s.height = shs;
8263             if(!Roo.isIE){
8264                 var cn = d.childNodes;
8265                 var sww = Math.max(0, (sw-12))+"px";
8266                 cn[0].childNodes[1].style.width = sww;
8267                 cn[1].childNodes[1].style.width = sww;
8268                 cn[2].childNodes[1].style.width = sww;
8269                 cn[1].style.height = Math.max(0, (sh-12))+"px";
8270             }
8271         }
8272     },
8273
8274     /**
8275      * Hides this shadow
8276      */
8277     hide : function(){
8278         if(this.el){
8279             this.el.dom.style.display = "none";
8280             Roo.Shadow.Pool.push(this.el);
8281             delete this.el;
8282         }
8283     },
8284
8285     /**
8286      * Adjust the z-index of this shadow
8287      * @param {Number} zindex The new z-index
8288      */
8289     setZIndex : function(z){
8290         this.zIndex = z;
8291         if(this.el){
8292             this.el.setStyle("z-index", z);
8293         }
8294     }
8295 };
8296
8297 // Private utility class that manages the internal Shadow cache
8298 Roo.Shadow.Pool = function(){
8299     var p = [];
8300     var markup = Roo.isIE ?
8301                  '<div class="x-ie-shadow"></div>' :
8302                  '<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>';
8303     return {
8304         pull : function(){
8305             var sh = p.shift();
8306             if(!sh){
8307                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
8308                 sh.autoBoxAdjust = false;
8309             }
8310             return sh;
8311         },
8312
8313         push : function(sh){
8314             p.push(sh);
8315         }
8316     };
8317 }();/*
8318  * Based on:
8319  * Ext JS Library 1.1.1
8320  * Copyright(c) 2006-2007, Ext JS, LLC.
8321  *
8322  * Originally Released Under LGPL - original licence link has changed is not relivant.
8323  *
8324  * Fork - LGPL
8325  * <script type="text/javascript">
8326  */
8327
8328 /**
8329  * @class Roo.BoxComponent
8330  * @extends Roo.Component
8331  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
8332  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
8333  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
8334  * layout containers.
8335  * @constructor
8336  * @param {Roo.Element/String/Object} config The configuration options.
8337  */
8338 Roo.BoxComponent = function(config){
8339     Roo.Component.call(this, config);
8340     this.addEvents({
8341         /**
8342          * @event resize
8343          * Fires after the component is resized.
8344              * @param {Roo.Component} this
8345              * @param {Number} adjWidth The box-adjusted width that was set
8346              * @param {Number} adjHeight The box-adjusted height that was set
8347              * @param {Number} rawWidth The width that was originally specified
8348              * @param {Number} rawHeight The height that was originally specified
8349              */
8350         resize : true,
8351         /**
8352          * @event move
8353          * Fires after the component is moved.
8354              * @param {Roo.Component} this
8355              * @param {Number} x The new x position
8356              * @param {Number} y The new y position
8357              */
8358         move : true
8359     });
8360 };
8361
8362 Roo.extend(Roo.BoxComponent, Roo.Component, {
8363     // private, set in afterRender to signify that the component has been rendered
8364     boxReady : false,
8365     // private, used to defer height settings to subclasses
8366     deferHeight: false,
8367     /** @cfg {Number} width
8368      * width (optional) size of component
8369      */
8370      /** @cfg {Number} height
8371      * height (optional) size of component
8372      */
8373      
8374     /**
8375      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
8376      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
8377      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
8378      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
8379      * @return {Roo.BoxComponent} this
8380      */
8381     setSize : function(w, h){
8382         // support for standard size objects
8383         if(typeof w == 'object'){
8384             h = w.height;
8385             w = w.width;
8386         }
8387         // not rendered
8388         if(!this.boxReady){
8389             this.width = w;
8390             this.height = h;
8391             return this;
8392         }
8393
8394         // prevent recalcs when not needed
8395         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
8396             return this;
8397         }
8398         this.lastSize = {width: w, height: h};
8399
8400         var adj = this.adjustSize(w, h);
8401         var aw = adj.width, ah = adj.height;
8402         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
8403             var rz = this.getResizeEl();
8404             if(!this.deferHeight && aw !== undefined && ah !== undefined){
8405                 rz.setSize(aw, ah);
8406             }else if(!this.deferHeight && ah !== undefined){
8407                 rz.setHeight(ah);
8408             }else if(aw !== undefined){
8409                 rz.setWidth(aw);
8410             }
8411             this.onResize(aw, ah, w, h);
8412             this.fireEvent('resize', this, aw, ah, w, h);
8413         }
8414         return this;
8415     },
8416
8417     /**
8418      * Gets the current size of the component's underlying element.
8419      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8420      */
8421     getSize : function(){
8422         return this.el.getSize();
8423     },
8424
8425     /**
8426      * Gets the current XY position of the component's underlying element.
8427      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8428      * @return {Array} The XY position of the element (e.g., [100, 200])
8429      */
8430     getPosition : function(local){
8431         if(local === true){
8432             return [this.el.getLeft(true), this.el.getTop(true)];
8433         }
8434         return this.xy || this.el.getXY();
8435     },
8436
8437     /**
8438      * Gets the current box measurements of the component's underlying element.
8439      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8440      * @returns {Object} box An object in the format {x, y, width, height}
8441      */
8442     getBox : function(local){
8443         var s = this.el.getSize();
8444         if(local){
8445             s.x = this.el.getLeft(true);
8446             s.y = this.el.getTop(true);
8447         }else{
8448             var xy = this.xy || this.el.getXY();
8449             s.x = xy[0];
8450             s.y = xy[1];
8451         }
8452         return s;
8453     },
8454
8455     /**
8456      * Sets the current box measurements of the component's underlying element.
8457      * @param {Object} box An object in the format {x, y, width, height}
8458      * @returns {Roo.BoxComponent} this
8459      */
8460     updateBox : function(box){
8461         this.setSize(box.width, box.height);
8462         this.setPagePosition(box.x, box.y);
8463         return this;
8464     },
8465
8466     // protected
8467     getResizeEl : function(){
8468         return this.resizeEl || this.el;
8469     },
8470
8471     // protected
8472     getPositionEl : function(){
8473         return this.positionEl || this.el;
8474     },
8475
8476     /**
8477      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
8478      * This method fires the move event.
8479      * @param {Number} left The new left
8480      * @param {Number} top The new top
8481      * @returns {Roo.BoxComponent} this
8482      */
8483     setPosition : function(x, y){
8484         this.x = x;
8485         this.y = y;
8486         if(!this.boxReady){
8487             return this;
8488         }
8489         var adj = this.adjustPosition(x, y);
8490         var ax = adj.x, ay = adj.y;
8491
8492         var el = this.getPositionEl();
8493         if(ax !== undefined || ay !== undefined){
8494             if(ax !== undefined && ay !== undefined){
8495                 el.setLeftTop(ax, ay);
8496             }else if(ax !== undefined){
8497                 el.setLeft(ax);
8498             }else if(ay !== undefined){
8499                 el.setTop(ay);
8500             }
8501             this.onPosition(ax, ay);
8502             this.fireEvent('move', this, ax, ay);
8503         }
8504         return this;
8505     },
8506
8507     /**
8508      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
8509      * This method fires the move event.
8510      * @param {Number} x The new x position
8511      * @param {Number} y The new y position
8512      * @returns {Roo.BoxComponent} this
8513      */
8514     setPagePosition : function(x, y){
8515         this.pageX = x;
8516         this.pageY = y;
8517         if(!this.boxReady){
8518             return;
8519         }
8520         if(x === undefined || y === undefined){ // cannot translate undefined points
8521             return;
8522         }
8523         var p = this.el.translatePoints(x, y);
8524         this.setPosition(p.left, p.top);
8525         return this;
8526     },
8527
8528     // private
8529     onRender : function(ct, position){
8530         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
8531         if(this.resizeEl){
8532             this.resizeEl = Roo.get(this.resizeEl);
8533         }
8534         if(this.positionEl){
8535             this.positionEl = Roo.get(this.positionEl);
8536         }
8537     },
8538
8539     // private
8540     afterRender : function(){
8541         Roo.BoxComponent.superclass.afterRender.call(this);
8542         this.boxReady = true;
8543         this.setSize(this.width, this.height);
8544         if(this.x || this.y){
8545             this.setPosition(this.x, this.y);
8546         }
8547         if(this.pageX || this.pageY){
8548             this.setPagePosition(this.pageX, this.pageY);
8549         }
8550     },
8551
8552     /**
8553      * Force the component's size to recalculate based on the underlying element's current height and width.
8554      * @returns {Roo.BoxComponent} this
8555      */
8556     syncSize : function(){
8557         delete this.lastSize;
8558         this.setSize(this.el.getWidth(), this.el.getHeight());
8559         return this;
8560     },
8561
8562     /**
8563      * Called after the component is resized, this method is empty by default but can be implemented by any
8564      * subclass that needs to perform custom logic after a resize occurs.
8565      * @param {Number} adjWidth The box-adjusted width that was set
8566      * @param {Number} adjHeight The box-adjusted height that was set
8567      * @param {Number} rawWidth The width that was originally specified
8568      * @param {Number} rawHeight The height that was originally specified
8569      */
8570     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
8571
8572     },
8573
8574     /**
8575      * Called after the component is moved, this method is empty by default but can be implemented by any
8576      * subclass that needs to perform custom logic after a move occurs.
8577      * @param {Number} x The new x position
8578      * @param {Number} y The new y position
8579      */
8580     onPosition : function(x, y){
8581
8582     },
8583
8584     // private
8585     adjustSize : function(w, h){
8586         if(this.autoWidth){
8587             w = 'auto';
8588         }
8589         if(this.autoHeight){
8590             h = 'auto';
8591         }
8592         return {width : w, height: h};
8593     },
8594
8595     // private
8596     adjustPosition : function(x, y){
8597         return {x : x, y: y};
8598     }
8599 });/*
8600  * Based on:
8601  * Ext JS Library 1.1.1
8602  * Copyright(c) 2006-2007, Ext JS, LLC.
8603  *
8604  * Originally Released Under LGPL - original licence link has changed is not relivant.
8605  *
8606  * Fork - LGPL
8607  * <script type="text/javascript">
8608  */
8609
8610
8611 /**
8612  * @class Roo.SplitBar
8613  * @extends Roo.util.Observable
8614  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
8615  * <br><br>
8616  * Usage:
8617  * <pre><code>
8618 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
8619                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
8620 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
8621 split.minSize = 100;
8622 split.maxSize = 600;
8623 split.animate = true;
8624 split.on('moved', splitterMoved);
8625 </code></pre>
8626  * @constructor
8627  * Create a new SplitBar
8628  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
8629  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
8630  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8631  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
8632                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
8633                         position of the SplitBar).
8634  */
8635 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
8636     
8637     /** @private */
8638     this.el = Roo.get(dragElement, true);
8639     this.el.dom.unselectable = "on";
8640     /** @private */
8641     this.resizingEl = Roo.get(resizingElement, true);
8642
8643     /**
8644      * @private
8645      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8646      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
8647      * @type Number
8648      */
8649     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
8650     
8651     /**
8652      * The minimum size of the resizing element. (Defaults to 0)
8653      * @type Number
8654      */
8655     this.minSize = 0;
8656     
8657     /**
8658      * The maximum size of the resizing element. (Defaults to 2000)
8659      * @type Number
8660      */
8661     this.maxSize = 2000;
8662     
8663     /**
8664      * Whether to animate the transition to the new size
8665      * @type Boolean
8666      */
8667     this.animate = false;
8668     
8669     /**
8670      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
8671      * @type Boolean
8672      */
8673     this.useShim = false;
8674     
8675     /** @private */
8676     this.shim = null;
8677     
8678     if(!existingProxy){
8679         /** @private */
8680         this.proxy = Roo.SplitBar.createProxy(this.orientation);
8681     }else{
8682         this.proxy = Roo.get(existingProxy).dom;
8683     }
8684     /** @private */
8685     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
8686     
8687     /** @private */
8688     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
8689     
8690     /** @private */
8691     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
8692     
8693     /** @private */
8694     this.dragSpecs = {};
8695     
8696     /**
8697      * @private The adapter to use to positon and resize elements
8698      */
8699     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
8700     this.adapter.init(this);
8701     
8702     if(this.orientation == Roo.SplitBar.HORIZONTAL){
8703         /** @private */
8704         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
8705         this.el.addClass("x-splitbar-h");
8706     }else{
8707         /** @private */
8708         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
8709         this.el.addClass("x-splitbar-v");
8710     }
8711     
8712     this.addEvents({
8713         /**
8714          * @event resize
8715          * Fires when the splitter is moved (alias for {@link #event-moved})
8716          * @param {Roo.SplitBar} this
8717          * @param {Number} newSize the new width or height
8718          */
8719         "resize" : true,
8720         /**
8721          * @event moved
8722          * Fires when the splitter is moved
8723          * @param {Roo.SplitBar} this
8724          * @param {Number} newSize the new width or height
8725          */
8726         "moved" : true,
8727         /**
8728          * @event beforeresize
8729          * Fires before the splitter is dragged
8730          * @param {Roo.SplitBar} this
8731          */
8732         "beforeresize" : true,
8733
8734         "beforeapply" : true
8735     });
8736
8737     Roo.util.Observable.call(this);
8738 };
8739
8740 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
8741     onStartProxyDrag : function(x, y){
8742         this.fireEvent("beforeresize", this);
8743         if(!this.overlay){
8744             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
8745             o.unselectable();
8746             o.enableDisplayMode("block");
8747             // all splitbars share the same overlay
8748             Roo.SplitBar.prototype.overlay = o;
8749         }
8750         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8751         this.overlay.show();
8752         Roo.get(this.proxy).setDisplayed("block");
8753         var size = this.adapter.getElementSize(this);
8754         this.activeMinSize = this.getMinimumSize();;
8755         this.activeMaxSize = this.getMaximumSize();;
8756         var c1 = size - this.activeMinSize;
8757         var c2 = Math.max(this.activeMaxSize - size, 0);
8758         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8759             this.dd.resetConstraints();
8760             this.dd.setXConstraint(
8761                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
8762                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
8763             );
8764             this.dd.setYConstraint(0, 0);
8765         }else{
8766             this.dd.resetConstraints();
8767             this.dd.setXConstraint(0, 0);
8768             this.dd.setYConstraint(
8769                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
8770                 this.placement == Roo.SplitBar.TOP ? c2 : c1
8771             );
8772          }
8773         this.dragSpecs.startSize = size;
8774         this.dragSpecs.startPoint = [x, y];
8775         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
8776     },
8777     
8778     /** 
8779      * @private Called after the drag operation by the DDProxy
8780      */
8781     onEndProxyDrag : function(e){
8782         Roo.get(this.proxy).setDisplayed(false);
8783         var endPoint = Roo.lib.Event.getXY(e);
8784         if(this.overlay){
8785             this.overlay.hide();
8786         }
8787         var newSize;
8788         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8789             newSize = this.dragSpecs.startSize + 
8790                 (this.placement == Roo.SplitBar.LEFT ?
8791                     endPoint[0] - this.dragSpecs.startPoint[0] :
8792                     this.dragSpecs.startPoint[0] - endPoint[0]
8793                 );
8794         }else{
8795             newSize = this.dragSpecs.startSize + 
8796                 (this.placement == Roo.SplitBar.TOP ?
8797                     endPoint[1] - this.dragSpecs.startPoint[1] :
8798                     this.dragSpecs.startPoint[1] - endPoint[1]
8799                 );
8800         }
8801         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
8802         if(newSize != this.dragSpecs.startSize){
8803             if(this.fireEvent('beforeapply', this, newSize) !== false){
8804                 this.adapter.setElementSize(this, newSize);
8805                 this.fireEvent("moved", this, newSize);
8806                 this.fireEvent("resize", this, newSize);
8807             }
8808         }
8809     },
8810     
8811     /**
8812      * Get the adapter this SplitBar uses
8813      * @return The adapter object
8814      */
8815     getAdapter : function(){
8816         return this.adapter;
8817     },
8818     
8819     /**
8820      * Set the adapter this SplitBar uses
8821      * @param {Object} adapter A SplitBar adapter object
8822      */
8823     setAdapter : function(adapter){
8824         this.adapter = adapter;
8825         this.adapter.init(this);
8826     },
8827     
8828     /**
8829      * Gets the minimum size for the resizing element
8830      * @return {Number} The minimum size
8831      */
8832     getMinimumSize : function(){
8833         return this.minSize;
8834     },
8835     
8836     /**
8837      * Sets the minimum size for the resizing element
8838      * @param {Number} minSize The minimum size
8839      */
8840     setMinimumSize : function(minSize){
8841         this.minSize = minSize;
8842     },
8843     
8844     /**
8845      * Gets the maximum size for the resizing element
8846      * @return {Number} The maximum size
8847      */
8848     getMaximumSize : function(){
8849         return this.maxSize;
8850     },
8851     
8852     /**
8853      * Sets the maximum size for the resizing element
8854      * @param {Number} maxSize The maximum size
8855      */
8856     setMaximumSize : function(maxSize){
8857         this.maxSize = maxSize;
8858     },
8859     
8860     /**
8861      * Sets the initialize size for the resizing element
8862      * @param {Number} size The initial size
8863      */
8864     setCurrentSize : function(size){
8865         var oldAnimate = this.animate;
8866         this.animate = false;
8867         this.adapter.setElementSize(this, size);
8868         this.animate = oldAnimate;
8869     },
8870     
8871     /**
8872      * Destroy this splitbar. 
8873      * @param {Boolean} removeEl True to remove the element
8874      */
8875     destroy : function(removeEl){
8876         if(this.shim){
8877             this.shim.remove();
8878         }
8879         this.dd.unreg();
8880         this.proxy.parentNode.removeChild(this.proxy);
8881         if(removeEl){
8882             this.el.remove();
8883         }
8884     }
8885 });
8886
8887 /**
8888  * @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.
8889  */
8890 Roo.SplitBar.createProxy = function(dir){
8891     var proxy = new Roo.Element(document.createElement("div"));
8892     proxy.unselectable();
8893     var cls = 'x-splitbar-proxy';
8894     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
8895     document.body.appendChild(proxy.dom);
8896     return proxy.dom;
8897 };
8898
8899 /** 
8900  * @class Roo.SplitBar.BasicLayoutAdapter
8901  * Default Adapter. It assumes the splitter and resizing element are not positioned
8902  * elements and only gets/sets the width of the element. Generally used for table based layouts.
8903  */
8904 Roo.SplitBar.BasicLayoutAdapter = function(){
8905 };
8906
8907 Roo.SplitBar.BasicLayoutAdapter.prototype = {
8908     // do nothing for now
8909     init : function(s){
8910     
8911     },
8912     /**
8913      * Called before drag operations to get the current size of the resizing element. 
8914      * @param {Roo.SplitBar} s The SplitBar using this adapter
8915      */
8916      getElementSize : function(s){
8917         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8918             return s.resizingEl.getWidth();
8919         }else{
8920             return s.resizingEl.getHeight();
8921         }
8922     },
8923     
8924     /**
8925      * Called after drag operations to set the size of the resizing element.
8926      * @param {Roo.SplitBar} s The SplitBar using this adapter
8927      * @param {Number} newSize The new size to set
8928      * @param {Function} onComplete A function to be invoked when resizing is complete
8929      */
8930     setElementSize : function(s, newSize, onComplete){
8931         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8932             if(!s.animate){
8933                 s.resizingEl.setWidth(newSize);
8934                 if(onComplete){
8935                     onComplete(s, newSize);
8936                 }
8937             }else{
8938                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
8939             }
8940         }else{
8941             
8942             if(!s.animate){
8943                 s.resizingEl.setHeight(newSize);
8944                 if(onComplete){
8945                     onComplete(s, newSize);
8946                 }
8947             }else{
8948                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
8949             }
8950         }
8951     }
8952 };
8953
8954 /** 
8955  *@class Roo.SplitBar.AbsoluteLayoutAdapter
8956  * @extends Roo.SplitBar.BasicLayoutAdapter
8957  * Adapter that  moves the splitter element to align with the resized sizing element. 
8958  * Used with an absolute positioned SplitBar.
8959  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
8960  * document.body, make sure you assign an id to the body element.
8961  */
8962 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
8963     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
8964     this.container = Roo.get(container);
8965 };
8966
8967 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
8968     init : function(s){
8969         this.basic.init(s);
8970     },
8971     
8972     getElementSize : function(s){
8973         return this.basic.getElementSize(s);
8974     },
8975     
8976     setElementSize : function(s, newSize, onComplete){
8977         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
8978     },
8979     
8980     moveSplitter : function(s){
8981         var yes = Roo.SplitBar;
8982         switch(s.placement){
8983             case yes.LEFT:
8984                 s.el.setX(s.resizingEl.getRight());
8985                 break;
8986             case yes.RIGHT:
8987                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
8988                 break;
8989             case yes.TOP:
8990                 s.el.setY(s.resizingEl.getBottom());
8991                 break;
8992             case yes.BOTTOM:
8993                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
8994                 break;
8995         }
8996     }
8997 };
8998
8999 /**
9000  * Orientation constant - Create a vertical SplitBar
9001  * @static
9002  * @type Number
9003  */
9004 Roo.SplitBar.VERTICAL = 1;
9005
9006 /**
9007  * Orientation constant - Create a horizontal SplitBar
9008  * @static
9009  * @type Number
9010  */
9011 Roo.SplitBar.HORIZONTAL = 2;
9012
9013 /**
9014  * Placement constant - The resizing element is to the left of the splitter element
9015  * @static
9016  * @type Number
9017  */
9018 Roo.SplitBar.LEFT = 1;
9019
9020 /**
9021  * Placement constant - The resizing element is to the right of the splitter element
9022  * @static
9023  * @type Number
9024  */
9025 Roo.SplitBar.RIGHT = 2;
9026
9027 /**
9028  * Placement constant - The resizing element is positioned above the splitter element
9029  * @static
9030  * @type Number
9031  */
9032 Roo.SplitBar.TOP = 3;
9033
9034 /**
9035  * Placement constant - The resizing element is positioned under splitter element
9036  * @static
9037  * @type Number
9038  */
9039 Roo.SplitBar.BOTTOM = 4;
9040 /*
9041  * Based on:
9042  * Ext JS Library 1.1.1
9043  * Copyright(c) 2006-2007, Ext JS, LLC.
9044  *
9045  * Originally Released Under LGPL - original licence link has changed is not relivant.
9046  *
9047  * Fork - LGPL
9048  * <script type="text/javascript">
9049  */
9050
9051 /**
9052  * @class Roo.View
9053  * @extends Roo.util.Observable
9054  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
9055  * This class also supports single and multi selection modes. <br>
9056  * Create a data model bound view:
9057  <pre><code>
9058  var store = new Roo.data.Store(...);
9059
9060  var view = new Roo.View({
9061     el : "my-element",
9062     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
9063  
9064     singleSelect: true,
9065     selectedClass: "ydataview-selected",
9066     store: store
9067  });
9068
9069  // listen for node click?
9070  view.on("click", function(vw, index, node, e){
9071  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9072  });
9073
9074  // load XML data
9075  dataModel.load("foobar.xml");
9076  </code></pre>
9077  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9078  * <br><br>
9079  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9080  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9081  * 
9082  * Note: old style constructor is still suported (container, template, config)
9083  * 
9084  * @constructor
9085  * Create a new View
9086  * @param {Object} config The config object
9087  * 
9088  */
9089 Roo.View = function(config, depreciated_tpl, depreciated_config){
9090     
9091     if (typeof(depreciated_tpl) == 'undefined') {
9092         // new way.. - universal constructor.
9093         Roo.apply(this, config);
9094         this.el  = Roo.get(this.el);
9095     } else {
9096         // old format..
9097         this.el  = Roo.get(config);
9098         this.tpl = depreciated_tpl;
9099         Roo.apply(this, depreciated_config);
9100     }
9101      
9102     
9103     if(typeof(this.tpl) == "string"){
9104         this.tpl = new Roo.Template(this.tpl);
9105     } else {
9106         // support xtype ctors..
9107         this.tpl = new Roo.factory(this.tpl, Roo);
9108     }
9109     
9110     
9111     this.tpl.compile();
9112    
9113
9114      
9115     /** @private */
9116     this.addEvents({
9117     /**
9118      * @event beforeclick
9119      * Fires before a click is processed. Returns false to cancel the default action.
9120      * @param {Roo.View} this
9121      * @param {Number} index The index of the target node
9122      * @param {HTMLElement} node The target node
9123      * @param {Roo.EventObject} e The raw event object
9124      */
9125         "beforeclick" : true,
9126     /**
9127      * @event click
9128      * Fires when a template node is clicked.
9129      * @param {Roo.View} this
9130      * @param {Number} index The index of the target node
9131      * @param {HTMLElement} node The target node
9132      * @param {Roo.EventObject} e The raw event object
9133      */
9134         "click" : true,
9135     /**
9136      * @event dblclick
9137      * Fires when a template node is double clicked.
9138      * @param {Roo.View} this
9139      * @param {Number} index The index of the target node
9140      * @param {HTMLElement} node The target node
9141      * @param {Roo.EventObject} e The raw event object
9142      */
9143         "dblclick" : true,
9144     /**
9145      * @event contextmenu
9146      * Fires when a template node is right clicked.
9147      * @param {Roo.View} this
9148      * @param {Number} index The index of the target node
9149      * @param {HTMLElement} node The target node
9150      * @param {Roo.EventObject} e The raw event object
9151      */
9152         "contextmenu" : true,
9153     /**
9154      * @event selectionchange
9155      * Fires when the selected nodes change.
9156      * @param {Roo.View} this
9157      * @param {Array} selections Array of the selected nodes
9158      */
9159         "selectionchange" : true,
9160
9161     /**
9162      * @event beforeselect
9163      * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9164      * @param {Roo.View} this
9165      * @param {HTMLElement} node The node to be selected
9166      * @param {Array} selections Array of currently selected nodes
9167      */
9168         "beforeselect" : true
9169     });
9170
9171     this.el.on({
9172         "click": this.onClick,
9173         "dblclick": this.onDblClick,
9174         "contextmenu": this.onContextMenu,
9175         scope:this
9176     });
9177
9178     this.selections = [];
9179     this.nodes = [];
9180     this.cmp = new Roo.CompositeElementLite([]);
9181     if(this.store){
9182         this.store = Roo.factory(this.store, Roo.data);
9183         this.setStore(this.store, true);
9184     }
9185     Roo.View.superclass.constructor.call(this);
9186 };
9187
9188 Roo.extend(Roo.View, Roo.util.Observable, {
9189     
9190      /**
9191      * @cfg {Roo.data.Store} store Data store to load data from.
9192      */
9193     store : false,
9194     
9195     /**
9196      * @cfg {String|Roo.Element} el The container element.
9197      */
9198     el : '',
9199     
9200     /**
9201      * @cfg {String|Roo.Template} tpl The template used by this View 
9202      */
9203     tpl : false,
9204     
9205     /**
9206      * @cfg {String} selectedClass The css class to add to selected nodes
9207      */
9208     selectedClass : "x-view-selected",
9209      /**
9210      * @cfg {String} emptyText The empty text to show when nothing is loaded.
9211      */
9212     emptyText : "",
9213     /**
9214      * @cfg {Boolean} multiSelect Allow multiple selection
9215      */
9216     
9217     multiSelect : false,
9218     /**
9219      * @cfg {Boolean} singleSelect Allow single selection
9220      */
9221     singleSelect:  false,
9222     
9223     /**
9224      * Returns the element this view is bound to.
9225      * @return {Roo.Element}
9226      */
9227     getEl : function(){
9228         return this.el;
9229     },
9230
9231     /**
9232      * Refreshes the view.
9233      */
9234     refresh : function(){
9235         var t = this.tpl;
9236         this.clearSelections();
9237         this.el.update("");
9238         var html = [];
9239         var records = this.store.getRange();
9240         if(records.length < 1){
9241             this.el.update(this.emptyText);
9242             return;
9243         }
9244         for(var i = 0, len = records.length; i < len; i++){
9245             var data = this.prepareData(records[i].data, i, records[i]);
9246             html[html.length] = t.apply(data);
9247         }
9248         this.el.update(html.join(""));
9249         this.nodes = this.el.dom.childNodes;
9250         this.updateIndexes(0);
9251     },
9252
9253     /**
9254      * Function to override to reformat the data that is sent to
9255      * the template for each node.
9256      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9257      * a JSON object for an UpdateManager bound view).
9258      */
9259     prepareData : function(data){
9260         return data;
9261     },
9262
9263     onUpdate : function(ds, record){
9264         this.clearSelections();
9265         var index = this.store.indexOf(record);
9266         var n = this.nodes[index];
9267         this.tpl.insertBefore(n, this.prepareData(record.data));
9268         n.parentNode.removeChild(n);
9269         this.updateIndexes(index, index);
9270     },
9271
9272     onAdd : function(ds, records, index){
9273         this.clearSelections();
9274         if(this.nodes.length == 0){
9275             this.refresh();
9276             return;
9277         }
9278         var n = this.nodes[index];
9279         for(var i = 0, len = records.length; i < len; i++){
9280             var d = this.prepareData(records[i].data);
9281             if(n){
9282                 this.tpl.insertBefore(n, d);
9283             }else{
9284                 this.tpl.append(this.el, d);
9285             }
9286         }
9287         this.updateIndexes(index);
9288     },
9289
9290     onRemove : function(ds, record, index){
9291         this.clearSelections();
9292         this.el.dom.removeChild(this.nodes[index]);
9293         this.updateIndexes(index);
9294     },
9295
9296     /**
9297      * Refresh an individual node.
9298      * @param {Number} index
9299      */
9300     refreshNode : function(index){
9301         this.onUpdate(this.store, this.store.getAt(index));
9302     },
9303
9304     updateIndexes : function(startIndex, endIndex){
9305         var ns = this.nodes;
9306         startIndex = startIndex || 0;
9307         endIndex = endIndex || ns.length - 1;
9308         for(var i = startIndex; i <= endIndex; i++){
9309             ns[i].nodeIndex = i;
9310         }
9311     },
9312
9313     /**
9314      * Changes the data store this view uses and refresh the view.
9315      * @param {Store} store
9316      */
9317     setStore : function(store, initial){
9318         if(!initial && this.store){
9319             this.store.un("datachanged", this.refresh);
9320             this.store.un("add", this.onAdd);
9321             this.store.un("remove", this.onRemove);
9322             this.store.un("update", this.onUpdate);
9323             this.store.un("clear", this.refresh);
9324         }
9325         if(store){
9326           
9327             store.on("datachanged", this.refresh, this);
9328             store.on("add", this.onAdd, this);
9329             store.on("remove", this.onRemove, this);
9330             store.on("update", this.onUpdate, this);
9331             store.on("clear", this.refresh, this);
9332         }
9333         
9334         if(store){
9335             this.refresh();
9336         }
9337     },
9338
9339     /**
9340      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9341      * @param {HTMLElement} node
9342      * @return {HTMLElement} The template node
9343      */
9344     findItemFromChild : function(node){
9345         var el = this.el.dom;
9346         if(!node || node.parentNode == el){
9347                     return node;
9348             }
9349             var p = node.parentNode;
9350             while(p && p != el){
9351             if(p.parentNode == el){
9352                 return p;
9353             }
9354             p = p.parentNode;
9355         }
9356             return null;
9357     },
9358
9359     /** @ignore */
9360     onClick : function(e){
9361         var item = this.findItemFromChild(e.getTarget());
9362         if(item){
9363             var index = this.indexOf(item);
9364             if(this.onItemClick(item, index, e) !== false){
9365                 this.fireEvent("click", this, index, item, e);
9366             }
9367         }else{
9368             this.clearSelections();
9369         }
9370     },
9371
9372     /** @ignore */
9373     onContextMenu : function(e){
9374         var item = this.findItemFromChild(e.getTarget());
9375         if(item){
9376             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9377         }
9378     },
9379
9380     /** @ignore */
9381     onDblClick : function(e){
9382         var item = this.findItemFromChild(e.getTarget());
9383         if(item){
9384             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9385         }
9386     },
9387
9388     onItemClick : function(item, index, e){
9389         if(this.fireEvent("beforeclick", this, index, item, e) === false){
9390             return false;
9391         }
9392         if(this.multiSelect || this.singleSelect){
9393             if(this.multiSelect && e.shiftKey && this.lastSelection){
9394                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9395             }else{
9396                 this.select(item, this.multiSelect && e.ctrlKey);
9397                 this.lastSelection = item;
9398             }
9399             e.preventDefault();
9400         }
9401         return true;
9402     },
9403
9404     /**
9405      * Get the number of selected nodes.
9406      * @return {Number}
9407      */
9408     getSelectionCount : function(){
9409         return this.selections.length;
9410     },
9411
9412     /**
9413      * Get the currently selected nodes.
9414      * @return {Array} An array of HTMLElements
9415      */
9416     getSelectedNodes : function(){
9417         return this.selections;
9418     },
9419
9420     /**
9421      * Get the indexes of the selected nodes.
9422      * @return {Array}
9423      */
9424     getSelectedIndexes : function(){
9425         var indexes = [], s = this.selections;
9426         for(var i = 0, len = s.length; i < len; i++){
9427             indexes.push(s[i].nodeIndex);
9428         }
9429         return indexes;
9430     },
9431
9432     /**
9433      * Clear all selections
9434      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9435      */
9436     clearSelections : function(suppressEvent){
9437         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9438             this.cmp.elements = this.selections;
9439             this.cmp.removeClass(this.selectedClass);
9440             this.selections = [];
9441             if(!suppressEvent){
9442                 this.fireEvent("selectionchange", this, this.selections);
9443             }
9444         }
9445     },
9446
9447     /**
9448      * Returns true if the passed node is selected
9449      * @param {HTMLElement/Number} node The node or node index
9450      * @return {Boolean}
9451      */
9452     isSelected : function(node){
9453         var s = this.selections;
9454         if(s.length < 1){
9455             return false;
9456         }
9457         node = this.getNode(node);
9458         return s.indexOf(node) !== -1;
9459     },
9460
9461     /**
9462      * Selects nodes.
9463      * @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
9464      * @param {Boolean} keepExisting (optional) true to keep existing selections
9465      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9466      */
9467     select : function(nodeInfo, keepExisting, suppressEvent){
9468         if(nodeInfo instanceof Array){
9469             if(!keepExisting){
9470                 this.clearSelections(true);
9471             }
9472             for(var i = 0, len = nodeInfo.length; i < len; i++){
9473                 this.select(nodeInfo[i], true, true);
9474             }
9475         } else{
9476             var node = this.getNode(nodeInfo);
9477             if(node && !this.isSelected(node)){
9478                 if(!keepExisting){
9479                     this.clearSelections(true);
9480                 }
9481                 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9482                     Roo.fly(node).addClass(this.selectedClass);
9483                     this.selections.push(node);
9484                     if(!suppressEvent){
9485                         this.fireEvent("selectionchange", this, this.selections);
9486                     }
9487                 }
9488             }
9489         }
9490     },
9491
9492     /**
9493      * Gets a template node.
9494      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9495      * @return {HTMLElement} The node or null if it wasn't found
9496      */
9497     getNode : function(nodeInfo){
9498         if(typeof nodeInfo == "string"){
9499             return document.getElementById(nodeInfo);
9500         }else if(typeof nodeInfo == "number"){
9501             return this.nodes[nodeInfo];
9502         }
9503         return nodeInfo;
9504     },
9505
9506     /**
9507      * Gets a range template nodes.
9508      * @param {Number} startIndex
9509      * @param {Number} endIndex
9510      * @return {Array} An array of nodes
9511      */
9512     getNodes : function(start, end){
9513         var ns = this.nodes;
9514         start = start || 0;
9515         end = typeof end == "undefined" ? ns.length - 1 : end;
9516         var nodes = [];
9517         if(start <= end){
9518             for(var i = start; i <= end; i++){
9519                 nodes.push(ns[i]);
9520             }
9521         } else{
9522             for(var i = start; i >= end; i--){
9523                 nodes.push(ns[i]);
9524             }
9525         }
9526         return nodes;
9527     },
9528
9529     /**
9530      * Finds the index of the passed node
9531      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9532      * @return {Number} The index of the node or -1
9533      */
9534     indexOf : function(node){
9535         node = this.getNode(node);
9536         if(typeof node.nodeIndex == "number"){
9537             return node.nodeIndex;
9538         }
9539         var ns = this.nodes;
9540         for(var i = 0, len = ns.length; i < len; i++){
9541             if(ns[i] == node){
9542                 return i;
9543             }
9544         }
9545         return -1;
9546     }
9547 });
9548 /*
9549  * Based on:
9550  * Ext JS Library 1.1.1
9551  * Copyright(c) 2006-2007, Ext JS, LLC.
9552  *
9553  * Originally Released Under LGPL - original licence link has changed is not relivant.
9554  *
9555  * Fork - LGPL
9556  * <script type="text/javascript">
9557  */
9558
9559 /**
9560  * @class Roo.JsonView
9561  * @extends Roo.View
9562  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
9563 <pre><code>
9564 var view = new Roo.JsonView({
9565     container: "my-element",
9566     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
9567     multiSelect: true, 
9568     jsonRoot: "data" 
9569 });
9570
9571 // listen for node click?
9572 view.on("click", function(vw, index, node, e){
9573     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9574 });
9575
9576 // direct load of JSON data
9577 view.load("foobar.php");
9578
9579 // Example from my blog list
9580 var tpl = new Roo.Template(
9581     '&lt;div class="entry"&gt;' +
9582     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
9583     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
9584     "&lt;/div&gt;&lt;hr /&gt;"
9585 );
9586
9587 var moreView = new Roo.JsonView({
9588     container :  "entry-list", 
9589     template : tpl,
9590     jsonRoot: "posts"
9591 });
9592 moreView.on("beforerender", this.sortEntries, this);
9593 moreView.load({
9594     url: "/blog/get-posts.php",
9595     params: "allposts=true",
9596     text: "Loading Blog Entries..."
9597 });
9598 </code></pre>
9599
9600 * Note: old code is supported with arguments : (container, template, config)
9601
9602
9603  * @constructor
9604  * Create a new JsonView
9605  * 
9606  * @param {Object} config The config object
9607  * 
9608  */
9609 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
9610     
9611     
9612     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
9613
9614     var um = this.el.getUpdateManager();
9615     um.setRenderer(this);
9616     um.on("update", this.onLoad, this);
9617     um.on("failure", this.onLoadException, this);
9618
9619     /**
9620      * @event beforerender
9621      * Fires before rendering of the downloaded JSON data.
9622      * @param {Roo.JsonView} this
9623      * @param {Object} data The JSON data loaded
9624      */
9625     /**
9626      * @event load
9627      * Fires when data is loaded.
9628      * @param {Roo.JsonView} this
9629      * @param {Object} data The JSON data loaded
9630      * @param {Object} response The raw Connect response object
9631      */
9632     /**
9633      * @event loadexception
9634      * Fires when loading fails.
9635      * @param {Roo.JsonView} this
9636      * @param {Object} response The raw Connect response object
9637      */
9638     this.addEvents({
9639         'beforerender' : true,
9640         'load' : true,
9641         'loadexception' : true
9642     });
9643 };
9644 Roo.extend(Roo.JsonView, Roo.View, {
9645     /**
9646      * @type {String} The root property in the loaded JSON object that contains the data
9647      */
9648     jsonRoot : "",
9649
9650     /**
9651      * Refreshes the view.
9652      */
9653     refresh : function(){
9654         this.clearSelections();
9655         this.el.update("");
9656         var html = [];
9657         var o = this.jsonData;
9658         if(o && o.length > 0){
9659             for(var i = 0, len = o.length; i < len; i++){
9660                 var data = this.prepareData(o[i], i, o);
9661                 html[html.length] = this.tpl.apply(data);
9662             }
9663         }else{
9664             html.push(this.emptyText);
9665         }
9666         this.el.update(html.join(""));
9667         this.nodes = this.el.dom.childNodes;
9668         this.updateIndexes(0);
9669     },
9670
9671     /**
9672      * 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.
9673      * @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:
9674      <pre><code>
9675      view.load({
9676          url: "your-url.php",
9677          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
9678          callback: yourFunction,
9679          scope: yourObject, //(optional scope)
9680          discardUrl: false,
9681          nocache: false,
9682          text: "Loading...",
9683          timeout: 30,
9684          scripts: false
9685      });
9686      </code></pre>
9687      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
9688      * 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.
9689      * @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}
9690      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9691      * @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.
9692      */
9693     load : function(){
9694         var um = this.el.getUpdateManager();
9695         um.update.apply(um, arguments);
9696     },
9697
9698     render : function(el, response){
9699         this.clearSelections();
9700         this.el.update("");
9701         var o;
9702         try{
9703             o = Roo.util.JSON.decode(response.responseText);
9704             if(this.jsonRoot){
9705                 
9706                 o = o[this.jsonRoot];
9707             }
9708         } catch(e){
9709         }
9710         /**
9711          * The current JSON data or null
9712          */
9713         this.jsonData = o;
9714         this.beforeRender();
9715         this.refresh();
9716     },
9717
9718 /**
9719  * Get the number of records in the current JSON dataset
9720  * @return {Number}
9721  */
9722     getCount : function(){
9723         return this.jsonData ? this.jsonData.length : 0;
9724     },
9725
9726 /**
9727  * Returns the JSON object for the specified node(s)
9728  * @param {HTMLElement/Array} node The node or an array of nodes
9729  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
9730  * you get the JSON object for the node
9731  */
9732     getNodeData : function(node){
9733         if(node instanceof Array){
9734             var data = [];
9735             for(var i = 0, len = node.length; i < len; i++){
9736                 data.push(this.getNodeData(node[i]));
9737             }
9738             return data;
9739         }
9740         return this.jsonData[this.indexOf(node)] || null;
9741     },
9742
9743     beforeRender : function(){
9744         this.snapshot = this.jsonData;
9745         if(this.sortInfo){
9746             this.sort.apply(this, this.sortInfo);
9747         }
9748         this.fireEvent("beforerender", this, this.jsonData);
9749     },
9750
9751     onLoad : function(el, o){
9752         this.fireEvent("load", this, this.jsonData, o);
9753     },
9754
9755     onLoadException : function(el, o){
9756         this.fireEvent("loadexception", this, o);
9757     },
9758
9759 /**
9760  * Filter the data by a specific property.
9761  * @param {String} property A property on your JSON objects
9762  * @param {String/RegExp} value Either string that the property values
9763  * should start with, or a RegExp to test against the property
9764  */
9765     filter : function(property, value){
9766         if(this.jsonData){
9767             var data = [];
9768             var ss = this.snapshot;
9769             if(typeof value == "string"){
9770                 var vlen = value.length;
9771                 if(vlen == 0){
9772                     this.clearFilter();
9773                     return;
9774                 }
9775                 value = value.toLowerCase();
9776                 for(var i = 0, len = ss.length; i < len; i++){
9777                     var o = ss[i];
9778                     if(o[property].substr(0, vlen).toLowerCase() == value){
9779                         data.push(o);
9780                     }
9781                 }
9782             } else if(value.exec){ // regex?
9783                 for(var i = 0, len = ss.length; i < len; i++){
9784                     var o = ss[i];
9785                     if(value.test(o[property])){
9786                         data.push(o);
9787                     }
9788                 }
9789             } else{
9790                 return;
9791             }
9792             this.jsonData = data;
9793             this.refresh();
9794         }
9795     },
9796
9797 /**
9798  * Filter by a function. The passed function will be called with each
9799  * object in the current dataset. If the function returns true the value is kept,
9800  * otherwise it is filtered.
9801  * @param {Function} fn
9802  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
9803  */
9804     filterBy : function(fn, scope){
9805         if(this.jsonData){
9806             var data = [];
9807             var ss = this.snapshot;
9808             for(var i = 0, len = ss.length; i < len; i++){
9809                 var o = ss[i];
9810                 if(fn.call(scope || this, o)){
9811                     data.push(o);
9812                 }
9813             }
9814             this.jsonData = data;
9815             this.refresh();
9816         }
9817     },
9818
9819 /**
9820  * Clears the current filter.
9821  */
9822     clearFilter : function(){
9823         if(this.snapshot && this.jsonData != this.snapshot){
9824             this.jsonData = this.snapshot;
9825             this.refresh();
9826         }
9827     },
9828
9829
9830 /**
9831  * Sorts the data for this view and refreshes it.
9832  * @param {String} property A property on your JSON objects to sort on
9833  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
9834  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
9835  */
9836     sort : function(property, dir, sortType){
9837         this.sortInfo = Array.prototype.slice.call(arguments, 0);
9838         if(this.jsonData){
9839             var p = property;
9840             var dsc = dir && dir.toLowerCase() == "desc";
9841             var f = function(o1, o2){
9842                 var v1 = sortType ? sortType(o1[p]) : o1[p];
9843                 var v2 = sortType ? sortType(o2[p]) : o2[p];
9844                 ;
9845                 if(v1 < v2){
9846                     return dsc ? +1 : -1;
9847                 } else if(v1 > v2){
9848                     return dsc ? -1 : +1;
9849                 } else{
9850                     return 0;
9851                 }
9852             };
9853             this.jsonData.sort(f);
9854             this.refresh();
9855             if(this.jsonData != this.snapshot){
9856                 this.snapshot.sort(f);
9857             }
9858         }
9859     }
9860 });/*
9861  * Based on:
9862  * Ext JS Library 1.1.1
9863  * Copyright(c) 2006-2007, Ext JS, LLC.
9864  *
9865  * Originally Released Under LGPL - original licence link has changed is not relivant.
9866  *
9867  * Fork - LGPL
9868  * <script type="text/javascript">
9869  */
9870  
9871
9872 /**
9873  * @class Roo.ColorPalette
9874  * @extends Roo.Component
9875  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
9876  * Here's an example of typical usage:
9877  * <pre><code>
9878 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
9879 cp.render('my-div');
9880
9881 cp.on('select', function(palette, selColor){
9882     // do something with selColor
9883 });
9884 </code></pre>
9885  * @constructor
9886  * Create a new ColorPalette
9887  * @param {Object} config The config object
9888  */
9889 Roo.ColorPalette = function(config){
9890     Roo.ColorPalette.superclass.constructor.call(this, config);
9891     this.addEvents({
9892         /**
9893              * @event select
9894              * Fires when a color is selected
9895              * @param {ColorPalette} this
9896              * @param {String} color The 6-digit color hex code (without the # symbol)
9897              */
9898         select: true
9899     });
9900
9901     if(this.handler){
9902         this.on("select", this.handler, this.scope, true);
9903     }
9904 };
9905 Roo.extend(Roo.ColorPalette, Roo.Component, {
9906     /**
9907      * @cfg {String} itemCls
9908      * The CSS class to apply to the containing element (defaults to "x-color-palette")
9909      */
9910     itemCls : "x-color-palette",
9911     /**
9912      * @cfg {String} value
9913      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
9914      * the hex codes are case-sensitive.
9915      */
9916     value : null,
9917     clickEvent:'click',
9918     // private
9919     ctype: "Roo.ColorPalette",
9920
9921     /**
9922      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
9923      */
9924     allowReselect : false,
9925
9926     /**
9927      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
9928      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
9929      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
9930      * of colors with the width setting until the box is symmetrical.</p>
9931      * <p>You can override individual colors if needed:</p>
9932      * <pre><code>
9933 var cp = new Roo.ColorPalette();
9934 cp.colors[0] = "FF0000";  // change the first box to red
9935 </code></pre>
9936
9937 Or you can provide a custom array of your own for complete control:
9938 <pre><code>
9939 var cp = new Roo.ColorPalette();
9940 cp.colors = ["000000", "993300", "333300"];
9941 </code></pre>
9942      * @type Array
9943      */
9944     colors : [
9945         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
9946         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
9947         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
9948         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
9949         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
9950     ],
9951
9952     // private
9953     onRender : function(container, position){
9954         var t = new Roo.MasterTemplate(
9955             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
9956         );
9957         var c = this.colors;
9958         for(var i = 0, len = c.length; i < len; i++){
9959             t.add([c[i]]);
9960         }
9961         var el = document.createElement("div");
9962         el.className = this.itemCls;
9963         t.overwrite(el);
9964         container.dom.insertBefore(el, position);
9965         this.el = Roo.get(el);
9966         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
9967         if(this.clickEvent != 'click'){
9968             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
9969         }
9970     },
9971
9972     // private
9973     afterRender : function(){
9974         Roo.ColorPalette.superclass.afterRender.call(this);
9975         if(this.value){
9976             var s = this.value;
9977             this.value = null;
9978             this.select(s);
9979         }
9980     },
9981
9982     // private
9983     handleClick : function(e, t){
9984         e.preventDefault();
9985         if(!this.disabled){
9986             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
9987             this.select(c.toUpperCase());
9988         }
9989     },
9990
9991     /**
9992      * Selects the specified color in the palette (fires the select event)
9993      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
9994      */
9995     select : function(color){
9996         color = color.replace("#", "");
9997         if(color != this.value || this.allowReselect){
9998             var el = this.el;
9999             if(this.value){
10000                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
10001             }
10002             el.child("a.color-"+color).addClass("x-color-palette-sel");
10003             this.value = color;
10004             this.fireEvent("select", this, color);
10005         }
10006     }
10007 });/*
10008  * Based on:
10009  * Ext JS Library 1.1.1
10010  * Copyright(c) 2006-2007, Ext JS, LLC.
10011  *
10012  * Originally Released Under LGPL - original licence link has changed is not relivant.
10013  *
10014  * Fork - LGPL
10015  * <script type="text/javascript">
10016  */
10017  
10018 /**
10019  * @class Roo.DatePicker
10020  * @extends Roo.Component
10021  * Simple date picker class.
10022  * @constructor
10023  * Create a new DatePicker
10024  * @param {Object} config The config object
10025  */
10026 Roo.DatePicker = function(config){
10027     Roo.DatePicker.superclass.constructor.call(this, config);
10028
10029     this.value = config && config.value ?
10030                  config.value.clearTime() : new Date().clearTime();
10031
10032     this.addEvents({
10033         /**
10034              * @event select
10035              * Fires when a date is selected
10036              * @param {DatePicker} this
10037              * @param {Date} date The selected date
10038              */
10039         select: true
10040     });
10041
10042     if(this.handler){
10043         this.on("select", this.handler,  this.scope || this);
10044     }
10045     // build the disabledDatesRE
10046     if(!this.disabledDatesRE && this.disabledDates){
10047         var dd = this.disabledDates;
10048         var re = "(?:";
10049         for(var i = 0; i < dd.length; i++){
10050             re += dd[i];
10051             if(i != dd.length-1) re += "|";
10052         }
10053         this.disabledDatesRE = new RegExp(re + ")");
10054     }
10055 };
10056
10057 Roo.extend(Roo.DatePicker, Roo.Component, {
10058     /**
10059      * @cfg {String} todayText
10060      * The text to display on the button that selects the current date (defaults to "Today")
10061      */
10062     todayText : "Today",
10063     /**
10064      * @cfg {String} okText
10065      * The text to display on the ok button
10066      */
10067     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
10068     /**
10069      * @cfg {String} cancelText
10070      * The text to display on the cancel button
10071      */
10072     cancelText : "Cancel",
10073     /**
10074      * @cfg {String} todayTip
10075      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
10076      */
10077     todayTip : "{0} (Spacebar)",
10078     /**
10079      * @cfg {Date} minDate
10080      * Minimum allowable date (JavaScript date object, defaults to null)
10081      */
10082     minDate : null,
10083     /**
10084      * @cfg {Date} maxDate
10085      * Maximum allowable date (JavaScript date object, defaults to null)
10086      */
10087     maxDate : null,
10088     /**
10089      * @cfg {String} minText
10090      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
10091      */
10092     minText : "This date is before the minimum date",
10093     /**
10094      * @cfg {String} maxText
10095      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
10096      */
10097     maxText : "This date is after the maximum date",
10098     /**
10099      * @cfg {String} format
10100      * The default date format string which can be overriden for localization support.  The format must be
10101      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
10102      */
10103     format : "m/d/y",
10104     /**
10105      * @cfg {Array} disabledDays
10106      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
10107      */
10108     disabledDays : null,
10109     /**
10110      * @cfg {String} disabledDaysText
10111      * The tooltip to display when the date falls on a disabled day (defaults to "")
10112      */
10113     disabledDaysText : "",
10114     /**
10115      * @cfg {RegExp} disabledDatesRE
10116      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
10117      */
10118     disabledDatesRE : null,
10119     /**
10120      * @cfg {String} disabledDatesText
10121      * The tooltip text to display when the date falls on a disabled date (defaults to "")
10122      */
10123     disabledDatesText : "",
10124     /**
10125      * @cfg {Boolean} constrainToViewport
10126      * True to constrain the date picker to the viewport (defaults to true)
10127      */
10128     constrainToViewport : true,
10129     /**
10130      * @cfg {Array} monthNames
10131      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
10132      */
10133     monthNames : Date.monthNames,
10134     /**
10135      * @cfg {Array} dayNames
10136      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
10137      */
10138     dayNames : Date.dayNames,
10139     /**
10140      * @cfg {String} nextText
10141      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
10142      */
10143     nextText: 'Next Month (Control+Right)',
10144     /**
10145      * @cfg {String} prevText
10146      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
10147      */
10148     prevText: 'Previous Month (Control+Left)',
10149     /**
10150      * @cfg {String} monthYearText
10151      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
10152      */
10153     monthYearText: 'Choose a month (Control+Up/Down to move years)',
10154     /**
10155      * @cfg {Number} startDay
10156      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
10157      */
10158     startDay : 0,
10159     /**
10160      * @cfg {Bool} showClear
10161      * Show a clear button (usefull for date form elements that can be blank.)
10162      */
10163     
10164     showClear: false,
10165     
10166     /**
10167      * Sets the value of the date field
10168      * @param {Date} value The date to set
10169      */
10170     setValue : function(value){
10171         var old = this.value;
10172         this.value = value.clearTime(true);
10173         if(this.el){
10174             this.update(this.value);
10175         }
10176     },
10177
10178     /**
10179      * Gets the current selected value of the date field
10180      * @return {Date} The selected date
10181      */
10182     getValue : function(){
10183         return this.value;
10184     },
10185
10186     // private
10187     focus : function(){
10188         if(this.el){
10189             this.update(this.activeDate);
10190         }
10191     },
10192
10193     // private
10194     onRender : function(container, position){
10195         var m = [
10196              '<table cellspacing="0">',
10197                 '<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>',
10198                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
10199         var dn = this.dayNames;
10200         for(var i = 0; i < 7; i++){
10201             var d = this.startDay+i;
10202             if(d > 6){
10203                 d = d-7;
10204             }
10205             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
10206         }
10207         m[m.length] = "</tr></thead><tbody><tr>";
10208         for(var i = 0; i < 42; i++) {
10209             if(i % 7 == 0 && i != 0){
10210                 m[m.length] = "</tr><tr>";
10211             }
10212             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
10213         }
10214         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
10215             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
10216
10217         var el = document.createElement("div");
10218         el.className = "x-date-picker";
10219         el.innerHTML = m.join("");
10220
10221         container.dom.insertBefore(el, position);
10222
10223         this.el = Roo.get(el);
10224         this.eventEl = Roo.get(el.firstChild);
10225
10226         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
10227             handler: this.showPrevMonth,
10228             scope: this,
10229             preventDefault:true,
10230             stopDefault:true
10231         });
10232
10233         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
10234             handler: this.showNextMonth,
10235             scope: this,
10236             preventDefault:true,
10237             stopDefault:true
10238         });
10239
10240         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
10241
10242         this.monthPicker = this.el.down('div.x-date-mp');
10243         this.monthPicker.enableDisplayMode('block');
10244         
10245         var kn = new Roo.KeyNav(this.eventEl, {
10246             "left" : function(e){
10247                 e.ctrlKey ?
10248                     this.showPrevMonth() :
10249                     this.update(this.activeDate.add("d", -1));
10250             },
10251
10252             "right" : function(e){
10253                 e.ctrlKey ?
10254                     this.showNextMonth() :
10255                     this.update(this.activeDate.add("d", 1));
10256             },
10257
10258             "up" : function(e){
10259                 e.ctrlKey ?
10260                     this.showNextYear() :
10261                     this.update(this.activeDate.add("d", -7));
10262             },
10263
10264             "down" : function(e){
10265                 e.ctrlKey ?
10266                     this.showPrevYear() :
10267                     this.update(this.activeDate.add("d", 7));
10268             },
10269
10270             "pageUp" : function(e){
10271                 this.showNextMonth();
10272             },
10273
10274             "pageDown" : function(e){
10275                 this.showPrevMonth();
10276             },
10277
10278             "enter" : function(e){
10279                 e.stopPropagation();
10280                 return true;
10281             },
10282
10283             scope : this
10284         });
10285
10286         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
10287
10288         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
10289
10290         this.el.unselectable();
10291         
10292         this.cells = this.el.select("table.x-date-inner tbody td");
10293         this.textNodes = this.el.query("table.x-date-inner tbody span");
10294
10295         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
10296             text: "&#160;",
10297             tooltip: this.monthYearText
10298         });
10299
10300         this.mbtn.on('click', this.showMonthPicker, this);
10301         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
10302
10303
10304         var today = (new Date()).dateFormat(this.format);
10305         
10306         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
10307         if (this.showClear) {
10308             baseTb.add( new Roo.Toolbar.Fill());
10309         }
10310         baseTb.add({
10311             text: String.format(this.todayText, today),
10312             tooltip: String.format(this.todayTip, today),
10313             handler: this.selectToday,
10314             scope: this
10315         });
10316         
10317         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
10318             
10319         //});
10320         if (this.showClear) {
10321             
10322             baseTb.add( new Roo.Toolbar.Fill());
10323             baseTb.add({
10324                 text: '&#160;',
10325                 cls: 'x-btn-icon x-btn-clear',
10326                 handler: function() {
10327                     //this.value = '';
10328                     this.fireEvent("select", this, '');
10329                 },
10330                 scope: this
10331             });
10332         }
10333         
10334         
10335         if(Roo.isIE){
10336             this.el.repaint();
10337         }
10338         this.update(this.value);
10339     },
10340
10341     createMonthPicker : function(){
10342         if(!this.monthPicker.dom.firstChild){
10343             var buf = ['<table border="0" cellspacing="0">'];
10344             for(var i = 0; i < 6; i++){
10345                 buf.push(
10346                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
10347                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
10348                     i == 0 ?
10349                     '<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>' :
10350                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
10351                 );
10352             }
10353             buf.push(
10354                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
10355                     this.okText,
10356                     '</button><button type="button" class="x-date-mp-cancel">',
10357                     this.cancelText,
10358                     '</button></td></tr>',
10359                 '</table>'
10360             );
10361             this.monthPicker.update(buf.join(''));
10362             this.monthPicker.on('click', this.onMonthClick, this);
10363             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
10364
10365             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
10366             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
10367
10368             this.mpMonths.each(function(m, a, i){
10369                 i += 1;
10370                 if((i%2) == 0){
10371                     m.dom.xmonth = 5 + Math.round(i * .5);
10372                 }else{
10373                     m.dom.xmonth = Math.round((i-1) * .5);
10374                 }
10375             });
10376         }
10377     },
10378
10379     showMonthPicker : function(){
10380         this.createMonthPicker();
10381         var size = this.el.getSize();
10382         this.monthPicker.setSize(size);
10383         this.monthPicker.child('table').setSize(size);
10384
10385         this.mpSelMonth = (this.activeDate || this.value).getMonth();
10386         this.updateMPMonth(this.mpSelMonth);
10387         this.mpSelYear = (this.activeDate || this.value).getFullYear();
10388         this.updateMPYear(this.mpSelYear);
10389
10390         this.monthPicker.slideIn('t', {duration:.2});
10391     },
10392
10393     updateMPYear : function(y){
10394         this.mpyear = y;
10395         var ys = this.mpYears.elements;
10396         for(var i = 1; i <= 10; i++){
10397             var td = ys[i-1], y2;
10398             if((i%2) == 0){
10399                 y2 = y + Math.round(i * .5);
10400                 td.firstChild.innerHTML = y2;
10401                 td.xyear = y2;
10402             }else{
10403                 y2 = y - (5-Math.round(i * .5));
10404                 td.firstChild.innerHTML = y2;
10405                 td.xyear = y2;
10406             }
10407             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
10408         }
10409     },
10410
10411     updateMPMonth : function(sm){
10412         this.mpMonths.each(function(m, a, i){
10413             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
10414         });
10415     },
10416
10417     selectMPMonth: function(m){
10418         
10419     },
10420
10421     onMonthClick : function(e, t){
10422         e.stopEvent();
10423         var el = new Roo.Element(t), pn;
10424         if(el.is('button.x-date-mp-cancel')){
10425             this.hideMonthPicker();
10426         }
10427         else if(el.is('button.x-date-mp-ok')){
10428             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10429             this.hideMonthPicker();
10430         }
10431         else if(pn = el.up('td.x-date-mp-month', 2)){
10432             this.mpMonths.removeClass('x-date-mp-sel');
10433             pn.addClass('x-date-mp-sel');
10434             this.mpSelMonth = pn.dom.xmonth;
10435         }
10436         else if(pn = el.up('td.x-date-mp-year', 2)){
10437             this.mpYears.removeClass('x-date-mp-sel');
10438             pn.addClass('x-date-mp-sel');
10439             this.mpSelYear = pn.dom.xyear;
10440         }
10441         else if(el.is('a.x-date-mp-prev')){
10442             this.updateMPYear(this.mpyear-10);
10443         }
10444         else if(el.is('a.x-date-mp-next')){
10445             this.updateMPYear(this.mpyear+10);
10446         }
10447     },
10448
10449     onMonthDblClick : function(e, t){
10450         e.stopEvent();
10451         var el = new Roo.Element(t), pn;
10452         if(pn = el.up('td.x-date-mp-month', 2)){
10453             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
10454             this.hideMonthPicker();
10455         }
10456         else if(pn = el.up('td.x-date-mp-year', 2)){
10457             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10458             this.hideMonthPicker();
10459         }
10460     },
10461
10462     hideMonthPicker : function(disableAnim){
10463         if(this.monthPicker){
10464             if(disableAnim === true){
10465                 this.monthPicker.hide();
10466             }else{
10467                 this.monthPicker.slideOut('t', {duration:.2});
10468             }
10469         }
10470     },
10471
10472     // private
10473     showPrevMonth : function(e){
10474         this.update(this.activeDate.add("mo", -1));
10475     },
10476
10477     // private
10478     showNextMonth : function(e){
10479         this.update(this.activeDate.add("mo", 1));
10480     },
10481
10482     // private
10483     showPrevYear : function(){
10484         this.update(this.activeDate.add("y", -1));
10485     },
10486
10487     // private
10488     showNextYear : function(){
10489         this.update(this.activeDate.add("y", 1));
10490     },
10491
10492     // private
10493     handleMouseWheel : function(e){
10494         var delta = e.getWheelDelta();
10495         if(delta > 0){
10496             this.showPrevMonth();
10497             e.stopEvent();
10498         } else if(delta < 0){
10499             this.showNextMonth();
10500             e.stopEvent();
10501         }
10502     },
10503
10504     // private
10505     handleDateClick : function(e, t){
10506         e.stopEvent();
10507         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
10508             this.setValue(new Date(t.dateValue));
10509             this.fireEvent("select", this, this.value);
10510         }
10511     },
10512
10513     // private
10514     selectToday : function(){
10515         this.setValue(new Date().clearTime());
10516         this.fireEvent("select", this, this.value);
10517     },
10518
10519     // private
10520     update : function(date){
10521         var vd = this.activeDate;
10522         this.activeDate = date;
10523         if(vd && this.el){
10524             var t = date.getTime();
10525             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10526                 this.cells.removeClass("x-date-selected");
10527                 this.cells.each(function(c){
10528                    if(c.dom.firstChild.dateValue == t){
10529                        c.addClass("x-date-selected");
10530                        setTimeout(function(){
10531                             try{c.dom.firstChild.focus();}catch(e){}
10532                        }, 50);
10533                        return false;
10534                    }
10535                 });
10536                 return;
10537             }
10538         }
10539         var days = date.getDaysInMonth();
10540         var firstOfMonth = date.getFirstDateOfMonth();
10541         var startingPos = firstOfMonth.getDay()-this.startDay;
10542
10543         if(startingPos <= this.startDay){
10544             startingPos += 7;
10545         }
10546
10547         var pm = date.add("mo", -1);
10548         var prevStart = pm.getDaysInMonth()-startingPos;
10549
10550         var cells = this.cells.elements;
10551         var textEls = this.textNodes;
10552         days += startingPos;
10553
10554         // convert everything to numbers so it's fast
10555         var day = 86400000;
10556         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10557         var today = new Date().clearTime().getTime();
10558         var sel = date.clearTime().getTime();
10559         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10560         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10561         var ddMatch = this.disabledDatesRE;
10562         var ddText = this.disabledDatesText;
10563         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10564         var ddaysText = this.disabledDaysText;
10565         var format = this.format;
10566
10567         var setCellClass = function(cal, cell){
10568             cell.title = "";
10569             var t = d.getTime();
10570             cell.firstChild.dateValue = t;
10571             if(t == today){
10572                 cell.className += " x-date-today";
10573                 cell.title = cal.todayText;
10574             }
10575             if(t == sel){
10576                 cell.className += " x-date-selected";
10577                 setTimeout(function(){
10578                     try{cell.firstChild.focus();}catch(e){}
10579                 }, 50);
10580             }
10581             // disabling
10582             if(t < min) {
10583                 cell.className = " x-date-disabled";
10584                 cell.title = cal.minText;
10585                 return;
10586             }
10587             if(t > max) {
10588                 cell.className = " x-date-disabled";
10589                 cell.title = cal.maxText;
10590                 return;
10591             }
10592             if(ddays){
10593                 if(ddays.indexOf(d.getDay()) != -1){
10594                     cell.title = ddaysText;
10595                     cell.className = " x-date-disabled";
10596                 }
10597             }
10598             if(ddMatch && format){
10599                 var fvalue = d.dateFormat(format);
10600                 if(ddMatch.test(fvalue)){
10601                     cell.title = ddText.replace("%0", fvalue);
10602                     cell.className = " x-date-disabled";
10603                 }
10604             }
10605         };
10606
10607         var i = 0;
10608         for(; i < startingPos; i++) {
10609             textEls[i].innerHTML = (++prevStart);
10610             d.setDate(d.getDate()+1);
10611             cells[i].className = "x-date-prevday";
10612             setCellClass(this, cells[i]);
10613         }
10614         for(; i < days; i++){
10615             intDay = i - startingPos + 1;
10616             textEls[i].innerHTML = (intDay);
10617             d.setDate(d.getDate()+1);
10618             cells[i].className = "x-date-active";
10619             setCellClass(this, cells[i]);
10620         }
10621         var extraDays = 0;
10622         for(; i < 42; i++) {
10623              textEls[i].innerHTML = (++extraDays);
10624              d.setDate(d.getDate()+1);
10625              cells[i].className = "x-date-nextday";
10626              setCellClass(this, cells[i]);
10627         }
10628
10629         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
10630
10631         if(!this.internalRender){
10632             var main = this.el.dom.firstChild;
10633             var w = main.offsetWidth;
10634             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10635             Roo.fly(main).setWidth(w);
10636             this.internalRender = true;
10637             // opera does not respect the auto grow header center column
10638             // then, after it gets a width opera refuses to recalculate
10639             // without a second pass
10640             if(Roo.isOpera && !this.secondPass){
10641                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10642                 this.secondPass = true;
10643                 this.update.defer(10, this, [date]);
10644             }
10645         }
10646     }
10647 });/*
10648  * Based on:
10649  * Ext JS Library 1.1.1
10650  * Copyright(c) 2006-2007, Ext JS, LLC.
10651  *
10652  * Originally Released Under LGPL - original licence link has changed is not relivant.
10653  *
10654  * Fork - LGPL
10655  * <script type="text/javascript">
10656  */
10657 /**
10658  * @class Roo.TabPanel
10659  * @extends Roo.util.Observable
10660  * A lightweight tab container.
10661  * <br><br>
10662  * Usage:
10663  * <pre><code>
10664 // basic tabs 1, built from existing content
10665 var tabs = new Roo.TabPanel("tabs1");
10666 tabs.addTab("script", "View Script");
10667 tabs.addTab("markup", "View Markup");
10668 tabs.activate("script");
10669
10670 // more advanced tabs, built from javascript
10671 var jtabs = new Roo.TabPanel("jtabs");
10672 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
10673
10674 // set up the UpdateManager
10675 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
10676 var updater = tab2.getUpdateManager();
10677 updater.setDefaultUrl("ajax1.htm");
10678 tab2.on('activate', updater.refresh, updater, true);
10679
10680 // Use setUrl for Ajax loading
10681 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
10682 tab3.setUrl("ajax2.htm", null, true);
10683
10684 // Disabled tab
10685 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
10686 tab4.disable();
10687
10688 jtabs.activate("jtabs-1");
10689  * </code></pre>
10690  * @constructor
10691  * Create a new TabPanel.
10692  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
10693  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
10694  */
10695 Roo.TabPanel = function(container, config){
10696     /**
10697     * The container element for this TabPanel.
10698     * @type Roo.Element
10699     */
10700     this.el = Roo.get(container, true);
10701     if(config){
10702         if(typeof config == "boolean"){
10703             this.tabPosition = config ? "bottom" : "top";
10704         }else{
10705             Roo.apply(this, config);
10706         }
10707     }
10708     if(this.tabPosition == "bottom"){
10709         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10710         this.el.addClass("x-tabs-bottom");
10711     }
10712     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
10713     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
10714     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
10715     if(Roo.isIE){
10716         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
10717     }
10718     if(this.tabPosition != "bottom"){
10719     /** The body element that contains {@link Roo.TabPanelItem} bodies.
10720      * @type Roo.Element
10721      */
10722       this.bodyEl = Roo.get(this.createBody(this.el.dom));
10723       this.el.addClass("x-tabs-top");
10724     }
10725     this.items = [];
10726
10727     this.bodyEl.setStyle("position", "relative");
10728
10729     this.active = null;
10730     this.activateDelegate = this.activate.createDelegate(this);
10731
10732     this.addEvents({
10733         /**
10734          * @event tabchange
10735          * Fires when the active tab changes
10736          * @param {Roo.TabPanel} this
10737          * @param {Roo.TabPanelItem} activePanel The new active tab
10738          */
10739         "tabchange": true,
10740         /**
10741          * @event beforetabchange
10742          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
10743          * @param {Roo.TabPanel} this
10744          * @param {Object} e Set cancel to true on this object to cancel the tab change
10745          * @param {Roo.TabPanelItem} tab The tab being changed to
10746          */
10747         "beforetabchange" : true
10748     });
10749
10750     Roo.EventManager.onWindowResize(this.onResize, this);
10751     this.cpad = this.el.getPadding("lr");
10752     this.hiddenCount = 0;
10753
10754     Roo.TabPanel.superclass.constructor.call(this);
10755 };
10756
10757 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
10758         /*
10759          *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
10760          */
10761     tabPosition : "top",
10762         /*
10763          *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
10764          */
10765     currentTabWidth : 0,
10766         /*
10767          *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
10768          */
10769     minTabWidth : 40,
10770         /*
10771          *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
10772          */
10773     maxTabWidth : 250,
10774         /*
10775          *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
10776          */
10777     preferredTabWidth : 175,
10778         /*
10779          *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
10780          */
10781     resizeTabs : false,
10782         /*
10783          *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
10784          */
10785     monitorResize : true,
10786
10787     /**
10788      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
10789      * @param {String} id The id of the div to use <b>or create</b>
10790      * @param {String} text The text for the tab
10791      * @param {String} content (optional) Content to put in the TabPanelItem body
10792      * @param {Boolean} closable (optional) True to create a close icon on the tab
10793      * @return {Roo.TabPanelItem} The created TabPanelItem
10794      */
10795     addTab : function(id, text, content, closable){
10796         var item = new Roo.TabPanelItem(this, id, text, closable);
10797         this.addTabItem(item);
10798         if(content){
10799             item.setContent(content);
10800         }
10801         return item;
10802     },
10803
10804     /**
10805      * Returns the {@link Roo.TabPanelItem} with the specified id/index
10806      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
10807      * @return {Roo.TabPanelItem}
10808      */
10809     getTab : function(id){
10810         return this.items[id];
10811     },
10812
10813     /**
10814      * Hides the {@link Roo.TabPanelItem} with the specified id/index
10815      * @param {String/Number} id The id or index of the TabPanelItem to hide.
10816      */
10817     hideTab : function(id){
10818         var t = this.items[id];
10819         if(!t.isHidden()){
10820            t.setHidden(true);
10821            this.hiddenCount++;
10822            this.autoSizeTabs();
10823         }
10824     },
10825
10826     /**
10827      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
10828      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
10829      */
10830     unhideTab : function(id){
10831         var t = this.items[id];
10832         if(t.isHidden()){
10833            t.setHidden(false);
10834            this.hiddenCount--;
10835            this.autoSizeTabs();
10836         }
10837     },
10838
10839     /**
10840      * Adds an existing {@link Roo.TabPanelItem}.
10841      * @param {Roo.TabPanelItem} item The TabPanelItem to add
10842      */
10843     addTabItem : function(item){
10844         this.items[item.id] = item;
10845         this.items.push(item);
10846         if(this.resizeTabs){
10847            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
10848            this.autoSizeTabs();
10849         }else{
10850             item.autoSize();
10851         }
10852     },
10853
10854     /**
10855      * Removes a {@link Roo.TabPanelItem}.
10856      * @param {String/Number} id The id or index of the TabPanelItem to remove.
10857      */
10858     removeTab : function(id){
10859         var items = this.items;
10860         var tab = items[id];
10861         if(!tab) { return; }
10862         var index = items.indexOf(tab);
10863         if(this.active == tab && items.length > 1){
10864             var newTab = this.getNextAvailable(index);
10865             if(newTab) {
10866                 newTab.activate();
10867             }
10868         }
10869         this.stripEl.dom.removeChild(tab.pnode.dom);
10870         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
10871             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
10872         }
10873         items.splice(index, 1);
10874         delete this.items[tab.id];
10875         tab.fireEvent("close", tab);
10876         tab.purgeListeners();
10877         this.autoSizeTabs();
10878     },
10879
10880     getNextAvailable : function(start){
10881         var items = this.items;
10882         var index = start;
10883         // look for a next tab that will slide over to
10884         // replace the one being removed
10885         while(index < items.length){
10886             var item = items[++index];
10887             if(item && !item.isHidden()){
10888                 return item;
10889             }
10890         }
10891         // if one isn't found select the previous tab (on the left)
10892         index = start;
10893         while(index >= 0){
10894             var item = items[--index];
10895             if(item && !item.isHidden()){
10896                 return item;
10897             }
10898         }
10899         return null;
10900     },
10901
10902     /**
10903      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
10904      * @param {String/Number} id The id or index of the TabPanelItem to disable.
10905      */
10906     disableTab : function(id){
10907         var tab = this.items[id];
10908         if(tab && this.active != tab){
10909             tab.disable();
10910         }
10911     },
10912
10913     /**
10914      * Enables a {@link Roo.TabPanelItem} that is disabled.
10915      * @param {String/Number} id The id or index of the TabPanelItem to enable.
10916      */
10917     enableTab : function(id){
10918         var tab = this.items[id];
10919         tab.enable();
10920     },
10921
10922     /**
10923      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
10924      * @param {String/Number} id The id or index of the TabPanelItem to activate.
10925      * @return {Roo.TabPanelItem} The TabPanelItem.
10926      */
10927     activate : function(id){
10928         var tab = this.items[id];
10929         if(!tab){
10930             return null;
10931         }
10932         if(tab == this.active || tab.disabled){
10933             return tab;
10934         }
10935         var e = {};
10936         this.fireEvent("beforetabchange", this, e, tab);
10937         if(e.cancel !== true && !tab.disabled){
10938             if(this.active){
10939                 this.active.hide();
10940             }
10941             this.active = this.items[id];
10942             this.active.show();
10943             this.fireEvent("tabchange", this, this.active);
10944         }
10945         return tab;
10946     },
10947
10948     /**
10949      * Gets the active {@link Roo.TabPanelItem}.
10950      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
10951      */
10952     getActiveTab : function(){
10953         return this.active;
10954     },
10955
10956     /**
10957      * Updates the tab body element to fit the height of the container element
10958      * for overflow scrolling
10959      * @param {Number} targetHeight (optional) Override the starting height from the elements height
10960      */
10961     syncHeight : function(targetHeight){
10962         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
10963         var bm = this.bodyEl.getMargins();
10964         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
10965         this.bodyEl.setHeight(newHeight);
10966         return newHeight;
10967     },
10968
10969     onResize : function(){
10970         if(this.monitorResize){
10971             this.autoSizeTabs();
10972         }
10973     },
10974
10975     /**
10976      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
10977      */
10978     beginUpdate : function(){
10979         this.updating = true;
10980     },
10981
10982     /**
10983      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
10984      */
10985     endUpdate : function(){
10986         this.updating = false;
10987         this.autoSizeTabs();
10988     },
10989
10990     /**
10991      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
10992      */
10993     autoSizeTabs : function(){
10994         var count = this.items.length;
10995         var vcount = count - this.hiddenCount;
10996         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
10997         var w = Math.max(this.el.getWidth() - this.cpad, 10);
10998         var availWidth = Math.floor(w / vcount);
10999         var b = this.stripBody;
11000         if(b.getWidth() > w){
11001             var tabs = this.items;
11002             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
11003             if(availWidth < this.minTabWidth){
11004                 /*if(!this.sleft){    // incomplete scrolling code
11005                     this.createScrollButtons();
11006                 }
11007                 this.showScroll();
11008                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
11009             }
11010         }else{
11011             if(this.currentTabWidth < this.preferredTabWidth){
11012                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
11013             }
11014         }
11015     },
11016
11017     /**
11018      * Returns the number of tabs in this TabPanel.
11019      * @return {Number}
11020      */
11021      getCount : function(){
11022          return this.items.length;
11023      },
11024
11025     /**
11026      * Resizes all the tabs to the passed width
11027      * @param {Number} The new width
11028      */
11029     setTabWidth : function(width){
11030         this.currentTabWidth = width;
11031         for(var i = 0, len = this.items.length; i < len; i++) {
11032                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
11033         }
11034     },
11035
11036     /**
11037      * Destroys this TabPanel
11038      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
11039      */
11040     destroy : function(removeEl){
11041         Roo.EventManager.removeResizeListener(this.onResize, this);
11042         for(var i = 0, len = this.items.length; i < len; i++){
11043             this.items[i].purgeListeners();
11044         }
11045         if(removeEl === true){
11046             this.el.update("");
11047             this.el.remove();
11048         }
11049     }
11050 });
11051
11052 /**
11053  * @class Roo.TabPanelItem
11054  * @extends Roo.util.Observable
11055  * Represents an individual item (tab plus body) in a TabPanel.
11056  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
11057  * @param {String} id The id of this TabPanelItem
11058  * @param {String} text The text for the tab of this TabPanelItem
11059  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
11060  */
11061 Roo.TabPanelItem = function(tabPanel, id, text, closable){
11062     /**
11063      * The {@link Roo.TabPanel} this TabPanelItem belongs to
11064      * @type Roo.TabPanel
11065      */
11066     this.tabPanel = tabPanel;
11067     /**
11068      * The id for this TabPanelItem
11069      * @type String
11070      */
11071     this.id = id;
11072     /** @private */
11073     this.disabled = false;
11074     /** @private */
11075     this.text = text;
11076     /** @private */
11077     this.loaded = false;
11078     this.closable = closable;
11079
11080     /**
11081      * The body element for this TabPanelItem.
11082      * @type Roo.Element
11083      */
11084     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
11085     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
11086     this.bodyEl.setStyle("display", "block");
11087     this.bodyEl.setStyle("zoom", "1");
11088     this.hideAction();
11089
11090     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
11091     /** @private */
11092     this.el = Roo.get(els.el, true);
11093     this.inner = Roo.get(els.inner, true);
11094     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
11095     this.pnode = Roo.get(els.el.parentNode, true);
11096     this.el.on("mousedown", this.onTabMouseDown, this);
11097     this.el.on("click", this.onTabClick, this);
11098     /** @private */
11099     if(closable){
11100         var c = Roo.get(els.close, true);
11101         c.dom.title = this.closeText;
11102         c.addClassOnOver("close-over");
11103         c.on("click", this.closeClick, this);
11104      }
11105
11106     this.addEvents({
11107          /**
11108          * @event activate
11109          * Fires when this tab becomes the active tab.
11110          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11111          * @param {Roo.TabPanelItem} this
11112          */
11113         "activate": true,
11114         /**
11115          * @event beforeclose
11116          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
11117          * @param {Roo.TabPanelItem} this
11118          * @param {Object} e Set cancel to true on this object to cancel the close.
11119          */
11120         "beforeclose": true,
11121         /**
11122          * @event close
11123          * Fires when this tab is closed.
11124          * @param {Roo.TabPanelItem} this
11125          */
11126          "close": true,
11127         /**
11128          * @event deactivate
11129          * Fires when this tab is no longer the active tab.
11130          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11131          * @param {Roo.TabPanelItem} this
11132          */
11133          "deactivate" : true
11134     });
11135     this.hidden = false;
11136
11137     Roo.TabPanelItem.superclass.constructor.call(this);
11138 };
11139
11140 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
11141     purgeListeners : function(){
11142        Roo.util.Observable.prototype.purgeListeners.call(this);
11143        this.el.removeAllListeners();
11144     },
11145     /**
11146      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
11147      */
11148     show : function(){
11149         this.pnode.addClass("on");
11150         this.showAction();
11151         if(Roo.isOpera){
11152             this.tabPanel.stripWrap.repaint();
11153         }
11154         this.fireEvent("activate", this.tabPanel, this);
11155     },
11156
11157     /**
11158      * Returns true if this tab is the active tab.
11159      * @return {Boolean}
11160      */
11161     isActive : function(){
11162         return this.tabPanel.getActiveTab() == this;
11163     },
11164
11165     /**
11166      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
11167      */
11168     hide : function(){
11169         this.pnode.removeClass("on");
11170         this.hideAction();
11171         this.fireEvent("deactivate", this.tabPanel, this);
11172     },
11173
11174     hideAction : function(){
11175         this.bodyEl.hide();
11176         this.bodyEl.setStyle("position", "absolute");
11177         this.bodyEl.setLeft("-20000px");
11178         this.bodyEl.setTop("-20000px");
11179     },
11180
11181     showAction : function(){
11182         this.bodyEl.setStyle("position", "relative");
11183         this.bodyEl.setTop("");
11184         this.bodyEl.setLeft("");
11185         this.bodyEl.show();
11186     },
11187
11188     /**
11189      * Set the tooltip for the tab.
11190      * @param {String} tooltip The tab's tooltip
11191      */
11192     setTooltip : function(text){
11193         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
11194             this.textEl.dom.qtip = text;
11195             this.textEl.dom.removeAttribute('title');
11196         }else{
11197             this.textEl.dom.title = text;
11198         }
11199     },
11200
11201     onTabClick : function(e){
11202         e.preventDefault();
11203         this.tabPanel.activate(this.id);
11204     },
11205
11206     onTabMouseDown : function(e){
11207         e.preventDefault();
11208         this.tabPanel.activate(this.id);
11209     },
11210
11211     getWidth : function(){
11212         return this.inner.getWidth();
11213     },
11214
11215     setWidth : function(width){
11216         var iwidth = width - this.pnode.getPadding("lr");
11217         this.inner.setWidth(iwidth);
11218         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
11219         this.pnode.setWidth(width);
11220     },
11221
11222     /**
11223      * Show or hide the tab
11224      * @param {Boolean} hidden True to hide or false to show.
11225      */
11226     setHidden : function(hidden){
11227         this.hidden = hidden;
11228         this.pnode.setStyle("display", hidden ? "none" : "");
11229     },
11230
11231     /**
11232      * Returns true if this tab is "hidden"
11233      * @return {Boolean}
11234      */
11235     isHidden : function(){
11236         return this.hidden;
11237     },
11238
11239     /**
11240      * Returns the text for this tab
11241      * @return {String}
11242      */
11243     getText : function(){
11244         return this.text;
11245     },
11246
11247     autoSize : function(){
11248         //this.el.beginMeasure();
11249         this.textEl.setWidth(1);
11250         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
11251         //this.el.endMeasure();
11252     },
11253
11254     /**
11255      * Sets the text for the tab (Note: this also sets the tooltip text)
11256      * @param {String} text The tab's text and tooltip
11257      */
11258     setText : function(text){
11259         this.text = text;
11260         this.textEl.update(text);
11261         this.setTooltip(text);
11262         if(!this.tabPanel.resizeTabs){
11263             this.autoSize();
11264         }
11265     },
11266     /**
11267      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
11268      */
11269     activate : function(){
11270         this.tabPanel.activate(this.id);
11271     },
11272
11273     /**
11274      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
11275      */
11276     disable : function(){
11277         if(this.tabPanel.active != this){
11278             this.disabled = true;
11279             this.pnode.addClass("disabled");
11280         }
11281     },
11282
11283     /**
11284      * Enables this TabPanelItem if it was previously disabled.
11285      */
11286     enable : function(){
11287         this.disabled = false;
11288         this.pnode.removeClass("disabled");
11289     },
11290
11291     /**
11292      * Sets the content for this TabPanelItem.
11293      * @param {String} content The content
11294      * @param {Boolean} loadScripts true to look for and load scripts
11295      */
11296     setContent : function(content, loadScripts){
11297         this.bodyEl.update(content, loadScripts);
11298     },
11299
11300     /**
11301      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
11302      * @return {Roo.UpdateManager} The UpdateManager
11303      */
11304     getUpdateManager : function(){
11305         return this.bodyEl.getUpdateManager();
11306     },
11307
11308     /**
11309      * Set a URL to be used to load the content for this TabPanelItem.
11310      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
11311      * @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)
11312      * @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)
11313      * @return {Roo.UpdateManager} The UpdateManager
11314      */
11315     setUrl : function(url, params, loadOnce){
11316         if(this.refreshDelegate){
11317             this.un('activate', this.refreshDelegate);
11318         }
11319         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
11320         this.on("activate", this.refreshDelegate);
11321         return this.bodyEl.getUpdateManager();
11322     },
11323
11324     /** @private */
11325     _handleRefresh : function(url, params, loadOnce){
11326         if(!loadOnce || !this.loaded){
11327             var updater = this.bodyEl.getUpdateManager();
11328             updater.update(url, params, this._setLoaded.createDelegate(this));
11329         }
11330     },
11331
11332     /**
11333      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
11334      *   Will fail silently if the setUrl method has not been called.
11335      *   This does not activate the panel, just updates its content.
11336      */
11337     refresh : function(){
11338         if(this.refreshDelegate){
11339            this.loaded = false;
11340            this.refreshDelegate();
11341         }
11342     },
11343
11344     /** @private */
11345     _setLoaded : function(){
11346         this.loaded = true;
11347     },
11348
11349     /** @private */
11350     closeClick : function(e){
11351         var o = {};
11352         e.stopEvent();
11353         this.fireEvent("beforeclose", this, o);
11354         if(o.cancel !== true){
11355             this.tabPanel.removeTab(this.id);
11356         }
11357     },
11358     /**
11359      * The text displayed in the tooltip for the close icon.
11360      * @type String
11361      */
11362     closeText : "Close this tab"
11363 });
11364
11365 /** @private */
11366 Roo.TabPanel.prototype.createStrip = function(container){
11367     var strip = document.createElement("div");
11368     strip.className = "x-tabs-wrap";
11369     container.appendChild(strip);
11370     return strip;
11371 };
11372 /** @private */
11373 Roo.TabPanel.prototype.createStripList = function(strip){
11374     // div wrapper for retard IE
11375     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>';
11376     return strip.firstChild.firstChild.firstChild.firstChild;
11377 };
11378 /** @private */
11379 Roo.TabPanel.prototype.createBody = function(container){
11380     var body = document.createElement("div");
11381     Roo.id(body, "tab-body");
11382     Roo.fly(body).addClass("x-tabs-body");
11383     container.appendChild(body);
11384     return body;
11385 };
11386 /** @private */
11387 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
11388     var body = Roo.getDom(id);
11389     if(!body){
11390         body = document.createElement("div");
11391         body.id = id;
11392     }
11393     Roo.fly(body).addClass("x-tabs-item-body");
11394     bodyEl.insertBefore(body, bodyEl.firstChild);
11395     return body;
11396 };
11397 /** @private */
11398 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
11399     var td = document.createElement("td");
11400     stripEl.appendChild(td);
11401     if(closable){
11402         td.className = "x-tabs-closable";
11403         if(!this.closeTpl){
11404             this.closeTpl = new Roo.Template(
11405                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11406                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
11407                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
11408             );
11409         }
11410         var el = this.closeTpl.overwrite(td, {"text": text});
11411         var close = el.getElementsByTagName("div")[0];
11412         var inner = el.getElementsByTagName("em")[0];
11413         return {"el": el, "close": close, "inner": inner};
11414     } else {
11415         if(!this.tabTpl){
11416             this.tabTpl = new Roo.Template(
11417                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11418                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
11419             );
11420         }
11421         var el = this.tabTpl.overwrite(td, {"text": text});
11422         var inner = el.getElementsByTagName("em")[0];
11423         return {"el": el, "inner": inner};
11424     }
11425 };/*
11426  * Based on:
11427  * Ext JS Library 1.1.1
11428  * Copyright(c) 2006-2007, Ext JS, LLC.
11429  *
11430  * Originally Released Under LGPL - original licence link has changed is not relivant.
11431  *
11432  * Fork - LGPL
11433  * <script type="text/javascript">
11434  */
11435
11436 /**
11437  * @class Roo.Button
11438  * @extends Roo.util.Observable
11439  * Simple Button class
11440  * @cfg {String} text The button text
11441  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
11442  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
11443  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
11444  * @cfg {Object} scope The scope of the handler
11445  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
11446  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
11447  * @cfg {Boolean} hidden True to start hidden (defaults to false)
11448  * @cfg {Boolean} disabled True to start disabled (defaults to false)
11449  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
11450  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
11451    applies if enableToggle = true)
11452  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
11453  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
11454   an {@link Roo.util.ClickRepeater} config object (defaults to false).
11455  * @constructor
11456  * Create a new button
11457  * @param {Object} config The config object
11458  */
11459 Roo.Button = function(renderTo, config)
11460 {
11461     if (!config) {
11462         config = renderTo;
11463         renderTo = config.renderTo || false;
11464     }
11465     
11466     Roo.apply(this, config);
11467     this.addEvents({
11468         /**
11469              * @event click
11470              * Fires when this button is clicked
11471              * @param {Button} this
11472              * @param {EventObject} e The click event
11473              */
11474             "click" : true,
11475         /**
11476              * @event toggle
11477              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
11478              * @param {Button} this
11479              * @param {Boolean} pressed
11480              */
11481             "toggle" : true,
11482         /**
11483              * @event mouseover
11484              * Fires when the mouse hovers over the button
11485              * @param {Button} this
11486              * @param {Event} e The event object
11487              */
11488         'mouseover' : true,
11489         /**
11490              * @event mouseout
11491              * Fires when the mouse exits the button
11492              * @param {Button} this
11493              * @param {Event} e The event object
11494              */
11495         'mouseout': true,
11496          /**
11497              * @event render
11498              * Fires when the button is rendered
11499              * @param {Button} this
11500              */
11501         'render': true
11502     });
11503     if(this.menu){
11504         this.menu = Roo.menu.MenuMgr.get(this.menu);
11505     }
11506     // register listeners first!!  - so render can be captured..
11507     Roo.util.Observable.call(this);
11508     if(renderTo){
11509         this.render(renderTo);
11510     }
11511     
11512   
11513 };
11514
11515 Roo.extend(Roo.Button, Roo.util.Observable, {
11516     /**
11517      * 
11518      */
11519     
11520     /**
11521      * Read-only. True if this button is hidden
11522      * @type Boolean
11523      */
11524     hidden : false,
11525     /**
11526      * Read-only. True if this button is disabled
11527      * @type Boolean
11528      */
11529     disabled : false,
11530     /**
11531      * Read-only. True if this button is pressed (only if enableToggle = true)
11532      * @type Boolean
11533      */
11534     pressed : false,
11535
11536     /**
11537      * @cfg {Number} tabIndex 
11538      * The DOM tabIndex for this button (defaults to undefined)
11539      */
11540     tabIndex : undefined,
11541
11542     /**
11543      * @cfg {Boolean} enableToggle
11544      * True to enable pressed/not pressed toggling (defaults to false)
11545      */
11546     enableToggle: false,
11547     /**
11548      * @cfg {Mixed} menu
11549      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
11550      */
11551     menu : undefined,
11552     /**
11553      * @cfg {String} menuAlign
11554      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
11555      */
11556     menuAlign : "tl-bl?",
11557
11558     /**
11559      * @cfg {String} iconCls
11560      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
11561      */
11562     iconCls : undefined,
11563     /**
11564      * @cfg {String} type
11565      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
11566      */
11567     type : 'button',
11568
11569     // private
11570     menuClassTarget: 'tr',
11571
11572     /**
11573      * @cfg {String} clickEvent
11574      * The type of event to map to the button's event handler (defaults to 'click')
11575      */
11576     clickEvent : 'click',
11577
11578     /**
11579      * @cfg {Boolean} handleMouseEvents
11580      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
11581      */
11582     handleMouseEvents : true,
11583
11584     /**
11585      * @cfg {String} tooltipType
11586      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
11587      */
11588     tooltipType : 'qtip',
11589
11590     /**
11591      * @cfg {String} cls
11592      * A CSS class to apply to the button's main element.
11593      */
11594     
11595     /**
11596      * @cfg {Roo.Template} template (Optional)
11597      * An {@link Roo.Template} with which to create the Button's main element. This Template must
11598      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
11599      * require code modifications if required elements (e.g. a button) aren't present.
11600      */
11601
11602     // private
11603     render : function(renderTo){
11604         var btn;
11605         if(this.hideParent){
11606             this.parentEl = Roo.get(renderTo);
11607         }
11608         if(!this.dhconfig){
11609             if(!this.template){
11610                 if(!Roo.Button.buttonTemplate){
11611                     // hideous table template
11612                     Roo.Button.buttonTemplate = new Roo.Template(
11613                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
11614                         '<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>',
11615                         "</tr></tbody></table>");
11616                 }
11617                 this.template = Roo.Button.buttonTemplate;
11618             }
11619             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
11620             var btnEl = btn.child("button:first");
11621             btnEl.on('focus', this.onFocus, this);
11622             btnEl.on('blur', this.onBlur, this);
11623             if(this.cls){
11624                 btn.addClass(this.cls);
11625             }
11626             if(this.icon){
11627                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
11628             }
11629             if(this.iconCls){
11630                 btnEl.addClass(this.iconCls);
11631                 if(!this.cls){
11632                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11633                 }
11634             }
11635             if(this.tabIndex !== undefined){
11636                 btnEl.dom.tabIndex = this.tabIndex;
11637             }
11638             if(this.tooltip){
11639                 if(typeof this.tooltip == 'object'){
11640                     Roo.QuickTips.tips(Roo.apply({
11641                           target: btnEl.id
11642                     }, this.tooltip));
11643                 } else {
11644                     btnEl.dom[this.tooltipType] = this.tooltip;
11645                 }
11646             }
11647         }else{
11648             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
11649         }
11650         this.el = btn;
11651         if(this.id){
11652             this.el.dom.id = this.el.id = this.id;
11653         }
11654         if(this.menu){
11655             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
11656             this.menu.on("show", this.onMenuShow, this);
11657             this.menu.on("hide", this.onMenuHide, this);
11658         }
11659         btn.addClass("x-btn");
11660         if(Roo.isIE && !Roo.isIE7){
11661             this.autoWidth.defer(1, this);
11662         }else{
11663             this.autoWidth();
11664         }
11665         if(this.handleMouseEvents){
11666             btn.on("mouseover", this.onMouseOver, this);
11667             btn.on("mouseout", this.onMouseOut, this);
11668             btn.on("mousedown", this.onMouseDown, this);
11669         }
11670         btn.on(this.clickEvent, this.onClick, this);
11671         //btn.on("mouseup", this.onMouseUp, this);
11672         if(this.hidden){
11673             this.hide();
11674         }
11675         if(this.disabled){
11676             this.disable();
11677         }
11678         Roo.ButtonToggleMgr.register(this);
11679         if(this.pressed){
11680             this.el.addClass("x-btn-pressed");
11681         }
11682         if(this.repeat){
11683             var repeater = new Roo.util.ClickRepeater(btn,
11684                 typeof this.repeat == "object" ? this.repeat : {}
11685             );
11686             repeater.on("click", this.onClick,  this);
11687         }
11688         
11689         this.fireEvent('render', this);
11690         
11691     },
11692     /**
11693      * Returns the button's underlying element
11694      * @return {Roo.Element} The element
11695      */
11696     getEl : function(){
11697         return this.el;  
11698     },
11699     
11700     /**
11701      * Destroys this Button and removes any listeners.
11702      */
11703     destroy : function(){
11704         Roo.ButtonToggleMgr.unregister(this);
11705         this.el.removeAllListeners();
11706         this.purgeListeners();
11707         this.el.remove();
11708     },
11709
11710     // private
11711     autoWidth : function(){
11712         if(this.el){
11713             this.el.setWidth("auto");
11714             if(Roo.isIE7 && Roo.isStrict){
11715                 var ib = this.el.child('button');
11716                 if(ib && ib.getWidth() > 20){
11717                     ib.clip();
11718                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11719                 }
11720             }
11721             if(this.minWidth){
11722                 if(this.hidden){
11723                     this.el.beginMeasure();
11724                 }
11725                 if(this.el.getWidth() < this.minWidth){
11726                     this.el.setWidth(this.minWidth);
11727                 }
11728                 if(this.hidden){
11729                     this.el.endMeasure();
11730                 }
11731             }
11732         }
11733     },
11734
11735     /**
11736      * Assigns this button's click handler
11737      * @param {Function} handler The function to call when the button is clicked
11738      * @param {Object} scope (optional) Scope for the function passed in
11739      */
11740     setHandler : function(handler, scope){
11741         this.handler = handler;
11742         this.scope = scope;  
11743     },
11744     
11745     /**
11746      * Sets this button's text
11747      * @param {String} text The button text
11748      */
11749     setText : function(text){
11750         this.text = text;
11751         if(this.el){
11752             this.el.child("td.x-btn-center button.x-btn-text").update(text);
11753         }
11754         this.autoWidth();
11755     },
11756     
11757     /**
11758      * Gets the text for this button
11759      * @return {String} The button text
11760      */
11761     getText : function(){
11762         return this.text;  
11763     },
11764     
11765     /**
11766      * Show this button
11767      */
11768     show: function(){
11769         this.hidden = false;
11770         if(this.el){
11771             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
11772         }
11773     },
11774     
11775     /**
11776      * Hide this button
11777      */
11778     hide: function(){
11779         this.hidden = true;
11780         if(this.el){
11781             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
11782         }
11783     },
11784     
11785     /**
11786      * Convenience function for boolean show/hide
11787      * @param {Boolean} visible True to show, false to hide
11788      */
11789     setVisible: function(visible){
11790         if(visible) {
11791             this.show();
11792         }else{
11793             this.hide();
11794         }
11795     },
11796     
11797     /**
11798      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
11799      * @param {Boolean} state (optional) Force a particular state
11800      */
11801     toggle : function(state){
11802         state = state === undefined ? !this.pressed : state;
11803         if(state != this.pressed){
11804             if(state){
11805                 this.el.addClass("x-btn-pressed");
11806                 this.pressed = true;
11807                 this.fireEvent("toggle", this, true);
11808             }else{
11809                 this.el.removeClass("x-btn-pressed");
11810                 this.pressed = false;
11811                 this.fireEvent("toggle", this, false);
11812             }
11813             if(this.toggleHandler){
11814                 this.toggleHandler.call(this.scope || this, this, state);
11815             }
11816         }
11817     },
11818     
11819     /**
11820      * Focus the button
11821      */
11822     focus : function(){
11823         this.el.child('button:first').focus();
11824     },
11825     
11826     /**
11827      * Disable this button
11828      */
11829     disable : function(){
11830         if(this.el){
11831             this.el.addClass("x-btn-disabled");
11832         }
11833         this.disabled = true;
11834     },
11835     
11836     /**
11837      * Enable this button
11838      */
11839     enable : function(){
11840         if(this.el){
11841             this.el.removeClass("x-btn-disabled");
11842         }
11843         this.disabled = false;
11844     },
11845
11846     /**
11847      * Convenience function for boolean enable/disable
11848      * @param {Boolean} enabled True to enable, false to disable
11849      */
11850     setDisabled : function(v){
11851         this[v !== true ? "enable" : "disable"]();
11852     },
11853
11854     // private
11855     onClick : function(e){
11856         if(e){
11857             e.preventDefault();
11858         }
11859         if(e.button != 0){
11860             return;
11861         }
11862         if(!this.disabled){
11863             if(this.enableToggle){
11864                 this.toggle();
11865             }
11866             if(this.menu && !this.menu.isVisible()){
11867                 this.menu.show(this.el, this.menuAlign);
11868             }
11869             this.fireEvent("click", this, e);
11870             if(this.handler){
11871                 this.el.removeClass("x-btn-over");
11872                 this.handler.call(this.scope || this, this, e);
11873             }
11874         }
11875     },
11876     // private
11877     onMouseOver : function(e){
11878         if(!this.disabled){
11879             this.el.addClass("x-btn-over");
11880             this.fireEvent('mouseover', this, e);
11881         }
11882     },
11883     // private
11884     onMouseOut : function(e){
11885         if(!e.within(this.el,  true)){
11886             this.el.removeClass("x-btn-over");
11887             this.fireEvent('mouseout', this, e);
11888         }
11889     },
11890     // private
11891     onFocus : function(e){
11892         if(!this.disabled){
11893             this.el.addClass("x-btn-focus");
11894         }
11895     },
11896     // private
11897     onBlur : function(e){
11898         this.el.removeClass("x-btn-focus");
11899     },
11900     // private
11901     onMouseDown : function(e){
11902         if(!this.disabled && e.button == 0){
11903             this.el.addClass("x-btn-click");
11904             Roo.get(document).on('mouseup', this.onMouseUp, this);
11905         }
11906     },
11907     // private
11908     onMouseUp : function(e){
11909         if(e.button == 0){
11910             this.el.removeClass("x-btn-click");
11911             Roo.get(document).un('mouseup', this.onMouseUp, this);
11912         }
11913     },
11914     // private
11915     onMenuShow : function(e){
11916         this.el.addClass("x-btn-menu-active");
11917     },
11918     // private
11919     onMenuHide : function(e){
11920         this.el.removeClass("x-btn-menu-active");
11921     }   
11922 });
11923
11924 // Private utility class used by Button
11925 Roo.ButtonToggleMgr = function(){
11926    var groups = {};
11927    
11928    function toggleGroup(btn, state){
11929        if(state){
11930            var g = groups[btn.toggleGroup];
11931            for(var i = 0, l = g.length; i < l; i++){
11932                if(g[i] != btn){
11933                    g[i].toggle(false);
11934                }
11935            }
11936        }
11937    }
11938    
11939    return {
11940        register : function(btn){
11941            if(!btn.toggleGroup){
11942                return;
11943            }
11944            var g = groups[btn.toggleGroup];
11945            if(!g){
11946                g = groups[btn.toggleGroup] = [];
11947            }
11948            g.push(btn);
11949            btn.on("toggle", toggleGroup);
11950        },
11951        
11952        unregister : function(btn){
11953            if(!btn.toggleGroup){
11954                return;
11955            }
11956            var g = groups[btn.toggleGroup];
11957            if(g){
11958                g.remove(btn);
11959                btn.un("toggle", toggleGroup);
11960            }
11961        }
11962    };
11963 }();/*
11964  * Based on:
11965  * Ext JS Library 1.1.1
11966  * Copyright(c) 2006-2007, Ext JS, LLC.
11967  *
11968  * Originally Released Under LGPL - original licence link has changed is not relivant.
11969  *
11970  * Fork - LGPL
11971  * <script type="text/javascript">
11972  */
11973  
11974 /**
11975  * @class Roo.SplitButton
11976  * @extends Roo.Button
11977  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
11978  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
11979  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
11980  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
11981  * @cfg {String} arrowTooltip The title attribute of the arrow
11982  * @constructor
11983  * Create a new menu button
11984  * @param {String/HTMLElement/Element} renderTo The element to append the button to
11985  * @param {Object} config The config object
11986  */
11987 Roo.SplitButton = function(renderTo, config){
11988     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
11989     /**
11990      * @event arrowclick
11991      * Fires when this button's arrow is clicked
11992      * @param {SplitButton} this
11993      * @param {EventObject} e The click event
11994      */
11995     this.addEvents({"arrowclick":true});
11996 };
11997
11998 Roo.extend(Roo.SplitButton, Roo.Button, {
11999     render : function(renderTo){
12000         // this is one sweet looking template!
12001         var tpl = new Roo.Template(
12002             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
12003             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
12004             '<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>',
12005             "</tbody></table></td><td>",
12006             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
12007             '<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>',
12008             "</tbody></table></td></tr></table>"
12009         );
12010         var btn = tpl.append(renderTo, [this.text, this.type], true);
12011         var btnEl = btn.child("button");
12012         if(this.cls){
12013             btn.addClass(this.cls);
12014         }
12015         if(this.icon){
12016             btnEl.setStyle('background-image', 'url(' +this.icon +')');
12017         }
12018         if(this.iconCls){
12019             btnEl.addClass(this.iconCls);
12020             if(!this.cls){
12021                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
12022             }
12023         }
12024         this.el = btn;
12025         if(this.handleMouseEvents){
12026             btn.on("mouseover", this.onMouseOver, this);
12027             btn.on("mouseout", this.onMouseOut, this);
12028             btn.on("mousedown", this.onMouseDown, this);
12029             btn.on("mouseup", this.onMouseUp, this);
12030         }
12031         btn.on(this.clickEvent, this.onClick, this);
12032         if(this.tooltip){
12033             if(typeof this.tooltip == 'object'){
12034                 Roo.QuickTips.tips(Roo.apply({
12035                       target: btnEl.id
12036                 }, this.tooltip));
12037             } else {
12038                 btnEl.dom[this.tooltipType] = this.tooltip;
12039             }
12040         }
12041         if(this.arrowTooltip){
12042             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
12043         }
12044         if(this.hidden){
12045             this.hide();
12046         }
12047         if(this.disabled){
12048             this.disable();
12049         }
12050         if(this.pressed){
12051             this.el.addClass("x-btn-pressed");
12052         }
12053         if(Roo.isIE && !Roo.isIE7){
12054             this.autoWidth.defer(1, this);
12055         }else{
12056             this.autoWidth();
12057         }
12058         if(this.menu){
12059             this.menu.on("show", this.onMenuShow, this);
12060             this.menu.on("hide", this.onMenuHide, this);
12061         }
12062         this.fireEvent('render', this);
12063     },
12064
12065     // private
12066     autoWidth : function(){
12067         if(this.el){
12068             var tbl = this.el.child("table:first");
12069             var tbl2 = this.el.child("table:last");
12070             this.el.setWidth("auto");
12071             tbl.setWidth("auto");
12072             if(Roo.isIE7 && Roo.isStrict){
12073                 var ib = this.el.child('button:first');
12074                 if(ib && ib.getWidth() > 20){
12075                     ib.clip();
12076                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
12077                 }
12078             }
12079             if(this.minWidth){
12080                 if(this.hidden){
12081                     this.el.beginMeasure();
12082                 }
12083                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
12084                     tbl.setWidth(this.minWidth-tbl2.getWidth());
12085                 }
12086                 if(this.hidden){
12087                     this.el.endMeasure();
12088                 }
12089             }
12090             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
12091         } 
12092     },
12093     /**
12094      * Sets this button's click handler
12095      * @param {Function} handler The function to call when the button is clicked
12096      * @param {Object} scope (optional) Scope for the function passed above
12097      */
12098     setHandler : function(handler, scope){
12099         this.handler = handler;
12100         this.scope = scope;  
12101     },
12102     
12103     /**
12104      * Sets this button's arrow click handler
12105      * @param {Function} handler The function to call when the arrow is clicked
12106      * @param {Object} scope (optional) Scope for the function passed above
12107      */
12108     setArrowHandler : function(handler, scope){
12109         this.arrowHandler = handler;
12110         this.scope = scope;  
12111     },
12112     
12113     /**
12114      * Focus the button
12115      */
12116     focus : function(){
12117         if(this.el){
12118             this.el.child("button:first").focus();
12119         }
12120     },
12121
12122     // private
12123     onClick : function(e){
12124         e.preventDefault();
12125         if(!this.disabled){
12126             if(e.getTarget(".x-btn-menu-arrow-wrap")){
12127                 if(this.menu && !this.menu.isVisible()){
12128                     this.menu.show(this.el, this.menuAlign);
12129                 }
12130                 this.fireEvent("arrowclick", this, e);
12131                 if(this.arrowHandler){
12132                     this.arrowHandler.call(this.scope || this, this, e);
12133                 }
12134             }else{
12135                 this.fireEvent("click", this, e);
12136                 if(this.handler){
12137                     this.handler.call(this.scope || this, this, e);
12138                 }
12139             }
12140         }
12141     },
12142     // private
12143     onMouseDown : function(e){
12144         if(!this.disabled){
12145             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
12146         }
12147     },
12148     // private
12149     onMouseUp : function(e){
12150         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
12151     }   
12152 });
12153
12154
12155 // backwards compat
12156 Roo.MenuButton = Roo.SplitButton;/*
12157  * Based on:
12158  * Ext JS Library 1.1.1
12159  * Copyright(c) 2006-2007, Ext JS, LLC.
12160  *
12161  * Originally Released Under LGPL - original licence link has changed is not relivant.
12162  *
12163  * Fork - LGPL
12164  * <script type="text/javascript">
12165  */
12166
12167 /**
12168  * @class Roo.Toolbar
12169  * Basic Toolbar class.
12170  * @constructor
12171  * Creates a new Toolbar
12172  * @param {Object} config The config object
12173  */ 
12174 Roo.Toolbar = function(container, buttons, config)
12175 {
12176     /// old consturctor format still supported..
12177     if(container instanceof Array){ // omit the container for later rendering
12178         buttons = container;
12179         config = buttons;
12180         container = null;
12181     }
12182     if (typeof(container) == 'object' && container.xtype) {
12183         config = container;
12184         container = config.container;
12185         buttons = config.buttons; // not really - use items!!
12186     }
12187     var xitems = [];
12188     if (config && config.items) {
12189         xitems = config.items;
12190         delete config.items;
12191     }
12192     Roo.apply(this, config);
12193     this.buttons = buttons;
12194     
12195     if(container){
12196         this.render(container);
12197     }
12198     Roo.each(xitems, function(b) {
12199         this.add(b);
12200     }, this);
12201     
12202 };
12203
12204 Roo.Toolbar.prototype = {
12205     /**
12206      * @cfg {Roo.data.Store} items
12207      * array of button configs or elements to add
12208      */
12209     
12210     /**
12211      * @cfg {String/HTMLElement/Element} container
12212      * The id or element that will contain the toolbar
12213      */
12214     // private
12215     render : function(ct){
12216         this.el = Roo.get(ct);
12217         if(this.cls){
12218             this.el.addClass(this.cls);
12219         }
12220         // using a table allows for vertical alignment
12221         // 100% width is needed by Safari...
12222         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
12223         this.tr = this.el.child("tr", true);
12224         var autoId = 0;
12225         this.items = new Roo.util.MixedCollection(false, function(o){
12226             return o.id || ("item" + (++autoId));
12227         });
12228         if(this.buttons){
12229             this.add.apply(this, this.buttons);
12230             delete this.buttons;
12231         }
12232     },
12233
12234     /**
12235      * Adds element(s) to the toolbar -- this function takes a variable number of 
12236      * arguments of mixed type and adds them to the toolbar.
12237      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
12238      * <ul>
12239      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
12240      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
12241      * <li>Field: Any form field (equivalent to {@link #addField})</li>
12242      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
12243      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
12244      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
12245      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
12246      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
12247      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
12248      * </ul>
12249      * @param {Mixed} arg2
12250      * @param {Mixed} etc.
12251      */
12252     add : function(){
12253         var a = arguments, l = a.length;
12254         for(var i = 0; i < l; i++){
12255             this._add(a[i]);
12256         }
12257     },
12258     // private..
12259     _add : function(el) {
12260         
12261         if (el.xtype) {
12262             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
12263         }
12264         
12265         if (el.applyTo){ // some kind of form field
12266             return this.addField(el);
12267         } 
12268         if (el.render){ // some kind of Toolbar.Item
12269             return this.addItem(el);
12270         }
12271         if (typeof el == "string"){ // string
12272             if(el == "separator" || el == "-"){
12273                 return this.addSeparator();
12274             }
12275             if (el == " "){
12276                 return this.addSpacer();
12277             }
12278             if(el == "->"){
12279                 return this.addFill();
12280             }
12281             return this.addText(el);
12282             
12283         }
12284         if(el.tagName){ // element
12285             return this.addElement(el);
12286         }
12287         if(typeof el == "object"){ // must be button config?
12288             return this.addButton(el);
12289         }
12290         // and now what?!?!
12291         return false;
12292         
12293     },
12294     
12295     /**
12296      * Add an Xtype element
12297      * @param {Object} xtype Xtype Object
12298      * @return {Object} created Object
12299      */
12300     addxtype : function(e){
12301         return this.add(e);  
12302     },
12303     
12304     /**
12305      * Returns the Element for this toolbar.
12306      * @return {Roo.Element}
12307      */
12308     getEl : function(){
12309         return this.el;  
12310     },
12311     
12312     /**
12313      * Adds a separator
12314      * @return {Roo.Toolbar.Item} The separator item
12315      */
12316     addSeparator : function(){
12317         return this.addItem(new Roo.Toolbar.Separator());
12318     },
12319
12320     /**
12321      * Adds a spacer element
12322      * @return {Roo.Toolbar.Spacer} The spacer item
12323      */
12324     addSpacer : function(){
12325         return this.addItem(new Roo.Toolbar.Spacer());
12326     },
12327
12328     /**
12329      * Adds a fill element that forces subsequent additions to the right side of the toolbar
12330      * @return {Roo.Toolbar.Fill} The fill item
12331      */
12332     addFill : function(){
12333         return this.addItem(new Roo.Toolbar.Fill());
12334     },
12335
12336     /**
12337      * Adds any standard HTML element to the toolbar
12338      * @param {String/HTMLElement/Element} el The element or id of the element to add
12339      * @return {Roo.Toolbar.Item} The element's item
12340      */
12341     addElement : function(el){
12342         return this.addItem(new Roo.Toolbar.Item(el));
12343     },
12344     /**
12345      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
12346      * @type Roo.util.MixedCollection  
12347      */
12348     items : false,
12349      
12350     /**
12351      * Adds any Toolbar.Item or subclass
12352      * @param {Roo.Toolbar.Item} item
12353      * @return {Roo.Toolbar.Item} The item
12354      */
12355     addItem : function(item){
12356         var td = this.nextBlock();
12357         item.render(td);
12358         this.items.add(item);
12359         return item;
12360     },
12361     
12362     /**
12363      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
12364      * @param {Object/Array} config A button config or array of configs
12365      * @return {Roo.Toolbar.Button/Array}
12366      */
12367     addButton : function(config){
12368         if(config instanceof Array){
12369             var buttons = [];
12370             for(var i = 0, len = config.length; i < len; i++) {
12371                 buttons.push(this.addButton(config[i]));
12372             }
12373             return buttons;
12374         }
12375         var b = config;
12376         if(!(config instanceof Roo.Toolbar.Button)){
12377             b = config.split ?
12378                 new Roo.Toolbar.SplitButton(config) :
12379                 new Roo.Toolbar.Button(config);
12380         }
12381         var td = this.nextBlock();
12382         b.render(td);
12383         this.items.add(b);
12384         return b;
12385     },
12386     
12387     /**
12388      * Adds text to the toolbar
12389      * @param {String} text The text to add
12390      * @return {Roo.Toolbar.Item} The element's item
12391      */
12392     addText : function(text){
12393         return this.addItem(new Roo.Toolbar.TextItem(text));
12394     },
12395     
12396     /**
12397      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
12398      * @param {Number} index The index where the item is to be inserted
12399      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
12400      * @return {Roo.Toolbar.Button/Item}
12401      */
12402     insertButton : function(index, item){
12403         if(item instanceof Array){
12404             var buttons = [];
12405             for(var i = 0, len = item.length; i < len; i++) {
12406                buttons.push(this.insertButton(index + i, item[i]));
12407             }
12408             return buttons;
12409         }
12410         if (!(item instanceof Roo.Toolbar.Button)){
12411            item = new Roo.Toolbar.Button(item);
12412         }
12413         var td = document.createElement("td");
12414         this.tr.insertBefore(td, this.tr.childNodes[index]);
12415         item.render(td);
12416         this.items.insert(index, item);
12417         return item;
12418     },
12419     
12420     /**
12421      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
12422      * @param {Object} config
12423      * @return {Roo.Toolbar.Item} The element's item
12424      */
12425     addDom : function(config, returnEl){
12426         var td = this.nextBlock();
12427         Roo.DomHelper.overwrite(td, config);
12428         var ti = new Roo.Toolbar.Item(td.firstChild);
12429         ti.render(td);
12430         this.items.add(ti);
12431         return ti;
12432     },
12433
12434     /**
12435      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
12436      * @type Roo.util.MixedCollection  
12437      */
12438     fields : false,
12439     
12440     /**
12441      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc). Note: the field should not have
12442      * been rendered yet. For a field that has already been rendered, use {@link #addElement}.
12443      * @param {Roo.form.Field} field
12444      * @return {Roo.ToolbarItem}
12445      */
12446      
12447       
12448     addField : function(field) {
12449         if (!this.fields) {
12450             var autoId = 0;
12451             this.fields = new Roo.util.MixedCollection(false, function(o){
12452                 return o.id || ("item" + (++autoId));
12453             });
12454
12455         }
12456         
12457         var td = this.nextBlock();
12458         field.render(td);
12459         var ti = new Roo.Toolbar.Item(td.firstChild);
12460         ti.render(td);
12461         this.items.add(ti);
12462         this.fields.add(field);
12463         return ti;
12464     },
12465     /**
12466      * Hide the toolbar
12467      * @method hide
12468      */
12469      
12470       
12471     hide : function()
12472     {
12473         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
12474         this.el.child('div').hide();
12475     },
12476     /**
12477      * Show the toolbar
12478      * @method show
12479      */
12480     show : function()
12481     {
12482         this.el.child('div').show();
12483     },
12484       
12485     // private
12486     nextBlock : function(){
12487         var td = document.createElement("td");
12488         this.tr.appendChild(td);
12489         return td;
12490     },
12491
12492     // private
12493     destroy : function(){
12494         if(this.items){ // rendered?
12495             Roo.destroy.apply(Roo, this.items.items);
12496         }
12497         if(this.fields){ // rendered?
12498             Roo.destroy.apply(Roo, this.fields.items);
12499         }
12500         Roo.Element.uncache(this.el, this.tr);
12501     }
12502 };
12503
12504 /**
12505  * @class Roo.Toolbar.Item
12506  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
12507  * @constructor
12508  * Creates a new Item
12509  * @param {HTMLElement} el 
12510  */
12511 Roo.Toolbar.Item = function(el){
12512     this.el = Roo.getDom(el);
12513     this.id = Roo.id(this.el);
12514     this.hidden = false;
12515 };
12516
12517 Roo.Toolbar.Item.prototype = {
12518     
12519     /**
12520      * Get this item's HTML Element
12521      * @return {HTMLElement}
12522      */
12523     getEl : function(){
12524        return this.el;  
12525     },
12526
12527     // private
12528     render : function(td){
12529         this.td = td;
12530         td.appendChild(this.el);
12531     },
12532     
12533     /**
12534      * Removes and destroys this item.
12535      */
12536     destroy : function(){
12537         this.td.parentNode.removeChild(this.td);
12538     },
12539     
12540     /**
12541      * Shows this item.
12542      */
12543     show: function(){
12544         this.hidden = false;
12545         this.td.style.display = "";
12546     },
12547     
12548     /**
12549      * Hides this item.
12550      */
12551     hide: function(){
12552         this.hidden = true;
12553         this.td.style.display = "none";
12554     },
12555     
12556     /**
12557      * Convenience function for boolean show/hide.
12558      * @param {Boolean} visible true to show/false to hide
12559      */
12560     setVisible: function(visible){
12561         if(visible) {
12562             this.show();
12563         }else{
12564             this.hide();
12565         }
12566     },
12567     
12568     /**
12569      * Try to focus this item.
12570      */
12571     focus : function(){
12572         Roo.fly(this.el).focus();
12573     },
12574     
12575     /**
12576      * Disables this item.
12577      */
12578     disable : function(){
12579         Roo.fly(this.td).addClass("x-item-disabled");
12580         this.disabled = true;
12581         this.el.disabled = true;
12582     },
12583     
12584     /**
12585      * Enables this item.
12586      */
12587     enable : function(){
12588         Roo.fly(this.td).removeClass("x-item-disabled");
12589         this.disabled = false;
12590         this.el.disabled = false;
12591     }
12592 };
12593
12594
12595 /**
12596  * @class Roo.Toolbar.Separator
12597  * @extends Roo.Toolbar.Item
12598  * A simple toolbar separator class
12599  * @constructor
12600  * Creates a new Separator
12601  */
12602 Roo.Toolbar.Separator = function(){
12603     var s = document.createElement("span");
12604     s.className = "ytb-sep";
12605     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
12606 };
12607 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
12608     enable:Roo.emptyFn,
12609     disable:Roo.emptyFn,
12610     focus:Roo.emptyFn
12611 });
12612
12613 /**
12614  * @class Roo.Toolbar.Spacer
12615  * @extends Roo.Toolbar.Item
12616  * A simple element that adds extra horizontal space to a toolbar.
12617  * @constructor
12618  * Creates a new Spacer
12619  */
12620 Roo.Toolbar.Spacer = function(){
12621     var s = document.createElement("div");
12622     s.className = "ytb-spacer";
12623     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
12624 };
12625 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
12626     enable:Roo.emptyFn,
12627     disable:Roo.emptyFn,
12628     focus:Roo.emptyFn
12629 });
12630
12631 /**
12632  * @class Roo.Toolbar.Fill
12633  * @extends Roo.Toolbar.Spacer
12634  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
12635  * @constructor
12636  * Creates a new Spacer
12637  */
12638 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
12639     // private
12640     render : function(td){
12641         td.style.width = '100%';
12642         Roo.Toolbar.Fill.superclass.render.call(this, td);
12643     }
12644 });
12645
12646 /**
12647  * @class Roo.Toolbar.TextItem
12648  * @extends Roo.Toolbar.Item
12649  * A simple class that renders text directly into a toolbar.
12650  * @constructor
12651  * Creates a new TextItem
12652  * @param {String} text
12653  */
12654 Roo.Toolbar.TextItem = function(text){
12655     if (typeof(text) == 'object') {
12656         text = text.text;
12657     }
12658     var s = document.createElement("span");
12659     s.className = "ytb-text";
12660     s.innerHTML = text;
12661     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
12662 };
12663 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
12664     enable:Roo.emptyFn,
12665     disable:Roo.emptyFn,
12666     focus:Roo.emptyFn
12667 });
12668
12669 /**
12670  * @class Roo.Toolbar.Button
12671  * @extends Roo.Button
12672  * A button that renders into a toolbar.
12673  * @constructor
12674  * Creates a new Button
12675  * @param {Object} config A standard {@link Roo.Button} config object
12676  */
12677 Roo.Toolbar.Button = function(config){
12678     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
12679 };
12680 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
12681     render : function(td){
12682         this.td = td;
12683         Roo.Toolbar.Button.superclass.render.call(this, td);
12684     },
12685     
12686     /**
12687      * Removes and destroys this button
12688      */
12689     destroy : function(){
12690         Roo.Toolbar.Button.superclass.destroy.call(this);
12691         this.td.parentNode.removeChild(this.td);
12692     },
12693     
12694     /**
12695      * Shows this button
12696      */
12697     show: function(){
12698         this.hidden = false;
12699         this.td.style.display = "";
12700     },
12701     
12702     /**
12703      * Hides this button
12704      */
12705     hide: function(){
12706         this.hidden = true;
12707         this.td.style.display = "none";
12708     },
12709
12710     /**
12711      * Disables this item
12712      */
12713     disable : function(){
12714         Roo.fly(this.td).addClass("x-item-disabled");
12715         this.disabled = true;
12716     },
12717
12718     /**
12719      * Enables this item
12720      */
12721     enable : function(){
12722         Roo.fly(this.td).removeClass("x-item-disabled");
12723         this.disabled = false;
12724     }
12725 });
12726 // backwards compat
12727 Roo.ToolbarButton = Roo.Toolbar.Button;
12728
12729 /**
12730  * @class Roo.Toolbar.SplitButton
12731  * @extends Roo.SplitButton
12732  * A menu button that renders into a toolbar.
12733  * @constructor
12734  * Creates a new SplitButton
12735  * @param {Object} config A standard {@link Roo.SplitButton} config object
12736  */
12737 Roo.Toolbar.SplitButton = function(config){
12738     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
12739 };
12740 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
12741     render : function(td){
12742         this.td = td;
12743         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
12744     },
12745     
12746     /**
12747      * Removes and destroys this button
12748      */
12749     destroy : function(){
12750         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
12751         this.td.parentNode.removeChild(this.td);
12752     },
12753     
12754     /**
12755      * Shows this button
12756      */
12757     show: function(){
12758         this.hidden = false;
12759         this.td.style.display = "";
12760     },
12761     
12762     /**
12763      * Hides this button
12764      */
12765     hide: function(){
12766         this.hidden = true;
12767         this.td.style.display = "none";
12768     }
12769 });
12770
12771 // backwards compat
12772 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
12773  * Based on:
12774  * Ext JS Library 1.1.1
12775  * Copyright(c) 2006-2007, Ext JS, LLC.
12776  *
12777  * Originally Released Under LGPL - original licence link has changed is not relivant.
12778  *
12779  * Fork - LGPL
12780  * <script type="text/javascript">
12781  */
12782  
12783 /**
12784  * @class Roo.PagingToolbar
12785  * @extends Roo.Toolbar
12786  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
12787  * @constructor
12788  * Create a new PagingToolbar
12789  * @param {Object} config The config object
12790  */
12791 Roo.PagingToolbar = function(el, ds, config)
12792 {
12793     // old args format still supported... - xtype is prefered..
12794     if (typeof(el) == 'object' && el.xtype) {
12795         // created from xtype...
12796         config = el;
12797         ds = el.dataSource;
12798         el = config.container;
12799     }
12800     var items = [];
12801     if (config.items) {
12802         items = config.items;
12803         config.items = [];
12804     }
12805     
12806     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
12807     this.ds = ds;
12808     this.cursor = 0;
12809     this.renderButtons(this.el);
12810     this.bind(ds);
12811     
12812     // supprot items array.
12813    
12814     Roo.each(items, function(e) {
12815         this.add(Roo.factory(e));
12816     },this);
12817     
12818 };
12819
12820 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
12821     /**
12822      * @cfg {Roo.data.Store} dataSource
12823      * The underlying data store providing the paged data
12824      */
12825     /**
12826      * @cfg {String/HTMLElement/Element} container
12827      * container The id or element that will contain the toolbar
12828      */
12829     /**
12830      * @cfg {Boolean} displayInfo
12831      * True to display the displayMsg (defaults to false)
12832      */
12833     /**
12834      * @cfg {Number} pageSize
12835      * The number of records to display per page (defaults to 20)
12836      */
12837     pageSize: 20,
12838     /**
12839      * @cfg {String} displayMsg
12840      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
12841      */
12842     displayMsg : 'Displaying {0} - {1} of {2}',
12843     /**
12844      * @cfg {String} emptyMsg
12845      * The message to display when no records are found (defaults to "No data to display")
12846      */
12847     emptyMsg : 'No data to display',
12848     /**
12849      * Customizable piece of the default paging text (defaults to "Page")
12850      * @type String
12851      */
12852     beforePageText : "Page",
12853     /**
12854      * Customizable piece of the default paging text (defaults to "of %0")
12855      * @type String
12856      */
12857     afterPageText : "of {0}",
12858     /**
12859      * Customizable piece of the default paging text (defaults to "First Page")
12860      * @type String
12861      */
12862     firstText : "First Page",
12863     /**
12864      * Customizable piece of the default paging text (defaults to "Previous Page")
12865      * @type String
12866      */
12867     prevText : "Previous Page",
12868     /**
12869      * Customizable piece of the default paging text (defaults to "Next Page")
12870      * @type String
12871      */
12872     nextText : "Next Page",
12873     /**
12874      * Customizable piece of the default paging text (defaults to "Last Page")
12875      * @type String
12876      */
12877     lastText : "Last Page",
12878     /**
12879      * Customizable piece of the default paging text (defaults to "Refresh")
12880      * @type String
12881      */
12882     refreshText : "Refresh",
12883
12884     // private
12885     renderButtons : function(el){
12886         Roo.PagingToolbar.superclass.render.call(this, el);
12887         this.first = this.addButton({
12888             tooltip: this.firstText,
12889             cls: "x-btn-icon x-grid-page-first",
12890             disabled: true,
12891             handler: this.onClick.createDelegate(this, ["first"])
12892         });
12893         this.prev = this.addButton({
12894             tooltip: this.prevText,
12895             cls: "x-btn-icon x-grid-page-prev",
12896             disabled: true,
12897             handler: this.onClick.createDelegate(this, ["prev"])
12898         });
12899         //this.addSeparator();
12900         this.add(this.beforePageText);
12901         this.field = Roo.get(this.addDom({
12902            tag: "input",
12903            type: "text",
12904            size: "3",
12905            value: "1",
12906            cls: "x-grid-page-number"
12907         }).el);
12908         this.field.on("keydown", this.onPagingKeydown, this);
12909         this.field.on("focus", function(){this.dom.select();});
12910         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
12911         this.field.setHeight(18);
12912         //this.addSeparator();
12913         this.next = this.addButton({
12914             tooltip: this.nextText,
12915             cls: "x-btn-icon x-grid-page-next",
12916             disabled: true,
12917             handler: this.onClick.createDelegate(this, ["next"])
12918         });
12919         this.last = this.addButton({
12920             tooltip: this.lastText,
12921             cls: "x-btn-icon x-grid-page-last",
12922             disabled: true,
12923             handler: this.onClick.createDelegate(this, ["last"])
12924         });
12925         //this.addSeparator();
12926         this.loading = this.addButton({
12927             tooltip: this.refreshText,
12928             cls: "x-btn-icon x-grid-loading",
12929             handler: this.onClick.createDelegate(this, ["refresh"])
12930         });
12931
12932         if(this.displayInfo){
12933             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
12934         }
12935     },
12936
12937     // private
12938     updateInfo : function(){
12939         if(this.displayEl){
12940             var count = this.ds.getCount();
12941             var msg = count == 0 ?
12942                 this.emptyMsg :
12943                 String.format(
12944                     this.displayMsg,
12945                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
12946                 );
12947             this.displayEl.update(msg);
12948         }
12949     },
12950
12951     // private
12952     onLoad : function(ds, r, o){
12953        this.cursor = o.params ? o.params.start : 0;
12954        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
12955
12956        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
12957        this.field.dom.value = ap;
12958        this.first.setDisabled(ap == 1);
12959        this.prev.setDisabled(ap == 1);
12960        this.next.setDisabled(ap == ps);
12961        this.last.setDisabled(ap == ps);
12962        this.loading.enable();
12963        this.updateInfo();
12964     },
12965
12966     // private
12967     getPageData : function(){
12968         var total = this.ds.getTotalCount();
12969         return {
12970             total : total,
12971             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
12972             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
12973         };
12974     },
12975
12976     // private
12977     onLoadError : function(){
12978         this.loading.enable();
12979     },
12980
12981     // private
12982     onPagingKeydown : function(e){
12983         var k = e.getKey();
12984         var d = this.getPageData();
12985         if(k == e.RETURN){
12986             var v = this.field.dom.value, pageNum;
12987             if(!v || isNaN(pageNum = parseInt(v, 10))){
12988                 this.field.dom.value = d.activePage;
12989                 return;
12990             }
12991             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
12992             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
12993             e.stopEvent();
12994         }
12995         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))
12996         {
12997           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
12998           this.field.dom.value = pageNum;
12999           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
13000           e.stopEvent();
13001         }
13002         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13003         {
13004           var v = this.field.dom.value, pageNum; 
13005           var increment = (e.shiftKey) ? 10 : 1;
13006           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13007             increment *= -1;
13008           if(!v || isNaN(pageNum = parseInt(v, 10))) {
13009             this.field.dom.value = d.activePage;
13010             return;
13011           }
13012           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
13013           {
13014             this.field.dom.value = parseInt(v, 10) + increment;
13015             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
13016             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13017           }
13018           e.stopEvent();
13019         }
13020     },
13021
13022     // private
13023     beforeLoad : function(){
13024         if(this.loading){
13025             this.loading.disable();
13026         }
13027     },
13028
13029     // private
13030     onClick : function(which){
13031         var ds = this.ds;
13032         switch(which){
13033             case "first":
13034                 ds.load({params:{start: 0, limit: this.pageSize}});
13035             break;
13036             case "prev":
13037                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
13038             break;
13039             case "next":
13040                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
13041             break;
13042             case "last":
13043                 var total = ds.getTotalCount();
13044                 var extra = total % this.pageSize;
13045                 var lastStart = extra ? (total - extra) : total-this.pageSize;
13046                 ds.load({params:{start: lastStart, limit: this.pageSize}});
13047             break;
13048             case "refresh":
13049                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
13050             break;
13051         }
13052     },
13053
13054     /**
13055      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
13056      * @param {Roo.data.Store} store The data store to unbind
13057      */
13058     unbind : function(ds){
13059         ds.un("beforeload", this.beforeLoad, this);
13060         ds.un("load", this.onLoad, this);
13061         ds.un("loadexception", this.onLoadError, this);
13062         ds.un("remove", this.updateInfo, this);
13063         ds.un("add", this.updateInfo, this);
13064         this.ds = undefined;
13065     },
13066
13067     /**
13068      * Binds the paging toolbar to the specified {@link Roo.data.Store}
13069      * @param {Roo.data.Store} store The data store to bind
13070      */
13071     bind : function(ds){
13072         ds.on("beforeload", this.beforeLoad, this);
13073         ds.on("load", this.onLoad, this);
13074         ds.on("loadexception", this.onLoadError, this);
13075         ds.on("remove", this.updateInfo, this);
13076         ds.on("add", this.updateInfo, this);
13077         this.ds = ds;
13078     }
13079 });/*
13080  * Based on:
13081  * Ext JS Library 1.1.1
13082  * Copyright(c) 2006-2007, Ext JS, LLC.
13083  *
13084  * Originally Released Under LGPL - original licence link has changed is not relivant.
13085  *
13086  * Fork - LGPL
13087  * <script type="text/javascript">
13088  */
13089
13090 /**
13091  * @class Roo.Resizable
13092  * @extends Roo.util.Observable
13093  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
13094  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
13095  * 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
13096  * the element will be wrapped for you automatically.</p>
13097  * <p>Here is the list of valid resize handles:</p>
13098  * <pre>
13099 Value   Description
13100 ------  -------------------
13101  'n'     north
13102  's'     south
13103  'e'     east
13104  'w'     west
13105  'nw'    northwest
13106  'sw'    southwest
13107  'se'    southeast
13108  'ne'    northeast
13109  'hd'    horizontal drag
13110  'all'   all
13111 </pre>
13112  * <p>Here's an example showing the creation of a typical Resizable:</p>
13113  * <pre><code>
13114 var resizer = new Roo.Resizable("element-id", {
13115     handles: 'all',
13116     minWidth: 200,
13117     minHeight: 100,
13118     maxWidth: 500,
13119     maxHeight: 400,
13120     pinned: true
13121 });
13122 resizer.on("resize", myHandler);
13123 </code></pre>
13124  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
13125  * resizer.east.setDisplayed(false);</p>
13126  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
13127  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
13128  * resize operation's new size (defaults to [0, 0])
13129  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
13130  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
13131  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
13132  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
13133  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
13134  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
13135  * @cfg {Number} width The width of the element in pixels (defaults to null)
13136  * @cfg {Number} height The height of the element in pixels (defaults to null)
13137  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
13138  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
13139  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
13140  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
13141  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
13142  * in favor of the handles config option (defaults to false)
13143  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
13144  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
13145  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
13146  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
13147  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
13148  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
13149  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
13150  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
13151  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
13152  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
13153  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
13154  * @constructor
13155  * Create a new resizable component
13156  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
13157  * @param {Object} config configuration options
13158   */
13159 Roo.Resizable = function(el, config)
13160 {
13161     this.el = Roo.get(el);
13162
13163     if(config && config.wrap){
13164         config.resizeChild = this.el;
13165         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
13166         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
13167         this.el.setStyle("overflow", "hidden");
13168         this.el.setPositioning(config.resizeChild.getPositioning());
13169         config.resizeChild.clearPositioning();
13170         if(!config.width || !config.height){
13171             var csize = config.resizeChild.getSize();
13172             this.el.setSize(csize.width, csize.height);
13173         }
13174         if(config.pinned && !config.adjustments){
13175             config.adjustments = "auto";
13176         }
13177     }
13178
13179     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
13180     this.proxy.unselectable();
13181     this.proxy.enableDisplayMode('block');
13182
13183     Roo.apply(this, config);
13184
13185     if(this.pinned){
13186         this.disableTrackOver = true;
13187         this.el.addClass("x-resizable-pinned");
13188     }
13189     // if the element isn't positioned, make it relative
13190     var position = this.el.getStyle("position");
13191     if(position != "absolute" && position != "fixed"){
13192         this.el.setStyle("position", "relative");
13193     }
13194     if(!this.handles){ // no handles passed, must be legacy style
13195         this.handles = 's,e,se';
13196         if(this.multiDirectional){
13197             this.handles += ',n,w';
13198         }
13199     }
13200     if(this.handles == "all"){
13201         this.handles = "n s e w ne nw se sw";
13202     }
13203     var hs = this.handles.split(/\s*?[,;]\s*?| /);
13204     var ps = Roo.Resizable.positions;
13205     for(var i = 0, len = hs.length; i < len; i++){
13206         if(hs[i] && ps[hs[i]]){
13207             var pos = ps[hs[i]];
13208             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
13209         }
13210     }
13211     // legacy
13212     this.corner = this.southeast;
13213     
13214     // updateBox = the box can move..
13215     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
13216         this.updateBox = true;
13217     }
13218
13219     this.activeHandle = null;
13220
13221     if(this.resizeChild){
13222         if(typeof this.resizeChild == "boolean"){
13223             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
13224         }else{
13225             this.resizeChild = Roo.get(this.resizeChild, true);
13226         }
13227     }
13228     
13229     if(this.adjustments == "auto"){
13230         var rc = this.resizeChild;
13231         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
13232         if(rc && (hw || hn)){
13233             rc.position("relative");
13234             rc.setLeft(hw ? hw.el.getWidth() : 0);
13235             rc.setTop(hn ? hn.el.getHeight() : 0);
13236         }
13237         this.adjustments = [
13238             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
13239             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
13240         ];
13241     }
13242
13243     if(this.draggable){
13244         this.dd = this.dynamic ?
13245             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
13246         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
13247     }
13248
13249     // public events
13250     this.addEvents({
13251         /**
13252          * @event beforeresize
13253          * Fired before resize is allowed. Set enabled to false to cancel resize.
13254          * @param {Roo.Resizable} this
13255          * @param {Roo.EventObject} e The mousedown event
13256          */
13257         "beforeresize" : true,
13258         /**
13259          * @event resize
13260          * Fired after a resize.
13261          * @param {Roo.Resizable} this
13262          * @param {Number} width The new width
13263          * @param {Number} height The new height
13264          * @param {Roo.EventObject} e The mouseup event
13265          */
13266         "resize" : true
13267     });
13268
13269     if(this.width !== null && this.height !== null){
13270         this.resizeTo(this.width, this.height);
13271     }else{
13272         this.updateChildSize();
13273     }
13274     if(Roo.isIE){
13275         this.el.dom.style.zoom = 1;
13276     }
13277     Roo.Resizable.superclass.constructor.call(this);
13278 };
13279
13280 Roo.extend(Roo.Resizable, Roo.util.Observable, {
13281         resizeChild : false,
13282         adjustments : [0, 0],
13283         minWidth : 5,
13284         minHeight : 5,
13285         maxWidth : 10000,
13286         maxHeight : 10000,
13287         enabled : true,
13288         animate : false,
13289         duration : .35,
13290         dynamic : false,
13291         handles : false,
13292         multiDirectional : false,
13293         disableTrackOver : false,
13294         easing : 'easeOutStrong',
13295         widthIncrement : 0,
13296         heightIncrement : 0,
13297         pinned : false,
13298         width : null,
13299         height : null,
13300         preserveRatio : false,
13301         transparent: false,
13302         minX: 0,
13303         minY: 0,
13304         draggable: false,
13305
13306         /**
13307          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
13308          */
13309         constrainTo: undefined,
13310         /**
13311          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
13312          */
13313         resizeRegion: undefined,
13314
13315
13316     /**
13317      * Perform a manual resize
13318      * @param {Number} width
13319      * @param {Number} height
13320      */
13321     resizeTo : function(width, height){
13322         this.el.setSize(width, height);
13323         this.updateChildSize();
13324         this.fireEvent("resize", this, width, height, null);
13325     },
13326
13327     // private
13328     startSizing : function(e, handle){
13329         this.fireEvent("beforeresize", this, e);
13330         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
13331
13332             if(!this.overlay){
13333                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
13334                 this.overlay.unselectable();
13335                 this.overlay.enableDisplayMode("block");
13336                 this.overlay.on("mousemove", this.onMouseMove, this);
13337                 this.overlay.on("mouseup", this.onMouseUp, this);
13338             }
13339             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
13340
13341             this.resizing = true;
13342             this.startBox = this.el.getBox();
13343             this.startPoint = e.getXY();
13344             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
13345                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
13346
13347             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13348             this.overlay.show();
13349
13350             if(this.constrainTo) {
13351                 var ct = Roo.get(this.constrainTo);
13352                 this.resizeRegion = ct.getRegion().adjust(
13353                     ct.getFrameWidth('t'),
13354                     ct.getFrameWidth('l'),
13355                     -ct.getFrameWidth('b'),
13356                     -ct.getFrameWidth('r')
13357                 );
13358             }
13359
13360             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
13361             this.proxy.show();
13362             this.proxy.setBox(this.startBox);
13363             if(!this.dynamic){
13364                 this.proxy.setStyle('visibility', 'visible');
13365             }
13366         }
13367     },
13368
13369     // private
13370     onMouseDown : function(handle, e){
13371         if(this.enabled){
13372             e.stopEvent();
13373             this.activeHandle = handle;
13374             this.startSizing(e, handle);
13375         }
13376     },
13377
13378     // private
13379     onMouseUp : function(e){
13380         var size = this.resizeElement();
13381         this.resizing = false;
13382         this.handleOut();
13383         this.overlay.hide();
13384         this.proxy.hide();
13385         this.fireEvent("resize", this, size.width, size.height, e);
13386     },
13387
13388     // private
13389     updateChildSize : function(){
13390         if(this.resizeChild){
13391             var el = this.el;
13392             var child = this.resizeChild;
13393             var adj = this.adjustments;
13394             if(el.dom.offsetWidth){
13395                 var b = el.getSize(true);
13396                 child.setSize(b.width+adj[0], b.height+adj[1]);
13397             }
13398             // Second call here for IE
13399             // The first call enables instant resizing and
13400             // the second call corrects scroll bars if they
13401             // exist
13402             if(Roo.isIE){
13403                 setTimeout(function(){
13404                     if(el.dom.offsetWidth){
13405                         var b = el.getSize(true);
13406                         child.setSize(b.width+adj[0], b.height+adj[1]);
13407                     }
13408                 }, 10);
13409             }
13410         }
13411     },
13412
13413     // private
13414     snap : function(value, inc, min){
13415         if(!inc || !value) return value;
13416         var newValue = value;
13417         var m = value % inc;
13418         if(m > 0){
13419             if(m > (inc/2)){
13420                 newValue = value + (inc-m);
13421             }else{
13422                 newValue = value - m;
13423             }
13424         }
13425         return Math.max(min, newValue);
13426     },
13427
13428     // private
13429     resizeElement : function(){
13430         var box = this.proxy.getBox();
13431         if(this.updateBox){
13432             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
13433         }else{
13434             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
13435         }
13436         this.updateChildSize();
13437         if(!this.dynamic){
13438             this.proxy.hide();
13439         }
13440         return box;
13441     },
13442
13443     // private
13444     constrain : function(v, diff, m, mx){
13445         if(v - diff < m){
13446             diff = v - m;
13447         }else if(v - diff > mx){
13448             diff = mx - v;
13449         }
13450         return diff;
13451     },
13452
13453     // private
13454     onMouseMove : function(e){
13455         if(this.enabled){
13456             try{// try catch so if something goes wrong the user doesn't get hung
13457
13458             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
13459                 return;
13460             }
13461
13462             //var curXY = this.startPoint;
13463             var curSize = this.curSize || this.startBox;
13464             var x = this.startBox.x, y = this.startBox.y;
13465             var ox = x, oy = y;
13466             var w = curSize.width, h = curSize.height;
13467             var ow = w, oh = h;
13468             var mw = this.minWidth, mh = this.minHeight;
13469             var mxw = this.maxWidth, mxh = this.maxHeight;
13470             var wi = this.widthIncrement;
13471             var hi = this.heightIncrement;
13472
13473             var eventXY = e.getXY();
13474             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
13475             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
13476
13477             var pos = this.activeHandle.position;
13478
13479             switch(pos){
13480                 case "east":
13481                     w += diffX;
13482                     w = Math.min(Math.max(mw, w), mxw);
13483                     break;
13484              
13485                 case "south":
13486                     h += diffY;
13487                     h = Math.min(Math.max(mh, h), mxh);
13488                     break;
13489                 case "southeast":
13490                     w += diffX;
13491                     h += diffY;
13492                     w = Math.min(Math.max(mw, w), mxw);
13493                     h = Math.min(Math.max(mh, h), mxh);
13494                     break;
13495                 case "north":
13496                     diffY = this.constrain(h, diffY, mh, mxh);
13497                     y += diffY;
13498                     h -= diffY;
13499                     break;
13500                 case "hdrag":
13501                     
13502                     if (wi) {
13503                         var adiffX = Math.abs(diffX);
13504                         var sub = (adiffX % wi); // how much 
13505                         if (sub > (wi/2)) { // far enough to snap
13506                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
13507                         } else {
13508                             // remove difference.. 
13509                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
13510                         }
13511                     }
13512                     x += diffX;
13513                     x = Math.max(this.minX, x);
13514                     break;
13515                 case "west":
13516                     diffX = this.constrain(w, diffX, mw, mxw);
13517                     x += diffX;
13518                     w -= diffX;
13519                     break;
13520                 case "northeast":
13521                     w += diffX;
13522                     w = Math.min(Math.max(mw, w), mxw);
13523                     diffY = this.constrain(h, diffY, mh, mxh);
13524                     y += diffY;
13525                     h -= diffY;
13526                     break;
13527                 case "northwest":
13528                     diffX = this.constrain(w, diffX, mw, mxw);
13529                     diffY = this.constrain(h, diffY, mh, mxh);
13530                     y += diffY;
13531                     h -= diffY;
13532                     x += diffX;
13533                     w -= diffX;
13534                     break;
13535                case "southwest":
13536                     diffX = this.constrain(w, diffX, mw, mxw);
13537                     h += diffY;
13538                     h = Math.min(Math.max(mh, h), mxh);
13539                     x += diffX;
13540                     w -= diffX;
13541                     break;
13542             }
13543
13544             var sw = this.snap(w, wi, mw);
13545             var sh = this.snap(h, hi, mh);
13546             if(sw != w || sh != h){
13547                 switch(pos){
13548                     case "northeast":
13549                         y -= sh - h;
13550                     break;
13551                     case "north":
13552                         y -= sh - h;
13553                         break;
13554                     case "southwest":
13555                         x -= sw - w;
13556                     break;
13557                     case "west":
13558                         x -= sw - w;
13559                         break;
13560                     case "northwest":
13561                         x -= sw - w;
13562                         y -= sh - h;
13563                     break;
13564                 }
13565                 w = sw;
13566                 h = sh;
13567             }
13568
13569             if(this.preserveRatio){
13570                 switch(pos){
13571                     case "southeast":
13572                     case "east":
13573                         h = oh * (w/ow);
13574                         h = Math.min(Math.max(mh, h), mxh);
13575                         w = ow * (h/oh);
13576                        break;
13577                     case "south":
13578                         w = ow * (h/oh);
13579                         w = Math.min(Math.max(mw, w), mxw);
13580                         h = oh * (w/ow);
13581                         break;
13582                     case "northeast":
13583                         w = ow * (h/oh);
13584                         w = Math.min(Math.max(mw, w), mxw);
13585                         h = oh * (w/ow);
13586                     break;
13587                     case "north":
13588                         var tw = w;
13589                         w = ow * (h/oh);
13590                         w = Math.min(Math.max(mw, w), mxw);
13591                         h = oh * (w/ow);
13592                         x += (tw - w) / 2;
13593                         break;
13594                     case "southwest":
13595                         h = oh * (w/ow);
13596                         h = Math.min(Math.max(mh, h), mxh);
13597                         var tw = w;
13598                         w = ow * (h/oh);
13599                         x += tw - w;
13600                         break;
13601                     case "west":
13602                         var th = h;
13603                         h = oh * (w/ow);
13604                         h = Math.min(Math.max(mh, h), mxh);
13605                         y += (th - h) / 2;
13606                         var tw = w;
13607                         w = ow * (h/oh);
13608                         x += tw - w;
13609                        break;
13610                     case "northwest":
13611                         var tw = w;
13612                         var th = h;
13613                         h = oh * (w/ow);
13614                         h = Math.min(Math.max(mh, h), mxh);
13615                         w = ow * (h/oh);
13616                         y += th - h;
13617                         x += tw - w;
13618                        break;
13619
13620                 }
13621             }
13622             if (pos == 'hdrag') {
13623                 w = ow;
13624             }
13625             this.proxy.setBounds(x, y, w, h);
13626             if(this.dynamic){
13627                 this.resizeElement();
13628             }
13629             }catch(e){}
13630         }
13631     },
13632
13633     // private
13634     handleOver : function(){
13635         if(this.enabled){
13636             this.el.addClass("x-resizable-over");
13637         }
13638     },
13639
13640     // private
13641     handleOut : function(){
13642         if(!this.resizing){
13643             this.el.removeClass("x-resizable-over");
13644         }
13645     },
13646
13647     /**
13648      * Returns the element this component is bound to.
13649      * @return {Roo.Element}
13650      */
13651     getEl : function(){
13652         return this.el;
13653     },
13654
13655     /**
13656      * Returns the resizeChild element (or null).
13657      * @return {Roo.Element}
13658      */
13659     getResizeChild : function(){
13660         return this.resizeChild;
13661     },
13662
13663     /**
13664      * Destroys this resizable. If the element was wrapped and
13665      * removeEl is not true then the element remains.
13666      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
13667      */
13668     destroy : function(removeEl){
13669         this.proxy.remove();
13670         if(this.overlay){
13671             this.overlay.removeAllListeners();
13672             this.overlay.remove();
13673         }
13674         var ps = Roo.Resizable.positions;
13675         for(var k in ps){
13676             if(typeof ps[k] != "function" && this[ps[k]]){
13677                 var h = this[ps[k]];
13678                 h.el.removeAllListeners();
13679                 h.el.remove();
13680             }
13681         }
13682         if(removeEl){
13683             this.el.update("");
13684             this.el.remove();
13685         }
13686     }
13687 });
13688
13689 // private
13690 // hash to map config positions to true positions
13691 Roo.Resizable.positions = {
13692     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
13693     hd: "hdrag"
13694 };
13695
13696 // private
13697 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
13698     if(!this.tpl){
13699         // only initialize the template if resizable is used
13700         var tpl = Roo.DomHelper.createTemplate(
13701             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
13702         );
13703         tpl.compile();
13704         Roo.Resizable.Handle.prototype.tpl = tpl;
13705     }
13706     this.position = pos;
13707     this.rz = rz;
13708     // show north drag fro topdra
13709     var handlepos = pos == 'hdrag' ? 'north' : pos;
13710     
13711     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
13712     if (pos == 'hdrag') {
13713         this.el.setStyle('cursor', 'pointer');
13714     }
13715     this.el.unselectable();
13716     if(transparent){
13717         this.el.setOpacity(0);
13718     }
13719     this.el.on("mousedown", this.onMouseDown, this);
13720     if(!disableTrackOver){
13721         this.el.on("mouseover", this.onMouseOver, this);
13722         this.el.on("mouseout", this.onMouseOut, this);
13723     }
13724 };
13725
13726 // private
13727 Roo.Resizable.Handle.prototype = {
13728     afterResize : function(rz){
13729         // do nothing
13730     },
13731     // private
13732     onMouseDown : function(e){
13733         this.rz.onMouseDown(this, e);
13734     },
13735     // private
13736     onMouseOver : function(e){
13737         this.rz.handleOver(this, e);
13738     },
13739     // private
13740     onMouseOut : function(e){
13741         this.rz.handleOut(this, e);
13742     }
13743 };/*
13744  * Based on:
13745  * Ext JS Library 1.1.1
13746  * Copyright(c) 2006-2007, Ext JS, LLC.
13747  *
13748  * Originally Released Under LGPL - original licence link has changed is not relivant.
13749  *
13750  * Fork - LGPL
13751  * <script type="text/javascript">
13752  */
13753
13754 /**
13755  * @class Roo.Editor
13756  * @extends Roo.Component
13757  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
13758  * @constructor
13759  * Create a new Editor
13760  * @param {Roo.form.Field} field The Field object (or descendant)
13761  * @param {Object} config The config object
13762  */
13763 Roo.Editor = function(field, config){
13764     Roo.Editor.superclass.constructor.call(this, config);
13765     this.field = field;
13766     this.addEvents({
13767         /**
13768              * @event beforestartedit
13769              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
13770              * false from the handler of this event.
13771              * @param {Editor} this
13772              * @param {Roo.Element} boundEl The underlying element bound to this editor
13773              * @param {Mixed} value The field value being set
13774              */
13775         "beforestartedit" : true,
13776         /**
13777              * @event startedit
13778              * Fires when this editor is displayed
13779              * @param {Roo.Element} boundEl The underlying element bound to this editor
13780              * @param {Mixed} value The starting field value
13781              */
13782         "startedit" : true,
13783         /**
13784              * @event beforecomplete
13785              * Fires after a change has been made to the field, but before the change is reflected in the underlying
13786              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
13787              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
13788              * event will not fire since no edit actually occurred.
13789              * @param {Editor} this
13790              * @param {Mixed} value The current field value
13791              * @param {Mixed} startValue The original field value
13792              */
13793         "beforecomplete" : true,
13794         /**
13795              * @event complete
13796              * Fires after editing is complete and any changed value has been written to the underlying field.
13797              * @param {Editor} this
13798              * @param {Mixed} value The current field value
13799              * @param {Mixed} startValue The original field value
13800              */
13801         "complete" : true,
13802         /**
13803          * @event specialkey
13804          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
13805          * {@link Roo.EventObject#getKey} to determine which key was pressed.
13806          * @param {Roo.form.Field} this
13807          * @param {Roo.EventObject} e The event object
13808          */
13809         "specialkey" : true
13810     });
13811 };
13812
13813 Roo.extend(Roo.Editor, Roo.Component, {
13814     /**
13815      * @cfg {Boolean/String} autosize
13816      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
13817      * or "height" to adopt the height only (defaults to false)
13818      */
13819     /**
13820      * @cfg {Boolean} revertInvalid
13821      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
13822      * validation fails (defaults to true)
13823      */
13824     /**
13825      * @cfg {Boolean} ignoreNoChange
13826      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
13827      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
13828      * will never be ignored.
13829      */
13830     /**
13831      * @cfg {Boolean} hideEl
13832      * False to keep the bound element visible while the editor is displayed (defaults to true)
13833      */
13834     /**
13835      * @cfg {Mixed} value
13836      * The data value of the underlying field (defaults to "")
13837      */
13838     value : "",
13839     /**
13840      * @cfg {String} alignment
13841      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
13842      */
13843     alignment: "c-c?",
13844     /**
13845      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
13846      * for bottom-right shadow (defaults to "frame")
13847      */
13848     shadow : "frame",
13849     /**
13850      * @cfg {Boolean} constrain True to constrain the editor to the viewport
13851      */
13852     constrain : false,
13853     /**
13854      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
13855      */
13856     completeOnEnter : false,
13857     /**
13858      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
13859      */
13860     cancelOnEsc : false,
13861     /**
13862      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
13863      */
13864     updateEl : false,
13865
13866     // private
13867     onRender : function(ct, position){
13868         this.el = new Roo.Layer({
13869             shadow: this.shadow,
13870             cls: "x-editor",
13871             parentEl : ct,
13872             shim : this.shim,
13873             shadowOffset:4,
13874             id: this.id,
13875             constrain: this.constrain
13876         });
13877         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
13878         if(this.field.msgTarget != 'title'){
13879             this.field.msgTarget = 'qtip';
13880         }
13881         this.field.render(this.el);
13882         if(Roo.isGecko){
13883             this.field.el.dom.setAttribute('autocomplete', 'off');
13884         }
13885         this.field.on("specialkey", this.onSpecialKey, this);
13886         if(this.swallowKeys){
13887             this.field.el.swallowEvent(['keydown','keypress']);
13888         }
13889         this.field.show();
13890         this.field.on("blur", this.onBlur, this);
13891         if(this.field.grow){
13892             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
13893         }
13894     },
13895
13896     onSpecialKey : function(field, e){
13897         //Roo.log('editor onSpecialKey');
13898         if(this.completeOnEnter && e.getKey() == e.ENTER){
13899             e.stopEvent();
13900             this.completeEdit();
13901         }else if(this.cancelOnEsc && e.getKey() == e.ESC){
13902             this.cancelEdit();
13903         }else{
13904             this.fireEvent('specialkey', field, e);
13905         }
13906     },
13907
13908     /**
13909      * Starts the editing process and shows the editor.
13910      * @param {String/HTMLElement/Element} el The element to edit
13911      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
13912       * to the innerHTML of el.
13913      */
13914     startEdit : function(el, value){
13915         if(this.editing){
13916             this.completeEdit();
13917         }
13918         this.boundEl = Roo.get(el);
13919         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
13920         if(!this.rendered){
13921             this.render(this.parentEl || document.body);
13922         }
13923         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
13924             return;
13925         }
13926         this.startValue = v;
13927         this.field.setValue(v);
13928         if(this.autoSize){
13929             var sz = this.boundEl.getSize();
13930             switch(this.autoSize){
13931                 case "width":
13932                 this.setSize(sz.width,  "");
13933                 break;
13934                 case "height":
13935                 this.setSize("",  sz.height);
13936                 break;
13937                 default:
13938                 this.setSize(sz.width,  sz.height);
13939             }
13940         }
13941         this.el.alignTo(this.boundEl, this.alignment);
13942         this.editing = true;
13943         if(Roo.QuickTips){
13944             Roo.QuickTips.disable();
13945         }
13946         this.show();
13947     },
13948
13949     /**
13950      * Sets the height and width of this editor.
13951      * @param {Number} width The new width
13952      * @param {Number} height The new height
13953      */
13954     setSize : function(w, h){
13955         this.field.setSize(w, h);
13956         if(this.el){
13957             this.el.sync();
13958         }
13959     },
13960
13961     /**
13962      * Realigns the editor to the bound field based on the current alignment config value.
13963      */
13964     realign : function(){
13965         this.el.alignTo(this.boundEl, this.alignment);
13966     },
13967
13968     /**
13969      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
13970      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
13971      */
13972     completeEdit : function(remainVisible){
13973         if(!this.editing){
13974             return;
13975         }
13976         var v = this.getValue();
13977         if(this.revertInvalid !== false && !this.field.isValid()){
13978             v = this.startValue;
13979             this.cancelEdit(true);
13980         }
13981         if(String(v) === String(this.startValue) && this.ignoreNoChange){
13982             this.editing = false;
13983             this.hide();
13984             return;
13985         }
13986         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
13987             this.editing = false;
13988             if(this.updateEl && this.boundEl){
13989                 this.boundEl.update(v);
13990             }
13991             if(remainVisible !== true){
13992                 this.hide();
13993             }
13994             this.fireEvent("complete", this, v, this.startValue);
13995         }
13996     },
13997
13998     // private
13999     onShow : function(){
14000         this.el.show();
14001         if(this.hideEl !== false){
14002             this.boundEl.hide();
14003         }
14004         this.field.show();
14005         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
14006             this.fixIEFocus = true;
14007             this.deferredFocus.defer(50, this);
14008         }else{
14009             this.field.focus();
14010         }
14011         this.fireEvent("startedit", this.boundEl, this.startValue);
14012     },
14013
14014     deferredFocus : function(){
14015         if(this.editing){
14016             this.field.focus();
14017         }
14018     },
14019
14020     /**
14021      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
14022      * reverted to the original starting value.
14023      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
14024      * cancel (defaults to false)
14025      */
14026     cancelEdit : function(remainVisible){
14027         if(this.editing){
14028             this.setValue(this.startValue);
14029             if(remainVisible !== true){
14030                 this.hide();
14031             }
14032         }
14033     },
14034
14035     // private
14036     onBlur : function(){
14037         if(this.allowBlur !== true && this.editing){
14038             this.completeEdit();
14039         }
14040     },
14041
14042     // private
14043     onHide : function(){
14044         if(this.editing){
14045             this.completeEdit();
14046             return;
14047         }
14048         this.field.blur();
14049         if(this.field.collapse){
14050             this.field.collapse();
14051         }
14052         this.el.hide();
14053         if(this.hideEl !== false){
14054             this.boundEl.show();
14055         }
14056         if(Roo.QuickTips){
14057             Roo.QuickTips.enable();
14058         }
14059     },
14060
14061     /**
14062      * Sets the data value of the editor
14063      * @param {Mixed} value Any valid value supported by the underlying field
14064      */
14065     setValue : function(v){
14066         this.field.setValue(v);
14067     },
14068
14069     /**
14070      * Gets the data value of the editor
14071      * @return {Mixed} The data value
14072      */
14073     getValue : function(){
14074         return this.field.getValue();
14075     }
14076 });/*
14077  * Based on:
14078  * Ext JS Library 1.1.1
14079  * Copyright(c) 2006-2007, Ext JS, LLC.
14080  *
14081  * Originally Released Under LGPL - original licence link has changed is not relivant.
14082  *
14083  * Fork - LGPL
14084  * <script type="text/javascript">
14085  */
14086  
14087 /**
14088  * @class Roo.BasicDialog
14089  * @extends Roo.util.Observable
14090  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
14091  * <pre><code>
14092 var dlg = new Roo.BasicDialog("my-dlg", {
14093     height: 200,
14094     width: 300,
14095     minHeight: 100,
14096     minWidth: 150,
14097     modal: true,
14098     proxyDrag: true,
14099     shadow: true
14100 });
14101 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
14102 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
14103 dlg.addButton('Cancel', dlg.hide, dlg);
14104 dlg.show();
14105 </code></pre>
14106   <b>A Dialog should always be a direct child of the body element.</b>
14107  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
14108  * @cfg {String} title Default text to display in the title bar (defaults to null)
14109  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14110  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14111  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
14112  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
14113  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
14114  * (defaults to null with no animation)
14115  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
14116  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
14117  * property for valid values (defaults to 'all')
14118  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
14119  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
14120  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
14121  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
14122  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
14123  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
14124  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
14125  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
14126  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
14127  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
14128  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
14129  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
14130  * draggable = true (defaults to false)
14131  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
14132  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14133  * shadow (defaults to false)
14134  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
14135  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
14136  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
14137  * @cfg {Array} buttons Array of buttons
14138  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
14139  * @constructor
14140  * Create a new BasicDialog.
14141  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
14142  * @param {Object} config Configuration options
14143  */
14144 Roo.BasicDialog = function(el, config){
14145     this.el = Roo.get(el);
14146     var dh = Roo.DomHelper;
14147     if(!this.el && config && config.autoCreate){
14148         if(typeof config.autoCreate == "object"){
14149             if(!config.autoCreate.id){
14150                 config.autoCreate.id = el;
14151             }
14152             this.el = dh.append(document.body,
14153                         config.autoCreate, true);
14154         }else{
14155             this.el = dh.append(document.body,
14156                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
14157         }
14158     }
14159     el = this.el;
14160     el.setDisplayed(true);
14161     el.hide = this.hideAction;
14162     this.id = el.id;
14163     el.addClass("x-dlg");
14164
14165     Roo.apply(this, config);
14166
14167     this.proxy = el.createProxy("x-dlg-proxy");
14168     this.proxy.hide = this.hideAction;
14169     this.proxy.setOpacity(.5);
14170     this.proxy.hide();
14171
14172     if(config.width){
14173         el.setWidth(config.width);
14174     }
14175     if(config.height){
14176         el.setHeight(config.height);
14177     }
14178     this.size = el.getSize();
14179     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
14180         this.xy = [config.x,config.y];
14181     }else{
14182         this.xy = el.getCenterXY(true);
14183     }
14184     /** The header element @type Roo.Element */
14185     this.header = el.child("> .x-dlg-hd");
14186     /** The body element @type Roo.Element */
14187     this.body = el.child("> .x-dlg-bd");
14188     /** The footer element @type Roo.Element */
14189     this.footer = el.child("> .x-dlg-ft");
14190
14191     if(!this.header){
14192         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
14193     }
14194     if(!this.body){
14195         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
14196     }
14197
14198     this.header.unselectable();
14199     if(this.title){
14200         this.header.update(this.title);
14201     }
14202     // this element allows the dialog to be focused for keyboard event
14203     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
14204     this.focusEl.swallowEvent("click", true);
14205
14206     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
14207
14208     // wrap the body and footer for special rendering
14209     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
14210     if(this.footer){
14211         this.bwrap.dom.appendChild(this.footer.dom);
14212     }
14213
14214     this.bg = this.el.createChild({
14215         tag: "div", cls:"x-dlg-bg",
14216         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
14217     });
14218     this.centerBg = this.bg.child("div.x-dlg-bg-center");
14219
14220
14221     if(this.autoScroll !== false && !this.autoTabs){
14222         this.body.setStyle("overflow", "auto");
14223     }
14224
14225     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
14226
14227     if(this.closable !== false){
14228         this.el.addClass("x-dlg-closable");
14229         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
14230         this.close.on("click", this.closeClick, this);
14231         this.close.addClassOnOver("x-dlg-close-over");
14232     }
14233     if(this.collapsible !== false){
14234         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
14235         this.collapseBtn.on("click", this.collapseClick, this);
14236         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
14237         this.header.on("dblclick", this.collapseClick, this);
14238     }
14239     if(this.resizable !== false){
14240         this.el.addClass("x-dlg-resizable");
14241         this.resizer = new Roo.Resizable(el, {
14242             minWidth: this.minWidth || 80,
14243             minHeight:this.minHeight || 80,
14244             handles: this.resizeHandles || "all",
14245             pinned: true
14246         });
14247         this.resizer.on("beforeresize", this.beforeResize, this);
14248         this.resizer.on("resize", this.onResize, this);
14249     }
14250     if(this.draggable !== false){
14251         el.addClass("x-dlg-draggable");
14252         if (!this.proxyDrag) {
14253             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
14254         }
14255         else {
14256             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
14257         }
14258         dd.setHandleElId(this.header.id);
14259         dd.endDrag = this.endMove.createDelegate(this);
14260         dd.startDrag = this.startMove.createDelegate(this);
14261         dd.onDrag = this.onDrag.createDelegate(this);
14262         dd.scroll = false;
14263         this.dd = dd;
14264     }
14265     if(this.modal){
14266         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
14267         this.mask.enableDisplayMode("block");
14268         this.mask.hide();
14269         this.el.addClass("x-dlg-modal");
14270     }
14271     if(this.shadow){
14272         this.shadow = new Roo.Shadow({
14273             mode : typeof this.shadow == "string" ? this.shadow : "sides",
14274             offset : this.shadowOffset
14275         });
14276     }else{
14277         this.shadowOffset = 0;
14278     }
14279     if(Roo.useShims && this.shim !== false){
14280         this.shim = this.el.createShim();
14281         this.shim.hide = this.hideAction;
14282         this.shim.hide();
14283     }else{
14284         this.shim = false;
14285     }
14286     if(this.autoTabs){
14287         this.initTabs();
14288     }
14289     if (this.buttons) { 
14290         var bts= this.buttons;
14291         this.buttons = [];
14292         Roo.each(bts, function(b) {
14293             this.addButton(b);
14294         }, this);
14295     }
14296     
14297     
14298     this.addEvents({
14299         /**
14300          * @event keydown
14301          * Fires when a key is pressed
14302          * @param {Roo.BasicDialog} this
14303          * @param {Roo.EventObject} e
14304          */
14305         "keydown" : true,
14306         /**
14307          * @event move
14308          * Fires when this dialog is moved by the user.
14309          * @param {Roo.BasicDialog} this
14310          * @param {Number} x The new page X
14311          * @param {Number} y The new page Y
14312          */
14313         "move" : true,
14314         /**
14315          * @event resize
14316          * Fires when this dialog is resized by the user.
14317          * @param {Roo.BasicDialog} this
14318          * @param {Number} width The new width
14319          * @param {Number} height The new height
14320          */
14321         "resize" : true,
14322         /**
14323          * @event beforehide
14324          * Fires before this dialog is hidden.
14325          * @param {Roo.BasicDialog} this
14326          */
14327         "beforehide" : true,
14328         /**
14329          * @event hide
14330          * Fires when this dialog is hidden.
14331          * @param {Roo.BasicDialog} this
14332          */
14333         "hide" : true,
14334         /**
14335          * @event beforeshow
14336          * Fires before this dialog is shown.
14337          * @param {Roo.BasicDialog} this
14338          */
14339         "beforeshow" : true,
14340         /**
14341          * @event show
14342          * Fires when this dialog is shown.
14343          * @param {Roo.BasicDialog} this
14344          */
14345         "show" : true
14346     });
14347     el.on("keydown", this.onKeyDown, this);
14348     el.on("mousedown", this.toFront, this);
14349     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
14350     this.el.hide();
14351     Roo.DialogManager.register(this);
14352     Roo.BasicDialog.superclass.constructor.call(this);
14353 };
14354
14355 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
14356     shadowOffset: Roo.isIE ? 6 : 5,
14357     minHeight: 80,
14358     minWidth: 200,
14359     minButtonWidth: 75,
14360     defaultButton: null,
14361     buttonAlign: "right",
14362     tabTag: 'div',
14363     firstShow: true,
14364
14365     /**
14366      * Sets the dialog title text
14367      * @param {String} text The title text to display
14368      * @return {Roo.BasicDialog} this
14369      */
14370     setTitle : function(text){
14371         this.header.update(text);
14372         return this;
14373     },
14374
14375     // private
14376     closeClick : function(){
14377         this.hide();
14378     },
14379
14380     // private
14381     collapseClick : function(){
14382         this[this.collapsed ? "expand" : "collapse"]();
14383     },
14384
14385     /**
14386      * Collapses the dialog to its minimized state (only the title bar is visible).
14387      * Equivalent to the user clicking the collapse dialog button.
14388      */
14389     collapse : function(){
14390         if(!this.collapsed){
14391             this.collapsed = true;
14392             this.el.addClass("x-dlg-collapsed");
14393             this.restoreHeight = this.el.getHeight();
14394             this.resizeTo(this.el.getWidth(), this.header.getHeight());
14395         }
14396     },
14397
14398     /**
14399      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
14400      * clicking the expand dialog button.
14401      */
14402     expand : function(){
14403         if(this.collapsed){
14404             this.collapsed = false;
14405             this.el.removeClass("x-dlg-collapsed");
14406             this.resizeTo(this.el.getWidth(), this.restoreHeight);
14407         }
14408     },
14409
14410     /**
14411      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
14412      * @return {Roo.TabPanel} The tabs component
14413      */
14414     initTabs : function(){
14415         var tabs = this.getTabs();
14416         while(tabs.getTab(0)){
14417             tabs.removeTab(0);
14418         }
14419         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
14420             var dom = el.dom;
14421             tabs.addTab(Roo.id(dom), dom.title);
14422             dom.title = "";
14423         });
14424         tabs.activate(0);
14425         return tabs;
14426     },
14427
14428     // private
14429     beforeResize : function(){
14430         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
14431     },
14432
14433     // private
14434     onResize : function(){
14435         this.refreshSize();
14436         this.syncBodyHeight();
14437         this.adjustAssets();
14438         this.focus();
14439         this.fireEvent("resize", this, this.size.width, this.size.height);
14440     },
14441
14442     // private
14443     onKeyDown : function(e){
14444         if(this.isVisible()){
14445             this.fireEvent("keydown", this, e);
14446         }
14447     },
14448
14449     /**
14450      * Resizes the dialog.
14451      * @param {Number} width
14452      * @param {Number} height
14453      * @return {Roo.BasicDialog} this
14454      */
14455     resizeTo : function(width, height){
14456         this.el.setSize(width, height);
14457         this.size = {width: width, height: height};
14458         this.syncBodyHeight();
14459         if(this.fixedcenter){
14460             this.center();
14461         }
14462         if(this.isVisible()){
14463             this.constrainXY();
14464             this.adjustAssets();
14465         }
14466         this.fireEvent("resize", this, width, height);
14467         return this;
14468     },
14469
14470
14471     /**
14472      * Resizes the dialog to fit the specified content size.
14473      * @param {Number} width
14474      * @param {Number} height
14475      * @return {Roo.BasicDialog} this
14476      */
14477     setContentSize : function(w, h){
14478         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
14479         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
14480         //if(!this.el.isBorderBox()){
14481             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
14482             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
14483         //}
14484         if(this.tabs){
14485             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
14486             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
14487         }
14488         this.resizeTo(w, h);
14489         return this;
14490     },
14491
14492     /**
14493      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
14494      * executed in response to a particular key being pressed while the dialog is active.
14495      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
14496      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14497      * @param {Function} fn The function to call
14498      * @param {Object} scope (optional) The scope of the function
14499      * @return {Roo.BasicDialog} this
14500      */
14501     addKeyListener : function(key, fn, scope){
14502         var keyCode, shift, ctrl, alt;
14503         if(typeof key == "object" && !(key instanceof Array)){
14504             keyCode = key["key"];
14505             shift = key["shift"];
14506             ctrl = key["ctrl"];
14507             alt = key["alt"];
14508         }else{
14509             keyCode = key;
14510         }
14511         var handler = function(dlg, e){
14512             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14513                 var k = e.getKey();
14514                 if(keyCode instanceof Array){
14515                     for(var i = 0, len = keyCode.length; i < len; i++){
14516                         if(keyCode[i] == k){
14517                           fn.call(scope || window, dlg, k, e);
14518                           return;
14519                         }
14520                     }
14521                 }else{
14522                     if(k == keyCode){
14523                         fn.call(scope || window, dlg, k, e);
14524                     }
14525                 }
14526             }
14527         };
14528         this.on("keydown", handler);
14529         return this;
14530     },
14531
14532     /**
14533      * Returns the TabPanel component (creates it if it doesn't exist).
14534      * Note: If you wish to simply check for the existence of tabs without creating them,
14535      * check for a null 'tabs' property.
14536      * @return {Roo.TabPanel} The tabs component
14537      */
14538     getTabs : function(){
14539         if(!this.tabs){
14540             this.el.addClass("x-dlg-auto-tabs");
14541             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
14542             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
14543         }
14544         return this.tabs;
14545     },
14546
14547     /**
14548      * Adds a button to the footer section of the dialog.
14549      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
14550      * object or a valid Roo.DomHelper element config
14551      * @param {Function} handler The function called when the button is clicked
14552      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
14553      * @return {Roo.Button} The new button
14554      */
14555     addButton : function(config, handler, scope){
14556         var dh = Roo.DomHelper;
14557         if(!this.footer){
14558             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
14559         }
14560         if(!this.btnContainer){
14561             var tb = this.footer.createChild({
14562
14563                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
14564                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
14565             }, null, true);
14566             this.btnContainer = tb.firstChild.firstChild.firstChild;
14567         }
14568         var bconfig = {
14569             handler: handler,
14570             scope: scope,
14571             minWidth: this.minButtonWidth,
14572             hideParent:true
14573         };
14574         if(typeof config == "string"){
14575             bconfig.text = config;
14576         }else{
14577             if(config.tag){
14578                 bconfig.dhconfig = config;
14579             }else{
14580                 Roo.apply(bconfig, config);
14581             }
14582         }
14583         var fc = false;
14584         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
14585             bconfig.position = Math.max(0, bconfig.position);
14586             fc = this.btnContainer.childNodes[bconfig.position];
14587         }
14588          
14589         var btn = new Roo.Button(
14590             fc ? 
14591                 this.btnContainer.insertBefore(document.createElement("td"),fc)
14592                 : this.btnContainer.appendChild(document.createElement("td")),
14593             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
14594             bconfig
14595         );
14596         this.syncBodyHeight();
14597         if(!this.buttons){
14598             /**
14599              * Array of all the buttons that have been added to this dialog via addButton
14600              * @type Array
14601              */
14602             this.buttons = [];
14603         }
14604         this.buttons.push(btn);
14605         return btn;
14606     },
14607
14608     /**
14609      * Sets the default button to be focused when the dialog is displayed.
14610      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
14611      * @return {Roo.BasicDialog} this
14612      */
14613     setDefaultButton : function(btn){
14614         this.defaultButton = btn;
14615         return this;
14616     },
14617
14618     // private
14619     getHeaderFooterHeight : function(safe){
14620         var height = 0;
14621         if(this.header){
14622            height += this.header.getHeight();
14623         }
14624         if(this.footer){
14625            var fm = this.footer.getMargins();
14626             height += (this.footer.getHeight()+fm.top+fm.bottom);
14627         }
14628         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
14629         height += this.centerBg.getPadding("tb");
14630         return height;
14631     },
14632
14633     // private
14634     syncBodyHeight : function(){
14635         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
14636         var height = this.size.height - this.getHeaderFooterHeight(false);
14637         bd.setHeight(height-bd.getMargins("tb"));
14638         var hh = this.header.getHeight();
14639         var h = this.size.height-hh;
14640         cb.setHeight(h);
14641         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
14642         bw.setHeight(h-cb.getPadding("tb"));
14643         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
14644         bd.setWidth(bw.getWidth(true));
14645         if(this.tabs){
14646             this.tabs.syncHeight();
14647             if(Roo.isIE){
14648                 this.tabs.el.repaint();
14649             }
14650         }
14651     },
14652
14653     /**
14654      * Restores the previous state of the dialog if Roo.state is configured.
14655      * @return {Roo.BasicDialog} this
14656      */
14657     restoreState : function(){
14658         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
14659         if(box && box.width){
14660             this.xy = [box.x, box.y];
14661             this.resizeTo(box.width, box.height);
14662         }
14663         return this;
14664     },
14665
14666     // private
14667     beforeShow : function(){
14668         this.expand();
14669         if(this.fixedcenter){
14670             this.xy = this.el.getCenterXY(true);
14671         }
14672         if(this.modal){
14673             Roo.get(document.body).addClass("x-body-masked");
14674             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14675             this.mask.show();
14676         }
14677         this.constrainXY();
14678     },
14679
14680     // private
14681     animShow : function(){
14682         var b = Roo.get(this.animateTarget).getBox();
14683         this.proxy.setSize(b.width, b.height);
14684         this.proxy.setLocation(b.x, b.y);
14685         this.proxy.show();
14686         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
14687                     true, .35, this.showEl.createDelegate(this));
14688     },
14689
14690     /**
14691      * Shows the dialog.
14692      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
14693      * @return {Roo.BasicDialog} this
14694      */
14695     show : function(animateTarget){
14696         if (this.fireEvent("beforeshow", this) === false){
14697             return;
14698         }
14699         if(this.syncHeightBeforeShow){
14700             this.syncBodyHeight();
14701         }else if(this.firstShow){
14702             this.firstShow = false;
14703             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
14704         }
14705         this.animateTarget = animateTarget || this.animateTarget;
14706         if(!this.el.isVisible()){
14707             this.beforeShow();
14708             if(this.animateTarget && Roo.get(this.animateTarget)){
14709                 this.animShow();
14710             }else{
14711                 this.showEl();
14712             }
14713         }
14714         return this;
14715     },
14716
14717     // private
14718     showEl : function(){
14719         this.proxy.hide();
14720         this.el.setXY(this.xy);
14721         this.el.show();
14722         this.adjustAssets(true);
14723         this.toFront();
14724         this.focus();
14725         // IE peekaboo bug - fix found by Dave Fenwick
14726         if(Roo.isIE){
14727             this.el.repaint();
14728         }
14729         this.fireEvent("show", this);
14730     },
14731
14732     /**
14733      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
14734      * dialog itself will receive focus.
14735      */
14736     focus : function(){
14737         if(this.defaultButton){
14738             this.defaultButton.focus();
14739         }else{
14740             this.focusEl.focus();
14741         }
14742     },
14743
14744     // private
14745     constrainXY : function(){
14746         if(this.constraintoviewport !== false){
14747             if(!this.viewSize){
14748                 if(this.container){
14749                     var s = this.container.getSize();
14750                     this.viewSize = [s.width, s.height];
14751                 }else{
14752                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
14753                 }
14754             }
14755             var s = Roo.get(this.container||document).getScroll();
14756
14757             var x = this.xy[0], y = this.xy[1];
14758             var w = this.size.width, h = this.size.height;
14759             var vw = this.viewSize[0], vh = this.viewSize[1];
14760             // only move it if it needs it
14761             var moved = false;
14762             // first validate right/bottom
14763             if(x + w > vw+s.left){
14764                 x = vw - w;
14765                 moved = true;
14766             }
14767             if(y + h > vh+s.top){
14768                 y = vh - h;
14769                 moved = true;
14770             }
14771             // then make sure top/left isn't negative
14772             if(x < s.left){
14773                 x = s.left;
14774                 moved = true;
14775             }
14776             if(y < s.top){
14777                 y = s.top;
14778                 moved = true;
14779             }
14780             if(moved){
14781                 // cache xy
14782                 this.xy = [x, y];
14783                 if(this.isVisible()){
14784                     this.el.setLocation(x, y);
14785                     this.adjustAssets();
14786                 }
14787             }
14788         }
14789     },
14790
14791     // private
14792     onDrag : function(){
14793         if(!this.proxyDrag){
14794             this.xy = this.el.getXY();
14795             this.adjustAssets();
14796         }
14797     },
14798
14799     // private
14800     adjustAssets : function(doShow){
14801         var x = this.xy[0], y = this.xy[1];
14802         var w = this.size.width, h = this.size.height;
14803         if(doShow === true){
14804             if(this.shadow){
14805                 this.shadow.show(this.el);
14806             }
14807             if(this.shim){
14808                 this.shim.show();
14809             }
14810         }
14811         if(this.shadow && this.shadow.isVisible()){
14812             this.shadow.show(this.el);
14813         }
14814         if(this.shim && this.shim.isVisible()){
14815             this.shim.setBounds(x, y, w, h);
14816         }
14817     },
14818
14819     // private
14820     adjustViewport : function(w, h){
14821         if(!w || !h){
14822             w = Roo.lib.Dom.getViewWidth();
14823             h = Roo.lib.Dom.getViewHeight();
14824         }
14825         // cache the size
14826         this.viewSize = [w, h];
14827         if(this.modal && this.mask.isVisible()){
14828             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
14829             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14830         }
14831         if(this.isVisible()){
14832             this.constrainXY();
14833         }
14834     },
14835
14836     /**
14837      * Destroys this dialog and all its supporting elements (including any tabs, shim,
14838      * shadow, proxy, mask, etc.)  Also removes all event listeners.
14839      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
14840      */
14841     destroy : function(removeEl){
14842         if(this.isVisible()){
14843             this.animateTarget = null;
14844             this.hide();
14845         }
14846         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
14847         if(this.tabs){
14848             this.tabs.destroy(removeEl);
14849         }
14850         Roo.destroy(
14851              this.shim,
14852              this.proxy,
14853              this.resizer,
14854              this.close,
14855              this.mask
14856         );
14857         if(this.dd){
14858             this.dd.unreg();
14859         }
14860         if(this.buttons){
14861            for(var i = 0, len = this.buttons.length; i < len; i++){
14862                this.buttons[i].destroy();
14863            }
14864         }
14865         this.el.removeAllListeners();
14866         if(removeEl === true){
14867             this.el.update("");
14868             this.el.remove();
14869         }
14870         Roo.DialogManager.unregister(this);
14871     },
14872
14873     // private
14874     startMove : function(){
14875         if(this.proxyDrag){
14876             this.proxy.show();
14877         }
14878         if(this.constraintoviewport !== false){
14879             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
14880         }
14881     },
14882
14883     // private
14884     endMove : function(){
14885         if(!this.proxyDrag){
14886             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
14887         }else{
14888             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
14889             this.proxy.hide();
14890         }
14891         this.refreshSize();
14892         this.adjustAssets();
14893         this.focus();
14894         this.fireEvent("move", this, this.xy[0], this.xy[1]);
14895     },
14896
14897     /**
14898      * Brings this dialog to the front of any other visible dialogs
14899      * @return {Roo.BasicDialog} this
14900      */
14901     toFront : function(){
14902         Roo.DialogManager.bringToFront(this);
14903         return this;
14904     },
14905
14906     /**
14907      * Sends this dialog to the back (under) of any other visible dialogs
14908      * @return {Roo.BasicDialog} this
14909      */
14910     toBack : function(){
14911         Roo.DialogManager.sendToBack(this);
14912         return this;
14913     },
14914
14915     /**
14916      * Centers this dialog in the viewport
14917      * @return {Roo.BasicDialog} this
14918      */
14919     center : function(){
14920         var xy = this.el.getCenterXY(true);
14921         this.moveTo(xy[0], xy[1]);
14922         return this;
14923     },
14924
14925     /**
14926      * Moves the dialog's top-left corner to the specified point
14927      * @param {Number} x
14928      * @param {Number} y
14929      * @return {Roo.BasicDialog} this
14930      */
14931     moveTo : function(x, y){
14932         this.xy = [x,y];
14933         if(this.isVisible()){
14934             this.el.setXY(this.xy);
14935             this.adjustAssets();
14936         }
14937         return this;
14938     },
14939
14940     /**
14941      * Aligns the dialog to the specified element
14942      * @param {String/HTMLElement/Roo.Element} element The element to align to.
14943      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
14944      * @param {Array} offsets (optional) Offset the positioning by [x, y]
14945      * @return {Roo.BasicDialog} this
14946      */
14947     alignTo : function(element, position, offsets){
14948         this.xy = this.el.getAlignToXY(element, position, offsets);
14949         if(this.isVisible()){
14950             this.el.setXY(this.xy);
14951             this.adjustAssets();
14952         }
14953         return this;
14954     },
14955
14956     /**
14957      * Anchors an element to another element and realigns it when the window is resized.
14958      * @param {String/HTMLElement/Roo.Element} element The element to align to.
14959      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
14960      * @param {Array} offsets (optional) Offset the positioning by [x, y]
14961      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
14962      * is a number, it is used as the buffer delay (defaults to 50ms).
14963      * @return {Roo.BasicDialog} this
14964      */
14965     anchorTo : function(el, alignment, offsets, monitorScroll){
14966         var action = function(){
14967             this.alignTo(el, alignment, offsets);
14968         };
14969         Roo.EventManager.onWindowResize(action, this);
14970         var tm = typeof monitorScroll;
14971         if(tm != 'undefined'){
14972             Roo.EventManager.on(window, 'scroll', action, this,
14973                 {buffer: tm == 'number' ? monitorScroll : 50});
14974         }
14975         action.call(this);
14976         return this;
14977     },
14978
14979     /**
14980      * Returns true if the dialog is visible
14981      * @return {Boolean}
14982      */
14983     isVisible : function(){
14984         return this.el.isVisible();
14985     },
14986
14987     // private
14988     animHide : function(callback){
14989         var b = Roo.get(this.animateTarget).getBox();
14990         this.proxy.show();
14991         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
14992         this.el.hide();
14993         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
14994                     this.hideEl.createDelegate(this, [callback]));
14995     },
14996
14997     /**
14998      * Hides the dialog.
14999      * @param {Function} callback (optional) Function to call when the dialog is hidden
15000      * @return {Roo.BasicDialog} this
15001      */
15002     hide : function(callback){
15003         if (this.fireEvent("beforehide", this) === false){
15004             return;
15005         }
15006         if(this.shadow){
15007             this.shadow.hide();
15008         }
15009         if(this.shim) {
15010           this.shim.hide();
15011         }
15012         // sometimes animateTarget seems to get set.. causing problems...
15013         // this just double checks..
15014         if(this.animateTarget && Roo.get(this.animateTarget)) {
15015            this.animHide(callback);
15016         }else{
15017             this.el.hide();
15018             this.hideEl(callback);
15019         }
15020         return this;
15021     },
15022
15023     // private
15024     hideEl : function(callback){
15025         this.proxy.hide();
15026         if(this.modal){
15027             this.mask.hide();
15028             Roo.get(document.body).removeClass("x-body-masked");
15029         }
15030         this.fireEvent("hide", this);
15031         if(typeof callback == "function"){
15032             callback();
15033         }
15034     },
15035
15036     // private
15037     hideAction : function(){
15038         this.setLeft("-10000px");
15039         this.setTop("-10000px");
15040         this.setStyle("visibility", "hidden");
15041     },
15042
15043     // private
15044     refreshSize : function(){
15045         this.size = this.el.getSize();
15046         this.xy = this.el.getXY();
15047         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
15048     },
15049
15050     // private
15051     // z-index is managed by the DialogManager and may be overwritten at any time
15052     setZIndex : function(index){
15053         if(this.modal){
15054             this.mask.setStyle("z-index", index);
15055         }
15056         if(this.shim){
15057             this.shim.setStyle("z-index", ++index);
15058         }
15059         if(this.shadow){
15060             this.shadow.setZIndex(++index);
15061         }
15062         this.el.setStyle("z-index", ++index);
15063         if(this.proxy){
15064             this.proxy.setStyle("z-index", ++index);
15065         }
15066         if(this.resizer){
15067             this.resizer.proxy.setStyle("z-index", ++index);
15068         }
15069
15070         this.lastZIndex = index;
15071     },
15072
15073     /**
15074      * Returns the element for this dialog
15075      * @return {Roo.Element} The underlying dialog Element
15076      */
15077     getEl : function(){
15078         return this.el;
15079     }
15080 });
15081
15082 /**
15083  * @class Roo.DialogManager
15084  * Provides global access to BasicDialogs that have been created and
15085  * support for z-indexing (layering) multiple open dialogs.
15086  */
15087 Roo.DialogManager = function(){
15088     var list = {};
15089     var accessList = [];
15090     var front = null;
15091
15092     // private
15093     var sortDialogs = function(d1, d2){
15094         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
15095     };
15096
15097     // private
15098     var orderDialogs = function(){
15099         accessList.sort(sortDialogs);
15100         var seed = Roo.DialogManager.zseed;
15101         for(var i = 0, len = accessList.length; i < len; i++){
15102             var dlg = accessList[i];
15103             if(dlg){
15104                 dlg.setZIndex(seed + (i*10));
15105             }
15106         }
15107     };
15108
15109     return {
15110         /**
15111          * The starting z-index for BasicDialogs (defaults to 9000)
15112          * @type Number The z-index value
15113          */
15114         zseed : 9000,
15115
15116         // private
15117         register : function(dlg){
15118             list[dlg.id] = dlg;
15119             accessList.push(dlg);
15120         },
15121
15122         // private
15123         unregister : function(dlg){
15124             delete list[dlg.id];
15125             var i=0;
15126             var len=0;
15127             if(!accessList.indexOf){
15128                 for(  i = 0, len = accessList.length; i < len; i++){
15129                     if(accessList[i] == dlg){
15130                         accessList.splice(i, 1);
15131                         return;
15132                     }
15133                 }
15134             }else{
15135                  i = accessList.indexOf(dlg);
15136                 if(i != -1){
15137                     accessList.splice(i, 1);
15138                 }
15139             }
15140         },
15141
15142         /**
15143          * Gets a registered dialog by id
15144          * @param {String/Object} id The id of the dialog or a dialog
15145          * @return {Roo.BasicDialog} this
15146          */
15147         get : function(id){
15148             return typeof id == "object" ? id : list[id];
15149         },
15150
15151         /**
15152          * Brings the specified dialog to the front
15153          * @param {String/Object} dlg The id of the dialog or a dialog
15154          * @return {Roo.BasicDialog} this
15155          */
15156         bringToFront : function(dlg){
15157             dlg = this.get(dlg);
15158             if(dlg != front){
15159                 front = dlg;
15160                 dlg._lastAccess = new Date().getTime();
15161                 orderDialogs();
15162             }
15163             return dlg;
15164         },
15165
15166         /**
15167          * Sends the specified dialog to the back
15168          * @param {String/Object} dlg The id of the dialog or a dialog
15169          * @return {Roo.BasicDialog} this
15170          */
15171         sendToBack : function(dlg){
15172             dlg = this.get(dlg);
15173             dlg._lastAccess = -(new Date().getTime());
15174             orderDialogs();
15175             return dlg;
15176         },
15177
15178         /**
15179          * Hides all dialogs
15180          */
15181         hideAll : function(){
15182             for(var id in list){
15183                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
15184                     list[id].hide();
15185                 }
15186             }
15187         }
15188     };
15189 }();
15190
15191 /**
15192  * @class Roo.LayoutDialog
15193  * @extends Roo.BasicDialog
15194  * Dialog which provides adjustments for working with a layout in a Dialog.
15195  * Add your necessary layout config options to the dialog's config.<br>
15196  * Example usage (including a nested layout):
15197  * <pre><code>
15198 if(!dialog){
15199     dialog = new Roo.LayoutDialog("download-dlg", {
15200         modal: true,
15201         width:600,
15202         height:450,
15203         shadow:true,
15204         minWidth:500,
15205         minHeight:350,
15206         autoTabs:true,
15207         proxyDrag:true,
15208         // layout config merges with the dialog config
15209         center:{
15210             tabPosition: "top",
15211             alwaysShowTabs: true
15212         }
15213     });
15214     dialog.addKeyListener(27, dialog.hide, dialog);
15215     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
15216     dialog.addButton("Build It!", this.getDownload, this);
15217
15218     // we can even add nested layouts
15219     var innerLayout = new Roo.BorderLayout("dl-inner", {
15220         east: {
15221             initialSize: 200,
15222             autoScroll:true,
15223             split:true
15224         },
15225         center: {
15226             autoScroll:true
15227         }
15228     });
15229     innerLayout.beginUpdate();
15230     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
15231     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
15232     innerLayout.endUpdate(true);
15233
15234     var layout = dialog.getLayout();
15235     layout.beginUpdate();
15236     layout.add("center", new Roo.ContentPanel("standard-panel",
15237                         {title: "Download the Source", fitToFrame:true}));
15238     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
15239                {title: "Build your own roo.js"}));
15240     layout.getRegion("center").showPanel(sp);
15241     layout.endUpdate();
15242 }
15243 </code></pre>
15244     * @constructor
15245     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
15246     * @param {Object} config configuration options
15247   */
15248 Roo.LayoutDialog = function(el, cfg){
15249     
15250     var config=  cfg;
15251     if (typeof(cfg) == 'undefined') {
15252         config = Roo.apply({}, el);
15253         // not sure why we use documentElement here.. - it should always be body.
15254         // IE7 borks horribly if we use documentElement.
15255         el = Roo.get( Roo.isIE ? (document.body || document.documentElement) : (document.documentElement || document.body) ).createChild();
15256         //config.autoCreate = true;
15257     }
15258     
15259     
15260     config.autoTabs = false;
15261     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
15262     this.body.setStyle({overflow:"hidden", position:"relative"});
15263     this.layout = new Roo.BorderLayout(this.body.dom, config);
15264     this.layout.monitorWindowResize = false;
15265     this.el.addClass("x-dlg-auto-layout");
15266     // fix case when center region overwrites center function
15267     this.center = Roo.BasicDialog.prototype.center;
15268     this.on("show", this.layout.layout, this.layout, true);
15269     if (config.items) {
15270         var xitems = config.items;
15271         delete config.items;
15272         Roo.each(xitems, this.addxtype, this);
15273     }
15274     
15275     
15276 };
15277 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
15278     /**
15279      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
15280      * @deprecated
15281      */
15282     endUpdate : function(){
15283         this.layout.endUpdate();
15284     },
15285
15286     /**
15287      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
15288      *  @deprecated
15289      */
15290     beginUpdate : function(){
15291         this.layout.beginUpdate();
15292     },
15293
15294     /**
15295      * Get the BorderLayout for this dialog
15296      * @return {Roo.BorderLayout}
15297      */
15298     getLayout : function(){
15299         return this.layout;
15300     },
15301
15302     showEl : function(){
15303         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
15304         if(Roo.isIE7){
15305             this.layout.layout();
15306         }
15307     },
15308
15309     // private
15310     // Use the syncHeightBeforeShow config option to control this automatically
15311     syncBodyHeight : function(){
15312         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
15313         if(this.layout){this.layout.layout();}
15314     },
15315     
15316       /**
15317      * Add an xtype element (actually adds to the layout.)
15318      * @return {Object} xdata xtype object data.
15319      */
15320     
15321     addxtype : function(c) {
15322         return this.layout.addxtype(c);
15323     }
15324 });/*
15325  * Based on:
15326  * Ext JS Library 1.1.1
15327  * Copyright(c) 2006-2007, Ext JS, LLC.
15328  *
15329  * Originally Released Under LGPL - original licence link has changed is not relivant.
15330  *
15331  * Fork - LGPL
15332  * <script type="text/javascript">
15333  */
15334  
15335 /**
15336  * @class Roo.MessageBox
15337  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
15338  * Example usage:
15339  *<pre><code>
15340 // Basic alert:
15341 Roo.Msg.alert('Status', 'Changes saved successfully.');
15342
15343 // Prompt for user data:
15344 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
15345     if (btn == 'ok'){
15346         // process text value...
15347     }
15348 });
15349
15350 // Show a dialog using config options:
15351 Roo.Msg.show({
15352    title:'Save Changes?',
15353    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
15354    buttons: Roo.Msg.YESNOCANCEL,
15355    fn: processResult,
15356    animEl: 'elId'
15357 });
15358 </code></pre>
15359  * @singleton
15360  */
15361 Roo.MessageBox = function(){
15362     var dlg, opt, mask, waitTimer;
15363     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
15364     var buttons, activeTextEl, bwidth;
15365
15366     // private
15367     var handleButton = function(button){
15368         dlg.hide();
15369         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
15370     };
15371
15372     // private
15373     var handleHide = function(){
15374         if(opt && opt.cls){
15375             dlg.el.removeClass(opt.cls);
15376         }
15377         if(waitTimer){
15378             Roo.TaskMgr.stop(waitTimer);
15379             waitTimer = null;
15380         }
15381     };
15382
15383     // private
15384     var updateButtons = function(b){
15385         var width = 0;
15386         if(!b){
15387             buttons["ok"].hide();
15388             buttons["cancel"].hide();
15389             buttons["yes"].hide();
15390             buttons["no"].hide();
15391             dlg.footer.dom.style.display = 'none';
15392             return width;
15393         }
15394         dlg.footer.dom.style.display = '';
15395         for(var k in buttons){
15396             if(typeof buttons[k] != "function"){
15397                 if(b[k]){
15398                     buttons[k].show();
15399                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
15400                     width += buttons[k].el.getWidth()+15;
15401                 }else{
15402                     buttons[k].hide();
15403                 }
15404             }
15405         }
15406         return width;
15407     };
15408
15409     // private
15410     var handleEsc = function(d, k, e){
15411         if(opt && opt.closable !== false){
15412             dlg.hide();
15413         }
15414         if(e){
15415             e.stopEvent();
15416         }
15417     };
15418
15419     return {
15420         /**
15421          * Returns a reference to the underlying {@link Roo.BasicDialog} element
15422          * @return {Roo.BasicDialog} The BasicDialog element
15423          */
15424         getDialog : function(){
15425            if(!dlg){
15426                 dlg = new Roo.BasicDialog("x-msg-box", {
15427                     autoCreate : true,
15428                     shadow: true,
15429                     draggable: true,
15430                     resizable:false,
15431                     constraintoviewport:false,
15432                     fixedcenter:true,
15433                     collapsible : false,
15434                     shim:true,
15435                     modal: true,
15436                     width:400, height:100,
15437                     buttonAlign:"center",
15438                     closeClick : function(){
15439                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
15440                             handleButton("no");
15441                         }else{
15442                             handleButton("cancel");
15443                         }
15444                     }
15445                 });
15446                 dlg.on("hide", handleHide);
15447                 mask = dlg.mask;
15448                 dlg.addKeyListener(27, handleEsc);
15449                 buttons = {};
15450                 var bt = this.buttonText;
15451                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
15452                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
15453                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
15454                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
15455                 bodyEl = dlg.body.createChild({
15456
15457                     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>'
15458                 });
15459                 msgEl = bodyEl.dom.firstChild;
15460                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
15461                 textboxEl.enableDisplayMode();
15462                 textboxEl.addKeyListener([10,13], function(){
15463                     if(dlg.isVisible() && opt && opt.buttons){
15464                         if(opt.buttons.ok){
15465                             handleButton("ok");
15466                         }else if(opt.buttons.yes){
15467                             handleButton("yes");
15468                         }
15469                     }
15470                 });
15471                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
15472                 textareaEl.enableDisplayMode();
15473                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
15474                 progressEl.enableDisplayMode();
15475                 var pf = progressEl.dom.firstChild;
15476                 if (pf) {
15477                     pp = Roo.get(pf.firstChild);
15478                     pp.setHeight(pf.offsetHeight);
15479                 }
15480                 
15481             }
15482             return dlg;
15483         },
15484
15485         /**
15486          * Updates the message box body text
15487          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
15488          * the XHTML-compliant non-breaking space character '&amp;#160;')
15489          * @return {Roo.MessageBox} This message box
15490          */
15491         updateText : function(text){
15492             if(!dlg.isVisible() && !opt.width){
15493                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
15494             }
15495             msgEl.innerHTML = text || '&#160;';
15496             var w = Math.max(Math.min(opt.width || msgEl.offsetWidth, this.maxWidth), 
15497                         Math.max(opt.minWidth || this.minWidth, bwidth));
15498             if(opt.prompt){
15499                 activeTextEl.setWidth(w);
15500             }
15501             if(dlg.isVisible()){
15502                 dlg.fixedcenter = false;
15503             }
15504             dlg.setContentSize(w, bodyEl.getHeight());
15505             if(dlg.isVisible()){
15506                 dlg.fixedcenter = true;
15507             }
15508             return this;
15509         },
15510
15511         /**
15512          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
15513          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
15514          * @param {Number} value Any number between 0 and 1 (e.g., .5)
15515          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
15516          * @return {Roo.MessageBox} This message box
15517          */
15518         updateProgress : function(value, text){
15519             if(text){
15520                 this.updateText(text);
15521             }
15522             if (pp) { // weird bug on my firefox - for some reason this is not defined
15523                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
15524             }
15525             return this;
15526         },        
15527
15528         /**
15529          * Returns true if the message box is currently displayed
15530          * @return {Boolean} True if the message box is visible, else false
15531          */
15532         isVisible : function(){
15533             return dlg && dlg.isVisible();  
15534         },
15535
15536         /**
15537          * Hides the message box if it is displayed
15538          */
15539         hide : function(){
15540             if(this.isVisible()){
15541                 dlg.hide();
15542             }  
15543         },
15544
15545         /**
15546          * Displays a new message box, or reinitializes an existing message box, based on the config options
15547          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
15548          * The following config object properties are supported:
15549          * <pre>
15550 Property    Type             Description
15551 ----------  ---------------  ------------------------------------------------------------------------------------
15552 animEl            String/Element   An id or Element from which the message box should animate as it opens and
15553                                    closes (defaults to undefined)
15554 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
15555                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
15556 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
15557                                    progress and wait dialogs will ignore this property and always hide the
15558                                    close button as they can only be closed programmatically.
15559 cls               String           A custom CSS class to apply to the message box element
15560 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
15561                                    displayed (defaults to 75)
15562 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
15563                                    function will be btn (the name of the button that was clicked, if applicable,
15564                                    e.g. "ok"), and text (the value of the active text field, if applicable).
15565                                    Progress and wait dialogs will ignore this option since they do not respond to
15566                                    user actions and can only be closed programmatically, so any required function
15567                                    should be called by the same code after it closes the dialog.
15568 icon              String           A CSS class that provides a background image to be used as an icon for
15569                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
15570 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
15571 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
15572 modal             Boolean          False to allow user interaction with the page while the message box is
15573                                    displayed (defaults to true)
15574 msg               String           A string that will replace the existing message box body text (defaults
15575                                    to the XHTML-compliant non-breaking space character '&#160;')
15576 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
15577 progress          Boolean          True to display a progress bar (defaults to false)
15578 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
15579 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
15580 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
15581 title             String           The title text
15582 value             String           The string value to set into the active textbox element if displayed
15583 wait              Boolean          True to display a progress bar (defaults to false)
15584 width             Number           The width of the dialog in pixels
15585 </pre>
15586          *
15587          * Example usage:
15588          * <pre><code>
15589 Roo.Msg.show({
15590    title: 'Address',
15591    msg: 'Please enter your address:',
15592    width: 300,
15593    buttons: Roo.MessageBox.OKCANCEL,
15594    multiline: true,
15595    fn: saveAddress,
15596    animEl: 'addAddressBtn'
15597 });
15598 </code></pre>
15599          * @param {Object} config Configuration options
15600          * @return {Roo.MessageBox} This message box
15601          */
15602         show : function(options){
15603             if(this.isVisible()){
15604                 this.hide();
15605             }
15606             var d = this.getDialog();
15607             opt = options;
15608             d.setTitle(opt.title || "&#160;");
15609             d.close.setDisplayed(opt.closable !== false);
15610             activeTextEl = textboxEl;
15611             opt.prompt = opt.prompt || (opt.multiline ? true : false);
15612             if(opt.prompt){
15613                 if(opt.multiline){
15614                     textboxEl.hide();
15615                     textareaEl.show();
15616                     textareaEl.setHeight(typeof opt.multiline == "number" ?
15617                         opt.multiline : this.defaultTextHeight);
15618                     activeTextEl = textareaEl;
15619                 }else{
15620                     textboxEl.show();
15621                     textareaEl.hide();
15622                 }
15623             }else{
15624                 textboxEl.hide();
15625                 textareaEl.hide();
15626             }
15627             progressEl.setDisplayed(opt.progress === true);
15628             this.updateProgress(0);
15629             activeTextEl.dom.value = opt.value || "";
15630             if(opt.prompt){
15631                 dlg.setDefaultButton(activeTextEl);
15632             }else{
15633                 var bs = opt.buttons;
15634                 var db = null;
15635                 if(bs && bs.ok){
15636                     db = buttons["ok"];
15637                 }else if(bs && bs.yes){
15638                     db = buttons["yes"];
15639                 }
15640                 dlg.setDefaultButton(db);
15641             }
15642             bwidth = updateButtons(opt.buttons);
15643             this.updateText(opt.msg);
15644             if(opt.cls){
15645                 d.el.addClass(opt.cls);
15646             }
15647             d.proxyDrag = opt.proxyDrag === true;
15648             d.modal = opt.modal !== false;
15649             d.mask = opt.modal !== false ? mask : false;
15650             if(!d.isVisible()){
15651                 // force it to the end of the z-index stack so it gets a cursor in FF
15652                 document.body.appendChild(dlg.el.dom);
15653                 d.animateTarget = null;
15654                 d.show(options.animEl);
15655             }
15656             return this;
15657         },
15658
15659         /**
15660          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
15661          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
15662          * and closing the message box when the process is complete.
15663          * @param {String} title The title bar text
15664          * @param {String} msg The message box body text
15665          * @return {Roo.MessageBox} This message box
15666          */
15667         progress : function(title, msg){
15668             this.show({
15669                 title : title,
15670                 msg : msg,
15671                 buttons: false,
15672                 progress:true,
15673                 closable:false,
15674                 minWidth: this.minProgressWidth,
15675                 modal : true
15676             });
15677             return this;
15678         },
15679
15680         /**
15681          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
15682          * If a callback function is passed it will be called after the user clicks the button, and the
15683          * id of the button that was clicked will be passed as the only parameter to the callback
15684          * (could also be the top-right close button).
15685          * @param {String} title The title bar text
15686          * @param {String} msg The message box body text
15687          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15688          * @param {Object} scope (optional) The scope of the callback function
15689          * @return {Roo.MessageBox} This message box
15690          */
15691         alert : function(title, msg, fn, scope){
15692             this.show({
15693                 title : title,
15694                 msg : msg,
15695                 buttons: this.OK,
15696                 fn: fn,
15697                 scope : scope,
15698                 modal : true
15699             });
15700             return this;
15701         },
15702
15703         /**
15704          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
15705          * interaction while waiting for a long-running process to complete that does not have defined intervals.
15706          * You are responsible for closing the message box when the process is complete.
15707          * @param {String} msg The message box body text
15708          * @param {String} title (optional) The title bar text
15709          * @return {Roo.MessageBox} This message box
15710          */
15711         wait : function(msg, title){
15712             this.show({
15713                 title : title,
15714                 msg : msg,
15715                 buttons: false,
15716                 closable:false,
15717                 progress:true,
15718                 modal:true,
15719                 width:300,
15720                 wait:true
15721             });
15722             waitTimer = Roo.TaskMgr.start({
15723                 run: function(i){
15724                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
15725                 },
15726                 interval: 1000
15727             });
15728             return this;
15729         },
15730
15731         /**
15732          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
15733          * If a callback function is passed it will be called after the user clicks either button, and the id of the
15734          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
15735          * @param {String} title The title bar text
15736          * @param {String} msg The message box body text
15737          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15738          * @param {Object} scope (optional) The scope of the callback function
15739          * @return {Roo.MessageBox} This message box
15740          */
15741         confirm : function(title, msg, fn, scope){
15742             this.show({
15743                 title : title,
15744                 msg : msg,
15745                 buttons: this.YESNO,
15746                 fn: fn,
15747                 scope : scope,
15748                 modal : true
15749             });
15750             return this;
15751         },
15752
15753         /**
15754          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
15755          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
15756          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
15757          * (could also be the top-right close button) and the text that was entered will be passed as the two
15758          * parameters to the callback.
15759          * @param {String} title The title bar text
15760          * @param {String} msg The message box body text
15761          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15762          * @param {Object} scope (optional) The scope of the callback function
15763          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
15764          * property, or the height in pixels to create the textbox (defaults to false / single-line)
15765          * @return {Roo.MessageBox} This message box
15766          */
15767         prompt : function(title, msg, fn, scope, multiline){
15768             this.show({
15769                 title : title,
15770                 msg : msg,
15771                 buttons: this.OKCANCEL,
15772                 fn: fn,
15773                 minWidth:250,
15774                 scope : scope,
15775                 prompt:true,
15776                 multiline: multiline,
15777                 modal : true
15778             });
15779             return this;
15780         },
15781
15782         /**
15783          * Button config that displays a single OK button
15784          * @type Object
15785          */
15786         OK : {ok:true},
15787         /**
15788          * Button config that displays Yes and No buttons
15789          * @type Object
15790          */
15791         YESNO : {yes:true, no:true},
15792         /**
15793          * Button config that displays OK and Cancel buttons
15794          * @type Object
15795          */
15796         OKCANCEL : {ok:true, cancel:true},
15797         /**
15798          * Button config that displays Yes, No and Cancel buttons
15799          * @type Object
15800          */
15801         YESNOCANCEL : {yes:true, no:true, cancel:true},
15802
15803         /**
15804          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
15805          * @type Number
15806          */
15807         defaultTextHeight : 75,
15808         /**
15809          * The maximum width in pixels of the message box (defaults to 600)
15810          * @type Number
15811          */
15812         maxWidth : 600,
15813         /**
15814          * The minimum width in pixels of the message box (defaults to 100)
15815          * @type Number
15816          */
15817         minWidth : 100,
15818         /**
15819          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
15820          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
15821          * @type Number
15822          */
15823         minProgressWidth : 250,
15824         /**
15825          * An object containing the default button text strings that can be overriden for localized language support.
15826          * Supported properties are: ok, cancel, yes and no.
15827          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
15828          * @type Object
15829          */
15830         buttonText : {
15831             ok : "OK",
15832             cancel : "Cancel",
15833             yes : "Yes",
15834             no : "No"
15835         }
15836     };
15837 }();
15838
15839 /**
15840  * Shorthand for {@link Roo.MessageBox}
15841  */
15842 Roo.Msg = Roo.MessageBox;/*
15843  * Based on:
15844  * Ext JS Library 1.1.1
15845  * Copyright(c) 2006-2007, Ext JS, LLC.
15846  *
15847  * Originally Released Under LGPL - original licence link has changed is not relivant.
15848  *
15849  * Fork - LGPL
15850  * <script type="text/javascript">
15851  */
15852 /**
15853  * @class Roo.QuickTips
15854  * Provides attractive and customizable tooltips for any element.
15855  * @singleton
15856  */
15857 Roo.QuickTips = function(){
15858     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
15859     var ce, bd, xy, dd;
15860     var visible = false, disabled = true, inited = false;
15861     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
15862     
15863     var onOver = function(e){
15864         if(disabled){
15865             return;
15866         }
15867         var t = e.getTarget();
15868         if(!t || t.nodeType !== 1 || t == document || t == document.body){
15869             return;
15870         }
15871         if(ce && t == ce.el){
15872             clearTimeout(hideProc);
15873             return;
15874         }
15875         if(t && tagEls[t.id]){
15876             tagEls[t.id].el = t;
15877             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
15878             return;
15879         }
15880         var ttp, et = Roo.fly(t);
15881         var ns = cfg.namespace;
15882         if(tm.interceptTitles && t.title){
15883             ttp = t.title;
15884             t.qtip = ttp;
15885             t.removeAttribute("title");
15886             e.preventDefault();
15887         }else{
15888             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
15889         }
15890         if(ttp){
15891             showProc = show.defer(tm.showDelay, tm, [{
15892                 el: t, 
15893                 text: ttp, 
15894                 width: et.getAttributeNS(ns, cfg.width),
15895                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
15896                 title: et.getAttributeNS(ns, cfg.title),
15897                     cls: et.getAttributeNS(ns, cfg.cls)
15898             }]);
15899         }
15900     };
15901     
15902     var onOut = function(e){
15903         clearTimeout(showProc);
15904         var t = e.getTarget();
15905         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
15906             hideProc = setTimeout(hide, tm.hideDelay);
15907         }
15908     };
15909     
15910     var onMove = function(e){
15911         if(disabled){
15912             return;
15913         }
15914         xy = e.getXY();
15915         xy[1] += 18;
15916         if(tm.trackMouse && ce){
15917             el.setXY(xy);
15918         }
15919     };
15920     
15921     var onDown = function(e){
15922         clearTimeout(showProc);
15923         clearTimeout(hideProc);
15924         if(!e.within(el)){
15925             if(tm.hideOnClick){
15926                 hide();
15927                 tm.disable();
15928                 tm.enable.defer(100, tm);
15929             }
15930         }
15931     };
15932     
15933     var getPad = function(){
15934         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
15935     };
15936
15937     var show = function(o){
15938         if(disabled){
15939             return;
15940         }
15941         clearTimeout(dismissProc);
15942         ce = o;
15943         if(removeCls){ // in case manually hidden
15944             el.removeClass(removeCls);
15945             removeCls = null;
15946         }
15947         if(ce.cls){
15948             el.addClass(ce.cls);
15949             removeCls = ce.cls;
15950         }
15951         if(ce.title){
15952             tipTitle.update(ce.title);
15953             tipTitle.show();
15954         }else{
15955             tipTitle.update('');
15956             tipTitle.hide();
15957         }
15958         el.dom.style.width  = tm.maxWidth+'px';
15959         //tipBody.dom.style.width = '';
15960         tipBodyText.update(o.text);
15961         var p = getPad(), w = ce.width;
15962         if(!w){
15963             var td = tipBodyText.dom;
15964             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
15965             if(aw > tm.maxWidth){
15966                 w = tm.maxWidth;
15967             }else if(aw < tm.minWidth){
15968                 w = tm.minWidth;
15969             }else{
15970                 w = aw;
15971             }
15972         }
15973         //tipBody.setWidth(w);
15974         el.setWidth(parseInt(w, 10) + p);
15975         if(ce.autoHide === false){
15976             close.setDisplayed(true);
15977             if(dd){
15978                 dd.unlock();
15979             }
15980         }else{
15981             close.setDisplayed(false);
15982             if(dd){
15983                 dd.lock();
15984             }
15985         }
15986         if(xy){
15987             el.avoidY = xy[1]-18;
15988             el.setXY(xy);
15989         }
15990         if(tm.animate){
15991             el.setOpacity(.1);
15992             el.setStyle("visibility", "visible");
15993             el.fadeIn({callback: afterShow});
15994         }else{
15995             afterShow();
15996         }
15997     };
15998     
15999     var afterShow = function(){
16000         if(ce){
16001             el.show();
16002             esc.enable();
16003             if(tm.autoDismiss && ce.autoHide !== false){
16004                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
16005             }
16006         }
16007     };
16008     
16009     var hide = function(noanim){
16010         clearTimeout(dismissProc);
16011         clearTimeout(hideProc);
16012         ce = null;
16013         if(el.isVisible()){
16014             esc.disable();
16015             if(noanim !== true && tm.animate){
16016                 el.fadeOut({callback: afterHide});
16017             }else{
16018                 afterHide();
16019             } 
16020         }
16021     };
16022     
16023     var afterHide = function(){
16024         el.hide();
16025         if(removeCls){
16026             el.removeClass(removeCls);
16027             removeCls = null;
16028         }
16029     };
16030     
16031     return {
16032         /**
16033         * @cfg {Number} minWidth
16034         * The minimum width of the quick tip (defaults to 40)
16035         */
16036        minWidth : 40,
16037         /**
16038         * @cfg {Number} maxWidth
16039         * The maximum width of the quick tip (defaults to 300)
16040         */
16041        maxWidth : 300,
16042         /**
16043         * @cfg {Boolean} interceptTitles
16044         * True to automatically use the element's DOM title value if available (defaults to false)
16045         */
16046        interceptTitles : false,
16047         /**
16048         * @cfg {Boolean} trackMouse
16049         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
16050         */
16051        trackMouse : false,
16052         /**
16053         * @cfg {Boolean} hideOnClick
16054         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
16055         */
16056        hideOnClick : true,
16057         /**
16058         * @cfg {Number} showDelay
16059         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
16060         */
16061        showDelay : 500,
16062         /**
16063         * @cfg {Number} hideDelay
16064         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
16065         */
16066        hideDelay : 200,
16067         /**
16068         * @cfg {Boolean} autoHide
16069         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
16070         * Used in conjunction with hideDelay.
16071         */
16072        autoHide : true,
16073         /**
16074         * @cfg {Boolean}
16075         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
16076         * (defaults to true).  Used in conjunction with autoDismissDelay.
16077         */
16078        autoDismiss : true,
16079         /**
16080         * @cfg {Number}
16081         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
16082         */
16083        autoDismissDelay : 5000,
16084        /**
16085         * @cfg {Boolean} animate
16086         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
16087         */
16088        animate : false,
16089
16090        /**
16091         * @cfg {String} title
16092         * Title text to display (defaults to '').  This can be any valid HTML markup.
16093         */
16094         title: '',
16095        /**
16096         * @cfg {String} text
16097         * Body text to display (defaults to '').  This can be any valid HTML markup.
16098         */
16099         text : '',
16100        /**
16101         * @cfg {String} cls
16102         * A CSS class to apply to the base quick tip element (defaults to '').
16103         */
16104         cls : '',
16105        /**
16106         * @cfg {Number} width
16107         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
16108         * minWidth or maxWidth.
16109         */
16110         width : null,
16111
16112     /**
16113      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
16114      * or display QuickTips in a page.
16115      */
16116        init : function(){
16117           tm = Roo.QuickTips;
16118           cfg = tm.tagConfig;
16119           if(!inited){
16120               if(!Roo.isReady){ // allow calling of init() before onReady
16121                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
16122                   return;
16123               }
16124               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
16125               el.fxDefaults = {stopFx: true};
16126               // maximum custom styling
16127               //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>');
16128               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>');              
16129               tipTitle = el.child('h3');
16130               tipTitle.enableDisplayMode("block");
16131               tipBody = el.child('div.x-tip-bd');
16132               tipBodyText = el.child('div.x-tip-bd-inner');
16133               //bdLeft = el.child('div.x-tip-bd-left');
16134               //bdRight = el.child('div.x-tip-bd-right');
16135               close = el.child('div.x-tip-close');
16136               close.enableDisplayMode("block");
16137               close.on("click", hide);
16138               var d = Roo.get(document);
16139               d.on("mousedown", onDown);
16140               d.on("mouseover", onOver);
16141               d.on("mouseout", onOut);
16142               d.on("mousemove", onMove);
16143               esc = d.addKeyListener(27, hide);
16144               esc.disable();
16145               if(Roo.dd.DD){
16146                   dd = el.initDD("default", null, {
16147                       onDrag : function(){
16148                           el.sync();  
16149                       }
16150                   });
16151                   dd.setHandleElId(tipTitle.id);
16152                   dd.lock();
16153               }
16154               inited = true;
16155           }
16156           this.enable(); 
16157        },
16158
16159     /**
16160      * Configures a new quick tip instance and assigns it to a target element.  The following config options
16161      * are supported:
16162      * <pre>
16163 Property    Type                   Description
16164 ----------  ---------------------  ------------------------------------------------------------------------
16165 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
16166      * </ul>
16167      * @param {Object} config The config object
16168      */
16169        register : function(config){
16170            var cs = config instanceof Array ? config : arguments;
16171            for(var i = 0, len = cs.length; i < len; i++) {
16172                var c = cs[i];
16173                var target = c.target;
16174                if(target){
16175                    if(target instanceof Array){
16176                        for(var j = 0, jlen = target.length; j < jlen; j++){
16177                            tagEls[target[j]] = c;
16178                        }
16179                    }else{
16180                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
16181                    }
16182                }
16183            }
16184        },
16185
16186     /**
16187      * Removes this quick tip from its element and destroys it.
16188      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
16189      */
16190        unregister : function(el){
16191            delete tagEls[Roo.id(el)];
16192        },
16193
16194     /**
16195      * Enable this quick tip.
16196      */
16197        enable : function(){
16198            if(inited && disabled){
16199                locks.pop();
16200                if(locks.length < 1){
16201                    disabled = false;
16202                }
16203            }
16204        },
16205
16206     /**
16207      * Disable this quick tip.
16208      */
16209        disable : function(){
16210           disabled = true;
16211           clearTimeout(showProc);
16212           clearTimeout(hideProc);
16213           clearTimeout(dismissProc);
16214           if(ce){
16215               hide(true);
16216           }
16217           locks.push(1);
16218        },
16219
16220     /**
16221      * Returns true if the quick tip is enabled, else false.
16222      */
16223        isEnabled : function(){
16224             return !disabled;
16225        },
16226
16227         // private
16228        tagConfig : {
16229            namespace : "ext",
16230            attribute : "qtip",
16231            width : "width",
16232            target : "target",
16233            title : "qtitle",
16234            hide : "hide",
16235            cls : "qclass"
16236        }
16237    };
16238 }();
16239
16240 // backwards compat
16241 Roo.QuickTips.tips = Roo.QuickTips.register;/*
16242  * Based on:
16243  * Ext JS Library 1.1.1
16244  * Copyright(c) 2006-2007, Ext JS, LLC.
16245  *
16246  * Originally Released Under LGPL - original licence link has changed is not relivant.
16247  *
16248  * Fork - LGPL
16249  * <script type="text/javascript">
16250  */
16251  
16252
16253 /**
16254  * @class Roo.tree.TreePanel
16255  * @extends Roo.data.Tree
16256
16257  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
16258  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
16259  * @cfg {Boolean} enableDD true to enable drag and drop
16260  * @cfg {Boolean} enableDrag true to enable just drag
16261  * @cfg {Boolean} enableDrop true to enable just drop
16262  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
16263  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
16264  * @cfg {String} ddGroup The DD group this TreePanel belongs to
16265  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
16266  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
16267  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
16268  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
16269  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
16270  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
16271  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
16272  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
16273  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
16274  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
16275  * @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>
16276  * @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>
16277  * 
16278  * @constructor
16279  * @param {String/HTMLElement/Element} el The container element
16280  * @param {Object} config
16281  */
16282 Roo.tree.TreePanel = function(el, config){
16283     var root = false;
16284     var loader = false;
16285     if (config.root) {
16286         root = config.root;
16287         delete config.root;
16288     }
16289     if (config.loader) {
16290         loader = config.loader;
16291         delete config.loader;
16292     }
16293     
16294     Roo.apply(this, config);
16295     Roo.tree.TreePanel.superclass.constructor.call(this);
16296     this.el = Roo.get(el);
16297     this.el.addClass('x-tree');
16298     //console.log(root);
16299     if (root) {
16300         this.setRootNode( Roo.factory(root, Roo.tree));
16301     }
16302     if (loader) {
16303         this.loader = Roo.factory(loader, Roo.tree);
16304     }
16305    /**
16306     * Read-only. The id of the container element becomes this TreePanel's id.
16307     */
16308    this.id = this.el.id;
16309    this.addEvents({
16310         /**
16311         * @event beforeload
16312         * Fires before a node is loaded, return false to cancel
16313         * @param {Node} node The node being loaded
16314         */
16315         "beforeload" : true,
16316         /**
16317         * @event load
16318         * Fires when a node is loaded
16319         * @param {Node} node The node that was loaded
16320         */
16321         "load" : true,
16322         /**
16323         * @event textchange
16324         * Fires when the text for a node is changed
16325         * @param {Node} node The node
16326         * @param {String} text The new text
16327         * @param {String} oldText The old text
16328         */
16329         "textchange" : true,
16330         /**
16331         * @event beforeexpand
16332         * Fires before a node is expanded, return false to cancel.
16333         * @param {Node} node The node
16334         * @param {Boolean} deep
16335         * @param {Boolean} anim
16336         */
16337         "beforeexpand" : true,
16338         /**
16339         * @event beforecollapse
16340         * Fires before a node is collapsed, return false to cancel.
16341         * @param {Node} node The node
16342         * @param {Boolean} deep
16343         * @param {Boolean} anim
16344         */
16345         "beforecollapse" : true,
16346         /**
16347         * @event expand
16348         * Fires when a node is expanded
16349         * @param {Node} node The node
16350         */
16351         "expand" : true,
16352         /**
16353         * @event disabledchange
16354         * Fires when the disabled status of a node changes
16355         * @param {Node} node The node
16356         * @param {Boolean} disabled
16357         */
16358         "disabledchange" : true,
16359         /**
16360         * @event collapse
16361         * Fires when a node is collapsed
16362         * @param {Node} node The node
16363         */
16364         "collapse" : true,
16365         /**
16366         * @event beforeclick
16367         * Fires before click processing on a node. Return false to cancel the default action.
16368         * @param {Node} node The node
16369         * @param {Roo.EventObject} e The event object
16370         */
16371         "beforeclick":true,
16372         /**
16373         * @event checkchange
16374         * Fires when a node with a checkbox's checked property changes
16375         * @param {Node} this This node
16376         * @param {Boolean} checked
16377         */
16378         "checkchange":true,
16379         /**
16380         * @event click
16381         * Fires when a node is clicked
16382         * @param {Node} node The node
16383         * @param {Roo.EventObject} e The event object
16384         */
16385         "click":true,
16386         /**
16387         * @event dblclick
16388         * Fires when a node is double clicked
16389         * @param {Node} node The node
16390         * @param {Roo.EventObject} e The event object
16391         */
16392         "dblclick":true,
16393         /**
16394         * @event contextmenu
16395         * Fires when a node is right clicked
16396         * @param {Node} node The node
16397         * @param {Roo.EventObject} e The event object
16398         */
16399         "contextmenu":true,
16400         /**
16401         * @event beforechildrenrendered
16402         * Fires right before the child nodes for a node are rendered
16403         * @param {Node} node The node
16404         */
16405         "beforechildrenrendered":true,
16406        /**
16407              * @event startdrag
16408              * Fires when a node starts being dragged
16409              * @param {Roo.tree.TreePanel} this
16410              * @param {Roo.tree.TreeNode} node
16411              * @param {event} e The raw browser event
16412              */ 
16413             "startdrag" : true,
16414             /**
16415              * @event enddrag
16416              * Fires when a drag operation is complete
16417              * @param {Roo.tree.TreePanel} this
16418              * @param {Roo.tree.TreeNode} node
16419              * @param {event} e The raw browser event
16420              */
16421             "enddrag" : true,
16422             /**
16423              * @event dragdrop
16424              * Fires when a dragged node is dropped on a valid DD target
16425              * @param {Roo.tree.TreePanel} this
16426              * @param {Roo.tree.TreeNode} node
16427              * @param {DD} dd The dd it was dropped on
16428              * @param {event} e The raw browser event
16429              */
16430             "dragdrop" : true,
16431             /**
16432              * @event beforenodedrop
16433              * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
16434              * passed to handlers has the following properties:<br />
16435              * <ul style="padding:5px;padding-left:16px;">
16436              * <li>tree - The TreePanel</li>
16437              * <li>target - The node being targeted for the drop</li>
16438              * <li>data - The drag data from the drag source</li>
16439              * <li>point - The point of the drop - append, above or below</li>
16440              * <li>source - The drag source</li>
16441              * <li>rawEvent - Raw mouse event</li>
16442              * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
16443              * to be inserted by setting them on this object.</li>
16444              * <li>cancel - Set this to true to cancel the drop.</li>
16445              * </ul>
16446              * @param {Object} dropEvent
16447              */
16448             "beforenodedrop" : true,
16449             /**
16450              * @event nodedrop
16451              * Fires after a DD object is dropped on a node in this tree. The dropEvent
16452              * passed to handlers has the following properties:<br />
16453              * <ul style="padding:5px;padding-left:16px;">
16454              * <li>tree - The TreePanel</li>
16455              * <li>target - The node being targeted for the drop</li>
16456              * <li>data - The drag data from the drag source</li>
16457              * <li>point - The point of the drop - append, above or below</li>
16458              * <li>source - The drag source</li>
16459              * <li>rawEvent - Raw mouse event</li>
16460              * <li>dropNode - Dropped node(s).</li>
16461              * </ul>
16462              * @param {Object} dropEvent
16463              */
16464             "nodedrop" : true,
16465              /**
16466              * @event nodedragover
16467              * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
16468              * passed to handlers has the following properties:<br />
16469              * <ul style="padding:5px;padding-left:16px;">
16470              * <li>tree - The TreePanel</li>
16471              * <li>target - The node being targeted for the drop</li>
16472              * <li>data - The drag data from the drag source</li>
16473              * <li>point - The point of the drop - append, above or below</li>
16474              * <li>source - The drag source</li>
16475              * <li>rawEvent - Raw mouse event</li>
16476              * <li>dropNode - Drop node(s) provided by the source.</li>
16477              * <li>cancel - Set this to true to signal drop not allowed.</li>
16478              * </ul>
16479              * @param {Object} dragOverEvent
16480              */
16481             "nodedragover" : true
16482         
16483    });
16484    if(this.singleExpand){
16485        this.on("beforeexpand", this.restrictExpand, this);
16486    }
16487 };
16488 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
16489     rootVisible : true,
16490     animate: Roo.enableFx,
16491     lines : true,
16492     enableDD : false,
16493     hlDrop : Roo.enableFx,
16494   
16495     renderer: false,
16496     
16497     rendererTip: false,
16498     // private
16499     restrictExpand : function(node){
16500         var p = node.parentNode;
16501         if(p){
16502             if(p.expandedChild && p.expandedChild.parentNode == p){
16503                 p.expandedChild.collapse();
16504             }
16505             p.expandedChild = node;
16506         }
16507     },
16508
16509     // private override
16510     setRootNode : function(node){
16511         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
16512         if(!this.rootVisible){
16513             node.ui = new Roo.tree.RootTreeNodeUI(node);
16514         }
16515         return node;
16516     },
16517
16518     /**
16519      * Returns the container element for this TreePanel
16520      */
16521     getEl : function(){
16522         return this.el;
16523     },
16524
16525     /**
16526      * Returns the default TreeLoader for this TreePanel
16527      */
16528     getLoader : function(){
16529         return this.loader;
16530     },
16531
16532     /**
16533      * Expand all nodes
16534      */
16535     expandAll : function(){
16536         this.root.expand(true);
16537     },
16538
16539     /**
16540      * Collapse all nodes
16541      */
16542     collapseAll : function(){
16543         this.root.collapse(true);
16544     },
16545
16546     /**
16547      * Returns the selection model used by this TreePanel
16548      */
16549     getSelectionModel : function(){
16550         if(!this.selModel){
16551             this.selModel = new Roo.tree.DefaultSelectionModel();
16552         }
16553         return this.selModel;
16554     },
16555
16556     /**
16557      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
16558      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
16559      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
16560      * @return {Array}
16561      */
16562     getChecked : function(a, startNode){
16563         startNode = startNode || this.root;
16564         var r = [];
16565         var f = function(){
16566             if(this.attributes.checked){
16567                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
16568             }
16569         }
16570         startNode.cascade(f);
16571         return r;
16572     },
16573
16574     /**
16575      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16576      * @param {String} path
16577      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16578      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
16579      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
16580      */
16581     expandPath : function(path, attr, callback){
16582         attr = attr || "id";
16583         var keys = path.split(this.pathSeparator);
16584         var curNode = this.root;
16585         if(curNode.attributes[attr] != keys[1]){ // invalid root
16586             if(callback){
16587                 callback(false, null);
16588             }
16589             return;
16590         }
16591         var index = 1;
16592         var f = function(){
16593             if(++index == keys.length){
16594                 if(callback){
16595                     callback(true, curNode);
16596                 }
16597                 return;
16598             }
16599             var c = curNode.findChild(attr, keys[index]);
16600             if(!c){
16601                 if(callback){
16602                     callback(false, curNode);
16603                 }
16604                 return;
16605             }
16606             curNode = c;
16607             c.expand(false, false, f);
16608         };
16609         curNode.expand(false, false, f);
16610     },
16611
16612     /**
16613      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16614      * @param {String} path
16615      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16616      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
16617      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
16618      */
16619     selectPath : function(path, attr, callback){
16620         attr = attr || "id";
16621         var keys = path.split(this.pathSeparator);
16622         var v = keys.pop();
16623         if(keys.length > 0){
16624             var f = function(success, node){
16625                 if(success && node){
16626                     var n = node.findChild(attr, v);
16627                     if(n){
16628                         n.select();
16629                         if(callback){
16630                             callback(true, n);
16631                         }
16632                     }else if(callback){
16633                         callback(false, n);
16634                     }
16635                 }else{
16636                     if(callback){
16637                         callback(false, n);
16638                     }
16639                 }
16640             };
16641             this.expandPath(keys.join(this.pathSeparator), attr, f);
16642         }else{
16643             this.root.select();
16644             if(callback){
16645                 callback(true, this.root);
16646             }
16647         }
16648     },
16649
16650     getTreeEl : function(){
16651         return this.el;
16652     },
16653
16654     /**
16655      * Trigger rendering of this TreePanel
16656      */
16657     render : function(){
16658         if (this.innerCt) {
16659             return this; // stop it rendering more than once!!
16660         }
16661         
16662         this.innerCt = this.el.createChild({tag:"ul",
16663                cls:"x-tree-root-ct " +
16664                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
16665
16666         if(this.containerScroll){
16667             Roo.dd.ScrollManager.register(this.el);
16668         }
16669         if((this.enableDD || this.enableDrop) && !this.dropZone){
16670            /**
16671             * The dropZone used by this tree if drop is enabled
16672             * @type Roo.tree.TreeDropZone
16673             */
16674              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
16675                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
16676            });
16677         }
16678         if((this.enableDD || this.enableDrag) && !this.dragZone){
16679            /**
16680             * The dragZone used by this tree if drag is enabled
16681             * @type Roo.tree.TreeDragZone
16682             */
16683             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
16684                ddGroup: this.ddGroup || "TreeDD",
16685                scroll: this.ddScroll
16686            });
16687         }
16688         this.getSelectionModel().init(this);
16689         if (!this.root) {
16690             console.log("ROOT not set in tree");
16691             return;
16692         }
16693         this.root.render();
16694         if(!this.rootVisible){
16695             this.root.renderChildren();
16696         }
16697         return this;
16698     }
16699 });/*
16700  * Based on:
16701  * Ext JS Library 1.1.1
16702  * Copyright(c) 2006-2007, Ext JS, LLC.
16703  *
16704  * Originally Released Under LGPL - original licence link has changed is not relivant.
16705  *
16706  * Fork - LGPL
16707  * <script type="text/javascript">
16708  */
16709  
16710
16711 /**
16712  * @class Roo.tree.DefaultSelectionModel
16713  * @extends Roo.util.Observable
16714  * The default single selection for a TreePanel.
16715  */
16716 Roo.tree.DefaultSelectionModel = function(){
16717    this.selNode = null;
16718    
16719    this.addEvents({
16720        /**
16721         * @event selectionchange
16722         * Fires when the selected node changes
16723         * @param {DefaultSelectionModel} this
16724         * @param {TreeNode} node the new selection
16725         */
16726        "selectionchange" : true,
16727
16728        /**
16729         * @event beforeselect
16730         * Fires before the selected node changes, return false to cancel the change
16731         * @param {DefaultSelectionModel} this
16732         * @param {TreeNode} node the new selection
16733         * @param {TreeNode} node the old selection
16734         */
16735        "beforeselect" : true
16736    });
16737 };
16738
16739 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
16740     init : function(tree){
16741         this.tree = tree;
16742         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16743         tree.on("click", this.onNodeClick, this);
16744     },
16745     
16746     onNodeClick : function(node, e){
16747         if (e.ctrlKey && this.selNode == node)  {
16748             this.unselect(node);
16749             return;
16750         }
16751         this.select(node);
16752     },
16753     
16754     /**
16755      * Select a node.
16756      * @param {TreeNode} node The node to select
16757      * @return {TreeNode} The selected node
16758      */
16759     select : function(node){
16760         var last = this.selNode;
16761         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
16762             if(last){
16763                 last.ui.onSelectedChange(false);
16764             }
16765             this.selNode = node;
16766             node.ui.onSelectedChange(true);
16767             this.fireEvent("selectionchange", this, node, last);
16768         }
16769         return node;
16770     },
16771     
16772     /**
16773      * Deselect a node.
16774      * @param {TreeNode} node The node to unselect
16775      */
16776     unselect : function(node){
16777         if(this.selNode == node){
16778             this.clearSelections();
16779         }    
16780     },
16781     
16782     /**
16783      * Clear all selections
16784      */
16785     clearSelections : function(){
16786         var n = this.selNode;
16787         if(n){
16788             n.ui.onSelectedChange(false);
16789             this.selNode = null;
16790             this.fireEvent("selectionchange", this, null);
16791         }
16792         return n;
16793     },
16794     
16795     /**
16796      * Get the selected node
16797      * @return {TreeNode} The selected node
16798      */
16799     getSelectedNode : function(){
16800         return this.selNode;    
16801     },
16802     
16803     /**
16804      * Returns true if the node is selected
16805      * @param {TreeNode} node The node to check
16806      * @return {Boolean}
16807      */
16808     isSelected : function(node){
16809         return this.selNode == node;  
16810     },
16811
16812     /**
16813      * Selects the node above the selected node in the tree, intelligently walking the nodes
16814      * @return TreeNode The new selection
16815      */
16816     selectPrevious : function(){
16817         var s = this.selNode || this.lastSelNode;
16818         if(!s){
16819             return null;
16820         }
16821         var ps = s.previousSibling;
16822         if(ps){
16823             if(!ps.isExpanded() || ps.childNodes.length < 1){
16824                 return this.select(ps);
16825             } else{
16826                 var lc = ps.lastChild;
16827                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
16828                     lc = lc.lastChild;
16829                 }
16830                 return this.select(lc);
16831             }
16832         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
16833             return this.select(s.parentNode);
16834         }
16835         return null;
16836     },
16837
16838     /**
16839      * Selects the node above the selected node in the tree, intelligently walking the nodes
16840      * @return TreeNode The new selection
16841      */
16842     selectNext : function(){
16843         var s = this.selNode || this.lastSelNode;
16844         if(!s){
16845             return null;
16846         }
16847         if(s.firstChild && s.isExpanded()){
16848              return this.select(s.firstChild);
16849          }else if(s.nextSibling){
16850              return this.select(s.nextSibling);
16851          }else if(s.parentNode){
16852             var newS = null;
16853             s.parentNode.bubble(function(){
16854                 if(this.nextSibling){
16855                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
16856                     return false;
16857                 }
16858             });
16859             return newS;
16860          }
16861         return null;
16862     },
16863
16864     onKeyDown : function(e){
16865         var s = this.selNode || this.lastSelNode;
16866         // undesirable, but required
16867         var sm = this;
16868         if(!s){
16869             return;
16870         }
16871         var k = e.getKey();
16872         switch(k){
16873              case e.DOWN:
16874                  e.stopEvent();
16875                  this.selectNext();
16876              break;
16877              case e.UP:
16878                  e.stopEvent();
16879                  this.selectPrevious();
16880              break;
16881              case e.RIGHT:
16882                  e.preventDefault();
16883                  if(s.hasChildNodes()){
16884                      if(!s.isExpanded()){
16885                          s.expand();
16886                      }else if(s.firstChild){
16887                          this.select(s.firstChild, e);
16888                      }
16889                  }
16890              break;
16891              case e.LEFT:
16892                  e.preventDefault();
16893                  if(s.hasChildNodes() && s.isExpanded()){
16894                      s.collapse();
16895                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
16896                      this.select(s.parentNode, e);
16897                  }
16898              break;
16899         };
16900     }
16901 });
16902
16903 /**
16904  * @class Roo.tree.MultiSelectionModel
16905  * @extends Roo.util.Observable
16906  * Multi selection for a TreePanel.
16907  */
16908 Roo.tree.MultiSelectionModel = function(){
16909    this.selNodes = [];
16910    this.selMap = {};
16911    this.addEvents({
16912        /**
16913         * @event selectionchange
16914         * Fires when the selected nodes change
16915         * @param {MultiSelectionModel} this
16916         * @param {Array} nodes Array of the selected nodes
16917         */
16918        "selectionchange" : true
16919    });
16920 };
16921
16922 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
16923     init : function(tree){
16924         this.tree = tree;
16925         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16926         tree.on("click", this.onNodeClick, this);
16927     },
16928     
16929     onNodeClick : function(node, e){
16930         this.select(node, e, e.ctrlKey);
16931     },
16932     
16933     /**
16934      * Select a node.
16935      * @param {TreeNode} node The node to select
16936      * @param {EventObject} e (optional) An event associated with the selection
16937      * @param {Boolean} keepExisting True to retain existing selections
16938      * @return {TreeNode} The selected node
16939      */
16940     select : function(node, e, keepExisting){
16941         if(keepExisting !== true){
16942             this.clearSelections(true);
16943         }
16944         if(this.isSelected(node)){
16945             this.lastSelNode = node;
16946             return node;
16947         }
16948         this.selNodes.push(node);
16949         this.selMap[node.id] = node;
16950         this.lastSelNode = node;
16951         node.ui.onSelectedChange(true);
16952         this.fireEvent("selectionchange", this, this.selNodes);
16953         return node;
16954     },
16955     
16956     /**
16957      * Deselect a node.
16958      * @param {TreeNode} node The node to unselect
16959      */
16960     unselect : function(node){
16961         if(this.selMap[node.id]){
16962             node.ui.onSelectedChange(false);
16963             var sn = this.selNodes;
16964             var index = -1;
16965             if(sn.indexOf){
16966                 index = sn.indexOf(node);
16967             }else{
16968                 for(var i = 0, len = sn.length; i < len; i++){
16969                     if(sn[i] == node){
16970                         index = i;
16971                         break;
16972                     }
16973                 }
16974             }
16975             if(index != -1){
16976                 this.selNodes.splice(index, 1);
16977             }
16978             delete this.selMap[node.id];
16979             this.fireEvent("selectionchange", this, this.selNodes);
16980         }
16981     },
16982     
16983     /**
16984      * Clear all selections
16985      */
16986     clearSelections : function(suppressEvent){
16987         var sn = this.selNodes;
16988         if(sn.length > 0){
16989             for(var i = 0, len = sn.length; i < len; i++){
16990                 sn[i].ui.onSelectedChange(false);
16991             }
16992             this.selNodes = [];
16993             this.selMap = {};
16994             if(suppressEvent !== true){
16995                 this.fireEvent("selectionchange", this, this.selNodes);
16996             }
16997         }
16998     },
16999     
17000     /**
17001      * Returns true if the node is selected
17002      * @param {TreeNode} node The node to check
17003      * @return {Boolean}
17004      */
17005     isSelected : function(node){
17006         return this.selMap[node.id] ? true : false;  
17007     },
17008     
17009     /**
17010      * Returns an array of the selected nodes
17011      * @return {Array}
17012      */
17013     getSelectedNodes : function(){
17014         return this.selNodes;    
17015     },
17016
17017     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
17018
17019     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
17020
17021     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
17022 });/*
17023  * Based on:
17024  * Ext JS Library 1.1.1
17025  * Copyright(c) 2006-2007, Ext JS, LLC.
17026  *
17027  * Originally Released Under LGPL - original licence link has changed is not relivant.
17028  *
17029  * Fork - LGPL
17030  * <script type="text/javascript">
17031  */
17032  
17033 /**
17034  * @class Roo.tree.TreeNode
17035  * @extends Roo.data.Node
17036  * @cfg {String} text The text for this node
17037  * @cfg {Boolean} expanded true to start the node expanded
17038  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
17039  * @cfg {Boolean} allowDrop false if this node cannot be drop on
17040  * @cfg {Boolean} disabled true to start the node disabled
17041  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
17042  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
17043  * @cfg {String} cls A css class to be added to the node
17044  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
17045  * @cfg {String} href URL of the link used for the node (defaults to #)
17046  * @cfg {String} hrefTarget target frame for the link
17047  * @cfg {String} qtip An Ext QuickTip for the node
17048  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
17049  * @cfg {Boolean} singleClickExpand True for single click expand on this node
17050  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
17051  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
17052  * (defaults to undefined with no checkbox rendered)
17053  * @constructor
17054  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
17055  */
17056 Roo.tree.TreeNode = function(attributes){
17057     attributes = attributes || {};
17058     if(typeof attributes == "string"){
17059         attributes = {text: attributes};
17060     }
17061     this.childrenRendered = false;
17062     this.rendered = false;
17063     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
17064     this.expanded = attributes.expanded === true;
17065     this.isTarget = attributes.isTarget !== false;
17066     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
17067     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
17068
17069     /**
17070      * Read-only. The text for this node. To change it use setText().
17071      * @type String
17072      */
17073     this.text = attributes.text;
17074     /**
17075      * True if this node is disabled.
17076      * @type Boolean
17077      */
17078     this.disabled = attributes.disabled === true;
17079
17080     this.addEvents({
17081         /**
17082         * @event textchange
17083         * Fires when the text for this node is changed
17084         * @param {Node} this This node
17085         * @param {String} text The new text
17086         * @param {String} oldText The old text
17087         */
17088         "textchange" : true,
17089         /**
17090         * @event beforeexpand
17091         * Fires before this node is expanded, return false to cancel.
17092         * @param {Node} this This node
17093         * @param {Boolean} deep
17094         * @param {Boolean} anim
17095         */
17096         "beforeexpand" : true,
17097         /**
17098         * @event beforecollapse
17099         * Fires before this node is collapsed, return false to cancel.
17100         * @param {Node} this This node
17101         * @param {Boolean} deep
17102         * @param {Boolean} anim
17103         */
17104         "beforecollapse" : true,
17105         /**
17106         * @event expand
17107         * Fires when this node is expanded
17108         * @param {Node} this This node
17109         */
17110         "expand" : true,
17111         /**
17112         * @event disabledchange
17113         * Fires when the disabled status of this node changes
17114         * @param {Node} this This node
17115         * @param {Boolean} disabled
17116         */
17117         "disabledchange" : true,
17118         /**
17119         * @event collapse
17120         * Fires when this node is collapsed
17121         * @param {Node} this This node
17122         */
17123         "collapse" : true,
17124         /**
17125         * @event beforeclick
17126         * Fires before click processing. Return false to cancel the default action.
17127         * @param {Node} this This node
17128         * @param {Roo.EventObject} e The event object
17129         */
17130         "beforeclick":true,
17131         /**
17132         * @event checkchange
17133         * Fires when a node with a checkbox's checked property changes
17134         * @param {Node} this This node
17135         * @param {Boolean} checked
17136         */
17137         "checkchange":true,
17138         /**
17139         * @event click
17140         * Fires when this node is clicked
17141         * @param {Node} this This node
17142         * @param {Roo.EventObject} e The event object
17143         */
17144         "click":true,
17145         /**
17146         * @event dblclick
17147         * Fires when this node is double clicked
17148         * @param {Node} this This node
17149         * @param {Roo.EventObject} e The event object
17150         */
17151         "dblclick":true,
17152         /**
17153         * @event contextmenu
17154         * Fires when this node is right clicked
17155         * @param {Node} this This node
17156         * @param {Roo.EventObject} e The event object
17157         */
17158         "contextmenu":true,
17159         /**
17160         * @event beforechildrenrendered
17161         * Fires right before the child nodes for this node are rendered
17162         * @param {Node} this This node
17163         */
17164         "beforechildrenrendered":true
17165     });
17166
17167     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
17168
17169     /**
17170      * Read-only. The UI for this node
17171      * @type TreeNodeUI
17172      */
17173     this.ui = new uiClass(this);
17174 };
17175 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
17176     preventHScroll: true,
17177     /**
17178      * Returns true if this node is expanded
17179      * @return {Boolean}
17180      */
17181     isExpanded : function(){
17182         return this.expanded;
17183     },
17184
17185     /**
17186      * Returns the UI object for this node
17187      * @return {TreeNodeUI}
17188      */
17189     getUI : function(){
17190         return this.ui;
17191     },
17192
17193     // private override
17194     setFirstChild : function(node){
17195         var of = this.firstChild;
17196         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
17197         if(this.childrenRendered && of && node != of){
17198             of.renderIndent(true, true);
17199         }
17200         if(this.rendered){
17201             this.renderIndent(true, true);
17202         }
17203     },
17204
17205     // private override
17206     setLastChild : function(node){
17207         var ol = this.lastChild;
17208         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
17209         if(this.childrenRendered && ol && node != ol){
17210             ol.renderIndent(true, true);
17211         }
17212         if(this.rendered){
17213             this.renderIndent(true, true);
17214         }
17215     },
17216
17217     // these methods are overridden to provide lazy rendering support
17218     // private override
17219     appendChild : function(){
17220         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
17221         if(node && this.childrenRendered){
17222             node.render();
17223         }
17224         this.ui.updateExpandIcon();
17225         return node;
17226     },
17227
17228     // private override
17229     removeChild : function(node){
17230         this.ownerTree.getSelectionModel().unselect(node);
17231         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
17232         // if it's been rendered remove dom node
17233         if(this.childrenRendered){
17234             node.ui.remove();
17235         }
17236         if(this.childNodes.length < 1){
17237             this.collapse(false, false);
17238         }else{
17239             this.ui.updateExpandIcon();
17240         }
17241         if(!this.firstChild) {
17242             this.childrenRendered = false;
17243         }
17244         return node;
17245     },
17246
17247     // private override
17248     insertBefore : function(node, refNode){
17249         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
17250         if(newNode && refNode && this.childrenRendered){
17251             node.render();
17252         }
17253         this.ui.updateExpandIcon();
17254         return newNode;
17255     },
17256
17257     /**
17258      * Sets the text for this node
17259      * @param {String} text
17260      */
17261     setText : function(text){
17262         var oldText = this.text;
17263         this.text = text;
17264         this.attributes.text = text;
17265         if(this.rendered){ // event without subscribing
17266             this.ui.onTextChange(this, text, oldText);
17267         }
17268         this.fireEvent("textchange", this, text, oldText);
17269     },
17270
17271     /**
17272      * Triggers selection of this node
17273      */
17274     select : function(){
17275         this.getOwnerTree().getSelectionModel().select(this);
17276     },
17277
17278     /**
17279      * Triggers deselection of this node
17280      */
17281     unselect : function(){
17282         this.getOwnerTree().getSelectionModel().unselect(this);
17283     },
17284
17285     /**
17286      * Returns true if this node is selected
17287      * @return {Boolean}
17288      */
17289     isSelected : function(){
17290         return this.getOwnerTree().getSelectionModel().isSelected(this);
17291     },
17292
17293     /**
17294      * Expand this node.
17295      * @param {Boolean} deep (optional) True to expand all children as well
17296      * @param {Boolean} anim (optional) false to cancel the default animation
17297      * @param {Function} callback (optional) A callback to be called when
17298      * expanding this node completes (does not wait for deep expand to complete).
17299      * Called with 1 parameter, this node.
17300      */
17301     expand : function(deep, anim, callback){
17302         if(!this.expanded){
17303             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
17304                 return;
17305             }
17306             if(!this.childrenRendered){
17307                 this.renderChildren();
17308             }
17309             this.expanded = true;
17310             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
17311                 this.ui.animExpand(function(){
17312                     this.fireEvent("expand", this);
17313                     if(typeof callback == "function"){
17314                         callback(this);
17315                     }
17316                     if(deep === true){
17317                         this.expandChildNodes(true);
17318                     }
17319                 }.createDelegate(this));
17320                 return;
17321             }else{
17322                 this.ui.expand();
17323                 this.fireEvent("expand", this);
17324                 if(typeof callback == "function"){
17325                     callback(this);
17326                 }
17327             }
17328         }else{
17329            if(typeof callback == "function"){
17330                callback(this);
17331            }
17332         }
17333         if(deep === true){
17334             this.expandChildNodes(true);
17335         }
17336     },
17337
17338     isHiddenRoot : function(){
17339         return this.isRoot && !this.getOwnerTree().rootVisible;
17340     },
17341
17342     /**
17343      * Collapse this node.
17344      * @param {Boolean} deep (optional) True to collapse all children as well
17345      * @param {Boolean} anim (optional) false to cancel the default animation
17346      */
17347     collapse : function(deep, anim){
17348         if(this.expanded && !this.isHiddenRoot()){
17349             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
17350                 return;
17351             }
17352             this.expanded = false;
17353             if((this.getOwnerTree().animate && anim !== false) || anim){
17354                 this.ui.animCollapse(function(){
17355                     this.fireEvent("collapse", this);
17356                     if(deep === true){
17357                         this.collapseChildNodes(true);
17358                     }
17359                 }.createDelegate(this));
17360                 return;
17361             }else{
17362                 this.ui.collapse();
17363                 this.fireEvent("collapse", this);
17364             }
17365         }
17366         if(deep === true){
17367             var cs = this.childNodes;
17368             for(var i = 0, len = cs.length; i < len; i++) {
17369                 cs[i].collapse(true, false);
17370             }
17371         }
17372     },
17373
17374     // private
17375     delayedExpand : function(delay){
17376         if(!this.expandProcId){
17377             this.expandProcId = this.expand.defer(delay, this);
17378         }
17379     },
17380
17381     // private
17382     cancelExpand : function(){
17383         if(this.expandProcId){
17384             clearTimeout(this.expandProcId);
17385         }
17386         this.expandProcId = false;
17387     },
17388
17389     /**
17390      * Toggles expanded/collapsed state of the node
17391      */
17392     toggle : function(){
17393         if(this.expanded){
17394             this.collapse();
17395         }else{
17396             this.expand();
17397         }
17398     },
17399
17400     /**
17401      * Ensures all parent nodes are expanded
17402      */
17403     ensureVisible : function(callback){
17404         var tree = this.getOwnerTree();
17405         tree.expandPath(this.parentNode.getPath(), false, function(){
17406             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
17407             Roo.callback(callback);
17408         }.createDelegate(this));
17409     },
17410
17411     /**
17412      * Expand all child nodes
17413      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
17414      */
17415     expandChildNodes : function(deep){
17416         var cs = this.childNodes;
17417         for(var i = 0, len = cs.length; i < len; i++) {
17418                 cs[i].expand(deep);
17419         }
17420     },
17421
17422     /**
17423      * Collapse all child nodes
17424      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
17425      */
17426     collapseChildNodes : function(deep){
17427         var cs = this.childNodes;
17428         for(var i = 0, len = cs.length; i < len; i++) {
17429                 cs[i].collapse(deep);
17430         }
17431     },
17432
17433     /**
17434      * Disables this node
17435      */
17436     disable : function(){
17437         this.disabled = true;
17438         this.unselect();
17439         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17440             this.ui.onDisableChange(this, true);
17441         }
17442         this.fireEvent("disabledchange", this, true);
17443     },
17444
17445     /**
17446      * Enables this node
17447      */
17448     enable : function(){
17449         this.disabled = false;
17450         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17451             this.ui.onDisableChange(this, false);
17452         }
17453         this.fireEvent("disabledchange", this, false);
17454     },
17455
17456     // private
17457     renderChildren : function(suppressEvent){
17458         if(suppressEvent !== false){
17459             this.fireEvent("beforechildrenrendered", this);
17460         }
17461         var cs = this.childNodes;
17462         for(var i = 0, len = cs.length; i < len; i++){
17463             cs[i].render(true);
17464         }
17465         this.childrenRendered = true;
17466     },
17467
17468     // private
17469     sort : function(fn, scope){
17470         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
17471         if(this.childrenRendered){
17472             var cs = this.childNodes;
17473             for(var i = 0, len = cs.length; i < len; i++){
17474                 cs[i].render(true);
17475             }
17476         }
17477     },
17478
17479     // private
17480     render : function(bulkRender){
17481         this.ui.render(bulkRender);
17482         if(!this.rendered){
17483             this.rendered = true;
17484             if(this.expanded){
17485                 this.expanded = false;
17486                 this.expand(false, false);
17487             }
17488         }
17489     },
17490
17491     // private
17492     renderIndent : function(deep, refresh){
17493         if(refresh){
17494             this.ui.childIndent = null;
17495         }
17496         this.ui.renderIndent();
17497         if(deep === true && this.childrenRendered){
17498             var cs = this.childNodes;
17499             for(var i = 0, len = cs.length; i < len; i++){
17500                 cs[i].renderIndent(true, refresh);
17501             }
17502         }
17503     }
17504 });/*
17505  * Based on:
17506  * Ext JS Library 1.1.1
17507  * Copyright(c) 2006-2007, Ext JS, LLC.
17508  *
17509  * Originally Released Under LGPL - original licence link has changed is not relivant.
17510  *
17511  * Fork - LGPL
17512  * <script type="text/javascript">
17513  */
17514  
17515 /**
17516  * @class Roo.tree.AsyncTreeNode
17517  * @extends Roo.tree.TreeNode
17518  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
17519  * @constructor
17520  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
17521  */
17522  Roo.tree.AsyncTreeNode = function(config){
17523     this.loaded = false;
17524     this.loading = false;
17525     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
17526     /**
17527     * @event beforeload
17528     * Fires before this node is loaded, return false to cancel
17529     * @param {Node} this This node
17530     */
17531     this.addEvents({'beforeload':true, 'load': true});
17532     /**
17533     * @event load
17534     * Fires when this node is loaded
17535     * @param {Node} this This node
17536     */
17537     /**
17538      * The loader used by this node (defaults to using the tree's defined loader)
17539      * @type TreeLoader
17540      * @property loader
17541      */
17542 };
17543 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
17544     expand : function(deep, anim, callback){
17545         if(this.loading){ // if an async load is already running, waiting til it's done
17546             var timer;
17547             var f = function(){
17548                 if(!this.loading){ // done loading
17549                     clearInterval(timer);
17550                     this.expand(deep, anim, callback);
17551                 }
17552             }.createDelegate(this);
17553             timer = setInterval(f, 200);
17554             return;
17555         }
17556         if(!this.loaded){
17557             if(this.fireEvent("beforeload", this) === false){
17558                 return;
17559             }
17560             this.loading = true;
17561             this.ui.beforeLoad(this);
17562             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
17563             if(loader){
17564                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
17565                 return;
17566             }
17567         }
17568         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
17569     },
17570     
17571     /**
17572      * Returns true if this node is currently loading
17573      * @return {Boolean}
17574      */
17575     isLoading : function(){
17576         return this.loading;  
17577     },
17578     
17579     loadComplete : function(deep, anim, callback){
17580         this.loading = false;
17581         this.loaded = true;
17582         this.ui.afterLoad(this);
17583         this.fireEvent("load", this);
17584         this.expand(deep, anim, callback);
17585     },
17586     
17587     /**
17588      * Returns true if this node has been loaded
17589      * @return {Boolean}
17590      */
17591     isLoaded : function(){
17592         return this.loaded;
17593     },
17594     
17595     hasChildNodes : function(){
17596         if(!this.isLeaf() && !this.loaded){
17597             return true;
17598         }else{
17599             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
17600         }
17601     },
17602
17603     /**
17604      * Trigger a reload for this node
17605      * @param {Function} callback
17606      */
17607     reload : function(callback){
17608         this.collapse(false, false);
17609         while(this.firstChild){
17610             this.removeChild(this.firstChild);
17611         }
17612         this.childrenRendered = false;
17613         this.loaded = false;
17614         if(this.isHiddenRoot()){
17615             this.expanded = false;
17616         }
17617         this.expand(false, false, callback);
17618     }
17619 });/*
17620  * Based on:
17621  * Ext JS Library 1.1.1
17622  * Copyright(c) 2006-2007, Ext JS, LLC.
17623  *
17624  * Originally Released Under LGPL - original licence link has changed is not relivant.
17625  *
17626  * Fork - LGPL
17627  * <script type="text/javascript">
17628  */
17629  
17630 /**
17631  * @class Roo.tree.TreeNodeUI
17632  * @constructor
17633  * @param {Object} node The node to render
17634  * The TreeNode UI implementation is separate from the
17635  * tree implementation. Unless you are customizing the tree UI,
17636  * you should never have to use this directly.
17637  */
17638 Roo.tree.TreeNodeUI = function(node){
17639     this.node = node;
17640     this.rendered = false;
17641     this.animating = false;
17642     this.emptyIcon = Roo.BLANK_IMAGE_URL;
17643 };
17644
17645 Roo.tree.TreeNodeUI.prototype = {
17646     removeChild : function(node){
17647         if(this.rendered){
17648             this.ctNode.removeChild(node.ui.getEl());
17649         }
17650     },
17651
17652     beforeLoad : function(){
17653          this.addClass("x-tree-node-loading");
17654     },
17655
17656     afterLoad : function(){
17657          this.removeClass("x-tree-node-loading");
17658     },
17659
17660     onTextChange : function(node, text, oldText){
17661         if(this.rendered){
17662             this.textNode.innerHTML = text;
17663         }
17664     },
17665
17666     onDisableChange : function(node, state){
17667         this.disabled = state;
17668         if(state){
17669             this.addClass("x-tree-node-disabled");
17670         }else{
17671             this.removeClass("x-tree-node-disabled");
17672         }
17673     },
17674
17675     onSelectedChange : function(state){
17676         if(state){
17677             this.focus();
17678             this.addClass("x-tree-selected");
17679         }else{
17680             //this.blur();
17681             this.removeClass("x-tree-selected");
17682         }
17683     },
17684
17685     onMove : function(tree, node, oldParent, newParent, index, refNode){
17686         this.childIndent = null;
17687         if(this.rendered){
17688             var targetNode = newParent.ui.getContainer();
17689             if(!targetNode){//target not rendered
17690                 this.holder = document.createElement("div");
17691                 this.holder.appendChild(this.wrap);
17692                 return;
17693             }
17694             var insertBefore = refNode ? refNode.ui.getEl() : null;
17695             if(insertBefore){
17696                 targetNode.insertBefore(this.wrap, insertBefore);
17697             }else{
17698                 targetNode.appendChild(this.wrap);
17699             }
17700             this.node.renderIndent(true);
17701         }
17702     },
17703
17704     addClass : function(cls){
17705         if(this.elNode){
17706             Roo.fly(this.elNode).addClass(cls);
17707         }
17708     },
17709
17710     removeClass : function(cls){
17711         if(this.elNode){
17712             Roo.fly(this.elNode).removeClass(cls);
17713         }
17714     },
17715
17716     remove : function(){
17717         if(this.rendered){
17718             this.holder = document.createElement("div");
17719             this.holder.appendChild(this.wrap);
17720         }
17721     },
17722
17723     fireEvent : function(){
17724         return this.node.fireEvent.apply(this.node, arguments);
17725     },
17726
17727     initEvents : function(){
17728         this.node.on("move", this.onMove, this);
17729         var E = Roo.EventManager;
17730         var a = this.anchor;
17731
17732         var el = Roo.fly(a, '_treeui');
17733
17734         if(Roo.isOpera){ // opera render bug ignores the CSS
17735             el.setStyle("text-decoration", "none");
17736         }
17737
17738         el.on("click", this.onClick, this);
17739         el.on("dblclick", this.onDblClick, this);
17740
17741         if(this.checkbox){
17742             Roo.EventManager.on(this.checkbox,
17743                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
17744         }
17745
17746         el.on("contextmenu", this.onContextMenu, this);
17747
17748         var icon = Roo.fly(this.iconNode);
17749         icon.on("click", this.onClick, this);
17750         icon.on("dblclick", this.onDblClick, this);
17751         icon.on("contextmenu", this.onContextMenu, this);
17752         E.on(this.ecNode, "click", this.ecClick, this, true);
17753
17754         if(this.node.disabled){
17755             this.addClass("x-tree-node-disabled");
17756         }
17757         if(this.node.hidden){
17758             this.addClass("x-tree-node-disabled");
17759         }
17760         var ot = this.node.getOwnerTree();
17761         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
17762         if(dd && (!this.node.isRoot || ot.rootVisible)){
17763             Roo.dd.Registry.register(this.elNode, {
17764                 node: this.node,
17765                 handles: this.getDDHandles(),
17766                 isHandle: false
17767             });
17768         }
17769     },
17770
17771     getDDHandles : function(){
17772         return [this.iconNode, this.textNode];
17773     },
17774
17775     hide : function(){
17776         if(this.rendered){
17777             this.wrap.style.display = "none";
17778         }
17779     },
17780
17781     show : function(){
17782         if(this.rendered){
17783             this.wrap.style.display = "";
17784         }
17785     },
17786
17787     onContextMenu : function(e){
17788         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
17789             e.preventDefault();
17790             this.focus();
17791             this.fireEvent("contextmenu", this.node, e);
17792         }
17793     },
17794
17795     onClick : function(e){
17796         if(this.dropping){
17797             e.stopEvent();
17798             return;
17799         }
17800         if(this.fireEvent("beforeclick", this.node, e) !== false){
17801             if(!this.disabled && this.node.attributes.href){
17802                 this.fireEvent("click", this.node, e);
17803                 return;
17804             }
17805             e.preventDefault();
17806             if(this.disabled){
17807                 return;
17808             }
17809
17810             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
17811                 this.node.toggle();
17812             }
17813
17814             this.fireEvent("click", this.node, e);
17815         }else{
17816             e.stopEvent();
17817         }
17818     },
17819
17820     onDblClick : function(e){
17821         e.preventDefault();
17822         if(this.disabled){
17823             return;
17824         }
17825         if(this.checkbox){
17826             this.toggleCheck();
17827         }
17828         if(!this.animating && this.node.hasChildNodes()){
17829             this.node.toggle();
17830         }
17831         this.fireEvent("dblclick", this.node, e);
17832     },
17833
17834     onCheckChange : function(){
17835         var checked = this.checkbox.checked;
17836         this.node.attributes.checked = checked;
17837         this.fireEvent('checkchange', this.node, checked);
17838     },
17839
17840     ecClick : function(e){
17841         if(!this.animating && this.node.hasChildNodes()){
17842             this.node.toggle();
17843         }
17844     },
17845
17846     startDrop : function(){
17847         this.dropping = true;
17848     },
17849
17850     // delayed drop so the click event doesn't get fired on a drop
17851     endDrop : function(){
17852        setTimeout(function(){
17853            this.dropping = false;
17854        }.createDelegate(this), 50);
17855     },
17856
17857     expand : function(){
17858         this.updateExpandIcon();
17859         this.ctNode.style.display = "";
17860     },
17861
17862     focus : function(){
17863         if(!this.node.preventHScroll){
17864             try{this.anchor.focus();
17865             }catch(e){}
17866         }else if(!Roo.isIE){
17867             try{
17868                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
17869                 var l = noscroll.scrollLeft;
17870                 this.anchor.focus();
17871                 noscroll.scrollLeft = l;
17872             }catch(e){}
17873         }
17874     },
17875
17876     toggleCheck : function(value){
17877         var cb = this.checkbox;
17878         if(cb){
17879             cb.checked = (value === undefined ? !cb.checked : value);
17880         }
17881     },
17882
17883     blur : function(){
17884         try{
17885             this.anchor.blur();
17886         }catch(e){}
17887     },
17888
17889     animExpand : function(callback){
17890         var ct = Roo.get(this.ctNode);
17891         ct.stopFx();
17892         if(!this.node.hasChildNodes()){
17893             this.updateExpandIcon();
17894             this.ctNode.style.display = "";
17895             Roo.callback(callback);
17896             return;
17897         }
17898         this.animating = true;
17899         this.updateExpandIcon();
17900
17901         ct.slideIn('t', {
17902            callback : function(){
17903                this.animating = false;
17904                Roo.callback(callback);
17905             },
17906             scope: this,
17907             duration: this.node.ownerTree.duration || .25
17908         });
17909     },
17910
17911     highlight : function(){
17912         var tree = this.node.getOwnerTree();
17913         Roo.fly(this.wrap).highlight(
17914             tree.hlColor || "C3DAF9",
17915             {endColor: tree.hlBaseColor}
17916         );
17917     },
17918
17919     collapse : function(){
17920         this.updateExpandIcon();
17921         this.ctNode.style.display = "none";
17922     },
17923
17924     animCollapse : function(callback){
17925         var ct = Roo.get(this.ctNode);
17926         ct.enableDisplayMode('block');
17927         ct.stopFx();
17928
17929         this.animating = true;
17930         this.updateExpandIcon();
17931
17932         ct.slideOut('t', {
17933             callback : function(){
17934                this.animating = false;
17935                Roo.callback(callback);
17936             },
17937             scope: this,
17938             duration: this.node.ownerTree.duration || .25
17939         });
17940     },
17941
17942     getContainer : function(){
17943         return this.ctNode;
17944     },
17945
17946     getEl : function(){
17947         return this.wrap;
17948     },
17949
17950     appendDDGhost : function(ghostNode){
17951         ghostNode.appendChild(this.elNode.cloneNode(true));
17952     },
17953
17954     getDDRepairXY : function(){
17955         return Roo.lib.Dom.getXY(this.iconNode);
17956     },
17957
17958     onRender : function(){
17959         this.render();
17960     },
17961
17962     render : function(bulkRender){
17963         var n = this.node, a = n.attributes;
17964         var targetNode = n.parentNode ?
17965               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
17966
17967         if(!this.rendered){
17968             this.rendered = true;
17969
17970             this.renderElements(n, a, targetNode, bulkRender);
17971
17972             if(a.qtip){
17973                if(this.textNode.setAttributeNS){
17974                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
17975                    if(a.qtipTitle){
17976                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
17977                    }
17978                }else{
17979                    this.textNode.setAttribute("ext:qtip", a.qtip);
17980                    if(a.qtipTitle){
17981                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
17982                    }
17983                }
17984             }else if(a.qtipCfg){
17985                 a.qtipCfg.target = Roo.id(this.textNode);
17986                 Roo.QuickTips.register(a.qtipCfg);
17987             }
17988             this.initEvents();
17989             if(!this.node.expanded){
17990                 this.updateExpandIcon();
17991             }
17992         }else{
17993             if(bulkRender === true) {
17994                 targetNode.appendChild(this.wrap);
17995             }
17996         }
17997     },
17998
17999     renderElements : function(n, a, targetNode, bulkRender){
18000         // add some indent caching, this helps performance when rendering a large tree
18001         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
18002         var t = n.getOwnerTree();
18003         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
18004         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
18005         var cb = typeof a.checked == 'boolean';
18006         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
18007         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
18008             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
18009             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
18010             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
18011             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
18012             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
18013              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
18014                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
18015             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
18016             "</li>"];
18017
18018         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
18019             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
18020                                 n.nextSibling.ui.getEl(), buf.join(""));
18021         }else{
18022             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
18023         }
18024
18025         this.elNode = this.wrap.childNodes[0];
18026         this.ctNode = this.wrap.childNodes[1];
18027         var cs = this.elNode.childNodes;
18028         this.indentNode = cs[0];
18029         this.ecNode = cs[1];
18030         this.iconNode = cs[2];
18031         var index = 3;
18032         if(cb){
18033             this.checkbox = cs[3];
18034             index++;
18035         }
18036         this.anchor = cs[index];
18037         this.textNode = cs[index].firstChild;
18038     },
18039
18040     getAnchor : function(){
18041         return this.anchor;
18042     },
18043
18044     getTextEl : function(){
18045         return this.textNode;
18046     },
18047
18048     getIconEl : function(){
18049         return this.iconNode;
18050     },
18051
18052     isChecked : function(){
18053         return this.checkbox ? this.checkbox.checked : false;
18054     },
18055
18056     updateExpandIcon : function(){
18057         if(this.rendered){
18058             var n = this.node, c1, c2;
18059             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
18060             var hasChild = n.hasChildNodes();
18061             if(hasChild){
18062                 if(n.expanded){
18063                     cls += "-minus";
18064                     c1 = "x-tree-node-collapsed";
18065                     c2 = "x-tree-node-expanded";
18066                 }else{
18067                     cls += "-plus";
18068                     c1 = "x-tree-node-expanded";
18069                     c2 = "x-tree-node-collapsed";
18070                 }
18071                 if(this.wasLeaf){
18072                     this.removeClass("x-tree-node-leaf");
18073                     this.wasLeaf = false;
18074                 }
18075                 if(this.c1 != c1 || this.c2 != c2){
18076                     Roo.fly(this.elNode).replaceClass(c1, c2);
18077                     this.c1 = c1; this.c2 = c2;
18078                 }
18079             }else{
18080                 if(!this.wasLeaf){
18081                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
18082                     delete this.c1;
18083                     delete this.c2;
18084                     this.wasLeaf = true;
18085                 }
18086             }
18087             var ecc = "x-tree-ec-icon "+cls;
18088             if(this.ecc != ecc){
18089                 this.ecNode.className = ecc;
18090                 this.ecc = ecc;
18091             }
18092         }
18093     },
18094
18095     getChildIndent : function(){
18096         if(!this.childIndent){
18097             var buf = [];
18098             var p = this.node;
18099             while(p){
18100                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
18101                     if(!p.isLast()) {
18102                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
18103                     } else {
18104                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
18105                     }
18106                 }
18107                 p = p.parentNode;
18108             }
18109             this.childIndent = buf.join("");
18110         }
18111         return this.childIndent;
18112     },
18113
18114     renderIndent : function(){
18115         if(this.rendered){
18116             var indent = "";
18117             var p = this.node.parentNode;
18118             if(p){
18119                 indent = p.ui.getChildIndent();
18120             }
18121             if(this.indentMarkup != indent){ // don't rerender if not required
18122                 this.indentNode.innerHTML = indent;
18123                 this.indentMarkup = indent;
18124             }
18125             this.updateExpandIcon();
18126         }
18127     }
18128 };
18129
18130 Roo.tree.RootTreeNodeUI = function(){
18131     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
18132 };
18133 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
18134     render : function(){
18135         if(!this.rendered){
18136             var targetNode = this.node.ownerTree.innerCt.dom;
18137             this.node.expanded = true;
18138             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
18139             this.wrap = this.ctNode = targetNode.firstChild;
18140         }
18141     },
18142     collapse : function(){
18143     },
18144     expand : function(){
18145     }
18146 });/*
18147  * Based on:
18148  * Ext JS Library 1.1.1
18149  * Copyright(c) 2006-2007, Ext JS, LLC.
18150  *
18151  * Originally Released Under LGPL - original licence link has changed is not relivant.
18152  *
18153  * Fork - LGPL
18154  * <script type="text/javascript">
18155  */
18156 /**
18157  * @class Roo.tree.TreeLoader
18158  * @extends Roo.util.Observable
18159  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
18160  * nodes from a specified URL. The response must be a javascript Array definition
18161  * who's elements are node definition objects. eg:
18162  * <pre><code>
18163    [{ 'id': 1, 'text': 'A folder Node', 'leaf': false },
18164     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }]
18165 </code></pre>
18166  * <br><br>
18167  * A server request is sent, and child nodes are loaded only when a node is expanded.
18168  * The loading node's id is passed to the server under the parameter name "node" to
18169  * enable the server to produce the correct child nodes.
18170  * <br><br>
18171  * To pass extra parameters, an event handler may be attached to the "beforeload"
18172  * event, and the parameters specified in the TreeLoader's baseParams property:
18173  * <pre><code>
18174     myTreeLoader.on("beforeload", function(treeLoader, node) {
18175         this.baseParams.category = node.attributes.category;
18176     }, this);
18177 </code></pre><
18178  * This would pass an HTTP parameter called "category" to the server containing
18179  * the value of the Node's "category" attribute.
18180  * @constructor
18181  * Creates a new Treeloader.
18182  * @param {Object} config A config object containing config properties.
18183  */
18184 Roo.tree.TreeLoader = function(config){
18185     this.baseParams = {};
18186     this.requestMethod = "POST";
18187     Roo.apply(this, config);
18188
18189     this.addEvents({
18190     
18191         /**
18192          * @event beforeload
18193          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
18194          * @param {Object} This TreeLoader object.
18195          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18196          * @param {Object} callback The callback function specified in the {@link #load} call.
18197          */
18198         beforeload : true,
18199         /**
18200          * @event load
18201          * Fires when the node has been successfuly loaded.
18202          * @param {Object} This TreeLoader object.
18203          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18204          * @param {Object} response The response object containing the data from the server.
18205          */
18206         load : true,
18207         /**
18208          * @event loadexception
18209          * Fires if the network request failed.
18210          * @param {Object} This TreeLoader object.
18211          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18212          * @param {Object} response The response object containing the data from the server.
18213          */
18214         loadexception : true,
18215         /**
18216          * @event create
18217          * Fires before a node is created, enabling you to return custom Node types 
18218          * @param {Object} This TreeLoader object.
18219          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
18220          */
18221         create : true
18222     });
18223
18224     Roo.tree.TreeLoader.superclass.constructor.call(this);
18225 };
18226
18227 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
18228     /**
18229     * @cfg {String} dataUrl The URL from which to request a Json string which
18230     * specifies an array of node definition object representing the child nodes
18231     * to be loaded.
18232     */
18233     /**
18234     * @cfg {Object} baseParams (optional) An object containing properties which
18235     * specify HTTP parameters to be passed to each request for child nodes.
18236     */
18237     /**
18238     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
18239     * created by this loader. If the attributes sent by the server have an attribute in this object,
18240     * they take priority.
18241     */
18242     /**
18243     * @cfg {Object} uiProviders (optional) An object containing properties which
18244     * 
18245     * DEPRECIATED - use 'create' event handler to modify attributes - which affect creation.
18246     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
18247     * <i>uiProvider</i> attribute of a returned child node is a string rather
18248     * than a reference to a TreeNodeUI implementation, this that string value
18249     * is used as a property name in the uiProviders object. You can define the provider named
18250     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
18251     */
18252     uiProviders : {},
18253
18254     /**
18255     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
18256     * child nodes before loading.
18257     */
18258     clearOnLoad : true,
18259
18260     /**
18261     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
18262     * property on loading, rather than expecting an array. (eg. more compatible to a standard
18263     * Grid query { data : [ .....] }
18264     */
18265     
18266     root : false,
18267      /**
18268     * @cfg {String} queryParam (optional) 
18269     * Name of the query as it will be passed on the querystring (defaults to 'node')
18270     * eg. the request will be ?node=[id]
18271     */
18272     
18273     
18274     queryParam: false,
18275     
18276     /**
18277      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
18278      * This is called automatically when a node is expanded, but may be used to reload
18279      * a node (or append new children if the {@link #clearOnLoad} option is false.)
18280      * @param {Roo.tree.TreeNode} node
18281      * @param {Function} callback
18282      */
18283     load : function(node, callback){
18284         if(this.clearOnLoad){
18285             while(node.firstChild){
18286                 node.removeChild(node.firstChild);
18287             }
18288         }
18289         if(node.attributes.children){ // preloaded json children
18290             var cs = node.attributes.children;
18291             for(var i = 0, len = cs.length; i < len; i++){
18292                 node.appendChild(this.createNode(cs[i]));
18293             }
18294             if(typeof callback == "function"){
18295                 callback();
18296             }
18297         }else if(this.dataUrl){
18298             this.requestData(node, callback);
18299         }
18300     },
18301
18302     getParams: function(node){
18303         var buf = [], bp = this.baseParams;
18304         for(var key in bp){
18305             if(typeof bp[key] != "function"){
18306                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
18307             }
18308         }
18309         var n = this.queryParam === false ? 'node' : this.queryParam;
18310         buf.push(n + "=", encodeURIComponent(node.id));
18311         return buf.join("");
18312     },
18313
18314     requestData : function(node, callback){
18315         if(this.fireEvent("beforeload", this, node, callback) !== false){
18316             this.transId = Roo.Ajax.request({
18317                 method:this.requestMethod,
18318                 url: this.dataUrl||this.url,
18319                 success: this.handleResponse,
18320                 failure: this.handleFailure,
18321                 scope: this,
18322                 argument: {callback: callback, node: node},
18323                 params: this.getParams(node)
18324             });
18325         }else{
18326             // if the load is cancelled, make sure we notify
18327             // the node that we are done
18328             if(typeof callback == "function"){
18329                 callback();
18330             }
18331         }
18332     },
18333
18334     isLoading : function(){
18335         return this.transId ? true : false;
18336     },
18337
18338     abort : function(){
18339         if(this.isLoading()){
18340             Roo.Ajax.abort(this.transId);
18341         }
18342     },
18343
18344     // private
18345     createNode : function(attr){
18346         // apply baseAttrs, nice idea Corey!
18347         if(this.baseAttrs){
18348             Roo.applyIf(attr, this.baseAttrs);
18349         }
18350         if(this.applyLoader !== false){
18351             attr.loader = this;
18352         }
18353         // uiProvider = depreciated..
18354         
18355         if(typeof(attr.uiProvider) == 'string'){
18356            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
18357                 /**  eval:var:attr */ eval(attr.uiProvider);
18358         }
18359         if(typeof(this.uiProviders['default']) != 'undefined') {
18360             attr.uiProvider = this.uiProviders['default'];
18361         }
18362         
18363         this.fireEvent('create', this, attr);
18364         
18365         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
18366         return(attr.leaf ?
18367                         new Roo.tree.TreeNode(attr) :
18368                         new Roo.tree.AsyncTreeNode(attr));
18369     },
18370
18371     processResponse : function(response, node, callback){
18372         var json = response.responseText;
18373         try {
18374             
18375             var o = /**  eval:var:zzzzzzzzzz */ eval("("+json+")");
18376             if (this.root !== false) {
18377                 o = o[this.root];
18378             }
18379             
18380             for(var i = 0, len = o.length; i < len; i++){
18381                 var n = this.createNode(o[i]);
18382                 if(n){
18383                     node.appendChild(n);
18384                 }
18385             }
18386             if(typeof callback == "function"){
18387                 callback(this, node);
18388             }
18389         }catch(e){
18390             this.handleFailure(response);
18391         }
18392     },
18393
18394     handleResponse : function(response){
18395         this.transId = false;
18396         var a = response.argument;
18397         this.processResponse(response, a.node, a.callback);
18398         this.fireEvent("load", this, a.node, response);
18399     },
18400
18401     handleFailure : function(response){
18402         this.transId = false;
18403         var a = response.argument;
18404         this.fireEvent("loadexception", this, a.node, response);
18405         if(typeof a.callback == "function"){
18406             a.callback(this, a.node);
18407         }
18408     }
18409 });/*
18410  * Based on:
18411  * Ext JS Library 1.1.1
18412  * Copyright(c) 2006-2007, Ext JS, LLC.
18413  *
18414  * Originally Released Under LGPL - original licence link has changed is not relivant.
18415  *
18416  * Fork - LGPL
18417  * <script type="text/javascript">
18418  */
18419
18420 /**
18421 * @class Roo.tree.TreeFilter
18422 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
18423 * @param {TreePanel} tree
18424 * @param {Object} config (optional)
18425  */
18426 Roo.tree.TreeFilter = function(tree, config){
18427     this.tree = tree;
18428     this.filtered = {};
18429     Roo.apply(this, config);
18430 };
18431
18432 Roo.tree.TreeFilter.prototype = {
18433     clearBlank:false,
18434     reverse:false,
18435     autoClear:false,
18436     remove:false,
18437
18438      /**
18439      * Filter the data by a specific attribute.
18440      * @param {String/RegExp} value Either string that the attribute value
18441      * should start with or a RegExp to test against the attribute
18442      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
18443      * @param {TreeNode} startNode (optional) The node to start the filter at.
18444      */
18445     filter : function(value, attr, startNode){
18446         attr = attr || "text";
18447         var f;
18448         if(typeof value == "string"){
18449             var vlen = value.length;
18450             // auto clear empty filter
18451             if(vlen == 0 && this.clearBlank){
18452                 this.clear();
18453                 return;
18454             }
18455             value = value.toLowerCase();
18456             f = function(n){
18457                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
18458             };
18459         }else if(value.exec){ // regex?
18460             f = function(n){
18461                 return value.test(n.attributes[attr]);
18462             };
18463         }else{
18464             throw 'Illegal filter type, must be string or regex';
18465         }
18466         this.filterBy(f, null, startNode);
18467         },
18468
18469     /**
18470      * Filter by a function. The passed function will be called with each
18471      * node in the tree (or from the startNode). If the function returns true, the node is kept
18472      * otherwise it is filtered. If a node is filtered, its children are also filtered.
18473      * @param {Function} fn The filter function
18474      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
18475      */
18476     filterBy : function(fn, scope, startNode){
18477         startNode = startNode || this.tree.root;
18478         if(this.autoClear){
18479             this.clear();
18480         }
18481         var af = this.filtered, rv = this.reverse;
18482         var f = function(n){
18483             if(n == startNode){
18484                 return true;
18485             }
18486             if(af[n.id]){
18487                 return false;
18488             }
18489             var m = fn.call(scope || n, n);
18490             if(!m || rv){
18491                 af[n.id] = n;
18492                 n.ui.hide();
18493                 return false;
18494             }
18495             return true;
18496         };
18497         startNode.cascade(f);
18498         if(this.remove){
18499            for(var id in af){
18500                if(typeof id != "function"){
18501                    var n = af[id];
18502                    if(n && n.parentNode){
18503                        n.parentNode.removeChild(n);
18504                    }
18505                }
18506            }
18507         }
18508     },
18509
18510     /**
18511      * Clears the current filter. Note: with the "remove" option
18512      * set a filter cannot be cleared.
18513      */
18514     clear : function(){
18515         var t = this.tree;
18516         var af = this.filtered;
18517         for(var id in af){
18518             if(typeof id != "function"){
18519                 var n = af[id];
18520                 if(n){
18521                     n.ui.show();
18522                 }
18523             }
18524         }
18525         this.filtered = {};
18526     }
18527 };
18528 /*
18529  * Based on:
18530  * Ext JS Library 1.1.1
18531  * Copyright(c) 2006-2007, Ext JS, LLC.
18532  *
18533  * Originally Released Under LGPL - original licence link has changed is not relivant.
18534  *
18535  * Fork - LGPL
18536  * <script type="text/javascript">
18537  */
18538  
18539
18540 /**
18541  * @class Roo.tree.TreeSorter
18542  * Provides sorting of nodes in a TreePanel
18543  * 
18544  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
18545  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
18546  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
18547  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
18548  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
18549  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
18550  * @constructor
18551  * @param {TreePanel} tree
18552  * @param {Object} config
18553  */
18554 Roo.tree.TreeSorter = function(tree, config){
18555     Roo.apply(this, config);
18556     tree.on("beforechildrenrendered", this.doSort, this);
18557     tree.on("append", this.updateSort, this);
18558     tree.on("insert", this.updateSort, this);
18559     
18560     var dsc = this.dir && this.dir.toLowerCase() == "desc";
18561     var p = this.property || "text";
18562     var sortType = this.sortType;
18563     var fs = this.folderSort;
18564     var cs = this.caseSensitive === true;
18565     var leafAttr = this.leafAttr || 'leaf';
18566
18567     this.sortFn = function(n1, n2){
18568         if(fs){
18569             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
18570                 return 1;
18571             }
18572             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
18573                 return -1;
18574             }
18575         }
18576         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
18577         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
18578         if(v1 < v2){
18579                         return dsc ? +1 : -1;
18580                 }else if(v1 > v2){
18581                         return dsc ? -1 : +1;
18582         }else{
18583                 return 0;
18584         }
18585     };
18586 };
18587
18588 Roo.tree.TreeSorter.prototype = {
18589     doSort : function(node){
18590         node.sort(this.sortFn);
18591     },
18592     
18593     compareNodes : function(n1, n2){
18594         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
18595     },
18596     
18597     updateSort : function(tree, node){
18598         if(node.childrenRendered){
18599             this.doSort.defer(1, this, [node]);
18600         }
18601     }
18602 };/*
18603  * Based on:
18604  * Ext JS Library 1.1.1
18605  * Copyright(c) 2006-2007, Ext JS, LLC.
18606  *
18607  * Originally Released Under LGPL - original licence link has changed is not relivant.
18608  *
18609  * Fork - LGPL
18610  * <script type="text/javascript">
18611  */
18612
18613 if(Roo.dd.DropZone){
18614     
18615 Roo.tree.TreeDropZone = function(tree, config){
18616     this.allowParentInsert = false;
18617     this.allowContainerDrop = false;
18618     this.appendOnly = false;
18619     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
18620     this.tree = tree;
18621     this.lastInsertClass = "x-tree-no-status";
18622     this.dragOverData = {};
18623 };
18624
18625 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
18626     ddGroup : "TreeDD",
18627     
18628     expandDelay : 1000,
18629     
18630     expandNode : function(node){
18631         if(node.hasChildNodes() && !node.isExpanded()){
18632             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
18633         }
18634     },
18635     
18636     queueExpand : function(node){
18637         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
18638     },
18639     
18640     cancelExpand : function(){
18641         if(this.expandProcId){
18642             clearTimeout(this.expandProcId);
18643             this.expandProcId = false;
18644         }
18645     },
18646     
18647     isValidDropPoint : function(n, pt, dd, e, data){
18648         if(!n || !data){ return false; }
18649         var targetNode = n.node;
18650         var dropNode = data.node;
18651         // default drop rules
18652         if(!(targetNode && targetNode.isTarget && pt)){
18653             return false;
18654         }
18655         if(pt == "append" && targetNode.allowChildren === false){
18656             return false;
18657         }
18658         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
18659             return false;
18660         }
18661         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
18662             return false;
18663         }
18664         // reuse the object
18665         var overEvent = this.dragOverData;
18666         overEvent.tree = this.tree;
18667         overEvent.target = targetNode;
18668         overEvent.data = data;
18669         overEvent.point = pt;
18670         overEvent.source = dd;
18671         overEvent.rawEvent = e;
18672         overEvent.dropNode = dropNode;
18673         overEvent.cancel = false;  
18674         var result = this.tree.fireEvent("nodedragover", overEvent);
18675         return overEvent.cancel === false && result !== false;
18676     },
18677     
18678     getDropPoint : function(e, n, dd){
18679         var tn = n.node;
18680         if(tn.isRoot){
18681             return tn.allowChildren !== false ? "append" : false; // always append for root
18682         }
18683         var dragEl = n.ddel;
18684         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
18685         var y = Roo.lib.Event.getPageY(e);
18686         //var noAppend = tn.allowChildren === false || tn.isLeaf();
18687         
18688         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
18689         var noAppend = tn.allowChildren === false;
18690         if(this.appendOnly || tn.parentNode.allowChildren === false){
18691             return noAppend ? false : "append";
18692         }
18693         var noBelow = false;
18694         if(!this.allowParentInsert){
18695             noBelow = tn.hasChildNodes() && tn.isExpanded();
18696         }
18697         var q = (b - t) / (noAppend ? 2 : 3);
18698         if(y >= t && y < (t + q)){
18699             return "above";
18700         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
18701             return "below";
18702         }else{
18703             return "append";
18704         }
18705     },
18706     
18707     onNodeEnter : function(n, dd, e, data){
18708         this.cancelExpand();
18709     },
18710     
18711     onNodeOver : function(n, dd, e, data){
18712         var pt = this.getDropPoint(e, n, dd);
18713         var node = n.node;
18714         
18715         // auto node expand check
18716         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
18717             this.queueExpand(node);
18718         }else if(pt != "append"){
18719             this.cancelExpand();
18720         }
18721         
18722         // set the insert point style on the target node
18723         var returnCls = this.dropNotAllowed;
18724         if(this.isValidDropPoint(n, pt, dd, e, data)){
18725            if(pt){
18726                var el = n.ddel;
18727                var cls;
18728                if(pt == "above"){
18729                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
18730                    cls = "x-tree-drag-insert-above";
18731                }else if(pt == "below"){
18732                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
18733                    cls = "x-tree-drag-insert-below";
18734                }else{
18735                    returnCls = "x-tree-drop-ok-append";
18736                    cls = "x-tree-drag-append";
18737                }
18738                if(this.lastInsertClass != cls){
18739                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
18740                    this.lastInsertClass = cls;
18741                }
18742            }
18743        }
18744        return returnCls;
18745     },
18746     
18747     onNodeOut : function(n, dd, e, data){
18748         this.cancelExpand();
18749         this.removeDropIndicators(n);
18750     },
18751     
18752     onNodeDrop : function(n, dd, e, data){
18753         var point = this.getDropPoint(e, n, dd);
18754         var targetNode = n.node;
18755         targetNode.ui.startDrop();
18756         if(!this.isValidDropPoint(n, point, dd, e, data)){
18757             targetNode.ui.endDrop();
18758             return false;
18759         }
18760         // first try to find the drop node
18761         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
18762         var dropEvent = {
18763             tree : this.tree,
18764             target: targetNode,
18765             data: data,
18766             point: point,
18767             source: dd,
18768             rawEvent: e,
18769             dropNode: dropNode,
18770             cancel: !dropNode   
18771         };
18772         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
18773         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
18774             targetNode.ui.endDrop();
18775             return false;
18776         }
18777         // allow target changing
18778         targetNode = dropEvent.target;
18779         if(point == "append" && !targetNode.isExpanded()){
18780             targetNode.expand(false, null, function(){
18781                 this.completeDrop(dropEvent);
18782             }.createDelegate(this));
18783         }else{
18784             this.completeDrop(dropEvent);
18785         }
18786         return true;
18787     },
18788     
18789     completeDrop : function(de){
18790         var ns = de.dropNode, p = de.point, t = de.target;
18791         if(!(ns instanceof Array)){
18792             ns = [ns];
18793         }
18794         var n;
18795         for(var i = 0, len = ns.length; i < len; i++){
18796             n = ns[i];
18797             if(p == "above"){
18798                 t.parentNode.insertBefore(n, t);
18799             }else if(p == "below"){
18800                 t.parentNode.insertBefore(n, t.nextSibling);
18801             }else{
18802                 t.appendChild(n);
18803             }
18804         }
18805         n.ui.focus();
18806         if(this.tree.hlDrop){
18807             n.ui.highlight();
18808         }
18809         t.ui.endDrop();
18810         this.tree.fireEvent("nodedrop", de);
18811     },
18812     
18813     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
18814         if(this.tree.hlDrop){
18815             dropNode.ui.focus();
18816             dropNode.ui.highlight();
18817         }
18818         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
18819     },
18820     
18821     getTree : function(){
18822         return this.tree;
18823     },
18824     
18825     removeDropIndicators : function(n){
18826         if(n && n.ddel){
18827             var el = n.ddel;
18828             Roo.fly(el).removeClass([
18829                     "x-tree-drag-insert-above",
18830                     "x-tree-drag-insert-below",
18831                     "x-tree-drag-append"]);
18832             this.lastInsertClass = "_noclass";
18833         }
18834     },
18835     
18836     beforeDragDrop : function(target, e, id){
18837         this.cancelExpand();
18838         return true;
18839     },
18840     
18841     afterRepair : function(data){
18842         if(data && Roo.enableFx){
18843             data.node.ui.highlight();
18844         }
18845         this.hideProxy();
18846     }    
18847 });
18848
18849 }
18850 /*
18851  * Based on:
18852  * Ext JS Library 1.1.1
18853  * Copyright(c) 2006-2007, Ext JS, LLC.
18854  *
18855  * Originally Released Under LGPL - original licence link has changed is not relivant.
18856  *
18857  * Fork - LGPL
18858  * <script type="text/javascript">
18859  */
18860  
18861
18862 if(Roo.dd.DragZone){
18863 Roo.tree.TreeDragZone = function(tree, config){
18864     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
18865     this.tree = tree;
18866 };
18867
18868 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
18869     ddGroup : "TreeDD",
18870     
18871     onBeforeDrag : function(data, e){
18872         var n = data.node;
18873         return n && n.draggable && !n.disabled;
18874     },
18875     
18876     onInitDrag : function(e){
18877         var data = this.dragData;
18878         this.tree.getSelectionModel().select(data.node);
18879         this.proxy.update("");
18880         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
18881         this.tree.fireEvent("startdrag", this.tree, data.node, e);
18882     },
18883     
18884     getRepairXY : function(e, data){
18885         return data.node.ui.getDDRepairXY();
18886     },
18887     
18888     onEndDrag : function(data, e){
18889         this.tree.fireEvent("enddrag", this.tree, data.node, e);
18890     },
18891     
18892     onValidDrop : function(dd, e, id){
18893         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
18894         this.hideProxy();
18895     },
18896     
18897     beforeInvalidDrop : function(e, id){
18898         // this scrolls the original position back into view
18899         var sm = this.tree.getSelectionModel();
18900         sm.clearSelections();
18901         sm.select(this.dragData.node);
18902     }
18903 });
18904 }/*
18905  * Based on:
18906  * Ext JS Library 1.1.1
18907  * Copyright(c) 2006-2007, Ext JS, LLC.
18908  *
18909  * Originally Released Under LGPL - original licence link has changed is not relivant.
18910  *
18911  * Fork - LGPL
18912  * <script type="text/javascript">
18913  */
18914 /**
18915  * @class Roo.tree.TreeEditor
18916  * @extends Roo.Editor
18917  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
18918  * as the editor field.
18919  * @constructor
18920  * @param {TreePanel} tree
18921  * @param {Object} config Either a prebuilt {@link Roo.form.Field} instance or a Field config object
18922  */
18923 Roo.tree.TreeEditor = function(tree, config){
18924     config = config || {};
18925     var field = config.events ? config : new Roo.form.TextField(config);
18926     Roo.tree.TreeEditor.superclass.constructor.call(this, field);
18927
18928     this.tree = tree;
18929
18930     tree.on('beforeclick', this.beforeNodeClick, this);
18931     tree.getTreeEl().on('mousedown', this.hide, this);
18932     this.on('complete', this.updateNode, this);
18933     this.on('beforestartedit', this.fitToTree, this);
18934     this.on('startedit', this.bindScroll, this, {delay:10});
18935     this.on('specialkey', this.onSpecialKey, this);
18936 };
18937
18938 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
18939     /**
18940      * @cfg {String} alignment
18941      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
18942      */
18943     alignment: "l-l",
18944     // inherit
18945     autoSize: false,
18946     /**
18947      * @cfg {Boolean} hideEl
18948      * True to hide the bound element while the editor is displayed (defaults to false)
18949      */
18950     hideEl : false,
18951     /**
18952      * @cfg {String} cls
18953      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
18954      */
18955     cls: "x-small-editor x-tree-editor",
18956     /**
18957      * @cfg {Boolean} shim
18958      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
18959      */
18960     shim:false,
18961     // inherit
18962     shadow:"frame",
18963     /**
18964      * @cfg {Number} maxWidth
18965      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
18966      * the containing tree element's size, it will be automatically limited for you to the container width, taking
18967      * scroll and client offsets into account prior to each edit.
18968      */
18969     maxWidth: 250,
18970
18971     editDelay : 350,
18972
18973     // private
18974     fitToTree : function(ed, el){
18975         var td = this.tree.getTreeEl().dom, nd = el.dom;
18976         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
18977             td.scrollLeft = nd.offsetLeft;
18978         }
18979         var w = Math.min(
18980                 this.maxWidth,
18981                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
18982         this.setSize(w, '');
18983     },
18984
18985     // private
18986     triggerEdit : function(node){
18987         this.completeEdit();
18988         this.editNode = node;
18989         this.startEdit(node.ui.textNode, node.text);
18990     },
18991
18992     // private
18993     bindScroll : function(){
18994         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
18995     },
18996
18997     // private
18998     beforeNodeClick : function(node, e){
18999         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
19000         this.lastClick = new Date();
19001         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
19002             e.stopEvent();
19003             this.triggerEdit(node);
19004             return false;
19005         }
19006     },
19007
19008     // private
19009     updateNode : function(ed, value){
19010         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
19011         this.editNode.setText(value);
19012     },
19013
19014     // private
19015     onHide : function(){
19016         Roo.tree.TreeEditor.superclass.onHide.call(this);
19017         if(this.editNode){
19018             this.editNode.ui.focus();
19019         }
19020     },
19021
19022     // private
19023     onSpecialKey : function(field, e){
19024         var k = e.getKey();
19025         if(k == e.ESC){
19026             e.stopEvent();
19027             this.cancelEdit();
19028         }else if(k == e.ENTER && !e.hasModifier()){
19029             e.stopEvent();
19030             this.completeEdit();
19031         }
19032     }
19033 });//<Script type="text/javascript">
19034 /*
19035  * Based on:
19036  * Ext JS Library 1.1.1
19037  * Copyright(c) 2006-2007, Ext JS, LLC.
19038  *
19039  * Originally Released Under LGPL - original licence link has changed is not relivant.
19040  *
19041  * Fork - LGPL
19042  * <script type="text/javascript">
19043  */
19044  
19045 /**
19046  * Not documented??? - probably should be...
19047  */
19048
19049 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
19050     //focus: Roo.emptyFn, // prevent odd scrolling behavior
19051     
19052     renderElements : function(n, a, targetNode, bulkRender){
19053         //consel.log("renderElements?");
19054         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
19055
19056         var t = n.getOwnerTree();
19057         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
19058         
19059         var cols = t.columns;
19060         var bw = t.borderWidth;
19061         var c = cols[0];
19062         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
19063          var cb = typeof a.checked == "boolean";
19064         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19065         var colcls = 'x-t-' + tid + '-c0';
19066         var buf = [
19067             '<li class="x-tree-node">',
19068             
19069                 
19070                 '<div class="x-tree-node-el ', a.cls,'">',
19071                     // extran...
19072                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
19073                 
19074                 
19075                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
19076                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
19077                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
19078                            (a.icon ? ' x-tree-node-inline-icon' : ''),
19079                            (a.iconCls ? ' '+a.iconCls : ''),
19080                            '" unselectable="on" />',
19081                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
19082                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
19083                              
19084                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19085                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
19086                             '<span unselectable="on" qtip="' + tx + '">',
19087                              tx,
19088                              '</span></a>' ,
19089                     '</div>',
19090                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19091                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
19092                  ];
19093         for(var i = 1, len = cols.length; i < len; i++){
19094             c = cols[i];
19095             colcls = 'x-t-' + tid + '-c' +i;
19096             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19097             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
19098                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
19099                       "</div>");
19100          }
19101          
19102          buf.push(
19103             '</a>',
19104             '<div class="x-clear"></div></div>',
19105             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
19106             "</li>");
19107         
19108         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
19109             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
19110                                 n.nextSibling.ui.getEl(), buf.join(""));
19111         }else{
19112             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
19113         }
19114         var el = this.wrap.firstChild;
19115         this.elRow = el;
19116         this.elNode = el.firstChild;
19117         this.ranchor = el.childNodes[1];
19118         this.ctNode = this.wrap.childNodes[1];
19119         var cs = el.firstChild.childNodes;
19120         this.indentNode = cs[0];
19121         this.ecNode = cs[1];
19122         this.iconNode = cs[2];
19123         var index = 3;
19124         if(cb){
19125             this.checkbox = cs[3];
19126             index++;
19127         }
19128         this.anchor = cs[index];
19129         
19130         this.textNode = cs[index].firstChild;
19131         
19132         //el.on("click", this.onClick, this);
19133         //el.on("dblclick", this.onDblClick, this);
19134         
19135         
19136        // console.log(this);
19137     },
19138     initEvents : function(){
19139         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
19140         
19141             
19142         var a = this.ranchor;
19143
19144         var el = Roo.get(a);
19145
19146         if(Roo.isOpera){ // opera render bug ignores the CSS
19147             el.setStyle("text-decoration", "none");
19148         }
19149
19150         el.on("click", this.onClick, this);
19151         el.on("dblclick", this.onDblClick, this);
19152         el.on("contextmenu", this.onContextMenu, this);
19153         
19154     },
19155     
19156     /*onSelectedChange : function(state){
19157         if(state){
19158             this.focus();
19159             this.addClass("x-tree-selected");
19160         }else{
19161             //this.blur();
19162             this.removeClass("x-tree-selected");
19163         }
19164     },*/
19165     addClass : function(cls){
19166         if(this.elRow){
19167             Roo.fly(this.elRow).addClass(cls);
19168         }
19169         
19170     },
19171     
19172     
19173     removeClass : function(cls){
19174         if(this.elRow){
19175             Roo.fly(this.elRow).removeClass(cls);
19176         }
19177     }
19178
19179     
19180     
19181 });//<Script type="text/javascript">
19182
19183 /*
19184  * Based on:
19185  * Ext JS Library 1.1.1
19186  * Copyright(c) 2006-2007, Ext JS, LLC.
19187  *
19188  * Originally Released Under LGPL - original licence link has changed is not relivant.
19189  *
19190  * Fork - LGPL
19191  * <script type="text/javascript">
19192  */
19193  
19194
19195 /**
19196  * @class Roo.tree.ColumnTree
19197  * @extends Roo.data.TreePanel
19198  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
19199  * @cfg {int} borderWidth  compined right/left border allowance
19200  * @constructor
19201  * @param {String/HTMLElement/Element} el The container element
19202  * @param {Object} config
19203  */
19204 Roo.tree.ColumnTree =  function(el, config)
19205 {
19206    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
19207    this.addEvents({
19208         /**
19209         * @event resize
19210         * Fire this event on a container when it resizes
19211         * @param {int} w Width
19212         * @param {int} h Height
19213         */
19214        "resize" : true
19215     });
19216     this.on('resize', this.onResize, this);
19217 };
19218
19219 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
19220     //lines:false,
19221     
19222     
19223     borderWidth: Roo.isBorderBox ? 0 : 2, 
19224     headEls : false,
19225     
19226     render : function(){
19227         // add the header.....
19228        
19229         Roo.tree.ColumnTree.superclass.render.apply(this);
19230         
19231         this.el.addClass('x-column-tree');
19232         
19233         this.headers = this.el.createChild(
19234             {cls:'x-tree-headers'},this.innerCt.dom);
19235    
19236         var cols = this.columns, c;
19237         var totalWidth = 0;
19238         this.headEls = [];
19239         var  len = cols.length;
19240         for(var i = 0; i < len; i++){
19241              c = cols[i];
19242              totalWidth += c.width;
19243             this.headEls.push(this.headers.createChild({
19244                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
19245                  cn: {
19246                      cls:'x-tree-hd-text',
19247                      html: c.header
19248                  },
19249                  style:'width:'+(c.width-this.borderWidth)+'px;'
19250              }));
19251         }
19252         this.headers.createChild({cls:'x-clear'});
19253         // prevent floats from wrapping when clipped
19254         this.headers.setWidth(totalWidth);
19255         //this.innerCt.setWidth(totalWidth);
19256         this.innerCt.setStyle({ overflow: 'auto' });
19257         this.onResize(this.width, this.height);
19258              
19259         
19260     },
19261     onResize : function(w,h)
19262     {
19263         this.height = h;
19264         this.width = w;
19265         // resize cols..
19266         this.innerCt.setWidth(this.width);
19267         this.innerCt.setHeight(this.height-20);
19268         
19269         // headers...
19270         var cols = this.columns, c;
19271         var totalWidth = 0;
19272         var expEl = false;
19273         var len = cols.length;
19274         for(var i = 0; i < len; i++){
19275             c = cols[i];
19276             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
19277                 // it's the expander..
19278                 expEl  = this.headEls[i];
19279                 continue;
19280             }
19281             totalWidth += c.width;
19282             
19283         }
19284         if (expEl) {
19285             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
19286         }
19287         this.headers.setWidth(w-20);
19288
19289         
19290         
19291         
19292     }
19293 });
19294 /*
19295  * Based on:
19296  * Ext JS Library 1.1.1
19297  * Copyright(c) 2006-2007, Ext JS, LLC.
19298  *
19299  * Originally Released Under LGPL - original licence link has changed is not relivant.
19300  *
19301  * Fork - LGPL
19302  * <script type="text/javascript">
19303  */
19304  
19305 /**
19306  * @class Roo.menu.Menu
19307  * @extends Roo.util.Observable
19308  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
19309  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
19310  * @constructor
19311  * Creates a new Menu
19312  * @param {Object} config Configuration options
19313  */
19314 Roo.menu.Menu = function(config){
19315     Roo.apply(this, config);
19316     this.id = this.id || Roo.id();
19317     this.addEvents({
19318         /**
19319          * @event beforeshow
19320          * Fires before this menu is displayed
19321          * @param {Roo.menu.Menu} this
19322          */
19323         beforeshow : true,
19324         /**
19325          * @event beforehide
19326          * Fires before this menu is hidden
19327          * @param {Roo.menu.Menu} this
19328          */
19329         beforehide : true,
19330         /**
19331          * @event show
19332          * Fires after this menu is displayed
19333          * @param {Roo.menu.Menu} this
19334          */
19335         show : true,
19336         /**
19337          * @event hide
19338          * Fires after this menu is hidden
19339          * @param {Roo.menu.Menu} this
19340          */
19341         hide : true,
19342         /**
19343          * @event click
19344          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19345          * @param {Roo.menu.Menu} this
19346          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19347          * @param {Roo.EventObject} e
19348          */
19349         click : true,
19350         /**
19351          * @event mouseover
19352          * Fires when the mouse is hovering over this menu
19353          * @param {Roo.menu.Menu} this
19354          * @param {Roo.EventObject} e
19355          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19356          */
19357         mouseover : true,
19358         /**
19359          * @event mouseout
19360          * Fires when the mouse exits this menu
19361          * @param {Roo.menu.Menu} this
19362          * @param {Roo.EventObject} e
19363          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19364          */
19365         mouseout : true,
19366         /**
19367          * @event itemclick
19368          * Fires when a menu item contained in this menu is clicked
19369          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
19370          * @param {Roo.EventObject} e
19371          */
19372         itemclick: true
19373     });
19374     if (this.registerMenu) {
19375         Roo.menu.MenuMgr.register(this);
19376     }
19377     
19378     var mis = this.items;
19379     this.items = new Roo.util.MixedCollection();
19380     if(mis){
19381         this.add.apply(this, mis);
19382     }
19383 };
19384
19385 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
19386     /**
19387      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
19388      */
19389     minWidth : 120,
19390     /**
19391      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
19392      * for bottom-right shadow (defaults to "sides")
19393      */
19394     shadow : "sides",
19395     /**
19396      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
19397      * this menu (defaults to "tl-tr?")
19398      */
19399     subMenuAlign : "tl-tr?",
19400     /**
19401      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
19402      * relative to its element of origin (defaults to "tl-bl?")
19403      */
19404     defaultAlign : "tl-bl?",
19405     /**
19406      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
19407      */
19408     allowOtherMenus : false,
19409     /**
19410      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
19411      */
19412     registerMenu : true,
19413
19414     hidden:true,
19415
19416     // private
19417     render : function(){
19418         if(this.el){
19419             return;
19420         }
19421         var el = this.el = new Roo.Layer({
19422             cls: "x-menu",
19423             shadow:this.shadow,
19424             constrain: false,
19425             parentEl: this.parentEl || document.body,
19426             zindex:15000
19427         });
19428
19429         this.keyNav = new Roo.menu.MenuNav(this);
19430
19431         if(this.plain){
19432             el.addClass("x-menu-plain");
19433         }
19434         if(this.cls){
19435             el.addClass(this.cls);
19436         }
19437         // generic focus element
19438         this.focusEl = el.createChild({
19439             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
19440         });
19441         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
19442         ul.on("click", this.onClick, this);
19443         ul.on("mouseover", this.onMouseOver, this);
19444         ul.on("mouseout", this.onMouseOut, this);
19445         this.items.each(function(item){
19446             var li = document.createElement("li");
19447             li.className = "x-menu-list-item";
19448             ul.dom.appendChild(li);
19449             item.render(li, this);
19450         }, this);
19451         this.ul = ul;
19452         this.autoWidth();
19453     },
19454
19455     // private
19456     autoWidth : function(){
19457         var el = this.el, ul = this.ul;
19458         if(!el){
19459             return;
19460         }
19461         var w = this.width;
19462         if(w){
19463             el.setWidth(w);
19464         }else if(Roo.isIE){
19465             el.setWidth(this.minWidth);
19466             var t = el.dom.offsetWidth; // force recalc
19467             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
19468         }
19469     },
19470
19471     // private
19472     delayAutoWidth : function(){
19473         if(this.rendered){
19474             if(!this.awTask){
19475                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
19476             }
19477             this.awTask.delay(20);
19478         }
19479     },
19480
19481     // private
19482     findTargetItem : function(e){
19483         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
19484         if(t && t.menuItemId){
19485             return this.items.get(t.menuItemId);
19486         }
19487     },
19488
19489     // private
19490     onClick : function(e){
19491         var t;
19492         if(t = this.findTargetItem(e)){
19493             t.onClick(e);
19494             this.fireEvent("click", this, t, e);
19495         }
19496     },
19497
19498     // private
19499     setActiveItem : function(item, autoExpand){
19500         if(item != this.activeItem){
19501             if(this.activeItem){
19502                 this.activeItem.deactivate();
19503             }
19504             this.activeItem = item;
19505             item.activate(autoExpand);
19506         }else if(autoExpand){
19507             item.expandMenu();
19508         }
19509     },
19510
19511     // private
19512     tryActivate : function(start, step){
19513         var items = this.items;
19514         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
19515             var item = items.get(i);
19516             if(!item.disabled && item.canActivate){
19517                 this.setActiveItem(item, false);
19518                 return item;
19519             }
19520         }
19521         return false;
19522     },
19523
19524     // private
19525     onMouseOver : function(e){
19526         var t;
19527         if(t = this.findTargetItem(e)){
19528             if(t.canActivate && !t.disabled){
19529                 this.setActiveItem(t, true);
19530             }
19531         }
19532         this.fireEvent("mouseover", this, e, t);
19533     },
19534
19535     // private
19536     onMouseOut : function(e){
19537         var t;
19538         if(t = this.findTargetItem(e)){
19539             if(t == this.activeItem && t.shouldDeactivate(e)){
19540                 this.activeItem.deactivate();
19541                 delete this.activeItem;
19542             }
19543         }
19544         this.fireEvent("mouseout", this, e, t);
19545     },
19546
19547     /**
19548      * Read-only.  Returns true if the menu is currently displayed, else false.
19549      * @type Boolean
19550      */
19551     isVisible : function(){
19552         return this.el && !this.hidden;
19553     },
19554
19555     /**
19556      * Displays this menu relative to another element
19557      * @param {String/HTMLElement/Roo.Element} element The element to align to
19558      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
19559      * the element (defaults to this.defaultAlign)
19560      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19561      */
19562     show : function(el, pos, parentMenu){
19563         this.parentMenu = parentMenu;
19564         if(!this.el){
19565             this.render();
19566         }
19567         this.fireEvent("beforeshow", this);
19568         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
19569     },
19570
19571     /**
19572      * Displays this menu at a specific xy position
19573      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
19574      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19575      */
19576     showAt : function(xy, parentMenu, /* private: */_e){
19577         this.parentMenu = parentMenu;
19578         if(!this.el){
19579             this.render();
19580         }
19581         if(_e !== false){
19582             this.fireEvent("beforeshow", this);
19583             xy = this.el.adjustForConstraints(xy);
19584         }
19585         this.el.setXY(xy);
19586         this.el.show();
19587         this.hidden = false;
19588         this.focus();
19589         this.fireEvent("show", this);
19590     },
19591
19592     focus : function(){
19593         if(!this.hidden){
19594             this.doFocus.defer(50, this);
19595         }
19596     },
19597
19598     doFocus : function(){
19599         if(!this.hidden){
19600             this.focusEl.focus();
19601         }
19602     },
19603
19604     /**
19605      * Hides this menu and optionally all parent menus
19606      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
19607      */
19608     hide : function(deep){
19609         if(this.el && this.isVisible()){
19610             this.fireEvent("beforehide", this);
19611             if(this.activeItem){
19612                 this.activeItem.deactivate();
19613                 this.activeItem = null;
19614             }
19615             this.el.hide();
19616             this.hidden = true;
19617             this.fireEvent("hide", this);
19618         }
19619         if(deep === true && this.parentMenu){
19620             this.parentMenu.hide(true);
19621         }
19622     },
19623
19624     /**
19625      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
19626      * Any of the following are valid:
19627      * <ul>
19628      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
19629      * <li>An HTMLElement object which will be converted to a menu item</li>
19630      * <li>A menu item config object that will be created as a new menu item</li>
19631      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
19632      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
19633      * </ul>
19634      * Usage:
19635      * <pre><code>
19636 // Create the menu
19637 var menu = new Roo.menu.Menu();
19638
19639 // Create a menu item to add by reference
19640 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
19641
19642 // Add a bunch of items at once using different methods.
19643 // Only the last item added will be returned.
19644 var item = menu.add(
19645     menuItem,                // add existing item by ref
19646     'Dynamic Item',          // new TextItem
19647     '-',                     // new separator
19648     { text: 'Config Item' }  // new item by config
19649 );
19650 </code></pre>
19651      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
19652      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
19653      */
19654     add : function(){
19655         var a = arguments, l = a.length, item;
19656         for(var i = 0; i < l; i++){
19657             var el = a[i];
19658             if ((typeof(el) == "object") && el.xtype && el.xns) {
19659                 el = Roo.factory(el, Roo.menu);
19660             }
19661             
19662             if(el.render){ // some kind of Item
19663                 item = this.addItem(el);
19664             }else if(typeof el == "string"){ // string
19665                 if(el == "separator" || el == "-"){
19666                     item = this.addSeparator();
19667                 }else{
19668                     item = this.addText(el);
19669                 }
19670             }else if(el.tagName || el.el){ // element
19671                 item = this.addElement(el);
19672             }else if(typeof el == "object"){ // must be menu item config?
19673                 item = this.addMenuItem(el);
19674             }
19675         }
19676         return item;
19677     },
19678
19679     /**
19680      * Returns this menu's underlying {@link Roo.Element} object
19681      * @return {Roo.Element} The element
19682      */
19683     getEl : function(){
19684         if(!this.el){
19685             this.render();
19686         }
19687         return this.el;
19688     },
19689
19690     /**
19691      * Adds a separator bar to the menu
19692      * @return {Roo.menu.Item} The menu item that was added
19693      */
19694     addSeparator : function(){
19695         return this.addItem(new Roo.menu.Separator());
19696     },
19697
19698     /**
19699      * Adds an {@link Roo.Element} object to the menu
19700      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
19701      * @return {Roo.menu.Item} The menu item that was added
19702      */
19703     addElement : function(el){
19704         return this.addItem(new Roo.menu.BaseItem(el));
19705     },
19706
19707     /**
19708      * Adds an existing object based on {@link Roo.menu.Item} to the menu
19709      * @param {Roo.menu.Item} item The menu item to add
19710      * @return {Roo.menu.Item} The menu item that was added
19711      */
19712     addItem : function(item){
19713         this.items.add(item);
19714         if(this.ul){
19715             var li = document.createElement("li");
19716             li.className = "x-menu-list-item";
19717             this.ul.dom.appendChild(li);
19718             item.render(li, this);
19719             this.delayAutoWidth();
19720         }
19721         return item;
19722     },
19723
19724     /**
19725      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
19726      * @param {Object} config A MenuItem config object
19727      * @return {Roo.menu.Item} The menu item that was added
19728      */
19729     addMenuItem : function(config){
19730         if(!(config instanceof Roo.menu.Item)){
19731             if(typeof config.checked == "boolean"){ // must be check menu item config?
19732                 config = new Roo.menu.CheckItem(config);
19733             }else{
19734                 config = new Roo.menu.Item(config);
19735             }
19736         }
19737         return this.addItem(config);
19738     },
19739
19740     /**
19741      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
19742      * @param {String} text The text to display in the menu item
19743      * @return {Roo.menu.Item} The menu item that was added
19744      */
19745     addText : function(text){
19746         return this.addItem(new Roo.menu.TextItem({ text : text }));
19747     },
19748
19749     /**
19750      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
19751      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
19752      * @param {Roo.menu.Item} item The menu item to add
19753      * @return {Roo.menu.Item} The menu item that was added
19754      */
19755     insert : function(index, item){
19756         this.items.insert(index, item);
19757         if(this.ul){
19758             var li = document.createElement("li");
19759             li.className = "x-menu-list-item";
19760             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
19761             item.render(li, this);
19762             this.delayAutoWidth();
19763         }
19764         return item;
19765     },
19766
19767     /**
19768      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
19769      * @param {Roo.menu.Item} item The menu item to remove
19770      */
19771     remove : function(item){
19772         this.items.removeKey(item.id);
19773         item.destroy();
19774     },
19775
19776     /**
19777      * Removes and destroys all items in the menu
19778      */
19779     removeAll : function(){
19780         var f;
19781         while(f = this.items.first()){
19782             this.remove(f);
19783         }
19784     }
19785 });
19786
19787 // MenuNav is a private utility class used internally by the Menu
19788 Roo.menu.MenuNav = function(menu){
19789     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
19790     this.scope = this.menu = menu;
19791 };
19792
19793 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
19794     doRelay : function(e, h){
19795         var k = e.getKey();
19796         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
19797             this.menu.tryActivate(0, 1);
19798             return false;
19799         }
19800         return h.call(this.scope || this, e, this.menu);
19801     },
19802
19803     up : function(e, m){
19804         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
19805             m.tryActivate(m.items.length-1, -1);
19806         }
19807     },
19808
19809     down : function(e, m){
19810         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
19811             m.tryActivate(0, 1);
19812         }
19813     },
19814
19815     right : function(e, m){
19816         if(m.activeItem){
19817             m.activeItem.expandMenu(true);
19818         }
19819     },
19820
19821     left : function(e, m){
19822         m.hide();
19823         if(m.parentMenu && m.parentMenu.activeItem){
19824             m.parentMenu.activeItem.activate();
19825         }
19826     },
19827
19828     enter : function(e, m){
19829         if(m.activeItem){
19830             e.stopPropagation();
19831             m.activeItem.onClick(e);
19832             m.fireEvent("click", this, m.activeItem);
19833             return true;
19834         }
19835     }
19836 });/*
19837  * Based on:
19838  * Ext JS Library 1.1.1
19839  * Copyright(c) 2006-2007, Ext JS, LLC.
19840  *
19841  * Originally Released Under LGPL - original licence link has changed is not relivant.
19842  *
19843  * Fork - LGPL
19844  * <script type="text/javascript">
19845  */
19846  
19847 /**
19848  * @class Roo.menu.MenuMgr
19849  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
19850  * @singleton
19851  */
19852 Roo.menu.MenuMgr = function(){
19853    var menus, active, groups = {}, attached = false, lastShow = new Date();
19854
19855    // private - called when first menu is created
19856    function init(){
19857        menus = {};
19858        active = new Roo.util.MixedCollection();
19859        Roo.get(document).addKeyListener(27, function(){
19860            if(active.length > 0){
19861                hideAll();
19862            }
19863        });
19864    }
19865
19866    // private
19867    function hideAll(){
19868        if(active && active.length > 0){
19869            var c = active.clone();
19870            c.each(function(m){
19871                m.hide();
19872            });
19873        }
19874    }
19875
19876    // private
19877    function onHide(m){
19878        active.remove(m);
19879        if(active.length < 1){
19880            Roo.get(document).un("mousedown", onMouseDown);
19881            attached = false;
19882        }
19883    }
19884
19885    // private
19886    function onShow(m){
19887        var last = active.last();
19888        lastShow = new Date();
19889        active.add(m);
19890        if(!attached){
19891            Roo.get(document).on("mousedown", onMouseDown);
19892            attached = true;
19893        }
19894        if(m.parentMenu){
19895           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
19896           m.parentMenu.activeChild = m;
19897        }else if(last && last.isVisible()){
19898           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
19899        }
19900    }
19901
19902    // private
19903    function onBeforeHide(m){
19904        if(m.activeChild){
19905            m.activeChild.hide();
19906        }
19907        if(m.autoHideTimer){
19908            clearTimeout(m.autoHideTimer);
19909            delete m.autoHideTimer;
19910        }
19911    }
19912
19913    // private
19914    function onBeforeShow(m){
19915        var pm = m.parentMenu;
19916        if(!pm && !m.allowOtherMenus){
19917            hideAll();
19918        }else if(pm && pm.activeChild && active != m){
19919            pm.activeChild.hide();
19920        }
19921    }
19922
19923    // private
19924    function onMouseDown(e){
19925        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
19926            hideAll();
19927        }
19928    }
19929
19930    // private
19931    function onBeforeCheck(mi, state){
19932        if(state){
19933            var g = groups[mi.group];
19934            for(var i = 0, l = g.length; i < l; i++){
19935                if(g[i] != mi){
19936                    g[i].setChecked(false);
19937                }
19938            }
19939        }
19940    }
19941
19942    return {
19943
19944        /**
19945         * Hides all menus that are currently visible
19946         */
19947        hideAll : function(){
19948             hideAll();  
19949        },
19950
19951        // private
19952        register : function(menu){
19953            if(!menus){
19954                init();
19955            }
19956            menus[menu.id] = menu;
19957            menu.on("beforehide", onBeforeHide);
19958            menu.on("hide", onHide);
19959            menu.on("beforeshow", onBeforeShow);
19960            menu.on("show", onShow);
19961            var g = menu.group;
19962            if(g && menu.events["checkchange"]){
19963                if(!groups[g]){
19964                    groups[g] = [];
19965                }
19966                groups[g].push(menu);
19967                menu.on("checkchange", onCheck);
19968            }
19969        },
19970
19971         /**
19972          * Returns a {@link Roo.menu.Menu} object
19973          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
19974          * be used to generate and return a new Menu instance.
19975          */
19976        get : function(menu){
19977            if(typeof menu == "string"){ // menu id
19978                return menus[menu];
19979            }else if(menu.events){  // menu instance
19980                return menu;
19981            }else if(typeof menu.length == 'number'){ // array of menu items?
19982                return new Roo.menu.Menu({items:menu});
19983            }else{ // otherwise, must be a config
19984                return new Roo.menu.Menu(menu);
19985            }
19986        },
19987
19988        // private
19989        unregister : function(menu){
19990            delete menus[menu.id];
19991            menu.un("beforehide", onBeforeHide);
19992            menu.un("hide", onHide);
19993            menu.un("beforeshow", onBeforeShow);
19994            menu.un("show", onShow);
19995            var g = menu.group;
19996            if(g && menu.events["checkchange"]){
19997                groups[g].remove(menu);
19998                menu.un("checkchange", onCheck);
19999            }
20000        },
20001
20002        // private
20003        registerCheckable : function(menuItem){
20004            var g = menuItem.group;
20005            if(g){
20006                if(!groups[g]){
20007                    groups[g] = [];
20008                }
20009                groups[g].push(menuItem);
20010                menuItem.on("beforecheckchange", onBeforeCheck);
20011            }
20012        },
20013
20014        // private
20015        unregisterCheckable : function(menuItem){
20016            var g = menuItem.group;
20017            if(g){
20018                groups[g].remove(menuItem);
20019                menuItem.un("beforecheckchange", onBeforeCheck);
20020            }
20021        }
20022    };
20023 }();/*
20024  * Based on:
20025  * Ext JS Library 1.1.1
20026  * Copyright(c) 2006-2007, Ext JS, LLC.
20027  *
20028  * Originally Released Under LGPL - original licence link has changed is not relivant.
20029  *
20030  * Fork - LGPL
20031  * <script type="text/javascript">
20032  */
20033  
20034
20035 /**
20036  * @class Roo.menu.BaseItem
20037  * @extends Roo.Component
20038  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
20039  * management and base configuration options shared by all menu components.
20040  * @constructor
20041  * Creates a new BaseItem
20042  * @param {Object} config Configuration options
20043  */
20044 Roo.menu.BaseItem = function(config){
20045     Roo.menu.BaseItem.superclass.constructor.call(this, config);
20046
20047     this.addEvents({
20048         /**
20049          * @event click
20050          * Fires when this item is clicked
20051          * @param {Roo.menu.BaseItem} this
20052          * @param {Roo.EventObject} e
20053          */
20054         click: true,
20055         /**
20056          * @event activate
20057          * Fires when this item is activated
20058          * @param {Roo.menu.BaseItem} this
20059          */
20060         activate : true,
20061         /**
20062          * @event deactivate
20063          * Fires when this item is deactivated
20064          * @param {Roo.menu.BaseItem} this
20065          */
20066         deactivate : true
20067     });
20068
20069     if(this.handler){
20070         this.on("click", this.handler, this.scope, true);
20071     }
20072 };
20073
20074 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
20075     /**
20076      * @cfg {Function} handler
20077      * A function that will handle the click event of this menu item (defaults to undefined)
20078      */
20079     /**
20080      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
20081      */
20082     canActivate : false,
20083     /**
20084      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
20085      */
20086     activeClass : "x-menu-item-active",
20087     /**
20088      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
20089      */
20090     hideOnClick : true,
20091     /**
20092      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
20093      */
20094     hideDelay : 100,
20095
20096     // private
20097     ctype: "Roo.menu.BaseItem",
20098
20099     // private
20100     actionMode : "container",
20101
20102     // private
20103     render : function(container, parentMenu){
20104         this.parentMenu = parentMenu;
20105         Roo.menu.BaseItem.superclass.render.call(this, container);
20106         this.container.menuItemId = this.id;
20107     },
20108
20109     // private
20110     onRender : function(container, position){
20111         this.el = Roo.get(this.el);
20112         container.dom.appendChild(this.el.dom);
20113     },
20114
20115     // private
20116     onClick : function(e){
20117         if(!this.disabled && this.fireEvent("click", this, e) !== false
20118                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
20119             this.handleClick(e);
20120         }else{
20121             e.stopEvent();
20122         }
20123     },
20124
20125     // private
20126     activate : function(){
20127         if(this.disabled){
20128             return false;
20129         }
20130         var li = this.container;
20131         li.addClass(this.activeClass);
20132         this.region = li.getRegion().adjust(2, 2, -2, -2);
20133         this.fireEvent("activate", this);
20134         return true;
20135     },
20136
20137     // private
20138     deactivate : function(){
20139         this.container.removeClass(this.activeClass);
20140         this.fireEvent("deactivate", this);
20141     },
20142
20143     // private
20144     shouldDeactivate : function(e){
20145         return !this.region || !this.region.contains(e.getPoint());
20146     },
20147
20148     // private
20149     handleClick : function(e){
20150         if(this.hideOnClick){
20151             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
20152         }
20153     },
20154
20155     // private
20156     expandMenu : function(autoActivate){
20157         // do nothing
20158     },
20159
20160     // private
20161     hideMenu : function(){
20162         // do nothing
20163     }
20164 });/*
20165  * Based on:
20166  * Ext JS Library 1.1.1
20167  * Copyright(c) 2006-2007, Ext JS, LLC.
20168  *
20169  * Originally Released Under LGPL - original licence link has changed is not relivant.
20170  *
20171  * Fork - LGPL
20172  * <script type="text/javascript">
20173  */
20174  
20175 /**
20176  * @class Roo.menu.Adapter
20177  * @extends Roo.menu.BaseItem
20178  * 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.
20179  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
20180  * @constructor
20181  * Creates a new Adapter
20182  * @param {Object} config Configuration options
20183  */
20184 Roo.menu.Adapter = function(component, config){
20185     Roo.menu.Adapter.superclass.constructor.call(this, config);
20186     this.component = component;
20187 };
20188 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
20189     // private
20190     canActivate : true,
20191
20192     // private
20193     onRender : function(container, position){
20194         this.component.render(container);
20195         this.el = this.component.getEl();
20196     },
20197
20198     // private
20199     activate : function(){
20200         if(this.disabled){
20201             return false;
20202         }
20203         this.component.focus();
20204         this.fireEvent("activate", this);
20205         return true;
20206     },
20207
20208     // private
20209     deactivate : function(){
20210         this.fireEvent("deactivate", this);
20211     },
20212
20213     // private
20214     disable : function(){
20215         this.component.disable();
20216         Roo.menu.Adapter.superclass.disable.call(this);
20217     },
20218
20219     // private
20220     enable : function(){
20221         this.component.enable();
20222         Roo.menu.Adapter.superclass.enable.call(this);
20223     }
20224 });/*
20225  * Based on:
20226  * Ext JS Library 1.1.1
20227  * Copyright(c) 2006-2007, Ext JS, LLC.
20228  *
20229  * Originally Released Under LGPL - original licence link has changed is not relivant.
20230  *
20231  * Fork - LGPL
20232  * <script type="text/javascript">
20233  */
20234
20235 /**
20236  * @class Roo.menu.TextItem
20237  * @extends Roo.menu.BaseItem
20238  * Adds a static text string to a menu, usually used as either a heading or group separator.
20239  * Note: old style constructor with text is still supported.
20240  * 
20241  * @constructor
20242  * Creates a new TextItem
20243  * @param {Object} cfg Configuration
20244  */
20245 Roo.menu.TextItem = function(cfg){
20246     if (typeof(cfg) == 'string') {
20247         this.text = cfg;
20248     } else {
20249         Roo.apply(this,cfg);
20250     }
20251     
20252     Roo.menu.TextItem.superclass.constructor.call(this);
20253 };
20254
20255 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
20256     /**
20257      * @cfg {Boolean} text Text to show on item.
20258      */
20259     text : '',
20260     
20261     /**
20262      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20263      */
20264     hideOnClick : false,
20265     /**
20266      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
20267      */
20268     itemCls : "x-menu-text",
20269
20270     // private
20271     onRender : function(){
20272         var s = document.createElement("span");
20273         s.className = this.itemCls;
20274         s.innerHTML = this.text;
20275         this.el = s;
20276         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
20277     }
20278 });/*
20279  * Based on:
20280  * Ext JS Library 1.1.1
20281  * Copyright(c) 2006-2007, Ext JS, LLC.
20282  *
20283  * Originally Released Under LGPL - original licence link has changed is not relivant.
20284  *
20285  * Fork - LGPL
20286  * <script type="text/javascript">
20287  */
20288
20289 /**
20290  * @class Roo.menu.Separator
20291  * @extends Roo.menu.BaseItem
20292  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
20293  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
20294  * @constructor
20295  * @param {Object} config Configuration options
20296  */
20297 Roo.menu.Separator = function(config){
20298     Roo.menu.Separator.superclass.constructor.call(this, config);
20299 };
20300
20301 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
20302     /**
20303      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
20304      */
20305     itemCls : "x-menu-sep",
20306     /**
20307      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20308      */
20309     hideOnClick : false,
20310
20311     // private
20312     onRender : function(li){
20313         var s = document.createElement("span");
20314         s.className = this.itemCls;
20315         s.innerHTML = "&#160;";
20316         this.el = s;
20317         li.addClass("x-menu-sep-li");
20318         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
20319     }
20320 });/*
20321  * Based on:
20322  * Ext JS Library 1.1.1
20323  * Copyright(c) 2006-2007, Ext JS, LLC.
20324  *
20325  * Originally Released Under LGPL - original licence link has changed is not relivant.
20326  *
20327  * Fork - LGPL
20328  * <script type="text/javascript">
20329  */
20330 /**
20331  * @class Roo.menu.Item
20332  * @extends Roo.menu.BaseItem
20333  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
20334  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
20335  * activation and click handling.
20336  * @constructor
20337  * Creates a new Item
20338  * @param {Object} config Configuration options
20339  */
20340 Roo.menu.Item = function(config){
20341     Roo.menu.Item.superclass.constructor.call(this, config);
20342     if(this.menu){
20343         this.menu = Roo.menu.MenuMgr.get(this.menu);
20344     }
20345 };
20346 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
20347     
20348     /**
20349      * @cfg {String} text
20350      * The text to show on the menu item.
20351      */
20352     text: '',
20353      /**
20354      * @cfg {String} HTML to render in menu
20355      * The text to show on the menu item (HTML version).
20356      */
20357     html: '',
20358     /**
20359      * @cfg {String} icon
20360      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
20361      */
20362     icon: undefined,
20363     /**
20364      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
20365      */
20366     itemCls : "x-menu-item",
20367     /**
20368      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
20369      */
20370     canActivate : true,
20371     /**
20372      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
20373      */
20374     showDelay: 200,
20375     // doc'd in BaseItem
20376     hideDelay: 200,
20377
20378     // private
20379     ctype: "Roo.menu.Item",
20380     
20381     // private
20382     onRender : function(container, position){
20383         var el = document.createElement("a");
20384         el.hideFocus = true;
20385         el.unselectable = "on";
20386         el.href = this.href || "#";
20387         if(this.hrefTarget){
20388             el.target = this.hrefTarget;
20389         }
20390         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
20391         
20392         var html = this.html.length ? this.html  : String.format('{0}',this.text);
20393         
20394         el.innerHTML = String.format(
20395                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
20396                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
20397         this.el = el;
20398         Roo.menu.Item.superclass.onRender.call(this, container, position);
20399     },
20400
20401     /**
20402      * Sets the text to display in this menu item
20403      * @param {String} text The text to display
20404      * @param {Boolean} isHTML true to indicate text is pure html.
20405      */
20406     setText : function(text, isHTML){
20407         if (isHTML) {
20408             this.html = text;
20409         } else {
20410             this.text = text;
20411             this.html = '';
20412         }
20413         if(this.rendered){
20414             var html = this.html.length ? this.html  : String.format('{0}',this.text);
20415      
20416             this.el.update(String.format(
20417                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
20418                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
20419             this.parentMenu.autoWidth();
20420         }
20421     },
20422
20423     // private
20424     handleClick : function(e){
20425         if(!this.href){ // if no link defined, stop the event automatically
20426             e.stopEvent();
20427         }
20428         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
20429     },
20430
20431     // private
20432     activate : function(autoExpand){
20433         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
20434             this.focus();
20435             if(autoExpand){
20436                 this.expandMenu();
20437             }
20438         }
20439         return true;
20440     },
20441
20442     // private
20443     shouldDeactivate : function(e){
20444         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
20445             if(this.menu && this.menu.isVisible()){
20446                 return !this.menu.getEl().getRegion().contains(e.getPoint());
20447             }
20448             return true;
20449         }
20450         return false;
20451     },
20452
20453     // private
20454     deactivate : function(){
20455         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
20456         this.hideMenu();
20457     },
20458
20459     // private
20460     expandMenu : function(autoActivate){
20461         if(!this.disabled && this.menu){
20462             clearTimeout(this.hideTimer);
20463             delete this.hideTimer;
20464             if(!this.menu.isVisible() && !this.showTimer){
20465                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
20466             }else if (this.menu.isVisible() && autoActivate){
20467                 this.menu.tryActivate(0, 1);
20468             }
20469         }
20470     },
20471
20472     // private
20473     deferExpand : function(autoActivate){
20474         delete this.showTimer;
20475         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
20476         if(autoActivate){
20477             this.menu.tryActivate(0, 1);
20478         }
20479     },
20480
20481     // private
20482     hideMenu : function(){
20483         clearTimeout(this.showTimer);
20484         delete this.showTimer;
20485         if(!this.hideTimer && this.menu && this.menu.isVisible()){
20486             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
20487         }
20488     },
20489
20490     // private
20491     deferHide : function(){
20492         delete this.hideTimer;
20493         this.menu.hide();
20494     }
20495 });/*
20496  * Based on:
20497  * Ext JS Library 1.1.1
20498  * Copyright(c) 2006-2007, Ext JS, LLC.
20499  *
20500  * Originally Released Under LGPL - original licence link has changed is not relivant.
20501  *
20502  * Fork - LGPL
20503  * <script type="text/javascript">
20504  */
20505  
20506 /**
20507  * @class Roo.menu.CheckItem
20508  * @extends Roo.menu.Item
20509  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
20510  * @constructor
20511  * Creates a new CheckItem
20512  * @param {Object} config Configuration options
20513  */
20514 Roo.menu.CheckItem = function(config){
20515     Roo.menu.CheckItem.superclass.constructor.call(this, config);
20516     this.addEvents({
20517         /**
20518          * @event beforecheckchange
20519          * Fires before the checked value is set, providing an opportunity to cancel if needed
20520          * @param {Roo.menu.CheckItem} this
20521          * @param {Boolean} checked The new checked value that will be set
20522          */
20523         "beforecheckchange" : true,
20524         /**
20525          * @event checkchange
20526          * Fires after the checked value has been set
20527          * @param {Roo.menu.CheckItem} this
20528          * @param {Boolean} checked The checked value that was set
20529          */
20530         "checkchange" : true
20531     });
20532     if(this.checkHandler){
20533         this.on('checkchange', this.checkHandler, this.scope);
20534     }
20535 };
20536 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
20537     /**
20538      * @cfg {String} group
20539      * All check items with the same group name will automatically be grouped into a single-select
20540      * radio button group (defaults to '')
20541      */
20542     /**
20543      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
20544      */
20545     itemCls : "x-menu-item x-menu-check-item",
20546     /**
20547      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
20548      */
20549     groupClass : "x-menu-group-item",
20550
20551     /**
20552      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
20553      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
20554      * initialized with checked = true will be rendered as checked.
20555      */
20556     checked: false,
20557
20558     // private
20559     ctype: "Roo.menu.CheckItem",
20560
20561     // private
20562     onRender : function(c){
20563         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
20564         if(this.group){
20565             this.el.addClass(this.groupClass);
20566         }
20567         Roo.menu.MenuMgr.registerCheckable(this);
20568         if(this.checked){
20569             this.checked = false;
20570             this.setChecked(true, true);
20571         }
20572     },
20573
20574     // private
20575     destroy : function(){
20576         if(this.rendered){
20577             Roo.menu.MenuMgr.unregisterCheckable(this);
20578         }
20579         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
20580     },
20581
20582     /**
20583      * Set the checked state of this item
20584      * @param {Boolean} checked The new checked value
20585      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
20586      */
20587     setChecked : function(state, suppressEvent){
20588         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
20589             if(this.container){
20590                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
20591             }
20592             this.checked = state;
20593             if(suppressEvent !== true){
20594                 this.fireEvent("checkchange", this, state);
20595             }
20596         }
20597     },
20598
20599     // private
20600     handleClick : function(e){
20601        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
20602            this.setChecked(!this.checked);
20603        }
20604        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
20605     }
20606 });/*
20607  * Based on:
20608  * Ext JS Library 1.1.1
20609  * Copyright(c) 2006-2007, Ext JS, LLC.
20610  *
20611  * Originally Released Under LGPL - original licence link has changed is not relivant.
20612  *
20613  * Fork - LGPL
20614  * <script type="text/javascript">
20615  */
20616  
20617 /**
20618  * @class Roo.menu.DateItem
20619  * @extends Roo.menu.Adapter
20620  * A menu item that wraps the {@link Roo.DatPicker} component.
20621  * @constructor
20622  * Creates a new DateItem
20623  * @param {Object} config Configuration options
20624  */
20625 Roo.menu.DateItem = function(config){
20626     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
20627     /** The Roo.DatePicker object @type Roo.DatePicker */
20628     this.picker = this.component;
20629     this.addEvents({select: true});
20630     
20631     this.picker.on("render", function(picker){
20632         picker.getEl().swallowEvent("click");
20633         picker.container.addClass("x-menu-date-item");
20634     });
20635
20636     this.picker.on("select", this.onSelect, this);
20637 };
20638
20639 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
20640     // private
20641     onSelect : function(picker, date){
20642         this.fireEvent("select", this, date, picker);
20643         Roo.menu.DateItem.superclass.handleClick.call(this);
20644     }
20645 });/*
20646  * Based on:
20647  * Ext JS Library 1.1.1
20648  * Copyright(c) 2006-2007, Ext JS, LLC.
20649  *
20650  * Originally Released Under LGPL - original licence link has changed is not relivant.
20651  *
20652  * Fork - LGPL
20653  * <script type="text/javascript">
20654  */
20655  
20656 /**
20657  * @class Roo.menu.ColorItem
20658  * @extends Roo.menu.Adapter
20659  * A menu item that wraps the {@link Roo.ColorPalette} component.
20660  * @constructor
20661  * Creates a new ColorItem
20662  * @param {Object} config Configuration options
20663  */
20664 Roo.menu.ColorItem = function(config){
20665     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
20666     /** The Roo.ColorPalette object @type Roo.ColorPalette */
20667     this.palette = this.component;
20668     this.relayEvents(this.palette, ["select"]);
20669     if(this.selectHandler){
20670         this.on('select', this.selectHandler, this.scope);
20671     }
20672 };
20673 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
20674  * Based on:
20675  * Ext JS Library 1.1.1
20676  * Copyright(c) 2006-2007, Ext JS, LLC.
20677  *
20678  * Originally Released Under LGPL - original licence link has changed is not relivant.
20679  *
20680  * Fork - LGPL
20681  * <script type="text/javascript">
20682  */
20683  
20684
20685 /**
20686  * @class Roo.menu.DateMenu
20687  * @extends Roo.menu.Menu
20688  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
20689  * @constructor
20690  * Creates a new DateMenu
20691  * @param {Object} config Configuration options
20692  */
20693 Roo.menu.DateMenu = function(config){
20694     Roo.menu.DateMenu.superclass.constructor.call(this, config);
20695     this.plain = true;
20696     var di = new Roo.menu.DateItem(config);
20697     this.add(di);
20698     /**
20699      * The {@link Roo.DatePicker} instance for this DateMenu
20700      * @type DatePicker
20701      */
20702     this.picker = di.picker;
20703     /**
20704      * @event select
20705      * @param {DatePicker} picker
20706      * @param {Date} date
20707      */
20708     this.relayEvents(di, ["select"]);
20709
20710     this.on('beforeshow', function(){
20711         if(this.picker){
20712             this.picker.hideMonthPicker(true);
20713         }
20714     }, this);
20715 };
20716 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
20717     cls:'x-date-menu'
20718 });/*
20719  * Based on:
20720  * Ext JS Library 1.1.1
20721  * Copyright(c) 2006-2007, Ext JS, LLC.
20722  *
20723  * Originally Released Under LGPL - original licence link has changed is not relivant.
20724  *
20725  * Fork - LGPL
20726  * <script type="text/javascript">
20727  */
20728  
20729
20730 /**
20731  * @class Roo.menu.ColorMenu
20732  * @extends Roo.menu.Menu
20733  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
20734  * @constructor
20735  * Creates a new ColorMenu
20736  * @param {Object} config Configuration options
20737  */
20738 Roo.menu.ColorMenu = function(config){
20739     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
20740     this.plain = true;
20741     var ci = new Roo.menu.ColorItem(config);
20742     this.add(ci);
20743     /**
20744      * The {@link Roo.ColorPalette} instance for this ColorMenu
20745      * @type ColorPalette
20746      */
20747     this.palette = ci.palette;
20748     /**
20749      * @event select
20750      * @param {ColorPalette} palette
20751      * @param {String} color
20752      */
20753     this.relayEvents(ci, ["select"]);
20754 };
20755 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
20756  * Based on:
20757  * Ext JS Library 1.1.1
20758  * Copyright(c) 2006-2007, Ext JS, LLC.
20759  *
20760  * Originally Released Under LGPL - original licence link has changed is not relivant.
20761  *
20762  * Fork - LGPL
20763  * <script type="text/javascript">
20764  */
20765  
20766 /**
20767  * @class Roo.form.Field
20768  * @extends Roo.BoxComponent
20769  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
20770  * @constructor
20771  * Creates a new Field
20772  * @param {Object} config Configuration options
20773  */
20774 Roo.form.Field = function(config){
20775     Roo.form.Field.superclass.constructor.call(this, config);
20776 };
20777
20778 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
20779     /**
20780      * @cfg {String} fieldLabel Label to use when rendering a form.
20781      */
20782        /**
20783      * @cfg {String} qtip Mouse over tip
20784      */
20785      
20786     /**
20787      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
20788      */
20789     invalidClass : "x-form-invalid",
20790     /**
20791      * @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")
20792      */
20793     invalidText : "The value in this field is invalid",
20794     /**
20795      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
20796      */
20797     focusClass : "x-form-focus",
20798     /**
20799      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
20800       automatic validation (defaults to "keyup").
20801      */
20802     validationEvent : "keyup",
20803     /**
20804      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
20805      */
20806     validateOnBlur : true,
20807     /**
20808      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
20809      */
20810     validationDelay : 250,
20811     /**
20812      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20813      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
20814      */
20815     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
20816     /**
20817      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
20818      */
20819     fieldClass : "x-form-field",
20820     /**
20821      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
20822      *<pre>
20823 Value         Description
20824 -----------   ----------------------------------------------------------------------
20825 qtip          Display a quick tip when the user hovers over the field
20826 title         Display a default browser title attribute popup
20827 under         Add a block div beneath the field containing the error text
20828 side          Add an error icon to the right of the field with a popup on hover
20829 [element id]  Add the error text directly to the innerHTML of the specified element
20830 </pre>
20831      */
20832     msgTarget : 'qtip',
20833     /**
20834      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
20835      */
20836     msgFx : 'normal',
20837
20838     /**
20839      * @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.
20840      */
20841     readOnly : false,
20842
20843     /**
20844      * @cfg {Boolean} disabled True to disable the field (defaults to false).
20845      */
20846     disabled : false,
20847
20848     /**
20849      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
20850      */
20851     inputType : undefined,
20852     
20853     /**
20854      * @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).
20855          */
20856         tabIndex : undefined,
20857         
20858     // private
20859     isFormField : true,
20860
20861     // private
20862     hasFocus : false,
20863     /**
20864      * @property {Roo.Element} fieldEl
20865      * Element Containing the rendered Field (with label etc.)
20866      */
20867     /**
20868      * @cfg {Mixed} value A value to initialize this field with.
20869      */
20870     value : undefined,
20871
20872     /**
20873      * @cfg {String} name The field's HTML name attribute.
20874      */
20875     /**
20876      * @cfg {String} cls A CSS class to apply to the field's underlying element.
20877      */
20878
20879         // private ??
20880         initComponent : function(){
20881         Roo.form.Field.superclass.initComponent.call(this);
20882         this.addEvents({
20883             /**
20884              * @event focus
20885              * Fires when this field receives input focus.
20886              * @param {Roo.form.Field} this
20887              */
20888             focus : true,
20889             /**
20890              * @event blur
20891              * Fires when this field loses input focus.
20892              * @param {Roo.form.Field} this
20893              */
20894             blur : true,
20895             /**
20896              * @event specialkey
20897              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
20898              * {@link Roo.EventObject#getKey} to determine which key was pressed.
20899              * @param {Roo.form.Field} this
20900              * @param {Roo.EventObject} e The event object
20901              */
20902             specialkey : true,
20903             /**
20904              * @event change
20905              * Fires just before the field blurs if the field value has changed.
20906              * @param {Roo.form.Field} this
20907              * @param {Mixed} newValue The new value
20908              * @param {Mixed} oldValue The original value
20909              */
20910             change : true,
20911             /**
20912              * @event invalid
20913              * Fires after the field has been marked as invalid.
20914              * @param {Roo.form.Field} this
20915              * @param {String} msg The validation message
20916              */
20917             invalid : true,
20918             /**
20919              * @event valid
20920              * Fires after the field has been validated with no errors.
20921              * @param {Roo.form.Field} this
20922              */
20923             valid : true,
20924              /**
20925              * @event keyup
20926              * Fires after the key up
20927              * @param {Roo.form.Field} this
20928              * @param {Roo.EventObject}  e The event Object
20929              */
20930             keyup : true
20931         });
20932     },
20933
20934     /**
20935      * Returns the name attribute of the field if available
20936      * @return {String} name The field name
20937      */
20938     getName: function(){
20939          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
20940     },
20941
20942     // private
20943     onRender : function(ct, position){
20944         Roo.form.Field.superclass.onRender.call(this, ct, position);
20945         if(!this.el){
20946             var cfg = this.getAutoCreate();
20947             if(!cfg.name){
20948                 cfg.name = this.name || this.id;
20949             }
20950             if(this.inputType){
20951                 cfg.type = this.inputType;
20952             }
20953             this.el = ct.createChild(cfg, position);
20954         }
20955         var type = this.el.dom.type;
20956         if(type){
20957             if(type == 'password'){
20958                 type = 'text';
20959             }
20960             this.el.addClass('x-form-'+type);
20961         }
20962         if(this.readOnly){
20963             this.el.dom.readOnly = true;
20964         }
20965         if(this.tabIndex !== undefined){
20966             this.el.dom.setAttribute('tabIndex', this.tabIndex);
20967         }
20968
20969         this.el.addClass([this.fieldClass, this.cls]);
20970         this.initValue();
20971     },
20972
20973     /**
20974      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
20975      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
20976      * @return {Roo.form.Field} this
20977      */
20978     applyTo : function(target){
20979         this.allowDomMove = false;
20980         this.el = Roo.get(target);
20981         this.render(this.el.dom.parentNode);
20982         return this;
20983     },
20984
20985     // private
20986     initValue : function(){
20987         if(this.value !== undefined){
20988             this.setValue(this.value);
20989         }else if(this.el.dom.value.length > 0){
20990             this.setValue(this.el.dom.value);
20991         }
20992     },
20993
20994     /**
20995      * Returns true if this field has been changed since it was originally loaded and is not disabled.
20996      */
20997     isDirty : function() {
20998         if(this.disabled) {
20999             return false;
21000         }
21001         return String(this.getValue()) !== String(this.originalValue);
21002     },
21003
21004     // private
21005     afterRender : function(){
21006         Roo.form.Field.superclass.afterRender.call(this);
21007         this.initEvents();
21008     },
21009
21010     // private
21011     fireKey : function(e){
21012         //Roo.log('field ' + e.getKey());
21013         if(e.isNavKeyPress()){
21014             this.fireEvent("specialkey", this, e);
21015         }
21016     },
21017
21018     /**
21019      * Resets the current field value to the originally loaded value and clears any validation messages
21020      */
21021     reset : function(){
21022         this.setValue(this.originalValue);
21023         this.clearInvalid();
21024     },
21025
21026     // private
21027     initEvents : function(){
21028         // safari killled keypress - so keydown is now used..
21029         this.el.on("keydown" , this.fireKey,  this);
21030         this.el.on("focus", this.onFocus,  this);
21031         this.el.on("blur", this.onBlur,  this);
21032         this.el.relayEvent('keyup', this);
21033
21034         // reference to original value for reset
21035         this.originalValue = this.getValue();
21036     },
21037
21038     // private
21039     onFocus : function(){
21040         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21041             this.el.addClass(this.focusClass);
21042         }
21043         if(!this.hasFocus){
21044             this.hasFocus = true;
21045             this.startValue = this.getValue();
21046             this.fireEvent("focus", this);
21047         }
21048     },
21049
21050     beforeBlur : Roo.emptyFn,
21051
21052     // private
21053     onBlur : function(){
21054         this.beforeBlur();
21055         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21056             this.el.removeClass(this.focusClass);
21057         }
21058         this.hasFocus = false;
21059         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
21060             this.validate();
21061         }
21062         var v = this.getValue();
21063         if(String(v) !== String(this.startValue)){
21064             this.fireEvent('change', this, v, this.startValue);
21065         }
21066         this.fireEvent("blur", this);
21067     },
21068
21069     /**
21070      * Returns whether or not the field value is currently valid
21071      * @param {Boolean} preventMark True to disable marking the field invalid
21072      * @return {Boolean} True if the value is valid, else false
21073      */
21074     isValid : function(preventMark){
21075         if(this.disabled){
21076             return true;
21077         }
21078         var restore = this.preventMark;
21079         this.preventMark = preventMark === true;
21080         var v = this.validateValue(this.processValue(this.getRawValue()));
21081         this.preventMark = restore;
21082         return v;
21083     },
21084
21085     /**
21086      * Validates the field value
21087      * @return {Boolean} True if the value is valid, else false
21088      */
21089     validate : function(){
21090         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
21091             this.clearInvalid();
21092             return true;
21093         }
21094         return false;
21095     },
21096
21097     processValue : function(value){
21098         return value;
21099     },
21100
21101     // private
21102     // Subclasses should provide the validation implementation by overriding this
21103     validateValue : function(value){
21104         return true;
21105     },
21106
21107     /**
21108      * Mark this field as invalid
21109      * @param {String} msg The validation message
21110      */
21111     markInvalid : function(msg){
21112         if(!this.rendered || this.preventMark){ // not rendered
21113             return;
21114         }
21115         this.el.addClass(this.invalidClass);
21116         msg = msg || this.invalidText;
21117         switch(this.msgTarget){
21118             case 'qtip':
21119                 this.el.dom.qtip = msg;
21120                 this.el.dom.qclass = 'x-form-invalid-tip';
21121                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
21122                     Roo.QuickTips.enable();
21123                 }
21124                 break;
21125             case 'title':
21126                 this.el.dom.title = msg;
21127                 break;
21128             case 'under':
21129                 if(!this.errorEl){
21130                     var elp = this.el.findParent('.x-form-element', 5, true);
21131                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
21132                     this.errorEl.setWidth(elp.getWidth(true)-20);
21133                 }
21134                 this.errorEl.update(msg);
21135                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
21136                 break;
21137             case 'side':
21138                 if(!this.errorIcon){
21139                     var elp = this.el.findParent('.x-form-element', 5, true);
21140                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
21141                 }
21142                 this.alignErrorIcon();
21143                 this.errorIcon.dom.qtip = msg;
21144                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
21145                 this.errorIcon.show();
21146                 this.on('resize', this.alignErrorIcon, this);
21147                 break;
21148             default:
21149                 var t = Roo.getDom(this.msgTarget);
21150                 t.innerHTML = msg;
21151                 t.style.display = this.msgDisplay;
21152                 break;
21153         }
21154         this.fireEvent('invalid', this, msg);
21155     },
21156
21157     // private
21158     alignErrorIcon : function(){
21159         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
21160     },
21161
21162     /**
21163      * Clear any invalid styles/messages for this field
21164      */
21165     clearInvalid : function(){
21166         if(!this.rendered || this.preventMark){ // not rendered
21167             return;
21168         }
21169         this.el.removeClass(this.invalidClass);
21170         switch(this.msgTarget){
21171             case 'qtip':
21172                 this.el.dom.qtip = '';
21173                 break;
21174             case 'title':
21175                 this.el.dom.title = '';
21176                 break;
21177             case 'under':
21178                 if(this.errorEl){
21179                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
21180                 }
21181                 break;
21182             case 'side':
21183                 if(this.errorIcon){
21184                     this.errorIcon.dom.qtip = '';
21185                     this.errorIcon.hide();
21186                     this.un('resize', this.alignErrorIcon, this);
21187                 }
21188                 break;
21189             default:
21190                 var t = Roo.getDom(this.msgTarget);
21191                 t.innerHTML = '';
21192                 t.style.display = 'none';
21193                 break;
21194         }
21195         this.fireEvent('valid', this);
21196     },
21197
21198     /**
21199      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
21200      * @return {Mixed} value The field value
21201      */
21202     getRawValue : function(){
21203         var v = this.el.getValue();
21204         if(v === this.emptyText){
21205             v = '';
21206         }
21207         return v;
21208     },
21209
21210     /**
21211      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
21212      * @return {Mixed} value The field value
21213      */
21214     getValue : function(){
21215         var v = this.el.getValue();
21216         if(v === this.emptyText || v === undefined){
21217             v = '';
21218         }
21219         return v;
21220     },
21221
21222     /**
21223      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
21224      * @param {Mixed} value The value to set
21225      */
21226     setRawValue : function(v){
21227         return this.el.dom.value = (v === null || v === undefined ? '' : v);
21228     },
21229
21230     /**
21231      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
21232      * @param {Mixed} value The value to set
21233      */
21234     setValue : function(v){
21235         this.value = v;
21236         if(this.rendered){
21237             this.el.dom.value = (v === null || v === undefined ? '' : v);
21238             this.validate();
21239         }
21240     },
21241
21242     adjustSize : function(w, h){
21243         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
21244         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
21245         return s;
21246     },
21247
21248     adjustWidth : function(tag, w){
21249         tag = tag.toLowerCase();
21250         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
21251             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
21252                 if(tag == 'input'){
21253                     return w + 2;
21254                 }
21255                 if(tag = 'textarea'){
21256                     return w-2;
21257                 }
21258             }else if(Roo.isOpera){
21259                 if(tag == 'input'){
21260                     return w + 2;
21261                 }
21262                 if(tag = 'textarea'){
21263                     return w-2;
21264                 }
21265             }
21266         }
21267         return w;
21268     }
21269 });
21270
21271
21272 // anything other than normal should be considered experimental
21273 Roo.form.Field.msgFx = {
21274     normal : {
21275         show: function(msgEl, f){
21276             msgEl.setDisplayed('block');
21277         },
21278
21279         hide : function(msgEl, f){
21280             msgEl.setDisplayed(false).update('');
21281         }
21282     },
21283
21284     slide : {
21285         show: function(msgEl, f){
21286             msgEl.slideIn('t', {stopFx:true});
21287         },
21288
21289         hide : function(msgEl, f){
21290             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
21291         }
21292     },
21293
21294     slideRight : {
21295         show: function(msgEl, f){
21296             msgEl.fixDisplay();
21297             msgEl.alignTo(f.el, 'tl-tr');
21298             msgEl.slideIn('l', {stopFx:true});
21299         },
21300
21301         hide : function(msgEl, f){
21302             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
21303         }
21304     }
21305 };/*
21306  * Based on:
21307  * Ext JS Library 1.1.1
21308  * Copyright(c) 2006-2007, Ext JS, LLC.
21309  *
21310  * Originally Released Under LGPL - original licence link has changed is not relivant.
21311  *
21312  * Fork - LGPL
21313  * <script type="text/javascript">
21314  */
21315  
21316
21317 /**
21318  * @class Roo.form.TextField
21319  * @extends Roo.form.Field
21320  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
21321  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
21322  * @constructor
21323  * Creates a new TextField
21324  * @param {Object} config Configuration options
21325  */
21326 Roo.form.TextField = function(config){
21327     Roo.form.TextField.superclass.constructor.call(this, config);
21328     this.addEvents({
21329         /**
21330          * @event autosize
21331          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
21332          * according to the default logic, but this event provides a hook for the developer to apply additional
21333          * logic at runtime to resize the field if needed.
21334              * @param {Roo.form.Field} this This text field
21335              * @param {Number} width The new field width
21336              */
21337         autosize : true
21338     });
21339 };
21340
21341 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
21342     /**
21343      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
21344      */
21345     grow : false,
21346     /**
21347      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
21348      */
21349     growMin : 30,
21350     /**
21351      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
21352      */
21353     growMax : 800,
21354     /**
21355      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
21356      */
21357     vtype : null,
21358     /**
21359      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
21360      */
21361     maskRe : null,
21362     /**
21363      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
21364      */
21365     disableKeyFilter : false,
21366     /**
21367      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
21368      */
21369     allowBlank : true,
21370     /**
21371      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
21372      */
21373     minLength : 0,
21374     /**
21375      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
21376      */
21377     maxLength : Number.MAX_VALUE,
21378     /**
21379      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
21380      */
21381     minLengthText : "The minimum length for this field is {0}",
21382     /**
21383      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
21384      */
21385     maxLengthText : "The maximum length for this field is {0}",
21386     /**
21387      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
21388      */
21389     selectOnFocus : false,
21390     /**
21391      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
21392      */
21393     blankText : "This field is required",
21394     /**
21395      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
21396      * If available, this function will be called only after the basic validators all return true, and will be passed the
21397      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
21398      */
21399     validator : null,
21400     /**
21401      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
21402      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
21403      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
21404      */
21405     regex : null,
21406     /**
21407      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
21408      */
21409     regexText : "",
21410     /**
21411      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
21412      */
21413     emptyText : null,
21414     /**
21415      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
21416      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
21417      */
21418     emptyClass : 'x-form-empty-field',
21419
21420     // private
21421     initEvents : function(){
21422         Roo.form.TextField.superclass.initEvents.call(this);
21423         if(this.validationEvent == 'keyup'){
21424             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
21425             this.el.on('keyup', this.filterValidation, this);
21426         }
21427         else if(this.validationEvent !== false){
21428             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
21429         }
21430         if(this.selectOnFocus || this.emptyText){
21431             this.on("focus", this.preFocus, this);
21432             if(this.emptyText){
21433                 this.on('blur', this.postBlur, this);
21434                 this.applyEmptyText();
21435             }
21436         }
21437         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
21438             this.el.on("keypress", this.filterKeys, this);
21439         }
21440         if(this.grow){
21441             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
21442             this.el.on("click", this.autoSize,  this);
21443         }
21444     },
21445
21446     processValue : function(value){
21447         if(this.stripCharsRe){
21448             var newValue = value.replace(this.stripCharsRe, '');
21449             if(newValue !== value){
21450                 this.setRawValue(newValue);
21451                 return newValue;
21452             }
21453         }
21454         return value;
21455     },
21456
21457     filterValidation : function(e){
21458         if(!e.isNavKeyPress()){
21459             this.validationTask.delay(this.validationDelay);
21460         }
21461     },
21462
21463     // private
21464     onKeyUp : function(e){
21465         if(!e.isNavKeyPress()){
21466             this.autoSize();
21467         }
21468     },
21469
21470     /**
21471      * Resets the current field value to the originally-loaded value and clears any validation messages.
21472      * Also adds emptyText and emptyClass if the original value was blank.
21473      */
21474     reset : function(){
21475         Roo.form.TextField.superclass.reset.call(this);
21476         this.applyEmptyText();
21477     },
21478
21479     applyEmptyText : function(){
21480         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
21481             this.setRawValue(this.emptyText);
21482             this.el.addClass(this.emptyClass);
21483         }
21484     },
21485
21486     // private
21487     preFocus : function(){
21488         if(this.emptyText){
21489             if(this.el.dom.value == this.emptyText){
21490                 this.setRawValue('');
21491             }
21492             this.el.removeClass(this.emptyClass);
21493         }
21494         if(this.selectOnFocus){
21495             this.el.dom.select();
21496         }
21497     },
21498
21499     // private
21500     postBlur : function(){
21501         this.applyEmptyText();
21502     },
21503
21504     // private
21505     filterKeys : function(e){
21506         var k = e.getKey();
21507         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
21508             return;
21509         }
21510         var c = e.getCharCode(), cc = String.fromCharCode(c);
21511         if(Roo.isIE && (e.isSpecialKey() || !cc)){
21512             return;
21513         }
21514         if(!this.maskRe.test(cc)){
21515             e.stopEvent();
21516         }
21517     },
21518
21519     setValue : function(v){
21520         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
21521             this.el.removeClass(this.emptyClass);
21522         }
21523         Roo.form.TextField.superclass.setValue.apply(this, arguments);
21524         this.applyEmptyText();
21525         this.autoSize();
21526     },
21527
21528     /**
21529      * Validates a value according to the field's validation rules and marks the field as invalid
21530      * if the validation fails
21531      * @param {Mixed} value The value to validate
21532      * @return {Boolean} True if the value is valid, else false
21533      */
21534     validateValue : function(value){
21535         if(value.length < 1 || value === this.emptyText){ // if it's blank
21536              if(this.allowBlank){
21537                 this.clearInvalid();
21538                 return true;
21539              }else{
21540                 this.markInvalid(this.blankText);
21541                 return false;
21542              }
21543         }
21544         if(value.length < this.minLength){
21545             this.markInvalid(String.format(this.minLengthText, this.minLength));
21546             return false;
21547         }
21548         if(value.length > this.maxLength){
21549             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
21550             return false;
21551         }
21552         if(this.vtype){
21553             var vt = Roo.form.VTypes;
21554             if(!vt[this.vtype](value, this)){
21555                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
21556                 return false;
21557             }
21558         }
21559         if(typeof this.validator == "function"){
21560             var msg = this.validator(value);
21561             if(msg !== true){
21562                 this.markInvalid(msg);
21563                 return false;
21564             }
21565         }
21566         if(this.regex && !this.regex.test(value)){
21567             this.markInvalid(this.regexText);
21568             return false;
21569         }
21570         return true;
21571     },
21572
21573     /**
21574      * Selects text in this field
21575      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
21576      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
21577      */
21578     selectText : function(start, end){
21579         var v = this.getRawValue();
21580         if(v.length > 0){
21581             start = start === undefined ? 0 : start;
21582             end = end === undefined ? v.length : end;
21583             var d = this.el.dom;
21584             if(d.setSelectionRange){
21585                 d.setSelectionRange(start, end);
21586             }else if(d.createTextRange){
21587                 var range = d.createTextRange();
21588                 range.moveStart("character", start);
21589                 range.moveEnd("character", v.length-end);
21590                 range.select();
21591             }
21592         }
21593     },
21594
21595     /**
21596      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
21597      * This only takes effect if grow = true, and fires the autosize event.
21598      */
21599     autoSize : function(){
21600         if(!this.grow || !this.rendered){
21601             return;
21602         }
21603         if(!this.metrics){
21604             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
21605         }
21606         var el = this.el;
21607         var v = el.dom.value;
21608         var d = document.createElement('div');
21609         d.appendChild(document.createTextNode(v));
21610         v = d.innerHTML;
21611         d = null;
21612         v += "&#160;";
21613         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
21614         this.el.setWidth(w);
21615         this.fireEvent("autosize", this, w);
21616     }
21617 });/*
21618  * Based on:
21619  * Ext JS Library 1.1.1
21620  * Copyright(c) 2006-2007, Ext JS, LLC.
21621  *
21622  * Originally Released Under LGPL - original licence link has changed is not relivant.
21623  *
21624  * Fork - LGPL
21625  * <script type="text/javascript">
21626  */
21627  
21628 /**
21629  * @class Roo.form.Hidden
21630  * @extends Roo.form.TextField
21631  * Simple Hidden element used on forms 
21632  * 
21633  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
21634  * 
21635  * @constructor
21636  * Creates a new Hidden form element.
21637  * @param {Object} config Configuration options
21638  */
21639
21640
21641
21642 // easy hidden field...
21643 Roo.form.Hidden = function(config){
21644     Roo.form.Hidden.superclass.constructor.call(this, config);
21645 };
21646   
21647 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
21648     fieldLabel:      '',
21649     inputType:      'hidden',
21650     width:          50,
21651     allowBlank:     true,
21652     labelSeparator: '',
21653     hidden:         true,
21654     itemCls :       'x-form-item-display-none'
21655
21656
21657 });
21658
21659
21660 /*
21661  * Based on:
21662  * Ext JS Library 1.1.1
21663  * Copyright(c) 2006-2007, Ext JS, LLC.
21664  *
21665  * Originally Released Under LGPL - original licence link has changed is not relivant.
21666  *
21667  * Fork - LGPL
21668  * <script type="text/javascript">
21669  */
21670  
21671 /**
21672  * @class Roo.form.TriggerField
21673  * @extends Roo.form.TextField
21674  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
21675  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
21676  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
21677  * for which you can provide a custom implementation.  For example:
21678  * <pre><code>
21679 var trigger = new Roo.form.TriggerField();
21680 trigger.onTriggerClick = myTriggerFn;
21681 trigger.applyTo('my-field');
21682 </code></pre>
21683  *
21684  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
21685  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
21686  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
21687  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
21688  * @constructor
21689  * Create a new TriggerField.
21690  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
21691  * to the base TextField)
21692  */
21693 Roo.form.TriggerField = function(config){
21694     this.mimicing = false;
21695     Roo.form.TriggerField.superclass.constructor.call(this, config);
21696 };
21697
21698 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
21699     /**
21700      * @cfg {String} triggerClass A CSS class to apply to the trigger
21701      */
21702     /**
21703      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21704      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
21705      */
21706     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
21707     /**
21708      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
21709      */
21710     hideTrigger:false,
21711
21712     /** @cfg {Boolean} grow @hide */
21713     /** @cfg {Number} growMin @hide */
21714     /** @cfg {Number} growMax @hide */
21715
21716     /**
21717      * @hide 
21718      * @method
21719      */
21720     autoSize: Roo.emptyFn,
21721     // private
21722     monitorTab : true,
21723     // private
21724     deferHeight : true,
21725
21726     
21727     actionMode : 'wrap',
21728     // private
21729     onResize : function(w, h){
21730         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
21731         if(typeof w == 'number'){
21732             var x = w - this.trigger.getWidth();
21733             this.el.setWidth(this.adjustWidth('input', x));
21734             this.trigger.setStyle('left', x+'px');
21735         }
21736     },
21737
21738     // private
21739     adjustSize : Roo.BoxComponent.prototype.adjustSize,
21740
21741     // private
21742     getResizeEl : function(){
21743         return this.wrap;
21744     },
21745
21746     // private
21747     getPositionEl : function(){
21748         return this.wrap;
21749     },
21750
21751     // private
21752     alignErrorIcon : function(){
21753         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
21754     },
21755
21756     // private
21757     onRender : function(ct, position){
21758         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
21759         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
21760         this.trigger = this.wrap.createChild(this.triggerConfig ||
21761                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
21762         if(this.hideTrigger){
21763             this.trigger.setDisplayed(false);
21764         }
21765         this.initTrigger();
21766         if(!this.width){
21767             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
21768         }
21769     },
21770
21771     // private
21772     initTrigger : function(){
21773         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
21774         this.trigger.addClassOnOver('x-form-trigger-over');
21775         this.trigger.addClassOnClick('x-form-trigger-click');
21776     },
21777
21778     // private
21779     onDestroy : function(){
21780         if(this.trigger){
21781             this.trigger.removeAllListeners();
21782             this.trigger.remove();
21783         }
21784         if(this.wrap){
21785             this.wrap.remove();
21786         }
21787         Roo.form.TriggerField.superclass.onDestroy.call(this);
21788     },
21789
21790     // private
21791     onFocus : function(){
21792         Roo.form.TriggerField.superclass.onFocus.call(this);
21793         if(!this.mimicing){
21794             this.wrap.addClass('x-trigger-wrap-focus');
21795             this.mimicing = true;
21796             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
21797             if(this.monitorTab){
21798                 this.el.on("keydown", this.checkTab, this);
21799             }
21800         }
21801     },
21802
21803     // private
21804     checkTab : function(e){
21805         if(e.getKey() == e.TAB){
21806             this.triggerBlur();
21807         }
21808     },
21809
21810     // private
21811     onBlur : function(){
21812         // do nothing
21813     },
21814
21815     // private
21816     mimicBlur : function(e, t){
21817         if(!this.wrap.contains(t) && this.validateBlur()){
21818             this.triggerBlur();
21819         }
21820     },
21821
21822     // private
21823     triggerBlur : function(){
21824         this.mimicing = false;
21825         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
21826         if(this.monitorTab){
21827             this.el.un("keydown", this.checkTab, this);
21828         }
21829         this.wrap.removeClass('x-trigger-wrap-focus');
21830         Roo.form.TriggerField.superclass.onBlur.call(this);
21831     },
21832
21833     // private
21834     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
21835     validateBlur : function(e, t){
21836         return true;
21837     },
21838
21839     // private
21840     onDisable : function(){
21841         Roo.form.TriggerField.superclass.onDisable.call(this);
21842         if(this.wrap){
21843             this.wrap.addClass('x-item-disabled');
21844         }
21845     },
21846
21847     // private
21848     onEnable : function(){
21849         Roo.form.TriggerField.superclass.onEnable.call(this);
21850         if(this.wrap){
21851             this.wrap.removeClass('x-item-disabled');
21852         }
21853     },
21854
21855     // private
21856     onShow : function(){
21857         var ae = this.getActionEl();
21858         
21859         if(ae){
21860             ae.dom.style.display = '';
21861             ae.dom.style.visibility = 'visible';
21862         }
21863     },
21864
21865     // private
21866     
21867     onHide : function(){
21868         var ae = this.getActionEl();
21869         ae.dom.style.display = 'none';
21870     },
21871
21872     /**
21873      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
21874      * by an implementing function.
21875      * @method
21876      * @param {EventObject} e
21877      */
21878     onTriggerClick : Roo.emptyFn
21879 });
21880
21881 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
21882 // to be extended by an implementing class.  For an example of implementing this class, see the custom
21883 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
21884 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
21885     initComponent : function(){
21886         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
21887
21888         this.triggerConfig = {
21889             tag:'span', cls:'x-form-twin-triggers', cn:[
21890             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
21891             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
21892         ]};
21893     },
21894
21895     getTrigger : function(index){
21896         return this.triggers[index];
21897     },
21898
21899     initTrigger : function(){
21900         var ts = this.trigger.select('.x-form-trigger', true);
21901         this.wrap.setStyle('overflow', 'hidden');
21902         var triggerField = this;
21903         ts.each(function(t, all, index){
21904             t.hide = function(){
21905                 var w = triggerField.wrap.getWidth();
21906                 this.dom.style.display = 'none';
21907                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21908             };
21909             t.show = function(){
21910                 var w = triggerField.wrap.getWidth();
21911                 this.dom.style.display = '';
21912                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21913             };
21914             var triggerIndex = 'Trigger'+(index+1);
21915
21916             if(this['hide'+triggerIndex]){
21917                 t.dom.style.display = 'none';
21918             }
21919             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
21920             t.addClassOnOver('x-form-trigger-over');
21921             t.addClassOnClick('x-form-trigger-click');
21922         }, this);
21923         this.triggers = ts.elements;
21924     },
21925
21926     onTrigger1Click : Roo.emptyFn,
21927     onTrigger2Click : Roo.emptyFn
21928 });/*
21929  * Based on:
21930  * Ext JS Library 1.1.1
21931  * Copyright(c) 2006-2007, Ext JS, LLC.
21932  *
21933  * Originally Released Under LGPL - original licence link has changed is not relivant.
21934  *
21935  * Fork - LGPL
21936  * <script type="text/javascript">
21937  */
21938  
21939 /**
21940  * @class Roo.form.TextArea
21941  * @extends Roo.form.TextField
21942  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
21943  * support for auto-sizing.
21944  * @constructor
21945  * Creates a new TextArea
21946  * @param {Object} config Configuration options
21947  */
21948 Roo.form.TextArea = function(config){
21949     Roo.form.TextArea.superclass.constructor.call(this, config);
21950     // these are provided exchanges for backwards compat
21951     // minHeight/maxHeight were replaced by growMin/growMax to be
21952     // compatible with TextField growing config values
21953     if(this.minHeight !== undefined){
21954         this.growMin = this.minHeight;
21955     }
21956     if(this.maxHeight !== undefined){
21957         this.growMax = this.maxHeight;
21958     }
21959 };
21960
21961 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
21962     /**
21963      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
21964      */
21965     growMin : 60,
21966     /**
21967      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
21968      */
21969     growMax: 1000,
21970     /**
21971      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
21972      * in the field (equivalent to setting overflow: hidden, defaults to false)
21973      */
21974     preventScrollbars: false,
21975     /**
21976      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21977      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
21978      */
21979
21980     // private
21981     onRender : function(ct, position){
21982         if(!this.el){
21983             this.defaultAutoCreate = {
21984                 tag: "textarea",
21985                 style:"width:300px;height:60px;",
21986                 autocomplete: "off"
21987             };
21988         }
21989         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
21990         if(this.grow){
21991             this.textSizeEl = Roo.DomHelper.append(document.body, {
21992                 tag: "pre", cls: "x-form-grow-sizer"
21993             });
21994             if(this.preventScrollbars){
21995                 this.el.setStyle("overflow", "hidden");
21996             }
21997             this.el.setHeight(this.growMin);
21998         }
21999     },
22000
22001     onDestroy : function(){
22002         if(this.textSizeEl){
22003             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
22004         }
22005         Roo.form.TextArea.superclass.onDestroy.call(this);
22006     },
22007
22008     // private
22009     onKeyUp : function(e){
22010         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
22011             this.autoSize();
22012         }
22013     },
22014
22015     /**
22016      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
22017      * This only takes effect if grow = true, and fires the autosize event if the height changes.
22018      */
22019     autoSize : function(){
22020         if(!this.grow || !this.textSizeEl){
22021             return;
22022         }
22023         var el = this.el;
22024         var v = el.dom.value;
22025         var ts = this.textSizeEl;
22026
22027         ts.innerHTML = '';
22028         ts.appendChild(document.createTextNode(v));
22029         v = ts.innerHTML;
22030
22031         Roo.fly(ts).setWidth(this.el.getWidth());
22032         if(v.length < 1){
22033             v = "&#160;&#160;";
22034         }else{
22035             if(Roo.isIE){
22036                 v = v.replace(/\n/g, '<p>&#160;</p>');
22037             }
22038             v += "&#160;\n&#160;";
22039         }
22040         ts.innerHTML = v;
22041         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
22042         if(h != this.lastHeight){
22043             this.lastHeight = h;
22044             this.el.setHeight(h);
22045             this.fireEvent("autosize", this, h);
22046         }
22047     }
22048 });/*
22049  * Based on:
22050  * Ext JS Library 1.1.1
22051  * Copyright(c) 2006-2007, Ext JS, LLC.
22052  *
22053  * Originally Released Under LGPL - original licence link has changed is not relivant.
22054  *
22055  * Fork - LGPL
22056  * <script type="text/javascript">
22057  */
22058  
22059
22060 /**
22061  * @class Roo.form.NumberField
22062  * @extends Roo.form.TextField
22063  * Numeric text field that provides automatic keystroke filtering and numeric validation.
22064  * @constructor
22065  * Creates a new NumberField
22066  * @param {Object} config Configuration options
22067  */
22068 Roo.form.NumberField = function(config){
22069     Roo.form.NumberField.superclass.constructor.call(this, config);
22070 };
22071
22072 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
22073     /**
22074      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
22075      */
22076     fieldClass: "x-form-field x-form-num-field",
22077     /**
22078      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
22079      */
22080     allowDecimals : true,
22081     /**
22082      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
22083      */
22084     decimalSeparator : ".",
22085     /**
22086      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
22087      */
22088     decimalPrecision : 2,
22089     /**
22090      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
22091      */
22092     allowNegative : true,
22093     /**
22094      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
22095      */
22096     minValue : Number.NEGATIVE_INFINITY,
22097     /**
22098      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
22099      */
22100     maxValue : Number.MAX_VALUE,
22101     /**
22102      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
22103      */
22104     minText : "The minimum value for this field is {0}",
22105     /**
22106      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
22107      */
22108     maxText : "The maximum value for this field is {0}",
22109     /**
22110      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
22111      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
22112      */
22113     nanText : "{0} is not a valid number",
22114
22115     // private
22116     initEvents : function(){
22117         Roo.form.NumberField.superclass.initEvents.call(this);
22118         var allowed = "0123456789";
22119         if(this.allowDecimals){
22120             allowed += this.decimalSeparator;
22121         }
22122         if(this.allowNegative){
22123             allowed += "-";
22124         }
22125         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
22126         var keyPress = function(e){
22127             var k = e.getKey();
22128             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
22129                 return;
22130             }
22131             var c = e.getCharCode();
22132             if(allowed.indexOf(String.fromCharCode(c)) === -1){
22133                 e.stopEvent();
22134             }
22135         };
22136         this.el.on("keypress", keyPress, this);
22137     },
22138
22139     // private
22140     validateValue : function(value){
22141         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
22142             return false;
22143         }
22144         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22145              return true;
22146         }
22147         var num = this.parseValue(value);
22148         if(isNaN(num)){
22149             this.markInvalid(String.format(this.nanText, value));
22150             return false;
22151         }
22152         if(num < this.minValue){
22153             this.markInvalid(String.format(this.minText, this.minValue));
22154             return false;
22155         }
22156         if(num > this.maxValue){
22157             this.markInvalid(String.format(this.maxText, this.maxValue));
22158             return false;
22159         }
22160         return true;
22161     },
22162
22163     getValue : function(){
22164         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
22165     },
22166
22167     // private
22168     parseValue : function(value){
22169         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
22170         return isNaN(value) ? '' : value;
22171     },
22172
22173     // private
22174     fixPrecision : function(value){
22175         var nan = isNaN(value);
22176         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
22177             return nan ? '' : value;
22178         }
22179         return parseFloat(value).toFixed(this.decimalPrecision);
22180     },
22181
22182     setValue : function(v){
22183         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
22184     },
22185
22186     // private
22187     decimalPrecisionFcn : function(v){
22188         return Math.floor(v);
22189     },
22190
22191     beforeBlur : function(){
22192         var v = this.parseValue(this.getRawValue());
22193         if(v){
22194             this.setValue(this.fixPrecision(v));
22195         }
22196     }
22197 });/*
22198  * Based on:
22199  * Ext JS Library 1.1.1
22200  * Copyright(c) 2006-2007, Ext JS, LLC.
22201  *
22202  * Originally Released Under LGPL - original licence link has changed is not relivant.
22203  *
22204  * Fork - LGPL
22205  * <script type="text/javascript">
22206  */
22207  
22208 /**
22209  * @class Roo.form.DateField
22210  * @extends Roo.form.TriggerField
22211  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22212 * @constructor
22213 * Create a new DateField
22214 * @param {Object} config
22215  */
22216 Roo.form.DateField = function(config){
22217     Roo.form.DateField.superclass.constructor.call(this, config);
22218     
22219       this.addEvents({
22220          
22221         /**
22222          * @event select
22223          * Fires when a date is selected
22224              * @param {Roo.form.DateField} combo This combo box
22225              * @param {Date} date The date selected
22226              */
22227         'select' : true
22228          
22229     });
22230     
22231     
22232     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22233     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22234     this.ddMatch = null;
22235     if(this.disabledDates){
22236         var dd = this.disabledDates;
22237         var re = "(?:";
22238         for(var i = 0; i < dd.length; i++){
22239             re += dd[i];
22240             if(i != dd.length-1) re += "|";
22241         }
22242         this.ddMatch = new RegExp(re + ")");
22243     }
22244 };
22245
22246 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
22247     /**
22248      * @cfg {String} format
22249      * The default date format string which can be overriden for localization support.  The format must be
22250      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22251      */
22252     format : "m/d/y",
22253     /**
22254      * @cfg {String} altFormats
22255      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22256      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22257      */
22258     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22259     /**
22260      * @cfg {Array} disabledDays
22261      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22262      */
22263     disabledDays : null,
22264     /**
22265      * @cfg {String} disabledDaysText
22266      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22267      */
22268     disabledDaysText : "Disabled",
22269     /**
22270      * @cfg {Array} disabledDates
22271      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22272      * expression so they are very powerful. Some examples:
22273      * <ul>
22274      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22275      * <li>["03/08", "09/16"] would disable those days for every year</li>
22276      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22277      * <li>["03/../2006"] would disable every day in March 2006</li>
22278      * <li>["^03"] would disable every day in every March</li>
22279      * </ul>
22280      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22281      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22282      */
22283     disabledDates : null,
22284     /**
22285      * @cfg {String} disabledDatesText
22286      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22287      */
22288     disabledDatesText : "Disabled",
22289     /**
22290      * @cfg {Date/String} minValue
22291      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22292      * valid format (defaults to null).
22293      */
22294     minValue : null,
22295     /**
22296      * @cfg {Date/String} maxValue
22297      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22298      * valid format (defaults to null).
22299      */
22300     maxValue : null,
22301     /**
22302      * @cfg {String} minText
22303      * The error text to display when the date in the cell is before minValue (defaults to
22304      * 'The date in this field must be after {minValue}').
22305      */
22306     minText : "The date in this field must be equal to or after {0}",
22307     /**
22308      * @cfg {String} maxText
22309      * The error text to display when the date in the cell is after maxValue (defaults to
22310      * 'The date in this field must be before {maxValue}').
22311      */
22312     maxText : "The date in this field must be equal to or before {0}",
22313     /**
22314      * @cfg {String} invalidText
22315      * The error text to display when the date in the field is invalid (defaults to
22316      * '{value} is not a valid date - it must be in the format {format}').
22317      */
22318     invalidText : "{0} is not a valid date - it must be in the format {1}",
22319     /**
22320      * @cfg {String} triggerClass
22321      * An additional CSS class used to style the trigger button.  The trigger will always get the
22322      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22323      * which displays a calendar icon).
22324      */
22325     triggerClass : 'x-form-date-trigger',
22326     
22327
22328     /**
22329      * @cfg {bool} useIso
22330      * if enabled, then the date field will use a hidden field to store the 
22331      * real value as iso formated date. default (false)
22332      */ 
22333     useIso : false,
22334     /**
22335      * @cfg {String/Object} autoCreate
22336      * A DomHelper element spec, or true for a default element spec (defaults to
22337      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22338      */ 
22339     // private
22340     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
22341     
22342     // private
22343     hiddenField: false,
22344     
22345     onRender : function(ct, position)
22346     {
22347         Roo.form.DateField.superclass.onRender.call(this, ct, position);
22348         if (this.useIso) {
22349             this.el.dom.removeAttribute('name'); 
22350             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22351                     'before', true);
22352             this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
22353             // prevent input submission
22354             this.hiddenName = this.name;
22355         }
22356             
22357             
22358     },
22359     
22360     // private
22361     validateValue : function(value)
22362     {
22363         value = this.formatDate(value);
22364         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
22365             return false;
22366         }
22367         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22368              return true;
22369         }
22370         var svalue = value;
22371         value = this.parseDate(value);
22372         if(!value){
22373             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22374             return false;
22375         }
22376         var time = value.getTime();
22377         if(this.minValue && time < this.minValue.getTime()){
22378             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22379             return false;
22380         }
22381         if(this.maxValue && time > this.maxValue.getTime()){
22382             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22383             return false;
22384         }
22385         if(this.disabledDays){
22386             var day = value.getDay();
22387             for(var i = 0; i < this.disabledDays.length; i++) {
22388                 if(day === this.disabledDays[i]){
22389                     this.markInvalid(this.disabledDaysText);
22390                     return false;
22391                 }
22392             }
22393         }
22394         var fvalue = this.formatDate(value);
22395         if(this.ddMatch && this.ddMatch.test(fvalue)){
22396             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22397             return false;
22398         }
22399         return true;
22400     },
22401
22402     // private
22403     // Provides logic to override the default TriggerField.validateBlur which just returns true
22404     validateBlur : function(){
22405         return !this.menu || !this.menu.isVisible();
22406     },
22407
22408     /**
22409      * Returns the current date value of the date field.
22410      * @return {Date} The date value
22411      */
22412     getValue : function(){
22413         
22414         return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
22415     },
22416
22417     /**
22418      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22419      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
22420      * (the default format used is "m/d/y").
22421      * <br />Usage:
22422      * <pre><code>
22423 //All of these calls set the same date value (May 4, 2006)
22424
22425 //Pass a date object:
22426 var dt = new Date('5/4/06');
22427 dateField.setValue(dt);
22428
22429 //Pass a date string (default format):
22430 dateField.setValue('5/4/06');
22431
22432 //Pass a date string (custom format):
22433 dateField.format = 'Y-m-d';
22434 dateField.setValue('2006-5-4');
22435 </code></pre>
22436      * @param {String/Date} date The date or valid date string
22437      */
22438     setValue : function(date){
22439         if (this.hiddenField) {
22440             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22441         }
22442         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22443     },
22444
22445     // private
22446     parseDate : function(value){
22447         if(!value || value instanceof Date){
22448             return value;
22449         }
22450         var v = Date.parseDate(value, this.format);
22451         if(!v && this.altFormats){
22452             if(!this.altFormatsArray){
22453                 this.altFormatsArray = this.altFormats.split("|");
22454             }
22455             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22456                 v = Date.parseDate(value, this.altFormatsArray[i]);
22457             }
22458         }
22459         return v;
22460     },
22461
22462     // private
22463     formatDate : function(date, fmt){
22464         return (!date || !(date instanceof Date)) ?
22465                date : date.dateFormat(fmt || this.format);
22466     },
22467
22468     // private
22469     menuListeners : {
22470         select: function(m, d){
22471             this.setValue(d);
22472             this.fireEvent('select', this, d);
22473         },
22474         show : function(){ // retain focus styling
22475             this.onFocus();
22476         },
22477         hide : function(){
22478             this.focus.defer(10, this);
22479             var ml = this.menuListeners;
22480             this.menu.un("select", ml.select,  this);
22481             this.menu.un("show", ml.show,  this);
22482             this.menu.un("hide", ml.hide,  this);
22483         }
22484     },
22485
22486     // private
22487     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22488     onTriggerClick : function(){
22489         if(this.disabled){
22490             return;
22491         }
22492         if(this.menu == null){
22493             this.menu = new Roo.menu.DateMenu();
22494         }
22495         Roo.apply(this.menu.picker,  {
22496             showClear: this.allowBlank,
22497             minDate : this.minValue,
22498             maxDate : this.maxValue,
22499             disabledDatesRE : this.ddMatch,
22500             disabledDatesText : this.disabledDatesText,
22501             disabledDays : this.disabledDays,
22502             disabledDaysText : this.disabledDaysText,
22503             format : this.format,
22504             minText : String.format(this.minText, this.formatDate(this.minValue)),
22505             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22506         });
22507         this.menu.on(Roo.apply({}, this.menuListeners, {
22508             scope:this
22509         }));
22510         this.menu.picker.setValue(this.getValue() || new Date());
22511         this.menu.show(this.el, "tl-bl?");
22512     },
22513
22514     beforeBlur : function(){
22515         var v = this.parseDate(this.getRawValue());
22516         if(v){
22517             this.setValue(v);
22518         }
22519     }
22520
22521     /** @cfg {Boolean} grow @hide */
22522     /** @cfg {Number} growMin @hide */
22523     /** @cfg {Number} growMax @hide */
22524     /**
22525      * @hide
22526      * @method autoSize
22527      */
22528 });/*
22529  * Based on:
22530  * Ext JS Library 1.1.1
22531  * Copyright(c) 2006-2007, Ext JS, LLC.
22532  *
22533  * Originally Released Under LGPL - original licence link has changed is not relivant.
22534  *
22535  * Fork - LGPL
22536  * <script type="text/javascript">
22537  */
22538  
22539
22540 /**
22541  * @class Roo.form.ComboBox
22542  * @extends Roo.form.TriggerField
22543  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
22544  * @constructor
22545  * Create a new ComboBox.
22546  * @param {Object} config Configuration options
22547  */
22548 Roo.form.ComboBox = function(config){
22549     Roo.form.ComboBox.superclass.constructor.call(this, config);
22550     this.addEvents({
22551         /**
22552          * @event expand
22553          * Fires when the dropdown list is expanded
22554              * @param {Roo.form.ComboBox} combo This combo box
22555              */
22556         'expand' : true,
22557         /**
22558          * @event collapse
22559          * Fires when the dropdown list is collapsed
22560              * @param {Roo.form.ComboBox} combo This combo box
22561              */
22562         'collapse' : true,
22563         /**
22564          * @event beforeselect
22565          * Fires before a list item is selected. Return false to cancel the selection.
22566              * @param {Roo.form.ComboBox} combo This combo box
22567              * @param {Roo.data.Record} record The data record returned from the underlying store
22568              * @param {Number} index The index of the selected item in the dropdown list
22569              */
22570         'beforeselect' : true,
22571         /**
22572          * @event select
22573          * Fires when a list item is selected
22574              * @param {Roo.form.ComboBox} combo This combo box
22575              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
22576              * @param {Number} index The index of the selected item in the dropdown list
22577              */
22578         'select' : true,
22579         /**
22580          * @event beforequery
22581          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
22582          * The event object passed has these properties:
22583              * @param {Roo.form.ComboBox} combo This combo box
22584              * @param {String} query The query
22585              * @param {Boolean} forceAll true to force "all" query
22586              * @param {Boolean} cancel true to cancel the query
22587              * @param {Object} e The query event object
22588              */
22589         'beforequery': true,
22590          /**
22591          * @event add
22592          * Fires when the 'add' icon is pressed (add a listener to enable add button)
22593              * @param {Roo.form.ComboBox} combo This combo box
22594              */
22595         'add' : true,
22596         /**
22597          * @event edit
22598          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
22599              * @param {Roo.form.ComboBox} combo This combo box
22600              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
22601              */
22602         'edit' : true
22603         
22604         
22605     });
22606     if(this.transform){
22607         this.allowDomMove = false;
22608         var s = Roo.getDom(this.transform);
22609         if(!this.hiddenName){
22610             this.hiddenName = s.name;
22611         }
22612         if(!this.store){
22613             this.mode = 'local';
22614             var d = [], opts = s.options;
22615             for(var i = 0, len = opts.length;i < len; i++){
22616                 var o = opts[i];
22617                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
22618                 if(o.selected) {
22619                     this.value = value;
22620                 }
22621                 d.push([value, o.text]);
22622             }
22623             this.store = new Roo.data.SimpleStore({
22624                 'id': 0,
22625                 fields: ['value', 'text'],
22626                 data : d
22627             });
22628             this.valueField = 'value';
22629             this.displayField = 'text';
22630         }
22631         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
22632         if(!this.lazyRender){
22633             this.target = true;
22634             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
22635             s.parentNode.removeChild(s); // remove it
22636             this.render(this.el.parentNode);
22637         }else{
22638             s.parentNode.removeChild(s); // remove it
22639         }
22640
22641     }
22642     if (this.store) {
22643         this.store = Roo.factory(this.store, Roo.data);
22644     }
22645     
22646     this.selectedIndex = -1;
22647     if(this.mode == 'local'){
22648         if(config.queryDelay === undefined){
22649             this.queryDelay = 10;
22650         }
22651         if(config.minChars === undefined){
22652             this.minChars = 0;
22653         }
22654     }
22655 };
22656
22657 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
22658     /**
22659      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
22660      */
22661     /**
22662      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
22663      * rendering into an Roo.Editor, defaults to false)
22664      */
22665     /**
22666      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
22667      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
22668      */
22669     /**
22670      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
22671      */
22672     /**
22673      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
22674      * the dropdown list (defaults to undefined, with no header element)
22675      */
22676
22677      /**
22678      * @cfg {String/Roo.Template} tpl The template to use to render the output
22679      */
22680      
22681     // private
22682     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
22683     /**
22684      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
22685      */
22686     listWidth: undefined,
22687     /**
22688      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
22689      * mode = 'remote' or 'text' if mode = 'local')
22690      */
22691     displayField: undefined,
22692     /**
22693      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
22694      * mode = 'remote' or 'value' if mode = 'local'). 
22695      * Note: use of a valueField requires the user make a selection
22696      * in order for a value to be mapped.
22697      */
22698     valueField: undefined,
22699     /**
22700      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
22701      * field's data value (defaults to the underlying DOM element's name)
22702      */
22703     hiddenName: undefined,
22704     /**
22705      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
22706      */
22707     listClass: '',
22708     /**
22709      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
22710      */
22711     selectedClass: 'x-combo-selected',
22712     /**
22713      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
22714      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
22715      * which displays a downward arrow icon).
22716      */
22717     triggerClass : 'x-form-arrow-trigger',
22718     /**
22719      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
22720      */
22721     shadow:'sides',
22722     /**
22723      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
22724      * anchor positions (defaults to 'tl-bl')
22725      */
22726     listAlign: 'tl-bl?',
22727     /**
22728      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
22729      */
22730     maxHeight: 300,
22731     /**
22732      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
22733      * query specified by the allQuery config option (defaults to 'query')
22734      */
22735     triggerAction: 'query',
22736     /**
22737      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
22738      * (defaults to 4, does not apply if editable = false)
22739      */
22740     minChars : 4,
22741     /**
22742      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
22743      * delay (typeAheadDelay) if it matches a known value (defaults to false)
22744      */
22745     typeAhead: false,
22746     /**
22747      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
22748      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
22749      */
22750     queryDelay: 500,
22751     /**
22752      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
22753      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
22754      */
22755     pageSize: 0,
22756     /**
22757      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
22758      * when editable = true (defaults to false)
22759      */
22760     selectOnFocus:false,
22761     /**
22762      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
22763      */
22764     queryParam: 'query',
22765     /**
22766      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
22767      * when mode = 'remote' (defaults to 'Loading...')
22768      */
22769     loadingText: 'Loading...',
22770     /**
22771      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
22772      */
22773     resizable: false,
22774     /**
22775      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
22776      */
22777     handleHeight : 8,
22778     /**
22779      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
22780      * traditional select (defaults to true)
22781      */
22782     editable: true,
22783     /**
22784      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
22785      */
22786     allQuery: '',
22787     /**
22788      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
22789      */
22790     mode: 'remote',
22791     /**
22792      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
22793      * listWidth has a higher value)
22794      */
22795     minListWidth : 70,
22796     /**
22797      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
22798      * allow the user to set arbitrary text into the field (defaults to false)
22799      */
22800     forceSelection:false,
22801     /**
22802      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
22803      * if typeAhead = true (defaults to 250)
22804      */
22805     typeAheadDelay : 250,
22806     /**
22807      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
22808      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
22809      */
22810     valueNotFoundText : undefined,
22811     /**
22812      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
22813      */
22814     blockFocus : false,
22815     
22816     /**
22817      * @cfg {Boolean} disableClear Disable showing of clear button.
22818      */
22819     disableClear : false,
22820     /**
22821      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
22822      */
22823     alwaysQuery : false,
22824     
22825     //private
22826     addicon : false,
22827     editicon: false,
22828     
22829     
22830     // private
22831     onRender : function(ct, position){
22832         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
22833         if(this.hiddenName){
22834             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
22835                     'before', true);
22836             this.hiddenField.value =
22837                 this.hiddenValue !== undefined ? this.hiddenValue :
22838                 this.value !== undefined ? this.value : '';
22839
22840             // prevent input submission
22841             this.el.dom.removeAttribute('name');
22842         }
22843         if(Roo.isGecko){
22844             this.el.dom.setAttribute('autocomplete', 'off');
22845         }
22846
22847         var cls = 'x-combo-list';
22848
22849         this.list = new Roo.Layer({
22850             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
22851         });
22852
22853         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
22854         this.list.setWidth(lw);
22855         this.list.swallowEvent('mousewheel');
22856         this.assetHeight = 0;
22857
22858         if(this.title){
22859             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
22860             this.assetHeight += this.header.getHeight();
22861         }
22862
22863         this.innerList = this.list.createChild({cls:cls+'-inner'});
22864         this.innerList.on('mouseover', this.onViewOver, this);
22865         this.innerList.on('mousemove', this.onViewMove, this);
22866         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
22867         
22868         if(this.allowBlank && !this.pageSize && !this.disableClear){
22869             this.footer = this.list.createChild({cls:cls+'-ft'});
22870             this.pageTb = new Roo.Toolbar(this.footer);
22871            
22872         }
22873         if(this.pageSize){
22874             this.footer = this.list.createChild({cls:cls+'-ft'});
22875             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
22876                     {pageSize: this.pageSize});
22877             
22878         }
22879         
22880         if (this.pageTb && this.allowBlank && !this.disableClear) {
22881             var _this = this;
22882             this.pageTb.add(new Roo.Toolbar.Fill(), {
22883                 cls: 'x-btn-icon x-btn-clear',
22884                 text: '&#160;',
22885                 handler: function()
22886                 {
22887                     _this.collapse();
22888                     _this.clearValue();
22889                     _this.onSelect(false, -1);
22890                 }
22891             });
22892         }
22893         if (this.footer) {
22894             this.assetHeight += this.footer.getHeight();
22895         }
22896         
22897
22898         if(!this.tpl){
22899             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
22900         }
22901
22902         this.view = new Roo.View(this.innerList, this.tpl, {
22903             singleSelect:true, store: this.store, selectedClass: this.selectedClass
22904         });
22905
22906         this.view.on('click', this.onViewClick, this);
22907
22908         this.store.on('beforeload', this.onBeforeLoad, this);
22909         this.store.on('load', this.onLoad, this);
22910         this.store.on('loadexception', this.collapse, this);
22911
22912         if(this.resizable){
22913             this.resizer = new Roo.Resizable(this.list,  {
22914                pinned:true, handles:'se'
22915             });
22916             this.resizer.on('resize', function(r, w, h){
22917                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
22918                 this.listWidth = w;
22919                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
22920                 this.restrictHeight();
22921             }, this);
22922             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
22923         }
22924         if(!this.editable){
22925             this.editable = true;
22926             this.setEditable(false);
22927         }  
22928         
22929         
22930         if (typeof(this.events.add.listeners) != 'undefined') {
22931             
22932             this.addicon = this.wrap.createChild(
22933                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
22934        
22935             this.addicon.on('click', function(e) {
22936                 this.fireEvent('add', this);
22937             }, this);
22938         }
22939         if (typeof(this.events.edit.listeners) != 'undefined') {
22940             
22941             this.editicon = this.wrap.createChild(
22942                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
22943             if (this.addicon) {
22944                 this.editicon.setStyle('margin-left', '40px');
22945             }
22946             this.editicon.on('click', function(e) {
22947                 
22948                 // we fire even  if inothing is selected..
22949                 this.fireEvent('edit', this, this.lastData );
22950                 
22951             }, this);
22952         }
22953         
22954         
22955         
22956     },
22957
22958     // private
22959     initEvents : function(){
22960         Roo.form.ComboBox.superclass.initEvents.call(this);
22961
22962         this.keyNav = new Roo.KeyNav(this.el, {
22963             "up" : function(e){
22964                 this.inKeyMode = true;
22965                 this.selectPrev();
22966             },
22967
22968             "down" : function(e){
22969                 if(!this.isExpanded()){
22970                     this.onTriggerClick();
22971                 }else{
22972                     this.inKeyMode = true;
22973                     this.selectNext();
22974                 }
22975             },
22976
22977             "enter" : function(e){
22978                 this.onViewClick();
22979                 //return true;
22980             },
22981
22982             "esc" : function(e){
22983                 this.collapse();
22984             },
22985
22986             "tab" : function(e){
22987                 this.onViewClick(false);
22988                 return true;
22989             },
22990
22991             scope : this,
22992
22993             doRelay : function(foo, bar, hname){
22994                 if(hname == 'down' || this.scope.isExpanded()){
22995                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
22996                 }
22997                 return true;
22998             },
22999
23000             forceKeyDown: true
23001         });
23002         this.queryDelay = Math.max(this.queryDelay || 10,
23003                 this.mode == 'local' ? 10 : 250);
23004         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
23005         if(this.typeAhead){
23006             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
23007         }
23008         if(this.editable !== false){
23009             this.el.on("keyup", this.onKeyUp, this);
23010         }
23011         if(this.forceSelection){
23012             this.on('blur', this.doForce, this);
23013         }
23014     },
23015
23016     onDestroy : function(){
23017         if(this.view){
23018             this.view.setStore(null);
23019             this.view.el.removeAllListeners();
23020             this.view.el.remove();
23021             this.view.purgeListeners();
23022         }
23023         if(this.list){
23024             this.list.destroy();
23025         }
23026         if(this.store){
23027             this.store.un('beforeload', this.onBeforeLoad, this);
23028             this.store.un('load', this.onLoad, this);
23029             this.store.un('loadexception', this.collapse, this);
23030         }
23031         Roo.form.ComboBox.superclass.onDestroy.call(this);
23032     },
23033
23034     // private
23035     fireKey : function(e){
23036         if(e.isNavKeyPress() && !this.list.isVisible()){
23037             this.fireEvent("specialkey", this, e);
23038         }
23039     },
23040
23041     // private
23042     onResize: function(w, h){
23043         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
23044         
23045         if(typeof w != 'number'){
23046             // we do not handle it!?!?
23047             return;
23048         }
23049         var tw = this.trigger.getWidth();
23050         tw += this.addicon ? this.addicon.getWidth() : 0;
23051         tw += this.editicon ? this.editicon.getWidth() : 0;
23052         var x = w - tw;
23053         this.el.setWidth( this.adjustWidth('input', x));
23054             
23055         this.trigger.setStyle('left', x+'px');
23056         
23057         if(this.list && this.listWidth === undefined){
23058             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
23059             this.list.setWidth(lw);
23060             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23061         }
23062         
23063     
23064         
23065     },
23066
23067     /**
23068      * Allow or prevent the user from directly editing the field text.  If false is passed,
23069      * the user will only be able to select from the items defined in the dropdown list.  This method
23070      * is the runtime equivalent of setting the 'editable' config option at config time.
23071      * @param {Boolean} value True to allow the user to directly edit the field text
23072      */
23073     setEditable : function(value){
23074         if(value == this.editable){
23075             return;
23076         }
23077         this.editable = value;
23078         if(!value){
23079             this.el.dom.setAttribute('readOnly', true);
23080             this.el.on('mousedown', this.onTriggerClick,  this);
23081             this.el.addClass('x-combo-noedit');
23082         }else{
23083             this.el.dom.setAttribute('readOnly', false);
23084             this.el.un('mousedown', this.onTriggerClick,  this);
23085             this.el.removeClass('x-combo-noedit');
23086         }
23087     },
23088
23089     // private
23090     onBeforeLoad : function(){
23091         if(!this.hasFocus){
23092             return;
23093         }
23094         this.innerList.update(this.loadingText ?
23095                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
23096         this.restrictHeight();
23097         this.selectedIndex = -1;
23098     },
23099
23100     // private
23101     onLoad : function(){
23102         if(!this.hasFocus){
23103             return;
23104         }
23105         if(this.store.getCount() > 0){
23106             this.expand();
23107             this.restrictHeight();
23108             if(this.lastQuery == this.allQuery){
23109                 if(this.editable){
23110                     this.el.dom.select();
23111                 }
23112                 if(!this.selectByValue(this.value, true)){
23113                     this.select(0, true);
23114                 }
23115             }else{
23116                 this.selectNext();
23117                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
23118                     this.taTask.delay(this.typeAheadDelay);
23119                 }
23120             }
23121         }else{
23122             this.onEmptyResults();
23123         }
23124         //this.el.focus();
23125     },
23126
23127     // private
23128     onTypeAhead : function(){
23129         if(this.store.getCount() > 0){
23130             var r = this.store.getAt(0);
23131             var newValue = r.data[this.displayField];
23132             var len = newValue.length;
23133             var selStart = this.getRawValue().length;
23134             if(selStart != len){
23135                 this.setRawValue(newValue);
23136                 this.selectText(selStart, newValue.length);
23137             }
23138         }
23139     },
23140
23141     // private
23142     onSelect : function(record, index){
23143         if(this.fireEvent('beforeselect', this, record, index) !== false){
23144             this.setFromData(index > -1 ? record.data : false);
23145             this.collapse();
23146             this.fireEvent('select', this, record, index);
23147         }
23148     },
23149
23150     /**
23151      * Returns the currently selected field value or empty string if no value is set.
23152      * @return {String} value The selected value
23153      */
23154     getValue : function(){
23155         if(this.valueField){
23156             return typeof this.value != 'undefined' ? this.value : '';
23157         }else{
23158             return Roo.form.ComboBox.superclass.getValue.call(this);
23159         }
23160     },
23161
23162     /**
23163      * Clears any text/value currently set in the field
23164      */
23165     clearValue : function(){
23166         if(this.hiddenField){
23167             this.hiddenField.value = '';
23168         }
23169         this.value = '';
23170         this.setRawValue('');
23171         this.lastSelectionText = '';
23172         this.applyEmptyText();
23173     },
23174
23175     /**
23176      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
23177      * will be displayed in the field.  If the value does not match the data value of an existing item,
23178      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
23179      * Otherwise the field will be blank (although the value will still be set).
23180      * @param {String} value The value to match
23181      */
23182     setValue : function(v){
23183         var text = v;
23184         if(this.valueField){
23185             var r = this.findRecord(this.valueField, v);
23186             if(r){
23187                 text = r.data[this.displayField];
23188             }else if(this.valueNotFoundText !== undefined){
23189                 text = this.valueNotFoundText;
23190             }
23191         }
23192         this.lastSelectionText = text;
23193         if(this.hiddenField){
23194             this.hiddenField.value = v;
23195         }
23196         Roo.form.ComboBox.superclass.setValue.call(this, text);
23197         this.value = v;
23198     },
23199     /**
23200      * @property {Object} the last set data for the element
23201      */
23202     
23203     lastData : false,
23204     /**
23205      * Sets the value of the field based on a object which is related to the record format for the store.
23206      * @param {Object} value the value to set as. or false on reset?
23207      */
23208     setFromData : function(o){
23209         var dv = ''; // display value
23210         var vv = ''; // value value..
23211         this.lastData = o;
23212         if (this.displayField) {
23213             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
23214         } else {
23215             // this is an error condition!!!
23216             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
23217         }
23218         
23219         if(this.valueField){
23220             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
23221         }
23222         if(this.hiddenField){
23223             this.hiddenField.value = vv;
23224             
23225             this.lastSelectionText = dv;
23226             Roo.form.ComboBox.superclass.setValue.call(this, dv);
23227             this.value = vv;
23228             return;
23229         }
23230         // no hidden field.. - we store the value in 'value', but still display
23231         // display field!!!!
23232         this.lastSelectionText = dv;
23233         Roo.form.ComboBox.superclass.setValue.call(this, dv);
23234         this.value = vv;
23235         
23236         
23237     },
23238     // private
23239     reset : function(){
23240         // overridden so that last data is reset..
23241         this.setValue(this.originalValue);
23242         this.clearInvalid();
23243         this.lastData = false;
23244     },
23245     // private
23246     findRecord : function(prop, value){
23247         var record;
23248         if(this.store.getCount() > 0){
23249             this.store.each(function(r){
23250                 if(r.data[prop] == value){
23251                     record = r;
23252                     return false;
23253                 }
23254             });
23255         }
23256         return record;
23257     },
23258
23259     // private
23260     onViewMove : function(e, t){
23261         this.inKeyMode = false;
23262     },
23263
23264     // private
23265     onViewOver : function(e, t){
23266         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
23267             return;
23268         }
23269         var item = this.view.findItemFromChild(t);
23270         if(item){
23271             var index = this.view.indexOf(item);
23272             this.select(index, false);
23273         }
23274     },
23275
23276     // private
23277     onViewClick : function(doFocus){
23278         var index = this.view.getSelectedIndexes()[0];
23279         var r = this.store.getAt(index);
23280         if(r){
23281             this.onSelect(r, index);
23282         }
23283         if(doFocus !== false && !this.blockFocus){
23284             this.el.focus();
23285         }
23286     },
23287
23288     // private
23289     restrictHeight : function(){
23290         this.innerList.dom.style.height = '';
23291         var inner = this.innerList.dom;
23292         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
23293         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
23294         this.list.beginUpdate();
23295         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
23296         this.list.alignTo(this.el, this.listAlign);
23297         this.list.endUpdate();
23298     },
23299
23300     // private
23301     onEmptyResults : function(){
23302         this.collapse();
23303     },
23304
23305     /**
23306      * Returns true if the dropdown list is expanded, else false.
23307      */
23308     isExpanded : function(){
23309         return this.list.isVisible();
23310     },
23311
23312     /**
23313      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
23314      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23315      * @param {String} value The data value of the item to select
23316      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23317      * selected item if it is not currently in view (defaults to true)
23318      * @return {Boolean} True if the value matched an item in the list, else false
23319      */
23320     selectByValue : function(v, scrollIntoView){
23321         if(v !== undefined && v !== null){
23322             var r = this.findRecord(this.valueField || this.displayField, v);
23323             if(r){
23324                 this.select(this.store.indexOf(r), scrollIntoView);
23325                 return true;
23326             }
23327         }
23328         return false;
23329     },
23330
23331     /**
23332      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
23333      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23334      * @param {Number} index The zero-based index of the list item to select
23335      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23336      * selected item if it is not currently in view (defaults to true)
23337      */
23338     select : function(index, scrollIntoView){
23339         this.selectedIndex = index;
23340         this.view.select(index);
23341         if(scrollIntoView !== false){
23342             var el = this.view.getNode(index);
23343             if(el){
23344                 this.innerList.scrollChildIntoView(el, false);
23345             }
23346         }
23347     },
23348
23349     // private
23350     selectNext : function(){
23351         var ct = this.store.getCount();
23352         if(ct > 0){
23353             if(this.selectedIndex == -1){
23354                 this.select(0);
23355             }else if(this.selectedIndex < ct-1){
23356                 this.select(this.selectedIndex+1);
23357             }
23358         }
23359     },
23360
23361     // private
23362     selectPrev : function(){
23363         var ct = this.store.getCount();
23364         if(ct > 0){
23365             if(this.selectedIndex == -1){
23366                 this.select(0);
23367             }else if(this.selectedIndex != 0){
23368                 this.select(this.selectedIndex-1);
23369             }
23370         }
23371     },
23372
23373     // private
23374     onKeyUp : function(e){
23375         if(this.editable !== false && !e.isSpecialKey()){
23376             this.lastKey = e.getKey();
23377             this.dqTask.delay(this.queryDelay);
23378         }
23379     },
23380
23381     // private
23382     validateBlur : function(){
23383         return !this.list || !this.list.isVisible();   
23384     },
23385
23386     // private
23387     initQuery : function(){
23388         this.doQuery(this.getRawValue());
23389     },
23390
23391     // private
23392     doForce : function(){
23393         if(this.el.dom.value.length > 0){
23394             this.el.dom.value =
23395                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
23396             this.applyEmptyText();
23397         }
23398     },
23399
23400     /**
23401      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
23402      * query allowing the query action to be canceled if needed.
23403      * @param {String} query The SQL query to execute
23404      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
23405      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
23406      * saved in the current store (defaults to false)
23407      */
23408     doQuery : function(q, forceAll){
23409         if(q === undefined || q === null){
23410             q = '';
23411         }
23412         var qe = {
23413             query: q,
23414             forceAll: forceAll,
23415             combo: this,
23416             cancel:false
23417         };
23418         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
23419             return false;
23420         }
23421         q = qe.query;
23422         forceAll = qe.forceAll;
23423         if(forceAll === true || (q.length >= this.minChars)){
23424             if(this.lastQuery != q || this.alwaysQuery){
23425                 this.lastQuery = q;
23426                 if(this.mode == 'local'){
23427                     this.selectedIndex = -1;
23428                     if(forceAll){
23429                         this.store.clearFilter();
23430                     }else{
23431                         this.store.filter(this.displayField, q);
23432                     }
23433                     this.onLoad();
23434                 }else{
23435                     this.store.baseParams[this.queryParam] = q;
23436                     this.store.load({
23437                         params: this.getParams(q)
23438                     });
23439                     this.expand();
23440                 }
23441             }else{
23442                 this.selectedIndex = -1;
23443                 this.onLoad();   
23444             }
23445         }
23446     },
23447
23448     // private
23449     getParams : function(q){
23450         var p = {};
23451         //p[this.queryParam] = q;
23452         if(this.pageSize){
23453             p.start = 0;
23454             p.limit = this.pageSize;
23455         }
23456         return p;
23457     },
23458
23459     /**
23460      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
23461      */
23462     collapse : function(){
23463         if(!this.isExpanded()){
23464             return;
23465         }
23466         this.list.hide();
23467         Roo.get(document).un('mousedown', this.collapseIf, this);
23468         Roo.get(document).un('mousewheel', this.collapseIf, this);
23469         if (!this.editable) {
23470             Roo.get(document).un('keydown', this.listKeyPress, this);
23471         }
23472         this.fireEvent('collapse', this);
23473     },
23474
23475     // private
23476     collapseIf : function(e){
23477         if(!e.within(this.wrap) && !e.within(this.list)){
23478             this.collapse();
23479         }
23480     },
23481
23482     /**
23483      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
23484      */
23485     expand : function(){
23486         if(this.isExpanded() || !this.hasFocus){
23487             return;
23488         }
23489         this.list.alignTo(this.el, this.listAlign);
23490         this.list.show();
23491         Roo.get(document).on('mousedown', this.collapseIf, this);
23492         Roo.get(document).on('mousewheel', this.collapseIf, this);
23493         if (!this.editable) {
23494             Roo.get(document).on('keydown', this.listKeyPress, this);
23495         }
23496         
23497         this.fireEvent('expand', this);
23498     },
23499
23500     // private
23501     // Implements the default empty TriggerField.onTriggerClick function
23502     onTriggerClick : function(){
23503         if(this.disabled){
23504             return;
23505         }
23506         if(this.isExpanded()){
23507             this.collapse();
23508             if (!this.blockFocus) {
23509                 this.el.focus();
23510             }
23511             
23512         }else {
23513             this.hasFocus = true;
23514             if(this.triggerAction == 'all') {
23515                 this.doQuery(this.allQuery, true);
23516             } else {
23517                 this.doQuery(this.getRawValue());
23518             }
23519             if (!this.blockFocus) {
23520                 this.el.focus();
23521             }
23522         }
23523     },
23524     listKeyPress : function(e)
23525     {
23526         //Roo.log('listkeypress');
23527         // scroll to first matching element based on key pres..
23528         if (e.isSpecialKey()) {
23529             return false;
23530         }
23531         var k = String.fromCharCode(e.getKey()).toUpperCase();
23532         //Roo.log(k);
23533         var match  = false;
23534         var csel = this.view.getSelectedNodes();
23535         var cselitem = false;
23536         if (csel.length) {
23537             var ix = this.view.indexOf(csel[0]);
23538             cselitem  = this.store.getAt(ix);
23539             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
23540                 cselitem = false;
23541             }
23542             
23543         }
23544         
23545         this.store.each(function(v) { 
23546             if (cselitem) {
23547                 // start at existing selection.
23548                 if (cselitem.id == v.id) {
23549                     cselitem = false;
23550                 }
23551                 return;
23552             }
23553                 
23554             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
23555                 match = this.store.indexOf(v);
23556                 return false;
23557             }
23558         }, this);
23559         
23560         if (match === false) {
23561             return true; // no more action?
23562         }
23563         // scroll to?
23564         this.view.select(match);
23565         var sn = Roo.get(this.view.getSelectedNodes()[0])
23566         sn.scrollIntoView(sn.dom.parentNode, false);
23567     }
23568
23569     /** 
23570     * @cfg {Boolean} grow 
23571     * @hide 
23572     */
23573     /** 
23574     * @cfg {Number} growMin 
23575     * @hide 
23576     */
23577     /** 
23578     * @cfg {Number} growMax 
23579     * @hide 
23580     */
23581     /**
23582      * @hide
23583      * @method autoSize
23584      */
23585 });/*
23586  * Based on:
23587  * Ext JS Library 1.1.1
23588  * Copyright(c) 2006-2007, Ext JS, LLC.
23589  *
23590  * Originally Released Under LGPL - original licence link has changed is not relivant.
23591  *
23592  * Fork - LGPL
23593  * <script type="text/javascript">
23594  */
23595 /**
23596  * @class Roo.form.Checkbox
23597  * @extends Roo.form.Field
23598  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
23599  * @constructor
23600  * Creates a new Checkbox
23601  * @param {Object} config Configuration options
23602  */
23603 Roo.form.Checkbox = function(config){
23604     Roo.form.Checkbox.superclass.constructor.call(this, config);
23605     this.addEvents({
23606         /**
23607          * @event check
23608          * Fires when the checkbox is checked or unchecked.
23609              * @param {Roo.form.Checkbox} this This checkbox
23610              * @param {Boolean} checked The new checked value
23611              */
23612         check : true
23613     });
23614 };
23615
23616 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
23617     /**
23618      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
23619      */
23620     focusClass : undefined,
23621     /**
23622      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
23623      */
23624     fieldClass: "x-form-field",
23625     /**
23626      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
23627      */
23628     checked: false,
23629     /**
23630      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
23631      * {tag: "input", type: "checkbox", autocomplete: "off"})
23632      */
23633     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
23634     /**
23635      * @cfg {String} boxLabel The text that appears beside the checkbox
23636      */
23637     boxLabel : "",
23638     /**
23639      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
23640      */  
23641     inputValue : '1',
23642     /**
23643      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23644      */
23645      valueOff: '0', // value when not checked..
23646
23647     actionMode : 'viewEl', 
23648     //
23649     // private
23650     itemCls : 'x-menu-check-item x-form-item',
23651     groupClass : 'x-menu-group-item',
23652     inputType : 'hidden',
23653     
23654     
23655     inSetChecked: false, // check that we are not calling self...
23656     
23657     inputElement: false, // real input element?
23658     basedOn: false, // ????
23659     
23660     isFormField: true, // not sure where this is needed!!!!
23661
23662     onResize : function(){
23663         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
23664         if(!this.boxLabel){
23665             this.el.alignTo(this.wrap, 'c-c');
23666         }
23667     },
23668
23669     initEvents : function(){
23670         Roo.form.Checkbox.superclass.initEvents.call(this);
23671         this.el.on("click", this.onClick,  this);
23672         this.el.on("change", this.onClick,  this);
23673     },
23674
23675
23676     getResizeEl : function(){
23677         return this.wrap;
23678     },
23679
23680     getPositionEl : function(){
23681         return this.wrap;
23682     },
23683
23684     // private
23685     onRender : function(ct, position){
23686         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
23687         /*
23688         if(this.inputValue !== undefined){
23689             this.el.dom.value = this.inputValue;
23690         }
23691         */
23692         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
23693         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
23694         var viewEl = this.wrap.createChild({ 
23695             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
23696         this.viewEl = viewEl;   
23697         this.wrap.on('click', this.onClick,  this); 
23698         
23699         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
23700         this.el.on('propertychange', this.setFromHidden,  this);  //ie
23701         
23702         
23703         
23704         if(this.boxLabel){
23705             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
23706         //    viewEl.on('click', this.onClick,  this); 
23707         }
23708         //if(this.checked){
23709             this.setChecked(this.checked);
23710         //}else{
23711             //this.checked = this.el.dom;
23712         //}
23713
23714     },
23715
23716     // private
23717     initValue : Roo.emptyFn,
23718
23719     /**
23720      * Returns the checked state of the checkbox.
23721      * @return {Boolean} True if checked, else false
23722      */
23723     getValue : function(){
23724         if(this.el){
23725             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
23726         }
23727         return this.valueOff;
23728         
23729     },
23730
23731         // private
23732     onClick : function(){ 
23733         this.setChecked(!this.checked);
23734
23735         //if(this.el.dom.checked != this.checked){
23736         //    this.setValue(this.el.dom.checked);
23737        // }
23738     },
23739
23740     /**
23741      * Sets the checked state of the checkbox.
23742      * On is always based on a string comparison between inputValue and the param.
23743      * @param {Boolean/String} value - the value to set 
23744      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
23745      */
23746     setValue : function(v,suppressEvent){
23747         
23748         
23749         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
23750         //if(this.el && this.el.dom){
23751         //    this.el.dom.checked = this.checked;
23752         //    this.el.dom.defaultChecked = this.checked;
23753         //}
23754         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
23755         //this.fireEvent("check", this, this.checked);
23756     },
23757     // private..
23758     setChecked : function(state,suppressEvent)
23759     {
23760         if (this.inSetChecked) {
23761             this.checked = state;
23762             return;
23763         }
23764         
23765     
23766         if(this.wrap){
23767             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
23768         }
23769         this.checked = state;
23770         if(suppressEvent !== true){
23771             this.fireEvent('check', this, state);
23772         }
23773         this.inSetChecked = true;
23774         this.el.dom.value = state ? this.inputValue : this.valueOff;
23775         this.inSetChecked = false;
23776         
23777     },
23778     // handle setting of hidden value by some other method!!?!?
23779     setFromHidden: function()
23780     {
23781         if(!this.el){
23782             return;
23783         }
23784         //console.log("SET FROM HIDDEN");
23785         //alert('setFrom hidden');
23786         this.setValue(this.el.dom.value);
23787     },
23788     
23789     onDestroy : function()
23790     {
23791         if(this.viewEl){
23792             Roo.get(this.viewEl).remove();
23793         }
23794          
23795         Roo.form.Checkbox.superclass.onDestroy.call(this);
23796     }
23797
23798 });/*
23799  * Based on:
23800  * Ext JS Library 1.1.1
23801  * Copyright(c) 2006-2007, Ext JS, LLC.
23802  *
23803  * Originally Released Under LGPL - original licence link has changed is not relivant.
23804  *
23805  * Fork - LGPL
23806  * <script type="text/javascript">
23807  */
23808  
23809 /**
23810  * @class Roo.form.Radio
23811  * @extends Roo.form.Checkbox
23812  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
23813  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
23814  * @constructor
23815  * Creates a new Radio
23816  * @param {Object} config Configuration options
23817  */
23818 Roo.form.Radio = function(){
23819     Roo.form.Radio.superclass.constructor.apply(this, arguments);
23820 };
23821 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
23822     inputType: 'radio',
23823
23824     /**
23825      * If this radio is part of a group, it will return the selected value
23826      * @return {String}
23827      */
23828     getGroupValue : function(){
23829         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
23830     }
23831 });//<script type="text/javascript">
23832
23833 /*
23834  * Ext JS Library 1.1.1
23835  * Copyright(c) 2006-2007, Ext JS, LLC.
23836  * licensing@extjs.com
23837  * 
23838  * http://www.extjs.com/license
23839  */
23840  
23841  /*
23842   * 
23843   * Known bugs:
23844   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
23845   * - IE ? - no idea how much works there.
23846   * 
23847   * 
23848   * 
23849   */
23850  
23851
23852 /**
23853  * @class Ext.form.HtmlEditor
23854  * @extends Ext.form.Field
23855  * Provides a lightweight HTML Editor component.
23856  * WARNING - THIS CURRENTlY ONLY WORKS ON FIREFOX - USE FCKeditor for a cross platform version
23857  * 
23858  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
23859  * supported by this editor.</b><br/><br/>
23860  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
23861  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23862  */
23863 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
23864       /**
23865      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23866      */
23867     toolbars : false,
23868     /**
23869      * @cfg {String} createLinkText The default text for the create link prompt
23870      */
23871     createLinkText : 'Please enter the URL for the link:',
23872     /**
23873      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
23874      */
23875     defaultLinkValue : 'http:/'+'/',
23876    
23877     
23878     // id of frame..
23879     frameId: false,
23880     
23881     // private properties
23882     validationEvent : false,
23883     deferHeight: true,
23884     initialized : false,
23885     activated : false,
23886     sourceEditMode : false,
23887     onFocus : Roo.emptyFn,
23888     iframePad:3,
23889     hideMode:'offsets',
23890     defaultAutoCreate : {
23891         tag: "textarea",
23892         style:"width:500px;height:300px;",
23893         autocomplete: "off"
23894     },
23895
23896     // private
23897     initComponent : function(){
23898         this.addEvents({
23899             /**
23900              * @event initialize
23901              * Fires when the editor is fully initialized (including the iframe)
23902              * @param {HtmlEditor} this
23903              */
23904             initialize: true,
23905             /**
23906              * @event activate
23907              * Fires when the editor is first receives the focus. Any insertion must wait
23908              * until after this event.
23909              * @param {HtmlEditor} this
23910              */
23911             activate: true,
23912              /**
23913              * @event beforesync
23914              * Fires before the textarea is updated with content from the editor iframe. Return false
23915              * to cancel the sync.
23916              * @param {HtmlEditor} this
23917              * @param {String} html
23918              */
23919             beforesync: true,
23920              /**
23921              * @event beforepush
23922              * Fires before the iframe editor is updated with content from the textarea. Return false
23923              * to cancel the push.
23924              * @param {HtmlEditor} this
23925              * @param {String} html
23926              */
23927             beforepush: true,
23928              /**
23929              * @event sync
23930              * Fires when the textarea is updated with content from the editor iframe.
23931              * @param {HtmlEditor} this
23932              * @param {String} html
23933              */
23934             sync: true,
23935              /**
23936              * @event push
23937              * Fires when the iframe editor is updated with content from the textarea.
23938              * @param {HtmlEditor} this
23939              * @param {String} html
23940              */
23941             push: true,
23942              /**
23943              * @event editmodechange
23944              * Fires when the editor switches edit modes
23945              * @param {HtmlEditor} this
23946              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23947              */
23948             editmodechange: true,
23949             /**
23950              * @event editorevent
23951              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23952              * @param {HtmlEditor} this
23953              */
23954             editorevent: true
23955         })
23956     },
23957
23958     /**
23959      * Protected method that will not generally be called directly. It
23960      * is called when the editor creates its toolbar. Override this method if you need to
23961      * add custom toolbar buttons.
23962      * @param {HtmlEditor} editor
23963      */
23964     createToolbar : function(editor){
23965         if (!editor.toolbars || !editor.toolbars.length) {
23966             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
23967         }
23968         
23969         for (var i =0 ; i < editor.toolbars.length;i++) {
23970             editor.toolbars[i] = Roo.factory(editor.toolbars[i], Roo.form.HtmlEditor);
23971             editor.toolbars[i].init(editor);
23972         }
23973          
23974         
23975     },
23976
23977     /**
23978      * Protected method that will not generally be called directly. It
23979      * is called when the editor initializes the iframe with HTML contents. Override this method if you
23980      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
23981      */
23982     getDocMarkup : function(){
23983         return '<html><head><style type="text/css">body{border:0;margin:0;padding:3px;height:98%;cursor:text;}</style></head><body></body></html>';
23984     },
23985
23986     // private
23987     onRender : function(ct, position){
23988         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
23989         this.el.dom.style.border = '0 none';
23990         this.el.dom.setAttribute('tabIndex', -1);
23991         this.el.addClass('x-hidden');
23992         if(Roo.isIE){ // fix IE 1px bogus margin
23993             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
23994         }
23995         this.wrap = this.el.wrap({
23996             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23997         });
23998
23999         this.frameId = Roo.id();
24000         this.createToolbar(this);
24001         
24002         
24003         
24004         
24005       
24006         
24007         var iframe = this.wrap.createChild({
24008             tag: 'iframe',
24009             id: this.frameId,
24010             name: this.frameId,
24011             frameBorder : 'no',
24012             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24013         });
24014         
24015        // console.log(iframe);
24016         //this.wrap.dom.appendChild(iframe);
24017
24018         this.iframe = iframe.dom;
24019
24020          this.assignDocWin();
24021         
24022         this.doc.designMode = 'on';
24023        
24024         this.doc.open();
24025         this.doc.write(this.getDocMarkup());
24026         this.doc.close();
24027
24028         
24029         var task = { // must defer to wait for browser to be ready
24030             run : function(){
24031                 //console.log("run task?" + this.doc.readyState);
24032                 this.assignDocWin();
24033                 if(this.doc.body || this.doc.readyState == 'complete'){
24034                     try {
24035                         this.doc.designMode="on";
24036                     } catch (e) {
24037                         return;
24038                     }
24039                     Roo.TaskMgr.stop(task);
24040                     this.initEditor.defer(10, this);
24041                 }
24042             },
24043             interval : 10,
24044             duration:10000,
24045             scope: this
24046         };
24047         Roo.TaskMgr.start(task);
24048
24049         if(!this.width){
24050             this.setSize(this.el.getSize());
24051         }
24052     },
24053
24054     // private
24055     onResize : function(w, h){
24056         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
24057         if(this.el && this.iframe){
24058             if(typeof w == 'number'){
24059                 var aw = w - this.wrap.getFrameWidth('lr');
24060                 this.el.setWidth(this.adjustWidth('textarea', aw));
24061                 this.iframe.style.width = aw + 'px';
24062             }
24063             if(typeof h == 'number'){
24064                 var tbh = 0;
24065                 for (var i =0; i < this.toolbars.length;i++) {
24066                     // fixme - ask toolbars for heights?
24067                     tbh += this.toolbars[i].tb.el.getHeight();
24068                 }
24069                 
24070                 
24071                 
24072                 
24073                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
24074                 this.el.setHeight(this.adjustWidth('textarea', ah));
24075                 this.iframe.style.height = ah + 'px';
24076                 if(this.doc){
24077                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
24078                 }
24079             }
24080         }
24081     },
24082
24083     /**
24084      * Toggles the editor between standard and source edit mode.
24085      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24086      */
24087     toggleSourceEdit : function(sourceEditMode){
24088         
24089         this.sourceEditMode = sourceEditMode === true;
24090         
24091         if(this.sourceEditMode){
24092           
24093             this.syncValue();
24094             this.iframe.className = 'x-hidden';
24095             this.el.removeClass('x-hidden');
24096             this.el.dom.removeAttribute('tabIndex');
24097             this.el.focus();
24098         }else{
24099              
24100             this.pushValue();
24101             this.iframe.className = '';
24102             this.el.addClass('x-hidden');
24103             this.el.dom.setAttribute('tabIndex', -1);
24104             this.deferFocus();
24105         }
24106         this.setSize(this.wrap.getSize());
24107         this.fireEvent('editmodechange', this, this.sourceEditMode);
24108     },
24109
24110     // private used internally
24111     createLink : function(){
24112         var url = prompt(this.createLinkText, this.defaultLinkValue);
24113         if(url && url != 'http:/'+'/'){
24114             this.relayCmd('createlink', url);
24115         }
24116     },
24117
24118     // private (for BoxComponent)
24119     adjustSize : Roo.BoxComponent.prototype.adjustSize,
24120
24121     // private (for BoxComponent)
24122     getResizeEl : function(){
24123         return this.wrap;
24124     },
24125
24126     // private (for BoxComponent)
24127     getPositionEl : function(){
24128         return this.wrap;
24129     },
24130
24131     // private
24132     initEvents : function(){
24133         this.originalValue = this.getValue();
24134     },
24135
24136     /**
24137      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24138      * @method
24139      */
24140     markInvalid : Roo.emptyFn,
24141     /**
24142      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24143      * @method
24144      */
24145     clearInvalid : Roo.emptyFn,
24146
24147     setValue : function(v){
24148         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
24149         this.pushValue();
24150     },
24151
24152     /**
24153      * Protected method that will not generally be called directly. If you need/want
24154      * custom HTML cleanup, this is the method you should override.
24155      * @param {String} html The HTML to be cleaned
24156      * return {String} The cleaned HTML
24157      */
24158     cleanHtml : function(html){
24159         html = String(html);
24160         if(html.length > 5){
24161             if(Roo.isSafari){ // strip safari nonsense
24162                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24163             }
24164         }
24165         if(html == '&nbsp;'){
24166             html = '';
24167         }
24168         return html;
24169     },
24170
24171     /**
24172      * Protected method that will not generally be called directly. Syncs the contents
24173      * of the editor iframe with the textarea.
24174      */
24175     syncValue : function(){
24176         if(this.initialized){
24177             var bd = (this.doc.body || this.doc.documentElement);
24178             var html = bd.innerHTML;
24179             if(Roo.isSafari){
24180                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24181                 var m = bs.match(/text-align:(.*?);/i);
24182                 if(m && m[1]){
24183                     html = '<div style="'+m[0]+'">' + html + '</div>';
24184                 }
24185             }
24186             html = this.cleanHtml(html);
24187             if(this.fireEvent('beforesync', this, html) !== false){
24188                 this.el.dom.value = html;
24189                 this.fireEvent('sync', this, html);
24190             }
24191         }
24192     },
24193
24194     /**
24195      * Protected method that will not generally be called directly. Pushes the value of the textarea
24196      * into the iframe editor.
24197      */
24198     pushValue : function(){
24199         if(this.initialized){
24200             var v = this.el.dom.value;
24201             if(v.length < 1){
24202                 v = '&#160;';
24203             }
24204             if(this.fireEvent('beforepush', this, v) !== false){
24205                 (this.doc.body || this.doc.documentElement).innerHTML = v;
24206                 this.fireEvent('push', this, v);
24207             }
24208         }
24209     },
24210
24211     // private
24212     deferFocus : function(){
24213         this.focus.defer(10, this);
24214     },
24215
24216     // doc'ed in Field
24217     focus : function(){
24218         if(this.win && !this.sourceEditMode){
24219             this.win.focus();
24220         }else{
24221             this.el.focus();
24222         }
24223     },
24224     
24225     assignDocWin: function()
24226     {
24227         var iframe = this.iframe;
24228         
24229          if(Roo.isIE){
24230             this.doc = iframe.contentWindow.document;
24231             this.win = iframe.contentWindow;
24232         } else {
24233             if (!Roo.get(this.frameId)) {
24234                 return;
24235             }
24236             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24237             this.win = Roo.get(this.frameId).dom.contentWindow;
24238         }
24239     },
24240     
24241     // private
24242     initEditor : function(){
24243         //console.log("INIT EDITOR");
24244         this.assignDocWin();
24245         
24246         
24247         
24248         this.doc.designMode="on";
24249         this.doc.open();
24250         this.doc.write(this.getDocMarkup());
24251         this.doc.close();
24252         
24253         var dbody = (this.doc.body || this.doc.documentElement);
24254         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24255         // this copies styles from the containing element into thsi one..
24256         // not sure why we need all of this..
24257         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24258         ss['background-attachment'] = 'fixed'; // w3c
24259         dbody.bgProperties = 'fixed'; // ie
24260         Roo.DomHelper.applyStyles(dbody, ss);
24261         Roo.EventManager.on(this.doc, {
24262             'mousedown': this.onEditorEvent,
24263             'dblclick': this.onEditorEvent,
24264             'click': this.onEditorEvent,
24265             'keyup': this.onEditorEvent,
24266             buffer:100,
24267             scope: this
24268         });
24269         if(Roo.isGecko){
24270             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24271         }
24272         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24273             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24274         }
24275         this.initialized = true;
24276
24277         this.fireEvent('initialize', this);
24278         this.pushValue();
24279     },
24280
24281     // private
24282     onDestroy : function(){
24283         
24284         
24285         
24286         if(this.rendered){
24287             
24288             for (var i =0; i < this.toolbars.length;i++) {
24289                 // fixme - ask toolbars for heights?
24290                 this.toolbars[i].onDestroy();
24291             }
24292             
24293             this.wrap.dom.innerHTML = '';
24294             this.wrap.remove();
24295         }
24296     },
24297
24298     // private
24299     onFirstFocus : function(){
24300         
24301         this.assignDocWin();
24302         
24303         
24304         this.activated = true;
24305         for (var i =0; i < this.toolbars.length;i++) {
24306             this.toolbars[i].onFirstFocus();
24307         }
24308        
24309         if(Roo.isGecko){ // prevent silly gecko errors
24310             this.win.focus();
24311             var s = this.win.getSelection();
24312             if(!s.focusNode || s.focusNode.nodeType != 3){
24313                 var r = s.getRangeAt(0);
24314                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24315                 r.collapse(true);
24316                 this.deferFocus();
24317             }
24318             try{
24319                 this.execCmd('useCSS', true);
24320                 this.execCmd('styleWithCSS', false);
24321             }catch(e){}
24322         }
24323         this.fireEvent('activate', this);
24324     },
24325
24326     // private
24327     adjustFont: function(btn){
24328         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24329         //if(Roo.isSafari){ // safari
24330         //    adjust *= 2;
24331        // }
24332         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24333         if(Roo.isSafari){ // safari
24334             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24335             v =  (v < 10) ? 10 : v;
24336             v =  (v > 48) ? 48 : v;
24337             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24338             
24339         }
24340         
24341         
24342         v = Math.max(1, v+adjust);
24343         
24344         this.execCmd('FontSize', v  );
24345     },
24346
24347     onEditorEvent : function(e){
24348         this.fireEvent('editorevent', this, e);
24349       //  this.updateToolbar();
24350         this.syncValue();
24351     },
24352
24353     insertTag : function(tg)
24354     {
24355         // could be a bit smarter... -> wrap the current selected tRoo..
24356         
24357         this.execCmd("formatblock",   tg);
24358         
24359     },
24360     
24361     insertText : function(txt)
24362     {
24363         
24364         
24365         range = this.createRange();
24366         range.deleteContents();
24367                //alert(Sender.getAttribute('label'));
24368                
24369         range.insertNode(this.doc.createTextNode(txt));
24370     } ,
24371     
24372     // private
24373     relayBtnCmd : function(btn){
24374         this.relayCmd(btn.cmd);
24375     },
24376
24377     /**
24378      * Executes a Midas editor command on the editor document and performs necessary focus and
24379      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24380      * @param {String} cmd The Midas command
24381      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24382      */
24383     relayCmd : function(cmd, value){
24384         this.win.focus();
24385         this.execCmd(cmd, value);
24386         this.fireEvent('editorevent', this);
24387         //this.updateToolbar();
24388         this.deferFocus();
24389     },
24390
24391     /**
24392      * Executes a Midas editor command directly on the editor document.
24393      * For visual commands, you should use {@link #relayCmd} instead.
24394      * <b>This should only be called after the editor is initialized.</b>
24395      * @param {String} cmd The Midas command
24396      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24397      */
24398     execCmd : function(cmd, value){
24399         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24400         this.syncValue();
24401     },
24402
24403    
24404     /**
24405      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24406      * to insert tRoo.
24407      * @param {String} text
24408      */
24409     insertAtCursor : function(text){
24410         if(!this.activated){
24411             return;
24412         }
24413         if(Roo.isIE){
24414             this.win.focus();
24415             var r = this.doc.selection.createRange();
24416             if(r){
24417                 r.collapse(true);
24418                 r.pasteHTML(text);
24419                 this.syncValue();
24420                 this.deferFocus();
24421             }
24422         }else if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24423             this.win.focus();
24424             this.execCmd('InsertHTML', text);
24425             this.deferFocus();
24426         }
24427     },
24428  // private
24429     mozKeyPress : function(e){
24430         if(e.ctrlKey){
24431             var c = e.getCharCode(), cmd;
24432           
24433             if(c > 0){
24434                 c = String.fromCharCode(c).toLowerCase();
24435                 switch(c){
24436                     case 'b':
24437                         cmd = 'bold';
24438                     break;
24439                     case 'i':
24440                         cmd = 'italic';
24441                     break;
24442                     case 'u':
24443                         cmd = 'underline';
24444                     case 'v':
24445                         this.cleanUpPaste.defer(100, this);
24446                         return;
24447                     break;
24448                 }
24449                 if(cmd){
24450                     this.win.focus();
24451                     this.execCmd(cmd);
24452                     this.deferFocus();
24453                     e.preventDefault();
24454                 }
24455                 
24456             }
24457         }
24458     },
24459
24460     // private
24461     fixKeys : function(){ // load time branching for fastest keydown performance
24462         if(Roo.isIE){
24463             return function(e){
24464                 var k = e.getKey(), r;
24465                 if(k == e.TAB){
24466                     e.stopEvent();
24467                     r = this.doc.selection.createRange();
24468                     if(r){
24469                         r.collapse(true);
24470                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24471                         this.deferFocus();
24472                     }
24473                     return;
24474                 }
24475                 
24476                 if(k == e.ENTER){
24477                     r = this.doc.selection.createRange();
24478                     if(r){
24479                         var target = r.parentElement();
24480                         if(!target || target.tagName.toLowerCase() != 'li'){
24481                             e.stopEvent();
24482                             r.pasteHTML('<br />');
24483                             r.collapse(false);
24484                             r.select();
24485                         }
24486                     }
24487                 }
24488                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24489                     this.cleanUpPaste.defer(100, this);
24490                     return;
24491                 }
24492                 
24493                 
24494             };
24495         }else if(Roo.isOpera){
24496             return function(e){
24497                 var k = e.getKey();
24498                 if(k == e.TAB){
24499                     e.stopEvent();
24500                     this.win.focus();
24501                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24502                     this.deferFocus();
24503                 }
24504                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24505                     this.cleanUpPaste.defer(100, this);
24506                     return;
24507                 }
24508                 
24509             };
24510         }else if(Roo.isSafari){
24511             return function(e){
24512                 var k = e.getKey();
24513                 
24514                 if(k == e.TAB){
24515                     e.stopEvent();
24516                     this.execCmd('InsertText','\t');
24517                     this.deferFocus();
24518                     return;
24519                 }
24520                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24521                     this.cleanUpPaste.defer(100, this);
24522                     return;
24523                 }
24524                 
24525              };
24526         }
24527     }(),
24528     
24529     getAllAncestors: function()
24530     {
24531         var p = this.getSelectedNode();
24532         var a = [];
24533         if (!p) {
24534             a.push(p); // push blank onto stack..
24535             p = this.getParentElement();
24536         }
24537         
24538         
24539         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24540             a.push(p);
24541             p = p.parentNode;
24542         }
24543         a.push(this.doc.body);
24544         return a;
24545     },
24546     lastSel : false,
24547     lastSelNode : false,
24548     
24549     
24550     getSelection : function() 
24551     {
24552         this.assignDocWin();
24553         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24554     },
24555     
24556     getSelectedNode: function() 
24557     {
24558         // this may only work on Gecko!!!
24559         
24560         // should we cache this!!!!
24561         
24562         
24563         
24564          
24565         var range = this.createRange(this.getSelection());
24566         
24567         if (Roo.isIE) {
24568             var parent = range.parentElement();
24569             while (true) {
24570                 var testRange = range.duplicate();
24571                 testRange.moveToElementText(parent);
24572                 if (testRange.inRange(range)) {
24573                     break;
24574                 }
24575                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24576                     break;
24577                 }
24578                 parent = parent.parentElement;
24579             }
24580             return parent;
24581         }
24582         
24583         
24584         var ar = range.endContainer.childNodes;
24585         if (!ar.length) {
24586             ar = range.commonAncestorContainer.childNodes;
24587             //alert(ar.length);
24588         }
24589         var nodes = [];
24590         var other_nodes = [];
24591         var has_other_nodes = false;
24592         for (var i=0;i<ar.length;i++) {
24593             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24594                 continue;
24595             }
24596             // fullly contained node.
24597             
24598             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24599                 nodes.push(ar[i]);
24600                 continue;
24601             }
24602             
24603             // probably selected..
24604             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24605                 other_nodes.push(ar[i]);
24606                 continue;
24607             }
24608             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24609                 continue;
24610             }
24611             
24612             
24613             has_other_nodes = true;
24614         }
24615         if (!nodes.length && other_nodes.length) {
24616             nodes= other_nodes;
24617         }
24618         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24619             return false;
24620         }
24621         
24622         return nodes[0];
24623     },
24624     createRange: function(sel)
24625     {
24626         // this has strange effects when using with 
24627         // top toolbar - not sure if it's a great idea.
24628         //this.editor.contentWindow.focus();
24629         if (typeof sel != "undefined") {
24630             try {
24631                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24632             } catch(e) {
24633                 return this.doc.createRange();
24634             }
24635         } else {
24636             return this.doc.createRange();
24637         }
24638     },
24639     getParentElement: function()
24640     {
24641         
24642         this.assignDocWin();
24643         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24644         
24645         var range = this.createRange(sel);
24646          
24647         try {
24648             var p = range.commonAncestorContainer;
24649             while (p.nodeType == 3) { // text node
24650                 p = p.parentNode;
24651             }
24652             return p;
24653         } catch (e) {
24654             return null;
24655         }
24656     
24657     },
24658     
24659     
24660     
24661     // BC Hacks - cause I cant work out what i was trying to do..
24662     rangeIntersectsNode : function(range, node)
24663     {
24664         var nodeRange = node.ownerDocument.createRange();
24665         try {
24666             nodeRange.selectNode(node);
24667         }
24668         catch (e) {
24669             nodeRange.selectNodeContents(node);
24670         }
24671
24672         return range.compareBoundaryPoints(Range.END_TO_START, nodeRange) == -1 &&
24673                  range.compareBoundaryPoints(Range.START_TO_END, nodeRange) == 1;
24674     },
24675     rangeCompareNode : function(range, node) {
24676         var nodeRange = node.ownerDocument.createRange();
24677         try {
24678             nodeRange.selectNode(node);
24679         } catch (e) {
24680             nodeRange.selectNodeContents(node);
24681         }
24682         var nodeIsBefore = range.compareBoundaryPoints(Range.START_TO_START, nodeRange) == 1;
24683         var nodeIsAfter = range.compareBoundaryPoints(Range.END_TO_END, nodeRange) == -1;
24684
24685         if (nodeIsBefore && !nodeIsAfter)
24686             return 0;
24687         if (!nodeIsBefore && nodeIsAfter)
24688             return 1;
24689         if (nodeIsBefore && nodeIsAfter)
24690             return 2;
24691
24692         return 3;
24693     },
24694
24695     // private? - in a new class?
24696     cleanUpPaste :  function()
24697     {
24698         // cleans up the whole document..
24699       //  console.log('cleanuppaste');
24700         this.cleanUpChildren(this.doc.body)
24701         
24702         
24703     },
24704     cleanUpChildren : function (n)
24705     {
24706         if (!n.childNodes.length) {
24707             return;
24708         }
24709         for (var i = n.childNodes.length-1; i > -1 ; i--) {
24710            this.cleanUpChild(n.childNodes[i]);
24711         }
24712     },
24713     
24714     
24715         
24716     
24717     cleanUpChild : function (node)
24718     {
24719         //console.log(node);
24720         if (node.nodeName == "#text") {
24721             // clean up silly Windows -- stuff?
24722             return; 
24723         }
24724         if (node.nodeName == "#comment") {
24725             node.parentNode.removeChild(node);
24726             // clean up silly Windows -- stuff?
24727             return; 
24728         }
24729         
24730         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
24731             // remove node.
24732             node.parentNode.removeChild(node);
24733             return;
24734             
24735         }
24736         if (!node.attributes || !node.attributes.length) {
24737             this.cleanUpChildren(node);
24738             return;
24739         }
24740         
24741         function cleanAttr(n,v)
24742         {
24743             
24744             if (v.match(/^\./) || v.match(/^\//)) {
24745                 return;
24746             }
24747             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
24748                 return;
24749             }
24750             Roo.log("(REMOVE)"+ node.tagName +'.' + n + '=' + v);
24751             node.removeAttribute(n);
24752             
24753         }
24754         
24755         function cleanStyle(n,v)
24756         {
24757             if (v.match(/expression/)) { //XSS?? should we even bother..
24758                 node.removeAttribute(n);
24759                 return;
24760             }
24761             
24762             
24763             var parts = v.split(/;/);
24764             Roo.each(parts, function(p) {
24765                 p = p.replace(/\s+/g,'');
24766                 if (!p.length) {
24767                     return;
24768                 }
24769                 var l = p.split(':').shift().replace(/\s+/g,'');
24770                 
24771                 if (Roo.form.HtmlEditor.cwhite.indexOf(l) < 0) {
24772                     Roo.log('(REMOVE)' + node.tagName +'.' + n + ':'+l + '=' + v);
24773                     node.removeAttribute(n);
24774                     return false;
24775                 }
24776             });
24777             
24778             
24779         }
24780         
24781         
24782         for (var i = node.attributes.length-1; i > -1 ; i--) {
24783             var a = node.attributes[i];
24784             //console.log(a);
24785             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
24786                 node.removeAttribute(a.name);
24787                 return;
24788             }
24789             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
24790                 cleanAttr(a.name,a.value); // fixme..
24791                 return;
24792             }
24793             if (a.name == 'style') {
24794                 cleanStyle(a.name,a.value);
24795             }
24796             /// clean up MS crap..
24797             if (a.name == 'class') {
24798                 if (a.value.match(/^Mso/)) {
24799                     node.className = '';
24800                 }
24801             }
24802             
24803             // style cleanup!?
24804             // class cleanup?
24805             
24806         }
24807         
24808         
24809         this.cleanUpChildren(node);
24810         
24811         
24812     }
24813     
24814     
24815     // hide stuff that is not compatible
24816     /**
24817      * @event blur
24818      * @hide
24819      */
24820     /**
24821      * @event change
24822      * @hide
24823      */
24824     /**
24825      * @event focus
24826      * @hide
24827      */
24828     /**
24829      * @event specialkey
24830      * @hide
24831      */
24832     /**
24833      * @cfg {String} fieldClass @hide
24834      */
24835     /**
24836      * @cfg {String} focusClass @hide
24837      */
24838     /**
24839      * @cfg {String} autoCreate @hide
24840      */
24841     /**
24842      * @cfg {String} inputType @hide
24843      */
24844     /**
24845      * @cfg {String} invalidClass @hide
24846      */
24847     /**
24848      * @cfg {String} invalidText @hide
24849      */
24850     /**
24851      * @cfg {String} msgFx @hide
24852      */
24853     /**
24854      * @cfg {String} validateOnBlur @hide
24855      */
24856 });
24857
24858 Roo.form.HtmlEditor.white = [
24859         'area', 'br', 'img', 'input', 'hr', 'wbr',
24860         
24861        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
24862        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
24863        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
24864        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
24865        'table',   'ul',         'xmp', 
24866        
24867        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
24868       'thead',   'tr', 
24869      
24870       'dir', 'menu', 'ol', 'ul', 'dl',
24871        
24872       'embed',  'object'
24873 ];
24874
24875
24876 Roo.form.HtmlEditor.black = [
24877     //    'embed',  'object', // enable - backend responsiblity to clean thiese
24878         'applet', // 
24879         'base',   'basefont', 'bgsound', 'blink',  'body', 
24880         'frame',  'frameset', 'head',    'html',   'ilayer', 
24881         'iframe', 'layer',  'link',     'meta',    'object',   
24882         'script', 'style' ,'title',  'xml' // clean later..
24883 ];
24884 Roo.form.HtmlEditor.clean = [
24885     'script', 'style', 'title', 'xml'
24886 ];
24887
24888 // attributes..
24889
24890 Roo.form.HtmlEditor.ablack = [
24891     'on'
24892 ];
24893     
24894 Roo.form.HtmlEditor.aclean = [ 
24895     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
24896 ];
24897
24898 // protocols..
24899 Roo.form.HtmlEditor.pwhite= [
24900         'http',  'https',  'mailto'
24901 ];
24902
24903 Roo.form.HtmlEditor.cwhite= [
24904         'text-align',
24905         'font-size'
24906 ];
24907
24908 // <script type="text/javascript">
24909 /*
24910  * Based on
24911  * Ext JS Library 1.1.1
24912  * Copyright(c) 2006-2007, Ext JS, LLC.
24913  *  
24914  
24915  */
24916
24917 /**
24918  * @class Roo.form.HtmlEditorToolbar1
24919  * Basic Toolbar
24920  * 
24921  * Usage:
24922  *
24923  new Roo.form.HtmlEditor({
24924     ....
24925     toolbars : [
24926         new Roo.form.HtmlEditorToolbar1({
24927             disable : { fonts: 1 , format: 1, ..., ... , ...],
24928             btns : [ .... ]
24929         })
24930     }
24931      
24932  * 
24933  * @cfg {Object} disable List of elements to disable..
24934  * @cfg {Array} btns List of additional buttons.
24935  * 
24936  * 
24937  * NEEDS Extra CSS? 
24938  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
24939  */
24940  
24941 Roo.form.HtmlEditor.ToolbarStandard = function(config)
24942 {
24943     
24944     Roo.apply(this, config);
24945     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
24946     // dont call parent... till later.
24947 }
24948
24949 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
24950     
24951     tb: false,
24952     
24953     rendered: false,
24954     
24955     editor : false,
24956     /**
24957      * @cfg {Object} disable  List of toolbar elements to disable
24958          
24959      */
24960     disable : false,
24961       /**
24962      * @cfg {Array} fontFamilies An array of available font families
24963      */
24964     fontFamilies : [
24965         'Arial',
24966         'Courier New',
24967         'Tahoma',
24968         'Times New Roman',
24969         'Verdana'
24970     ],
24971     
24972     specialChars : [
24973            "&#169;",
24974           "&#174;",     
24975           "&#8482;",    
24976           "&#163;" ,    
24977          // "&#8212;",    
24978           "&#8230;",    
24979           "&#247;" ,    
24980         //  "&#225;" ,     ?? a acute?
24981            "&#8364;"    , //Euro
24982        //   "&#8220;"    ,
24983         //  "&#8221;"    ,
24984         //  "&#8226;"    ,
24985           "&#176;"  //   , // degrees
24986
24987          // "&#233;"     , // e ecute
24988          // "&#250;"     , // u ecute?
24989     ],
24990     inputElements : [ 
24991             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
24992             "input:submit", "input:button", "select", "textarea", "label" ],
24993     formats : [
24994         ["p"] ,  
24995         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
24996         ["pre"],[ "code"], 
24997         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
24998     ],
24999      /**
25000      * @cfg {String} defaultFont default font to use.
25001      */
25002     defaultFont: 'tahoma',
25003    
25004     fontSelect : false,
25005     
25006     
25007     formatCombo : false,
25008     
25009     init : function(editor)
25010     {
25011         this.editor = editor;
25012         
25013         
25014         var fid = editor.frameId;
25015         var etb = this;
25016         function btn(id, toggle, handler){
25017             var xid = fid + '-'+ id ;
25018             return {
25019                 id : xid,
25020                 cmd : id,
25021                 cls : 'x-btn-icon x-edit-'+id,
25022                 enableToggle:toggle !== false,
25023                 scope: editor, // was editor...
25024                 handler:handler||editor.relayBtnCmd,
25025                 clickEvent:'mousedown',
25026                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
25027                 tabIndex:-1
25028             };
25029         }
25030         
25031         
25032         
25033         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
25034         this.tb = tb;
25035          // stop form submits
25036         tb.el.on('click', function(e){
25037             e.preventDefault(); // what does this do?
25038         });
25039
25040         if(!this.disable.font && !Roo.isSafari){
25041             /* why no safari for fonts
25042             editor.fontSelect = tb.el.createChild({
25043                 tag:'select',
25044                 tabIndex: -1,
25045                 cls:'x-font-select',
25046                 html: editor.createFontOptions()
25047             });
25048             editor.fontSelect.on('change', function(){
25049                 var font = editor.fontSelect.dom.value;
25050                 editor.relayCmd('fontname', font);
25051                 editor.deferFocus();
25052             }, editor);
25053             tb.add(
25054                 editor.fontSelect.dom,
25055                 '-'
25056             );
25057             */
25058         };
25059         if(!this.disable.formats){
25060             this.formatCombo = new Roo.form.ComboBox({
25061                 store: new Roo.data.SimpleStore({
25062                     id : 'tag',
25063                     fields: ['tag'],
25064                     data : this.formats // from states.js
25065                 }),
25066                 blockFocus : true,
25067                 //autoCreate : {tag: "div",  size: "20"},
25068                 displayField:'tag',
25069                 typeAhead: false,
25070                 mode: 'local',
25071                 editable : false,
25072                 triggerAction: 'all',
25073                 emptyText:'Add tag',
25074                 selectOnFocus:true,
25075                 width:135,
25076                 listeners : {
25077                     'select': function(c, r, i) {
25078                         editor.insertTag(r.get('tag'));
25079                         editor.focus();
25080                     }
25081                 }
25082
25083             });
25084             tb.addField(this.formatCombo);
25085             
25086         }
25087         
25088         if(!this.disable.format){
25089             tb.add(
25090                 btn('bold'),
25091                 btn('italic'),
25092                 btn('underline')
25093             );
25094         };
25095         if(!this.disable.fontSize){
25096             tb.add(
25097                 '-',
25098                 
25099                 
25100                 btn('increasefontsize', false, editor.adjustFont),
25101                 btn('decreasefontsize', false, editor.adjustFont)
25102             );
25103         };
25104         
25105         
25106         if(this.disable.colors){
25107             tb.add(
25108                 '-', {
25109                     id:editor.frameId +'-forecolor',
25110                     cls:'x-btn-icon x-edit-forecolor',
25111                     clickEvent:'mousedown',
25112                     tooltip: this.buttonTips['forecolor'] || undefined,
25113                     tabIndex:-1,
25114                     menu : new Roo.menu.ColorMenu({
25115                         allowReselect: true,
25116                         focus: Roo.emptyFn,
25117                         value:'000000',
25118                         plain:true,
25119                         selectHandler: function(cp, color){
25120                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
25121                             editor.deferFocus();
25122                         },
25123                         scope: editor,
25124                         clickEvent:'mousedown'
25125                     })
25126                 }, {
25127                     id:editor.frameId +'backcolor',
25128                     cls:'x-btn-icon x-edit-backcolor',
25129                     clickEvent:'mousedown',
25130                     tooltip: this.buttonTips['backcolor'] || undefined,
25131                     tabIndex:-1,
25132                     menu : new Roo.menu.ColorMenu({
25133                         focus: Roo.emptyFn,
25134                         value:'FFFFFF',
25135                         plain:true,
25136                         allowReselect: true,
25137                         selectHandler: function(cp, color){
25138                             if(Roo.isGecko){
25139                                 editor.execCmd('useCSS', false);
25140                                 editor.execCmd('hilitecolor', color);
25141                                 editor.execCmd('useCSS', true);
25142                                 editor.deferFocus();
25143                             }else{
25144                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
25145                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
25146                                 editor.deferFocus();
25147                             }
25148                         },
25149                         scope:editor,
25150                         clickEvent:'mousedown'
25151                     })
25152                 }
25153             );
25154         };
25155         // now add all the items...
25156         
25157
25158         if(!this.disable.alignments){
25159             tb.add(
25160                 '-',
25161                 btn('justifyleft'),
25162                 btn('justifycenter'),
25163                 btn('justifyright')
25164             );
25165         };
25166
25167         //if(!Roo.isSafari){
25168             if(!this.disable.links){
25169                 tb.add(
25170                     '-',
25171                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
25172                 );
25173             };
25174
25175             if(!this.disable.lists){
25176                 tb.add(
25177                     '-',
25178                     btn('insertorderedlist'),
25179                     btn('insertunorderedlist')
25180                 );
25181             }
25182             if(!this.disable.sourceEdit){
25183                 tb.add(
25184                     '-',
25185                     btn('sourceedit', true, function(btn){
25186                         this.toggleSourceEdit(btn.pressed);
25187                     })
25188                 );
25189             }
25190         //}
25191         
25192         var smenu = { };
25193         // special menu.. - needs to be tidied up..
25194         if (!this.disable.special) {
25195             smenu = {
25196                 text: "&#169;",
25197                 cls: 'x-edit-none',
25198                 menu : {
25199                     items : []
25200                    }
25201             };
25202             for (var i =0; i < this.specialChars.length; i++) {
25203                 smenu.menu.items.push({
25204                     
25205                     html: this.specialChars[i],
25206                     handler: function(a,b) {
25207                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
25208                         
25209                     },
25210                     tabIndex:-1
25211                 });
25212             }
25213             
25214             
25215             tb.add(smenu);
25216             
25217             
25218         }
25219         if (this.btns) {
25220             for(var i =0; i< this.btns.length;i++) {
25221                 var b = this.btns[i];
25222                 b.cls =  'x-edit-none';
25223                 b.scope = editor;
25224                 tb.add(b);
25225             }
25226         
25227         }
25228         
25229         
25230         
25231         // disable everything...
25232         
25233         this.tb.items.each(function(item){
25234            if(item.id != editor.frameId+ '-sourceedit'){
25235                 item.disable();
25236             }
25237         });
25238         this.rendered = true;
25239         
25240         // the all the btns;
25241         editor.on('editorevent', this.updateToolbar, this);
25242         // other toolbars need to implement this..
25243         //editor.on('editmodechange', this.updateToolbar, this);
25244     },
25245     
25246     
25247     
25248     /**
25249      * Protected method that will not generally be called directly. It triggers
25250      * a toolbar update by reading the markup state of the current selection in the editor.
25251      */
25252     updateToolbar: function(){
25253
25254         if(!this.editor.activated){
25255             this.editor.onFirstFocus();
25256             return;
25257         }
25258
25259         var btns = this.tb.items.map, 
25260             doc = this.editor.doc,
25261             frameId = this.editor.frameId;
25262
25263         if(!this.disable.font && !Roo.isSafari){
25264             /*
25265             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
25266             if(name != this.fontSelect.dom.value){
25267                 this.fontSelect.dom.value = name;
25268             }
25269             */
25270         }
25271         if(!this.disable.format){
25272             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
25273             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
25274             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
25275         }
25276         if(!this.disable.alignments){
25277             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
25278             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
25279             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
25280         }
25281         if(!Roo.isSafari && !this.disable.lists){
25282             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
25283             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
25284         }
25285         
25286         var ans = this.editor.getAllAncestors();
25287         if (this.formatCombo) {
25288             
25289             
25290             var store = this.formatCombo.store;
25291             this.formatCombo.setValue("");
25292             for (var i =0; i < ans.length;i++) {
25293                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
25294                     // select it..
25295                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
25296                     break;
25297                 }
25298             }
25299         }
25300         
25301         
25302         
25303         // hides menus... - so this cant be on a menu...
25304         Roo.menu.MenuMgr.hideAll();
25305
25306         //this.editorsyncValue();
25307     },
25308    
25309     
25310     createFontOptions : function(){
25311         var buf = [], fs = this.fontFamilies, ff, lc;
25312         for(var i = 0, len = fs.length; i< len; i++){
25313             ff = fs[i];
25314             lc = ff.toLowerCase();
25315             buf.push(
25316                 '<option value="',lc,'" style="font-family:',ff,';"',
25317                     (this.defaultFont == lc ? ' selected="true">' : '>'),
25318                     ff,
25319                 '</option>'
25320             );
25321         }
25322         return buf.join('');
25323     },
25324     
25325     toggleSourceEdit : function(sourceEditMode){
25326         if(sourceEditMode === undefined){
25327             sourceEditMode = !this.sourceEditMode;
25328         }
25329         this.sourceEditMode = sourceEditMode === true;
25330         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
25331         // just toggle the button?
25332         if(btn.pressed !== this.editor.sourceEditMode){
25333             btn.toggle(this.editor.sourceEditMode);
25334             return;
25335         }
25336         
25337         if(this.sourceEditMode){
25338             this.tb.items.each(function(item){
25339                 if(item.cmd != 'sourceedit'){
25340                     item.disable();
25341                 }
25342             });
25343           
25344         }else{
25345             if(this.initialized){
25346                 this.tb.items.each(function(item){
25347                     item.enable();
25348                 });
25349             }
25350             
25351         }
25352         // tell the editor that it's been pressed..
25353         this.editor.toggleSourceEdit(sourceEditMode);
25354        
25355     },
25356      /**
25357      * Object collection of toolbar tooltips for the buttons in the editor. The key
25358      * is the command id associated with that button and the value is a valid QuickTips object.
25359      * For example:
25360 <pre><code>
25361 {
25362     bold : {
25363         title: 'Bold (Ctrl+B)',
25364         text: 'Make the selected text bold.',
25365         cls: 'x-html-editor-tip'
25366     },
25367     italic : {
25368         title: 'Italic (Ctrl+I)',
25369         text: 'Make the selected text italic.',
25370         cls: 'x-html-editor-tip'
25371     },
25372     ...
25373 </code></pre>
25374     * @type Object
25375      */
25376     buttonTips : {
25377         bold : {
25378             title: 'Bold (Ctrl+B)',
25379             text: 'Make the selected text bold.',
25380             cls: 'x-html-editor-tip'
25381         },
25382         italic : {
25383             title: 'Italic (Ctrl+I)',
25384             text: 'Make the selected text italic.',
25385             cls: 'x-html-editor-tip'
25386         },
25387         underline : {
25388             title: 'Underline (Ctrl+U)',
25389             text: 'Underline the selected text.',
25390             cls: 'x-html-editor-tip'
25391         },
25392         increasefontsize : {
25393             title: 'Grow Text',
25394             text: 'Increase the font size.',
25395             cls: 'x-html-editor-tip'
25396         },
25397         decreasefontsize : {
25398             title: 'Shrink Text',
25399             text: 'Decrease the font size.',
25400             cls: 'x-html-editor-tip'
25401         },
25402         backcolor : {
25403             title: 'Text Highlight Color',
25404             text: 'Change the background color of the selected text.',
25405             cls: 'x-html-editor-tip'
25406         },
25407         forecolor : {
25408             title: 'Font Color',
25409             text: 'Change the color of the selected text.',
25410             cls: 'x-html-editor-tip'
25411         },
25412         justifyleft : {
25413             title: 'Align Text Left',
25414             text: 'Align text to the left.',
25415             cls: 'x-html-editor-tip'
25416         },
25417         justifycenter : {
25418             title: 'Center Text',
25419             text: 'Center text in the editor.',
25420             cls: 'x-html-editor-tip'
25421         },
25422         justifyright : {
25423             title: 'Align Text Right',
25424             text: 'Align text to the right.',
25425             cls: 'x-html-editor-tip'
25426         },
25427         insertunorderedlist : {
25428             title: 'Bullet List',
25429             text: 'Start a bulleted list.',
25430             cls: 'x-html-editor-tip'
25431         },
25432         insertorderedlist : {
25433             title: 'Numbered List',
25434             text: 'Start a numbered list.',
25435             cls: 'x-html-editor-tip'
25436         },
25437         createlink : {
25438             title: 'Hyperlink',
25439             text: 'Make the selected text a hyperlink.',
25440             cls: 'x-html-editor-tip'
25441         },
25442         sourceedit : {
25443             title: 'Source Edit',
25444             text: 'Switch to source editing mode.',
25445             cls: 'x-html-editor-tip'
25446         }
25447     },
25448     // private
25449     onDestroy : function(){
25450         if(this.rendered){
25451             
25452             this.tb.items.each(function(item){
25453                 if(item.menu){
25454                     item.menu.removeAll();
25455                     if(item.menu.el){
25456                         item.menu.el.destroy();
25457                     }
25458                 }
25459                 item.destroy();
25460             });
25461              
25462         }
25463     },
25464     onFirstFocus: function() {
25465         this.tb.items.each(function(item){
25466            item.enable();
25467         });
25468     }
25469 });
25470
25471
25472
25473
25474 // <script type="text/javascript">
25475 /*
25476  * Based on
25477  * Ext JS Library 1.1.1
25478  * Copyright(c) 2006-2007, Ext JS, LLC.
25479  *  
25480  
25481  */
25482
25483  
25484 /**
25485  * @class Roo.form.HtmlEditor.ToolbarContext
25486  * Context Toolbar
25487  * 
25488  * Usage:
25489  *
25490  new Roo.form.HtmlEditor({
25491     ....
25492     toolbars : [
25493         new Roo.form.HtmlEditor.ToolbarStandard(),
25494         new Roo.form.HtmlEditor.ToolbarContext()
25495         })
25496     }
25497      
25498  * 
25499  * @config : {Object} disable List of elements to disable.. (not done yet.)
25500  * 
25501  * 
25502  */
25503
25504 Roo.form.HtmlEditor.ToolbarContext = function(config)
25505 {
25506     
25507     Roo.apply(this, config);
25508     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
25509     // dont call parent... till later.
25510 }
25511 Roo.form.HtmlEditor.ToolbarContext.types = {
25512     'IMG' : {
25513         width : {
25514             title: "Width",
25515             width: 40
25516         },
25517         height:  {
25518             title: "Height",
25519             width: 40
25520         },
25521         align: {
25522             title: "Align",
25523             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
25524             width : 80
25525             
25526         },
25527         border: {
25528             title: "Border",
25529             width: 40
25530         },
25531         alt: {
25532             title: "Alt",
25533             width: 120
25534         },
25535         src : {
25536             title: "Src",
25537             width: 220
25538         }
25539         
25540     },
25541     'A' : {
25542         name : {
25543             title: "Name",
25544             width: 50
25545         },
25546         href:  {
25547             title: "Href",
25548             width: 220
25549         } // border?
25550         
25551     },
25552     'TABLE' : {
25553         rows : {
25554             title: "Rows",
25555             width: 20
25556         },
25557         cols : {
25558             title: "Cols",
25559             width: 20
25560         },
25561         width : {
25562             title: "Width",
25563             width: 40
25564         },
25565         height : {
25566             title: "Height",
25567             width: 40
25568         },
25569         border : {
25570             title: "Border",
25571             width: 20
25572         }
25573     },
25574     'TD' : {
25575         width : {
25576             title: "Width",
25577             width: 40
25578         },
25579         height : {
25580             title: "Height",
25581             width: 40
25582         },   
25583         align: {
25584             title: "Align",
25585             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
25586             width: 40
25587         },
25588         valign: {
25589             title: "Valign",
25590             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
25591             width: 40
25592         },
25593         colspan: {
25594             title: "Colspan",
25595             width: 20
25596             
25597         }
25598     },
25599     'INPUT' : {
25600         name : {
25601             title: "name",
25602             width: 120
25603         },
25604         value : {
25605             title: "Value",
25606             width: 120
25607         },
25608         width : {
25609             title: "Width",
25610             width: 40
25611         }
25612     },
25613     'LABEL' : {
25614         'for' : {
25615             title: "For",
25616             width: 120
25617         }
25618     },
25619     'TEXTAREA' : {
25620           name : {
25621             title: "name",
25622             width: 120
25623         },
25624         rows : {
25625             title: "Rows",
25626             width: 20
25627         },
25628         cols : {
25629             title: "Cols",
25630             width: 20
25631         }
25632     },
25633     'SELECT' : {
25634         name : {
25635             title: "name",
25636             width: 120
25637         },
25638         selectoptions : {
25639             title: "Options",
25640             width: 200
25641         }
25642     },
25643     'BODY' : {
25644         title : {
25645             title: "title",
25646             width: 120,
25647             disabled : true
25648         }
25649     }
25650 };
25651
25652
25653
25654 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
25655     
25656     tb: false,
25657     
25658     rendered: false,
25659     
25660     editor : false,
25661     /**
25662      * @cfg {Object} disable  List of toolbar elements to disable
25663          
25664      */
25665     disable : false,
25666     
25667     
25668     
25669     toolbars : false,
25670     
25671     init : function(editor)
25672     {
25673         this.editor = editor;
25674         
25675         
25676         var fid = editor.frameId;
25677         var etb = this;
25678         function btn(id, toggle, handler){
25679             var xid = fid + '-'+ id ;
25680             return {
25681                 id : xid,
25682                 cmd : id,
25683                 cls : 'x-btn-icon x-edit-'+id,
25684                 enableToggle:toggle !== false,
25685                 scope: editor, // was editor...
25686                 handler:handler||editor.relayBtnCmd,
25687                 clickEvent:'mousedown',
25688                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
25689                 tabIndex:-1
25690             };
25691         }
25692         // create a new element.
25693         var wdiv = editor.wrap.createChild({
25694                 tag: 'div'
25695             }, editor.wrap.dom.firstChild.nextSibling, true);
25696         
25697         // can we do this more than once??
25698         
25699          // stop form submits
25700       
25701  
25702         // disable everything...
25703         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
25704         this.toolbars = {};
25705            
25706         for (var i in  ty) {
25707           
25708             this.toolbars[i] = this.buildToolbar(ty[i],i);
25709         }
25710         this.tb = this.toolbars.BODY;
25711         this.tb.el.show();
25712         
25713          
25714         this.rendered = true;
25715         
25716         // the all the btns;
25717         editor.on('editorevent', this.updateToolbar, this);
25718         // other toolbars need to implement this..
25719         //editor.on('editmodechange', this.updateToolbar, this);
25720     },
25721     
25722     
25723     
25724     /**
25725      * Protected method that will not generally be called directly. It triggers
25726      * a toolbar update by reading the markup state of the current selection in the editor.
25727      */
25728     updateToolbar: function(){
25729
25730         if(!this.editor.activated){
25731             this.editor.onFirstFocus();
25732             return;
25733         }
25734
25735         
25736         var ans = this.editor.getAllAncestors();
25737         
25738         // pick
25739         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
25740         var sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
25741         sel = sel ? sel : this.editor.doc.body;
25742         sel = sel.tagName.length ? sel : this.editor.doc.body;
25743         var tn = sel.tagName.toUpperCase();
25744         sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
25745         tn = sel.tagName.toUpperCase();
25746         if (this.tb.name  == tn) {
25747             return; // no change
25748         }
25749         this.tb.el.hide();
25750         ///console.log("show: " + tn);
25751         this.tb =  this.toolbars[tn];
25752         this.tb.el.show();
25753         this.tb.fields.each(function(e) {
25754             e.setValue(sel.getAttribute(e.name));
25755         });
25756         this.tb.selectedNode = sel;
25757         
25758         
25759         Roo.menu.MenuMgr.hideAll();
25760
25761         //this.editorsyncValue();
25762     },
25763    
25764        
25765     // private
25766     onDestroy : function(){
25767         if(this.rendered){
25768             
25769             this.tb.items.each(function(item){
25770                 if(item.menu){
25771                     item.menu.removeAll();
25772                     if(item.menu.el){
25773                         item.menu.el.destroy();
25774                     }
25775                 }
25776                 item.destroy();
25777             });
25778              
25779         }
25780     },
25781     onFirstFocus: function() {
25782         // need to do this for all the toolbars..
25783         this.tb.items.each(function(item){
25784            item.enable();
25785         });
25786     },
25787     buildToolbar: function(tlist, nm)
25788     {
25789         var editor = this.editor;
25790          // create a new element.
25791         var wdiv = editor.wrap.createChild({
25792                 tag: 'div'
25793             }, editor.wrap.dom.firstChild.nextSibling, true);
25794         
25795        
25796         var tb = new Roo.Toolbar(wdiv);
25797         tb.add(nm+ ":&nbsp;");
25798         for (var i in tlist) {
25799             var item = tlist[i];
25800             tb.add(item.title + ":&nbsp;");
25801             if (item.opts) {
25802                 // fixme
25803                 
25804               
25805                 tb.addField( new Roo.form.ComboBox({
25806                     store: new Roo.data.SimpleStore({
25807                         id : 'val',
25808                         fields: ['val'],
25809                         data : item.opts // from states.js
25810                     }),
25811                     name : i,
25812                     displayField:'val',
25813                     typeAhead: false,
25814                     mode: 'local',
25815                     editable : false,
25816                     triggerAction: 'all',
25817                     emptyText:'Select',
25818                     selectOnFocus:true,
25819                     width: item.width ? item.width  : 130,
25820                     listeners : {
25821                         'select': function(c, r, i) {
25822                             tb.selectedNode.setAttribute(c.name, r.get('val'));
25823                         }
25824                     }
25825
25826                 }));
25827                 continue;
25828                     
25829                 
25830                 
25831                 
25832                 
25833                 tb.addField( new Roo.form.TextField({
25834                     name: i,
25835                     width: 100,
25836                     //allowBlank:false,
25837                     value: ''
25838                 }));
25839                 continue;
25840             }
25841             tb.addField( new Roo.form.TextField({
25842                 name: i,
25843                 width: item.width,
25844                 //allowBlank:true,
25845                 value: '',
25846                 listeners: {
25847                     'change' : function(f, nv, ov) {
25848                         tb.selectedNode.setAttribute(f.name, nv);
25849                     }
25850                 }
25851             }));
25852              
25853         }
25854         tb.el.on('click', function(e){
25855             e.preventDefault(); // what does this do?
25856         });
25857         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
25858         tb.el.hide();
25859         tb.name = nm;
25860         // dont need to disable them... as they will get hidden
25861         return tb;
25862          
25863         
25864     }
25865     
25866     
25867     
25868     
25869 });
25870
25871
25872
25873
25874
25875 /*
25876  * Based on:
25877  * Ext JS Library 1.1.1
25878  * Copyright(c) 2006-2007, Ext JS, LLC.
25879  *
25880  * Originally Released Under LGPL - original licence link has changed is not relivant.
25881  *
25882  * Fork - LGPL
25883  * <script type="text/javascript">
25884  */
25885  
25886 /**
25887  * @class Roo.form.BasicForm
25888  * @extends Roo.util.Observable
25889  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
25890  * @constructor
25891  * @param {String/HTMLElement/Roo.Element} el The form element or its id
25892  * @param {Object} config Configuration options
25893  */
25894 Roo.form.BasicForm = function(el, config){
25895     this.allItems = [];
25896     this.childForms = [];
25897     Roo.apply(this, config);
25898     /*
25899      * The Roo.form.Field items in this form.
25900      * @type MixedCollection
25901      */
25902      
25903      
25904     this.items = new Roo.util.MixedCollection(false, function(o){
25905         return o.id || (o.id = Roo.id());
25906     });
25907     this.addEvents({
25908         /**
25909          * @event beforeaction
25910          * Fires before any action is performed. Return false to cancel the action.
25911          * @param {Form} this
25912          * @param {Action} action The action to be performed
25913          */
25914         beforeaction: true,
25915         /**
25916          * @event actionfailed
25917          * Fires when an action fails.
25918          * @param {Form} this
25919          * @param {Action} action The action that failed
25920          */
25921         actionfailed : true,
25922         /**
25923          * @event actioncomplete
25924          * Fires when an action is completed.
25925          * @param {Form} this
25926          * @param {Action} action The action that completed
25927          */
25928         actioncomplete : true
25929     });
25930     if(el){
25931         this.initEl(el);
25932     }
25933     Roo.form.BasicForm.superclass.constructor.call(this);
25934 };
25935
25936 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
25937     /**
25938      * @cfg {String} method
25939      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
25940      */
25941     /**
25942      * @cfg {DataReader} reader
25943      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
25944      * This is optional as there is built-in support for processing JSON.
25945      */
25946     /**
25947      * @cfg {DataReader} errorReader
25948      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
25949      * This is completely optional as there is built-in support for processing JSON.
25950      */
25951     /**
25952      * @cfg {String} url
25953      * The URL to use for form actions if one isn't supplied in the action options.
25954      */
25955     /**
25956      * @cfg {Boolean} fileUpload
25957      * Set to true if this form is a file upload.
25958      */
25959     /**
25960      * @cfg {Object} baseParams
25961      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
25962      */
25963     /**
25964      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
25965      */
25966     timeout: 30,
25967
25968     // private
25969     activeAction : null,
25970
25971     /**
25972      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
25973      * or setValues() data instead of when the form was first created.
25974      */
25975     trackResetOnLoad : false,
25976     
25977     
25978     /**
25979      * childForms - used for multi-tab forms
25980      * @type {Array}
25981      */
25982     childForms : false,
25983     
25984     /**
25985      * allItems - full list of fields.
25986      * @type {Array}
25987      */
25988     allItems : false,
25989     
25990     /**
25991      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
25992      * element by passing it or its id or mask the form itself by passing in true.
25993      * @type Mixed
25994      */
25995     waitMsgTarget : undefined,
25996
25997     // private
25998     initEl : function(el){
25999         this.el = Roo.get(el);
26000         this.id = this.el.id || Roo.id();
26001         this.el.on('submit', this.onSubmit, this);
26002         this.el.addClass('x-form');
26003     },
26004
26005     // private
26006     onSubmit : function(e){
26007         e.stopEvent();
26008     },
26009
26010     /**
26011      * Returns true if client-side validation on the form is successful.
26012      * @return Boolean
26013      */
26014     isValid : function(){
26015         var valid = true;
26016         this.items.each(function(f){
26017            if(!f.validate()){
26018                valid = false;
26019            }
26020         });
26021         return valid;
26022     },
26023
26024     /**
26025      * Returns true if any fields in this form have changed since their original load.
26026      * @return Boolean
26027      */
26028     isDirty : function(){
26029         var dirty = false;
26030         this.items.each(function(f){
26031            if(f.isDirty()){
26032                dirty = true;
26033                return false;
26034            }
26035         });
26036         return dirty;
26037     },
26038
26039     /**
26040      * Performs a predefined action (submit or load) or custom actions you define on this form.
26041      * @param {String} actionName The name of the action type
26042      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
26043      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
26044      * accept other config options):
26045      * <pre>
26046 Property          Type             Description
26047 ----------------  ---------------  ----------------------------------------------------------------------------------
26048 url               String           The url for the action (defaults to the form's url)
26049 method            String           The form method to use (defaults to the form's method, or POST if not defined)
26050 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
26051 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
26052                                    validate the form on the client (defaults to false)
26053      * </pre>
26054      * @return {BasicForm} this
26055      */
26056     doAction : function(action, options){
26057         if(typeof action == 'string'){
26058             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
26059         }
26060         if(this.fireEvent('beforeaction', this, action) !== false){
26061             this.beforeAction(action);
26062             action.run.defer(100, action);
26063         }
26064         return this;
26065     },
26066
26067     /**
26068      * Shortcut to do a submit action.
26069      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
26070      * @return {BasicForm} this
26071      */
26072     submit : function(options){
26073         this.doAction('submit', options);
26074         return this;
26075     },
26076
26077     /**
26078      * Shortcut to do a load action.
26079      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
26080      * @return {BasicForm} this
26081      */
26082     load : function(options){
26083         this.doAction('load', options);
26084         return this;
26085     },
26086
26087     /**
26088      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
26089      * @param {Record} record The record to edit
26090      * @return {BasicForm} this
26091      */
26092     updateRecord : function(record){
26093         record.beginEdit();
26094         var fs = record.fields;
26095         fs.each(function(f){
26096             var field = this.findField(f.name);
26097             if(field){
26098                 record.set(f.name, field.getValue());
26099             }
26100         }, this);
26101         record.endEdit();
26102         return this;
26103     },
26104
26105     /**
26106      * Loads an Roo.data.Record into this form.
26107      * @param {Record} record The record to load
26108      * @return {BasicForm} this
26109      */
26110     loadRecord : function(record){
26111         this.setValues(record.data);
26112         return this;
26113     },
26114
26115     // private
26116     beforeAction : function(action){
26117         var o = action.options;
26118         if(o.waitMsg){
26119             if(this.waitMsgTarget === true){
26120                 this.el.mask(o.waitMsg, 'x-mask-loading');
26121             }else if(this.waitMsgTarget){
26122                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
26123                 this.waitMsgTarget.mask(o.waitMsg, 'x-mask-loading');
26124             }else{
26125                 Roo.MessageBox.wait(o.waitMsg, o.waitTitle || this.waitTitle || 'Please Wait...');
26126             }
26127         }
26128     },
26129
26130     // private
26131     afterAction : function(action, success){
26132         this.activeAction = null;
26133         var o = action.options;
26134         if(o.waitMsg){
26135             if(this.waitMsgTarget === true){
26136                 this.el.unmask();
26137             }else if(this.waitMsgTarget){
26138                 this.waitMsgTarget.unmask();
26139             }else{
26140                 Roo.MessageBox.updateProgress(1);
26141                 Roo.MessageBox.hide();
26142             }
26143         }
26144         if(success){
26145             if(o.reset){
26146                 this.reset();
26147             }
26148             Roo.callback(o.success, o.scope, [this, action]);
26149             this.fireEvent('actioncomplete', this, action);
26150         }else{
26151             Roo.callback(o.failure, o.scope, [this, action]);
26152             this.fireEvent('actionfailed', this, action);
26153         }
26154     },
26155
26156     /**
26157      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
26158      * @param {String} id The value to search for
26159      * @return Field
26160      */
26161     findField : function(id){
26162         var field = this.items.get(id);
26163         if(!field){
26164             this.items.each(function(f){
26165                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
26166                     field = f;
26167                     return false;
26168                 }
26169             });
26170         }
26171         return field || null;
26172     },
26173
26174     /**
26175      * Add a secondary form to this one, 
26176      * Used to provide tabbed forms. One form is primary, with hidden values 
26177      * which mirror the elements from the other forms.
26178      * 
26179      * @param {Roo.form.Form} form to add.
26180      * 
26181      */
26182     addForm : function(form)
26183     {
26184        
26185         if (this.childForms.indexOf(form) > -1) {
26186             // already added..
26187             return;
26188         }
26189         this.childForms.push(form);
26190         var n = '';
26191         Roo.each(form.allItems, function (fe) {
26192             
26193             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
26194             if (this.findField(n)) { // already added..
26195                 return;
26196             }
26197             var add = new Roo.form.Hidden({
26198                 name : n
26199             });
26200             add.render(this.el);
26201             
26202             this.add( add );
26203         }, this);
26204         
26205     },
26206     /**
26207      * Mark fields in this form invalid in bulk.
26208      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
26209      * @return {BasicForm} this
26210      */
26211     markInvalid : function(errors){
26212         if(errors instanceof Array){
26213             for(var i = 0, len = errors.length; i < len; i++){
26214                 var fieldError = errors[i];
26215                 var f = this.findField(fieldError.id);
26216                 if(f){
26217                     f.markInvalid(fieldError.msg);
26218                 }
26219             }
26220         }else{
26221             var field, id;
26222             for(id in errors){
26223                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
26224                     field.markInvalid(errors[id]);
26225                 }
26226             }
26227         }
26228         Roo.each(this.childForms || [], function (f) {
26229             f.markInvalid(errors);
26230         });
26231         
26232         return this;
26233     },
26234
26235     /**
26236      * Set values for fields in this form in bulk.
26237      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
26238      * @return {BasicForm} this
26239      */
26240     setValues : function(values){
26241         if(values instanceof Array){ // array of objects
26242             for(var i = 0, len = values.length; i < len; i++){
26243                 var v = values[i];
26244                 var f = this.findField(v.id);
26245                 if(f){
26246                     f.setValue(v.value);
26247                     if(this.trackResetOnLoad){
26248                         f.originalValue = f.getValue();
26249                     }
26250                 }
26251             }
26252         }else{ // object hash
26253             var field, id;
26254             for(id in values){
26255                 if(typeof values[id] != 'function' && (field = this.findField(id))){
26256                     
26257                     if (field.setFromData && 
26258                         field.valueField && 
26259                         field.displayField &&
26260                         // combos' with local stores can 
26261                         // be queried via setValue()
26262                         // to set their value..
26263                         (field.store && !field.store.isLocal)
26264                         ) {
26265                         // it's a combo
26266                         var sd = { };
26267                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
26268                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
26269                         field.setFromData(sd);
26270                         
26271                     } else {
26272                         field.setValue(values[id]);
26273                     }
26274                     
26275                     
26276                     if(this.trackResetOnLoad){
26277                         field.originalValue = field.getValue();
26278                     }
26279                 }
26280             }
26281         }
26282          
26283         Roo.each(this.childForms || [], function (f) {
26284             f.setValues(values);
26285         });
26286                 
26287         return this;
26288     },
26289
26290     /**
26291      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
26292      * they are returned as an array.
26293      * @param {Boolean} asString
26294      * @return {Object}
26295      */
26296     getValues : function(asString){
26297         if (this.childForms) {
26298             // copy values from the child forms
26299             Roo.each(this.childForms, function (f) {
26300                 this.setValues(f.getValues());
26301             }, this);
26302         }
26303         
26304         
26305         
26306         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
26307         if(asString === true){
26308             return fs;
26309         }
26310         return Roo.urlDecode(fs);
26311     },
26312     
26313     /**
26314      * Returns the fields in this form as an object with key/value pairs. 
26315      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
26316      * @return {Object}
26317      */
26318     getFieldValues : function()
26319     {
26320         if (this.childForms) {
26321             // copy values from the child forms
26322             Roo.each(this.childForms, function (f) {
26323                 this.setValues(f.getValues());
26324             }, this);
26325         }
26326         
26327         var ret = {};
26328         this.items.each(function(f){
26329             if (!f.getName()) {
26330                 return;
26331             }
26332             var v = f.getValue();
26333             if ((typeof(v) == 'object') && f.getRawValue) {
26334                 v = f.getRawValue() ; // dates..
26335             }
26336             ret[f.getName()] = v;
26337         });
26338         
26339         return ret;
26340     },
26341
26342     /**
26343      * Clears all invalid messages in this form.
26344      * @return {BasicForm} this
26345      */
26346     clearInvalid : function(){
26347         this.items.each(function(f){
26348            f.clearInvalid();
26349         });
26350         
26351         Roo.each(this.childForms || [], function (f) {
26352             f.clearInvalid();
26353         });
26354         
26355         
26356         return this;
26357     },
26358
26359     /**
26360      * Resets this form.
26361      * @return {BasicForm} this
26362      */
26363     reset : function(){
26364         this.items.each(function(f){
26365             f.reset();
26366         });
26367         
26368         Roo.each(this.childForms || [], function (f) {
26369             f.reset();
26370         });
26371        
26372         
26373         return this;
26374     },
26375
26376     /**
26377      * Add Roo.form components to this form.
26378      * @param {Field} field1
26379      * @param {Field} field2 (optional)
26380      * @param {Field} etc (optional)
26381      * @return {BasicForm} this
26382      */
26383     add : function(){
26384         this.items.addAll(Array.prototype.slice.call(arguments, 0));
26385         return this;
26386     },
26387
26388
26389     /**
26390      * Removes a field from the items collection (does NOT remove its markup).
26391      * @param {Field} field
26392      * @return {BasicForm} this
26393      */
26394     remove : function(field){
26395         this.items.remove(field);
26396         return this;
26397     },
26398
26399     /**
26400      * Looks at the fields in this form, checks them for an id attribute,
26401      * and calls applyTo on the existing dom element with that id.
26402      * @return {BasicForm} this
26403      */
26404     render : function(){
26405         this.items.each(function(f){
26406             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
26407                 f.applyTo(f.id);
26408             }
26409         });
26410         return this;
26411     },
26412
26413     /**
26414      * Calls {@link Ext#apply} for all fields in this form with the passed object.
26415      * @param {Object} values
26416      * @return {BasicForm} this
26417      */
26418     applyToFields : function(o){
26419         this.items.each(function(f){
26420            Roo.apply(f, o);
26421         });
26422         return this;
26423     },
26424
26425     /**
26426      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
26427      * @param {Object} values
26428      * @return {BasicForm} this
26429      */
26430     applyIfToFields : function(o){
26431         this.items.each(function(f){
26432            Roo.applyIf(f, o);
26433         });
26434         return this;
26435     }
26436 });
26437
26438 // back compat
26439 Roo.BasicForm = Roo.form.BasicForm;/*
26440  * Based on:
26441  * Ext JS Library 1.1.1
26442  * Copyright(c) 2006-2007, Ext JS, LLC.
26443  *
26444  * Originally Released Under LGPL - original licence link has changed is not relivant.
26445  *
26446  * Fork - LGPL
26447  * <script type="text/javascript">
26448  */
26449
26450 /**
26451  * @class Roo.form.Form
26452  * @extends Roo.form.BasicForm
26453  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
26454  * @constructor
26455  * @param {Object} config Configuration options
26456  */
26457 Roo.form.Form = function(config){
26458     var xitems =  [];
26459     if (config.items) {
26460         xitems = config.items;
26461         delete config.items;
26462     }
26463    
26464     
26465     Roo.form.Form.superclass.constructor.call(this, null, config);
26466     this.url = this.url || this.action;
26467     if(!this.root){
26468         this.root = new Roo.form.Layout(Roo.applyIf({
26469             id: Roo.id()
26470         }, config));
26471     }
26472     this.active = this.root;
26473     /**
26474      * Array of all the buttons that have been added to this form via {@link addButton}
26475      * @type Array
26476      */
26477     this.buttons = [];
26478     this.allItems = [];
26479     this.addEvents({
26480         /**
26481          * @event clientvalidation
26482          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
26483          * @param {Form} this
26484          * @param {Boolean} valid true if the form has passed client-side validation
26485          */
26486         clientvalidation: true,
26487         /**
26488          * @event rendered
26489          * Fires when the form is rendered
26490          * @param {Roo.form.Form} form
26491          */
26492         rendered : true
26493     });
26494     
26495     if (this.progressUrl) {
26496             // push a hidden field onto the list of fields..
26497             this.addxtype( {
26498                     xns: Roo.form, 
26499                     xtype : 'Hidden', 
26500                     name : 'UPLOAD_IDENTIFIER' 
26501             });
26502         }
26503         
26504     
26505     Roo.each(xitems, this.addxtype, this);
26506     
26507     
26508     
26509 };
26510
26511 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
26512     /**
26513      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
26514      */
26515     /**
26516      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
26517      */
26518     /**
26519      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
26520      */
26521     buttonAlign:'center',
26522
26523     /**
26524      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
26525      */
26526     minButtonWidth:75,
26527
26528     /**
26529      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
26530      * This property cascades to child containers if not set.
26531      */
26532     labelAlign:'left',
26533
26534     /**
26535      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
26536      * fires a looping event with that state. This is required to bind buttons to the valid
26537      * state using the config value formBind:true on the button.
26538      */
26539     monitorValid : false,
26540
26541     /**
26542      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
26543      */
26544     monitorPoll : 200,
26545     
26546     /**
26547      * @cfg {String} progressUrl - Url to return progress data 
26548      */
26549     
26550     progressUrl : false,
26551   
26552     /**
26553      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
26554      * fields are added and the column is closed. If no fields are passed the column remains open
26555      * until end() is called.
26556      * @param {Object} config The config to pass to the column
26557      * @param {Field} field1 (optional)
26558      * @param {Field} field2 (optional)
26559      * @param {Field} etc (optional)
26560      * @return Column The column container object
26561      */
26562     column : function(c){
26563         var col = new Roo.form.Column(c);
26564         this.start(col);
26565         if(arguments.length > 1){ // duplicate code required because of Opera
26566             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
26567             this.end();
26568         }
26569         return col;
26570     },
26571
26572     /**
26573      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
26574      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
26575      * until end() is called.
26576      * @param {Object} config The config to pass to the fieldset
26577      * @param {Field} field1 (optional)
26578      * @param {Field} field2 (optional)
26579      * @param {Field} etc (optional)
26580      * @return FieldSet The fieldset container object
26581      */
26582     fieldset : function(c){
26583         var fs = new Roo.form.FieldSet(c);
26584         this.start(fs);
26585         if(arguments.length > 1){ // duplicate code required because of Opera
26586             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
26587             this.end();
26588         }
26589         return fs;
26590     },
26591
26592     /**
26593      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
26594      * fields are added and the container is closed. If no fields are passed the container remains open
26595      * until end() is called.
26596      * @param {Object} config The config to pass to the Layout
26597      * @param {Field} field1 (optional)
26598      * @param {Field} field2 (optional)
26599      * @param {Field} etc (optional)
26600      * @return Layout The container object
26601      */
26602     container : function(c){
26603         var l = new Roo.form.Layout(c);
26604         this.start(l);
26605         if(arguments.length > 1){ // duplicate code required because of Opera
26606             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
26607             this.end();
26608         }
26609         return l;
26610     },
26611
26612     /**
26613      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
26614      * @param {Object} container A Roo.form.Layout or subclass of Layout
26615      * @return {Form} this
26616      */
26617     start : function(c){
26618         // cascade label info
26619         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
26620         this.active.stack.push(c);
26621         c.ownerCt = this.active;
26622         this.active = c;
26623         return this;
26624     },
26625
26626     /**
26627      * Closes the current open container
26628      * @return {Form} this
26629      */
26630     end : function(){
26631         if(this.active == this.root){
26632             return this;
26633         }
26634         this.active = this.active.ownerCt;
26635         return this;
26636     },
26637
26638     /**
26639      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
26640      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
26641      * as the label of the field.
26642      * @param {Field} field1
26643      * @param {Field} field2 (optional)
26644      * @param {Field} etc. (optional)
26645      * @return {Form} this
26646      */
26647     add : function(){
26648         this.active.stack.push.apply(this.active.stack, arguments);
26649         this.allItems.push.apply(this.allItems,arguments);
26650         var r = [];
26651         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
26652             if(a[i].isFormField){
26653                 r.push(a[i]);
26654             }
26655         }
26656         if(r.length > 0){
26657             Roo.form.Form.superclass.add.apply(this, r);
26658         }
26659         return this;
26660     },
26661     
26662
26663     
26664     
26665     
26666      /**
26667      * Find any element that has been added to a form, using it's ID or name
26668      * This can include framesets, columns etc. along with regular fields..
26669      * @param {String} id - id or name to find.
26670      
26671      * @return {Element} e - or false if nothing found.
26672      */
26673     findbyId : function(id)
26674     {
26675         var ret = false;
26676         if (!id) {
26677             return ret;
26678         }
26679         Ext.each(this.allItems, function(f){
26680             if (f.id == id || f.name == id ){
26681                 ret = f;
26682                 return false;
26683             }
26684         });
26685         return ret;
26686     },
26687
26688     
26689     
26690     /**
26691      * Render this form into the passed container. This should only be called once!
26692      * @param {String/HTMLElement/Element} container The element this component should be rendered into
26693      * @return {Form} this
26694      */
26695     render : function(ct)
26696     {
26697         
26698         
26699         
26700         ct = Roo.get(ct);
26701         var o = this.autoCreate || {
26702             tag: 'form',
26703             method : this.method || 'POST',
26704             id : this.id || Roo.id()
26705         };
26706         this.initEl(ct.createChild(o));
26707
26708         this.root.render(this.el);
26709         
26710        
26711              
26712         this.items.each(function(f){
26713             f.render('x-form-el-'+f.id);
26714         });
26715
26716         if(this.buttons.length > 0){
26717             // tables are required to maintain order and for correct IE layout
26718             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
26719                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
26720                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
26721             }}, null, true);
26722             var tr = tb.getElementsByTagName('tr')[0];
26723             for(var i = 0, len = this.buttons.length; i < len; i++) {
26724                 var b = this.buttons[i];
26725                 var td = document.createElement('td');
26726                 td.className = 'x-form-btn-td';
26727                 b.render(tr.appendChild(td));
26728             }
26729         }
26730         if(this.monitorValid){ // initialize after render
26731             this.startMonitoring();
26732         }
26733         this.fireEvent('rendered', this);
26734         return this;
26735     },
26736
26737     /**
26738      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
26739      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
26740      * object or a valid Roo.DomHelper element config
26741      * @param {Function} handler The function called when the button is clicked
26742      * @param {Object} scope (optional) The scope of the handler function
26743      * @return {Roo.Button}
26744      */
26745     addButton : function(config, handler, scope){
26746         var bc = {
26747             handler: handler,
26748             scope: scope,
26749             minWidth: this.minButtonWidth,
26750             hideParent:true
26751         };
26752         if(typeof config == "string"){
26753             bc.text = config;
26754         }else{
26755             Roo.apply(bc, config);
26756         }
26757         var btn = new Roo.Button(null, bc);
26758         this.buttons.push(btn);
26759         return btn;
26760     },
26761
26762      /**
26763      * Adds a series of form elements (using the xtype property as the factory method.
26764      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
26765      * @param {Object} config 
26766      */
26767     
26768     addxtype : function()
26769     {
26770         var ar = Array.prototype.slice.call(arguments, 0);
26771         var ret = false;
26772         for(var i = 0; i < ar.length; i++) {
26773             if (!ar[i]) {
26774                 continue; // skip -- if this happends something invalid got sent, we 
26775                 // should ignore it, as basically that interface element will not show up
26776                 // and that should be pretty obvious!!
26777             }
26778             
26779             if (Roo.form[ar[i].xtype]) {
26780                 ar[i].form = this;
26781                 var fe = Roo.factory(ar[i], Roo.form);
26782                 if (!ret) {
26783                     ret = fe;
26784                 }
26785                 fe.form = this;
26786                 if (fe.store) {
26787                     fe.store.form = this;
26788                 }
26789                 if (fe.isLayout) {  
26790                          
26791                     this.start(fe);
26792                     this.allItems.push(fe);
26793                     if (fe.items && fe.addxtype) {
26794                         fe.addxtype.apply(fe, fe.items);
26795                         delete fe.items;
26796                     }
26797                      this.end();
26798                     continue;
26799                 }
26800                 
26801                 
26802                  
26803                 this.add(fe);
26804               //  console.log('adding ' + ar[i].xtype);
26805             }
26806             if (ar[i].xtype == 'Button') {  
26807                 //console.log('adding button');
26808                 //console.log(ar[i]);
26809                 this.addButton(ar[i]);
26810                 this.allItems.push(fe);
26811                 continue;
26812             }
26813             
26814             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
26815                 alert('end is not supported on xtype any more, use items');
26816             //    this.end();
26817             //    //console.log('adding end');
26818             }
26819             
26820         }
26821         return ret;
26822     },
26823     
26824     /**
26825      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
26826      * option "monitorValid"
26827      */
26828     startMonitoring : function(){
26829         if(!this.bound){
26830             this.bound = true;
26831             Roo.TaskMgr.start({
26832                 run : this.bindHandler,
26833                 interval : this.monitorPoll || 200,
26834                 scope: this
26835             });
26836         }
26837     },
26838
26839     /**
26840      * Stops monitoring of the valid state of this form
26841      */
26842     stopMonitoring : function(){
26843         this.bound = false;
26844     },
26845
26846     // private
26847     bindHandler : function(){
26848         if(!this.bound){
26849             return false; // stops binding
26850         }
26851         var valid = true;
26852         this.items.each(function(f){
26853             if(!f.isValid(true)){
26854                 valid = false;
26855                 return false;
26856             }
26857         });
26858         for(var i = 0, len = this.buttons.length; i < len; i++){
26859             var btn = this.buttons[i];
26860             if(btn.formBind === true && btn.disabled === valid){
26861                 btn.setDisabled(!valid);
26862             }
26863         }
26864         this.fireEvent('clientvalidation', this, valid);
26865     }
26866     
26867     
26868     
26869     
26870     
26871     
26872     
26873     
26874 });
26875
26876
26877 // back compat
26878 Roo.Form = Roo.form.Form;
26879 /*
26880  * Based on:
26881  * Ext JS Library 1.1.1
26882  * Copyright(c) 2006-2007, Ext JS, LLC.
26883  *
26884  * Originally Released Under LGPL - original licence link has changed is not relivant.
26885  *
26886  * Fork - LGPL
26887  * <script type="text/javascript">
26888  */
26889  
26890  /**
26891  * @class Roo.form.Action
26892  * Internal Class used to handle form actions
26893  * @constructor
26894  * @param {Roo.form.BasicForm} el The form element or its id
26895  * @param {Object} config Configuration options
26896  */
26897  
26898  
26899 // define the action interface
26900 Roo.form.Action = function(form, options){
26901     this.form = form;
26902     this.options = options || {};
26903 };
26904 /**
26905  * Client Validation Failed
26906  * @const 
26907  */
26908 Roo.form.Action.CLIENT_INVALID = 'client';
26909 /**
26910  * Server Validation Failed
26911  * @const 
26912  */
26913  Roo.form.Action.SERVER_INVALID = 'server';
26914  /**
26915  * Connect to Server Failed
26916  * @const 
26917  */
26918 Roo.form.Action.CONNECT_FAILURE = 'connect';
26919 /**
26920  * Reading Data from Server Failed
26921  * @const 
26922  */
26923 Roo.form.Action.LOAD_FAILURE = 'load';
26924
26925 Roo.form.Action.prototype = {
26926     type : 'default',
26927     failureType : undefined,
26928     response : undefined,
26929     result : undefined,
26930
26931     // interface method
26932     run : function(options){
26933
26934     },
26935
26936     // interface method
26937     success : function(response){
26938
26939     },
26940
26941     // interface method
26942     handleResponse : function(response){
26943
26944     },
26945
26946     // default connection failure
26947     failure : function(response){
26948         this.response = response;
26949         this.failureType = Roo.form.Action.CONNECT_FAILURE;
26950         this.form.afterAction(this, false);
26951     },
26952
26953     processResponse : function(response){
26954         this.response = response;
26955         if(!response.responseText){
26956             return true;
26957         }
26958         this.result = this.handleResponse(response);
26959         return this.result;
26960     },
26961
26962     // utility functions used internally
26963     getUrl : function(appendParams){
26964         var url = this.options.url || this.form.url || this.form.el.dom.action;
26965         if(appendParams){
26966             var p = this.getParams();
26967             if(p){
26968                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
26969             }
26970         }
26971         return url;
26972     },
26973
26974     getMethod : function(){
26975         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
26976     },
26977
26978     getParams : function(){
26979         var bp = this.form.baseParams;
26980         var p = this.options.params;
26981         if(p){
26982             if(typeof p == "object"){
26983                 p = Roo.urlEncode(Roo.applyIf(p, bp));
26984             }else if(typeof p == 'string' && bp){
26985                 p += '&' + Roo.urlEncode(bp);
26986             }
26987         }else if(bp){
26988             p = Roo.urlEncode(bp);
26989         }
26990         return p;
26991     },
26992
26993     createCallback : function(){
26994         return {
26995             success: this.success,
26996             failure: this.failure,
26997             scope: this,
26998             timeout: (this.form.timeout*1000),
26999             upload: this.form.fileUpload ? this.success : undefined
27000         };
27001     }
27002 };
27003
27004 Roo.form.Action.Submit = function(form, options){
27005     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
27006 };
27007
27008 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
27009     type : 'submit',
27010
27011     haveProgress : false,
27012     uploadComplete : false,
27013     
27014     // uploadProgress indicator.
27015     uploadProgress : function()
27016     {
27017         if (!this.form.progressUrl) {
27018             return;
27019         }
27020         
27021         if (!this.haveProgress) {
27022             Roo.MessageBox.progress("Uploading", "Uploading");
27023         }
27024         if (this.uploadComplete) {
27025            Roo.MessageBox.hide();
27026            return;
27027         }
27028         
27029         this.haveProgress = true;
27030    
27031         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
27032         
27033         var c = new Roo.data.Connection();
27034         c.request({
27035             url : this.form.progressUrl,
27036             params: {
27037                 id : uid
27038             },
27039             method: 'GET',
27040             success : function(req){
27041                //console.log(data);
27042                 var rdata = false;
27043                 var edata;
27044                 try  {
27045                    rdata = Roo.decode(req.responseText)
27046                 } catch (e) {
27047                     Roo.log("Invalid data from server..");
27048                     Roo.log(edata);
27049                     return;
27050                 }
27051                 if (!rdata || !rdata.success) {
27052                     Roo.log(rdata);
27053                     return;
27054                 }
27055                 var data = rdata.data;
27056                 
27057                 if (this.uploadComplete) {
27058                    Roo.MessageBox.hide();
27059                    return;
27060                 }
27061                    
27062                 if (data){
27063                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
27064                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
27065                     );
27066                 }
27067                 this.uploadProgress.defer(2000,this);
27068             },
27069        
27070             failure: function(data) {
27071                 Roo.log('progress url failed ');
27072                 Roo.log(data);
27073             },
27074             scope : this
27075         });
27076            
27077     },
27078     
27079     
27080     run : function()
27081     {
27082         // run get Values on the form, so it syncs any secondary forms.
27083         this.form.getValues();
27084         
27085         var o = this.options;
27086         var method = this.getMethod();
27087         var isPost = method == 'POST';
27088         if(o.clientValidation === false || this.form.isValid()){
27089             
27090             if (this.form.progressUrl) {
27091                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
27092                     (new Date() * 1) + '' + Math.random());
27093                     
27094             } 
27095             
27096             Roo.Ajax.request(Roo.apply(this.createCallback(), {
27097                 form:this.form.el.dom,
27098                 url:this.getUrl(!isPost),
27099                 method: method,
27100                 params:isPost ? this.getParams() : null,
27101                 isUpload: this.form.fileUpload
27102             }));
27103             
27104             this.uploadProgress();
27105
27106         }else if (o.clientValidation !== false){ // client validation failed
27107             this.failureType = Roo.form.Action.CLIENT_INVALID;
27108             this.form.afterAction(this, false);
27109         }
27110     },
27111
27112     success : function(response)
27113     {
27114         this.uploadComplete= true;
27115         if (this.haveProgress) {
27116             Roo.MessageBox.hide();
27117         }
27118         
27119         var result = this.processResponse(response);
27120         if(result === true || result.success){
27121             this.form.afterAction(this, true);
27122             return;
27123         }
27124         if(result.errors){
27125             this.form.markInvalid(result.errors);
27126             this.failureType = Roo.form.Action.SERVER_INVALID;
27127         }
27128         this.form.afterAction(this, false);
27129     },
27130     failure : function(response)
27131     {
27132         this.uploadComplete= true;
27133         if (this.haveProgress) {
27134             Roo.MessageBox.hide();
27135         }
27136         
27137         this.response = response;
27138         this.failureType = Roo.form.Action.CONNECT_FAILURE;
27139         this.form.afterAction(this, false);
27140     },
27141     
27142     handleResponse : function(response){
27143         if(this.form.errorReader){
27144             var rs = this.form.errorReader.read(response);
27145             var errors = [];
27146             if(rs.records){
27147                 for(var i = 0, len = rs.records.length; i < len; i++) {
27148                     var r = rs.records[i];
27149                     errors[i] = r.data;
27150                 }
27151             }
27152             if(errors.length < 1){
27153                 errors = null;
27154             }
27155             return {
27156                 success : rs.success,
27157                 errors : errors
27158             };
27159         }
27160         var ret = false;
27161         try {
27162             ret = Roo.decode(response.responseText);
27163         } catch (e) {
27164             ret = {
27165                 success: false,
27166                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
27167                 errors : []
27168             };
27169         }
27170         return ret;
27171         
27172     }
27173 });
27174
27175
27176 Roo.form.Action.Load = function(form, options){
27177     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
27178     this.reader = this.form.reader;
27179 };
27180
27181 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
27182     type : 'load',
27183
27184     run : function(){
27185         Roo.Ajax.request(Roo.apply(
27186                 this.createCallback(), {
27187                     method:this.getMethod(),
27188                     url:this.getUrl(false),
27189                     params:this.getParams()
27190         }));
27191     },
27192
27193     success : function(response){
27194         var result = this.processResponse(response);
27195         if(result === true || !result.success || !result.data){
27196             this.failureType = Roo.form.Action.LOAD_FAILURE;
27197             this.form.afterAction(this, false);
27198             return;
27199         }
27200         this.form.clearInvalid();
27201         this.form.setValues(result.data);
27202         this.form.afterAction(this, true);
27203     },
27204
27205     handleResponse : function(response){
27206         if(this.form.reader){
27207             var rs = this.form.reader.read(response);
27208             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
27209             return {
27210                 success : rs.success,
27211                 data : data
27212             };
27213         }
27214         return Roo.decode(response.responseText);
27215     }
27216 });
27217
27218 Roo.form.Action.ACTION_TYPES = {
27219     'load' : Roo.form.Action.Load,
27220     'submit' : Roo.form.Action.Submit
27221 };/*
27222  * Based on:
27223  * Ext JS Library 1.1.1
27224  * Copyright(c) 2006-2007, Ext JS, LLC.
27225  *
27226  * Originally Released Under LGPL - original licence link has changed is not relivant.
27227  *
27228  * Fork - LGPL
27229  * <script type="text/javascript">
27230  */
27231  
27232 /**
27233  * @class Roo.form.Layout
27234  * @extends Roo.Component
27235  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
27236  * @constructor
27237  * @param {Object} config Configuration options
27238  */
27239 Roo.form.Layout = function(config){
27240     var xitems = [];
27241     if (config.items) {
27242         xitems = config.items;
27243         delete config.items;
27244     }
27245     Roo.form.Layout.superclass.constructor.call(this, config);
27246     this.stack = [];
27247     Roo.each(xitems, this.addxtype, this);
27248      
27249 };
27250
27251 Roo.extend(Roo.form.Layout, Roo.Component, {
27252     /**
27253      * @cfg {String/Object} autoCreate
27254      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
27255      */
27256     /**
27257      * @cfg {String/Object/Function} style
27258      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
27259      * a function which returns such a specification.
27260      */
27261     /**
27262      * @cfg {String} labelAlign
27263      * Valid values are "left," "top" and "right" (defaults to "left")
27264      */
27265     /**
27266      * @cfg {Number} labelWidth
27267      * Fixed width in pixels of all field labels (defaults to undefined)
27268      */
27269     /**
27270      * @cfg {Boolean} clear
27271      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
27272      */
27273     clear : true,
27274     /**
27275      * @cfg {String} labelSeparator
27276      * The separator to use after field labels (defaults to ':')
27277      */
27278     labelSeparator : ':',
27279     /**
27280      * @cfg {Boolean} hideLabels
27281      * True to suppress the display of field labels in this layout (defaults to false)
27282      */
27283     hideLabels : false,
27284
27285     // private
27286     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
27287     
27288     isLayout : true,
27289     
27290     // private
27291     onRender : function(ct, position){
27292         if(this.el){ // from markup
27293             this.el = Roo.get(this.el);
27294         }else {  // generate
27295             var cfg = this.getAutoCreate();
27296             this.el = ct.createChild(cfg, position);
27297         }
27298         if(this.style){
27299             this.el.applyStyles(this.style);
27300         }
27301         if(this.labelAlign){
27302             this.el.addClass('x-form-label-'+this.labelAlign);
27303         }
27304         if(this.hideLabels){
27305             this.labelStyle = "display:none";
27306             this.elementStyle = "padding-left:0;";
27307         }else{
27308             if(typeof this.labelWidth == 'number'){
27309                 this.labelStyle = "width:"+this.labelWidth+"px;";
27310                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
27311             }
27312             if(this.labelAlign == 'top'){
27313                 this.labelStyle = "width:auto;";
27314                 this.elementStyle = "padding-left:0;";
27315             }
27316         }
27317         var stack = this.stack;
27318         var slen = stack.length;
27319         if(slen > 0){
27320             if(!this.fieldTpl){
27321                 var t = new Roo.Template(
27322                     '<div class="x-form-item {5}">',
27323                         '<label for="{0}" style="{2}">{1}{4}</label>',
27324                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
27325                         '</div>',
27326                     '</div><div class="x-form-clear-left"></div>'
27327                 );
27328                 t.disableFormats = true;
27329                 t.compile();
27330                 Roo.form.Layout.prototype.fieldTpl = t;
27331             }
27332             for(var i = 0; i < slen; i++) {
27333                 if(stack[i].isFormField){
27334                     this.renderField(stack[i]);
27335                 }else{
27336                     this.renderComponent(stack[i]);
27337                 }
27338             }
27339         }
27340         if(this.clear){
27341             this.el.createChild({cls:'x-form-clear'});
27342         }
27343     },
27344
27345     // private
27346     renderField : function(f){
27347         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
27348                f.id, //0
27349                f.fieldLabel, //1
27350                f.labelStyle||this.labelStyle||'', //2
27351                this.elementStyle||'', //3
27352                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
27353                f.itemCls||this.itemCls||''  //5
27354        ], true).getPrevSibling());
27355     },
27356
27357     // private
27358     renderComponent : function(c){
27359         c.render(c.isLayout ? this.el : this.el.createChild());    
27360     },
27361     /**
27362      * Adds a object form elements (using the xtype property as the factory method.)
27363      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
27364      * @param {Object} config 
27365      */
27366     addxtype : function(o)
27367     {
27368         // create the lement.
27369         o.form = this.form;
27370         var fe = Roo.factory(o, Roo.form);
27371         this.form.allItems.push(fe);
27372         this.stack.push(fe);
27373         
27374         if (fe.isFormField) {
27375             this.form.items.add(fe);
27376         }
27377          
27378         return fe;
27379     }
27380 });
27381
27382 /**
27383  * @class Roo.form.Column
27384  * @extends Roo.form.Layout
27385  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
27386  * @constructor
27387  * @param {Object} config Configuration options
27388  */
27389 Roo.form.Column = function(config){
27390     Roo.form.Column.superclass.constructor.call(this, config);
27391 };
27392
27393 Roo.extend(Roo.form.Column, Roo.form.Layout, {
27394     /**
27395      * @cfg {Number/String} width
27396      * The fixed width of the column in pixels or CSS value (defaults to "auto")
27397      */
27398     /**
27399      * @cfg {String/Object} autoCreate
27400      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
27401      */
27402
27403     // private
27404     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
27405
27406     // private
27407     onRender : function(ct, position){
27408         Roo.form.Column.superclass.onRender.call(this, ct, position);
27409         if(this.width){
27410             this.el.setWidth(this.width);
27411         }
27412     }
27413 });
27414
27415
27416 /**
27417  * @class Roo.form.Row
27418  * @extends Roo.form.Layout
27419  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
27420  * @constructor
27421  * @param {Object} config Configuration options
27422  */
27423
27424  
27425 Roo.form.Row = function(config){
27426     Roo.form.Row.superclass.constructor.call(this, config);
27427 };
27428  
27429 Roo.extend(Roo.form.Row, Roo.form.Layout, {
27430       /**
27431      * @cfg {Number/String} width
27432      * The fixed width of the column in pixels or CSS value (defaults to "auto")
27433      */
27434     /**
27435      * @cfg {Number/String} height
27436      * The fixed height of the column in pixels or CSS value (defaults to "auto")
27437      */
27438     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
27439     
27440     padWidth : 20,
27441     // private
27442     onRender : function(ct, position){
27443         //console.log('row render');
27444         if(!this.rowTpl){
27445             var t = new Roo.Template(
27446                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
27447                     '<label for="{0}" style="{2}">{1}{4}</label>',
27448                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
27449                     '</div>',
27450                 '</div>'
27451             );
27452             t.disableFormats = true;
27453             t.compile();
27454             Roo.form.Layout.prototype.rowTpl = t;
27455         }
27456         this.fieldTpl = this.rowTpl;
27457         
27458         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
27459         var labelWidth = 100;
27460         
27461         if ((this.labelAlign != 'top')) {
27462             if (typeof this.labelWidth == 'number') {
27463                 labelWidth = this.labelWidth
27464             }
27465             this.padWidth =  20 + labelWidth;
27466             
27467         }
27468         
27469         Roo.form.Column.superclass.onRender.call(this, ct, position);
27470         if(this.width){
27471             this.el.setWidth(this.width);
27472         }
27473         if(this.height){
27474             this.el.setHeight(this.height);
27475         }
27476     },
27477     
27478     // private
27479     renderField : function(f){
27480         f.fieldEl = this.fieldTpl.append(this.el, [
27481                f.id, f.fieldLabel,
27482                f.labelStyle||this.labelStyle||'',
27483                this.elementStyle||'',
27484                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
27485                f.itemCls||this.itemCls||'',
27486                f.width ? f.width + this.padWidth : 160 + this.padWidth
27487        ],true);
27488     }
27489 });
27490  
27491
27492 /**
27493  * @class Roo.form.FieldSet
27494  * @extends Roo.form.Layout
27495  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
27496  * @constructor
27497  * @param {Object} config Configuration options
27498  */
27499 Roo.form.FieldSet = function(config){
27500     Roo.form.FieldSet.superclass.constructor.call(this, config);
27501 };
27502
27503 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
27504     /**
27505      * @cfg {String} legend
27506      * The text to display as the legend for the FieldSet (defaults to '')
27507      */
27508     /**
27509      * @cfg {String/Object} autoCreate
27510      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
27511      */
27512
27513     // private
27514     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
27515
27516     // private
27517     onRender : function(ct, position){
27518         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
27519         if(this.legend){
27520             this.setLegend(this.legend);
27521         }
27522     },
27523
27524     // private
27525     setLegend : function(text){
27526         if(this.rendered){
27527             this.el.child('legend').update(text);
27528         }
27529     }
27530 });/*
27531  * Based on:
27532  * Ext JS Library 1.1.1
27533  * Copyright(c) 2006-2007, Ext JS, LLC.
27534  *
27535  * Originally Released Under LGPL - original licence link has changed is not relivant.
27536  *
27537  * Fork - LGPL
27538  * <script type="text/javascript">
27539  */
27540 /**
27541  * @class Roo.form.VTypes
27542  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
27543  * @singleton
27544  */
27545 Roo.form.VTypes = function(){
27546     // closure these in so they are only created once.
27547     var alpha = /^[a-zA-Z_]+$/;
27548     var alphanum = /^[a-zA-Z0-9_]+$/;
27549     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
27550     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
27551
27552     // All these messages and functions are configurable
27553     return {
27554         /**
27555          * The function used to validate email addresses
27556          * @param {String} value The email address
27557          */
27558         'email' : function(v){
27559             return email.test(v);
27560         },
27561         /**
27562          * The error text to display when the email validation function returns false
27563          * @type String
27564          */
27565         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
27566         /**
27567          * The keystroke filter mask to be applied on email input
27568          * @type RegExp
27569          */
27570         'emailMask' : /[a-z0-9_\.\-@]/i,
27571
27572         /**
27573          * The function used to validate URLs
27574          * @param {String} value The URL
27575          */
27576         'url' : function(v){
27577             return url.test(v);
27578         },
27579         /**
27580          * The error text to display when the url validation function returns false
27581          * @type String
27582          */
27583         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
27584         
27585         /**
27586          * The function used to validate alpha values
27587          * @param {String} value The value
27588          */
27589         'alpha' : function(v){
27590             return alpha.test(v);
27591         },
27592         /**
27593          * The error text to display when the alpha validation function returns false
27594          * @type String
27595          */
27596         'alphaText' : 'This field should only contain letters and _',
27597         /**
27598          * The keystroke filter mask to be applied on alpha input
27599          * @type RegExp
27600          */
27601         'alphaMask' : /[a-z_]/i,
27602
27603         /**
27604          * The function used to validate alphanumeric values
27605          * @param {String} value The value
27606          */
27607         'alphanum' : function(v){
27608             return alphanum.test(v);
27609         },
27610         /**
27611          * The error text to display when the alphanumeric validation function returns false
27612          * @type String
27613          */
27614         'alphanumText' : 'This field should only contain letters, numbers and _',
27615         /**
27616          * The keystroke filter mask to be applied on alphanumeric input
27617          * @type RegExp
27618          */
27619         'alphanumMask' : /[a-z0-9_]/i
27620     };
27621 }();//<script type="text/javascript">
27622
27623 /**
27624  * @class Roo.form.FCKeditor
27625  * @extends Roo.form.TextArea
27626  * Wrapper around the FCKEditor http://www.fckeditor.net
27627  * @constructor
27628  * Creates a new FCKeditor
27629  * @param {Object} config Configuration options
27630  */
27631 Roo.form.FCKeditor = function(config){
27632     Roo.form.FCKeditor.superclass.constructor.call(this, config);
27633     this.addEvents({
27634          /**
27635          * @event editorinit
27636          * Fired when the editor is initialized - you can add extra handlers here..
27637          * @param {FCKeditor} this
27638          * @param {Object} the FCK object.
27639          */
27640         editorinit : true
27641     });
27642     
27643     
27644 };
27645 Roo.form.FCKeditor.editors = { };
27646 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
27647 {
27648     //defaultAutoCreate : {
27649     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
27650     //},
27651     // private
27652     /**
27653      * @cfg {Object} fck options - see fck manual for details.
27654      */
27655     fckconfig : false,
27656     
27657     /**
27658      * @cfg {Object} fck toolbar set (Basic or Default)
27659      */
27660     toolbarSet : 'Basic',
27661     /**
27662      * @cfg {Object} fck BasePath
27663      */ 
27664     basePath : '/fckeditor/',
27665     
27666     
27667     frame : false,
27668     
27669     value : '',
27670     
27671    
27672     onRender : function(ct, position)
27673     {
27674         if(!this.el){
27675             this.defaultAutoCreate = {
27676                 tag: "textarea",
27677                 style:"width:300px;height:60px;",
27678                 autocomplete: "off"
27679             };
27680         }
27681         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
27682         /*
27683         if(this.grow){
27684             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
27685             if(this.preventScrollbars){
27686                 this.el.setStyle("overflow", "hidden");
27687             }
27688             this.el.setHeight(this.growMin);
27689         }
27690         */
27691         //console.log('onrender' + this.getId() );
27692         Roo.form.FCKeditor.editors[this.getId()] = this;
27693          
27694
27695         this.replaceTextarea() ;
27696         
27697     },
27698     
27699     getEditor : function() {
27700         return this.fckEditor;
27701     },
27702     /**
27703      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
27704      * @param {Mixed} value The value to set
27705      */
27706     
27707     
27708     setValue : function(value)
27709     {
27710         //console.log('setValue: ' + value);
27711         
27712         if(typeof(value) == 'undefined') { // not sure why this is happending...
27713             return;
27714         }
27715         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
27716         
27717         //if(!this.el || !this.getEditor()) {
27718         //    this.value = value;
27719             //this.setValue.defer(100,this,[value]);    
27720         //    return;
27721         //} 
27722         
27723         if(!this.getEditor()) {
27724             return;
27725         }
27726         
27727         this.getEditor().SetData(value);
27728         
27729         //
27730
27731     },
27732
27733     /**
27734      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
27735      * @return {Mixed} value The field value
27736      */
27737     getValue : function()
27738     {
27739         
27740         if (this.frame && this.frame.dom.style.display == 'none') {
27741             return Roo.form.FCKeditor.superclass.getValue.call(this);
27742         }
27743         
27744         if(!this.el || !this.getEditor()) {
27745            
27746            // this.getValue.defer(100,this); 
27747             return this.value;
27748         }
27749        
27750         
27751         var value=this.getEditor().GetData();
27752         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
27753         return Roo.form.FCKeditor.superclass.getValue.call(this);
27754         
27755
27756     },
27757
27758     /**
27759      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
27760      * @return {Mixed} value The field value
27761      */
27762     getRawValue : function()
27763     {
27764         if (this.frame && this.frame.dom.style.display == 'none') {
27765             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
27766         }
27767         
27768         if(!this.el || !this.getEditor()) {
27769             //this.getRawValue.defer(100,this); 
27770             return this.value;
27771             return;
27772         }
27773         
27774         
27775         
27776         var value=this.getEditor().GetData();
27777         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
27778         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
27779          
27780     },
27781     
27782     setSize : function(w,h) {
27783         
27784         
27785         
27786         //if (this.frame && this.frame.dom.style.display == 'none') {
27787         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
27788         //    return;
27789         //}
27790         //if(!this.el || !this.getEditor()) {
27791         //    this.setSize.defer(100,this, [w,h]); 
27792         //    return;
27793         //}
27794         
27795         
27796         
27797         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
27798         
27799         this.frame.dom.setAttribute('width', w);
27800         this.frame.dom.setAttribute('height', h);
27801         this.frame.setSize(w,h);
27802         
27803     },
27804     
27805     toggleSourceEdit : function(value) {
27806         
27807       
27808          
27809         this.el.dom.style.display = value ? '' : 'none';
27810         this.frame.dom.style.display = value ?  'none' : '';
27811         
27812     },
27813     
27814     
27815     focus: function(tag)
27816     {
27817         if (this.frame.dom.style.display == 'none') {
27818             return Roo.form.FCKeditor.superclass.focus.call(this);
27819         }
27820         if(!this.el || !this.getEditor()) {
27821             this.focus.defer(100,this, [tag]); 
27822             return;
27823         }
27824         
27825         
27826         
27827         
27828         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
27829         this.getEditor().Focus();
27830         if (tgs.length) {
27831             if (!this.getEditor().Selection.GetSelection()) {
27832                 this.focus.defer(100,this, [tag]); 
27833                 return;
27834             }
27835             
27836             
27837             var r = this.getEditor().EditorDocument.createRange();
27838             r.setStart(tgs[0],0);
27839             r.setEnd(tgs[0],0);
27840             this.getEditor().Selection.GetSelection().removeAllRanges();
27841             this.getEditor().Selection.GetSelection().addRange(r);
27842             this.getEditor().Focus();
27843         }
27844         
27845     },
27846     
27847     
27848     
27849     replaceTextarea : function()
27850     {
27851         if ( document.getElementById( this.getId() + '___Frame' ) )
27852             return ;
27853         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
27854         //{
27855             // We must check the elements firstly using the Id and then the name.
27856         var oTextarea = document.getElementById( this.getId() );
27857         
27858         var colElementsByName = document.getElementsByName( this.getId() ) ;
27859          
27860         oTextarea.style.display = 'none' ;
27861
27862         if ( oTextarea.tabIndex ) {            
27863             this.TabIndex = oTextarea.tabIndex ;
27864         }
27865         
27866         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
27867         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
27868         this.frame = Roo.get(this.getId() + '___Frame')
27869     },
27870     
27871     _getConfigHtml : function()
27872     {
27873         var sConfig = '' ;
27874
27875         for ( var o in this.fckconfig ) {
27876             sConfig += sConfig.length > 0  ? '&amp;' : '';
27877             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
27878         }
27879
27880         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
27881     },
27882     
27883     
27884     _getIFrameHtml : function()
27885     {
27886         var sFile = 'fckeditor.html' ;
27887         /* no idea what this is about..
27888         try
27889         {
27890             if ( (/fcksource=true/i).test( window.top.location.search ) )
27891                 sFile = 'fckeditor.original.html' ;
27892         }
27893         catch (e) { 
27894         */
27895
27896         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
27897         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
27898         
27899         
27900         var html = '<iframe id="' + this.getId() +
27901             '___Frame" src="' + sLink +
27902             '" width="' + this.width +
27903             '" height="' + this.height + '"' +
27904             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
27905             ' frameborder="0" scrolling="no"></iframe>' ;
27906
27907         return html ;
27908     },
27909     
27910     _insertHtmlBefore : function( html, element )
27911     {
27912         if ( element.insertAdjacentHTML )       {
27913             // IE
27914             element.insertAdjacentHTML( 'beforeBegin', html ) ;
27915         } else { // Gecko
27916             var oRange = document.createRange() ;
27917             oRange.setStartBefore( element ) ;
27918             var oFragment = oRange.createContextualFragment( html );
27919             element.parentNode.insertBefore( oFragment, element ) ;
27920         }
27921     }
27922     
27923     
27924   
27925     
27926     
27927     
27928     
27929
27930 });
27931
27932 //Roo.reg('fckeditor', Roo.form.FCKeditor);
27933
27934 function FCKeditor_OnComplete(editorInstance){
27935     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
27936     f.fckEditor = editorInstance;
27937     //console.log("loaded");
27938     f.fireEvent('editorinit', f, editorInstance);
27939
27940   
27941
27942  
27943
27944
27945
27946
27947
27948
27949
27950
27951
27952
27953
27954
27955
27956
27957
27958 //<script type="text/javascript">
27959 /**
27960  * @class Roo.form.GridField
27961  * @extends Roo.form.Field
27962  * Embed a grid (or editable grid into a form)
27963  * STATUS ALPHA
27964  * 
27965  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
27966  * it needs 
27967  * xgrid.store = Roo.data.Store
27968  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
27969  * xgrid.store.reader = Roo.data.JsonReader 
27970  * 
27971  * 
27972  * @constructor
27973  * Creates a new GridField
27974  * @param {Object} config Configuration options
27975  */
27976 Roo.form.GridField = function(config){
27977     Roo.form.GridField.superclass.constructor.call(this, config);
27978      
27979 };
27980
27981 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
27982     /**
27983      * @cfg {Number} width  - used to restrict width of grid..
27984      */
27985     width : 100,
27986     /**
27987      * @cfg {Number} height - used to restrict height of grid..
27988      */
27989     height : 50,
27990      /**
27991      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
27992          * 
27993          *}
27994      */
27995     xgrid : false, 
27996     /**
27997      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
27998      * {tag: "input", type: "checkbox", autocomplete: "off"})
27999      */
28000    // defaultAutoCreate : { tag: 'div' },
28001     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
28002     /**
28003      * @cfg {String} addTitle Text to include for adding a title.
28004      */
28005     addTitle : false,
28006     //
28007     onResize : function(){
28008         Roo.form.Field.superclass.onResize.apply(this, arguments);
28009     },
28010
28011     initEvents : function(){
28012         // Roo.form.Checkbox.superclass.initEvents.call(this);
28013         // has no events...
28014        
28015     },
28016
28017
28018     getResizeEl : function(){
28019         return this.wrap;
28020     },
28021
28022     getPositionEl : function(){
28023         return this.wrap;
28024     },
28025
28026     // private
28027     onRender : function(ct, position){
28028         
28029         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
28030         var style = this.style;
28031         delete this.style;
28032         
28033         Roo.form.GridField.superclass.onRender.call(this, ct, position);
28034         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
28035         this.viewEl = this.wrap.createChild({ tag: 'div' });
28036         if (style) {
28037             this.viewEl.applyStyles(style);
28038         }
28039         if (this.width) {
28040             this.viewEl.setWidth(this.width);
28041         }
28042         if (this.height) {
28043             this.viewEl.setHeight(this.height);
28044         }
28045         //if(this.inputValue !== undefined){
28046         //this.setValue(this.value);
28047         
28048         
28049         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
28050         
28051         
28052         this.grid.render();
28053         this.grid.getDataSource().on('remove', this.refreshValue, this);
28054         this.grid.getDataSource().on('update', this.refreshValue, this);
28055         this.grid.on('afteredit', this.refreshValue, this);
28056  
28057     },
28058      
28059     
28060     /**
28061      * Sets the value of the item. 
28062      * @param {String} either an object  or a string..
28063      */
28064     setValue : function(v){
28065         //this.value = v;
28066         v = v || []; // empty set..
28067         // this does not seem smart - it really only affects memoryproxy grids..
28068         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
28069             var ds = this.grid.getDataSource();
28070             // assumes a json reader..
28071             var data = {}
28072             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
28073             ds.loadData( data);
28074         }
28075         Roo.form.GridField.superclass.setValue.call(this, v);
28076         this.refreshValue();
28077         // should load data in the grid really....
28078     },
28079     
28080     // private
28081     refreshValue: function() {
28082          var val = [];
28083         this.grid.getDataSource().each(function(r) {
28084             val.push(r.data);
28085         });
28086         this.el.dom.value = Roo.encode(val);
28087     }
28088     
28089      
28090     
28091     
28092 });/*
28093  * Based on:
28094  * Ext JS Library 1.1.1
28095  * Copyright(c) 2006-2007, Ext JS, LLC.
28096  *
28097  * Originally Released Under LGPL - original licence link has changed is not relivant.
28098  *
28099  * Fork - LGPL
28100  * <script type="text/javascript">
28101  */
28102 /**
28103  * @class Roo.form.DisplayField
28104  * @extends Roo.form.Field
28105  * A generic Field to display non-editable data.
28106  * @constructor
28107  * Creates a new Display Field item.
28108  * @param {Object} config Configuration options
28109  */
28110 Roo.form.DisplayField = function(config){
28111     Roo.form.DisplayField.superclass.constructor.call(this, config);
28112     
28113 };
28114
28115 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
28116     inputType:      'hidden',
28117     allowBlank:     true,
28118     readOnly:         true,
28119     
28120  
28121     /**
28122      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
28123      */
28124     focusClass : undefined,
28125     /**
28126      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
28127      */
28128     fieldClass: 'x-form-field',
28129     
28130      /**
28131      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
28132      */
28133     valueRenderer: undefined,
28134     
28135     width: 100,
28136     /**
28137      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
28138      * {tag: "input", type: "checkbox", autocomplete: "off"})
28139      */
28140      
28141  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
28142
28143     onResize : function(){
28144         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
28145         
28146     },
28147
28148     initEvents : function(){
28149         // Roo.form.Checkbox.superclass.initEvents.call(this);
28150         // has no events...
28151        
28152     },
28153
28154
28155     getResizeEl : function(){
28156         return this.wrap;
28157     },
28158
28159     getPositionEl : function(){
28160         return this.wrap;
28161     },
28162
28163     // private
28164     onRender : function(ct, position){
28165         
28166         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
28167         //if(this.inputValue !== undefined){
28168         this.wrap = this.el.wrap();
28169         
28170         this.viewEl = this.wrap.createChild({ tag: 'div'});
28171         
28172         if (this.bodyStyle) {
28173             this.viewEl.applyStyles(this.bodyStyle);
28174         }
28175         //this.viewEl.setStyle('padding', '2px');
28176         
28177         this.setValue(this.value);
28178         
28179     },
28180 /*
28181     // private
28182     initValue : Roo.emptyFn,
28183
28184   */
28185
28186         // private
28187     onClick : function(){
28188         
28189     },
28190
28191     /**
28192      * Sets the checked state of the checkbox.
28193      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
28194      */
28195     setValue : function(v){
28196         this.value = v;
28197         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
28198         // this might be called before we have a dom element..
28199         if (!this.viewEl) {
28200             return;
28201         }
28202         this.viewEl.dom.innerHTML = html;
28203         Roo.form.DisplayField.superclass.setValue.call(this, v);
28204
28205     }
28206 });//<script type="text/javasscript">
28207  
28208
28209 /**
28210  * @class Roo.DDView
28211  * A DnD enabled version of Roo.View.
28212  * @param {Element/String} container The Element in which to create the View.
28213  * @param {String} tpl The template string used to create the markup for each element of the View
28214  * @param {Object} config The configuration properties. These include all the config options of
28215  * {@link Roo.View} plus some specific to this class.<br>
28216  * <p>
28217  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
28218  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
28219  * <p>
28220  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
28221 .x-view-drag-insert-above {
28222         border-top:1px dotted #3366cc;
28223 }
28224 .x-view-drag-insert-below {
28225         border-bottom:1px dotted #3366cc;
28226 }
28227 </code></pre>
28228  * 
28229  */
28230  
28231 Roo.DDView = function(container, tpl, config) {
28232     Roo.DDView.superclass.constructor.apply(this, arguments);
28233     this.getEl().setStyle("outline", "0px none");
28234     this.getEl().unselectable();
28235     if (this.dragGroup) {
28236                 this.setDraggable(this.dragGroup.split(","));
28237     }
28238     if (this.dropGroup) {
28239                 this.setDroppable(this.dropGroup.split(","));
28240     }
28241     if (this.deletable) {
28242         this.setDeletable();
28243     }
28244     this.isDirtyFlag = false;
28245         this.addEvents({
28246                 "drop" : true
28247         });
28248 };
28249
28250 Roo.extend(Roo.DDView, Roo.View, {
28251 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
28252 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
28253 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
28254 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
28255
28256         isFormField: true,
28257
28258         reset: Roo.emptyFn,
28259         
28260         clearInvalid: Roo.form.Field.prototype.clearInvalid,
28261
28262         validate: function() {
28263                 return true;
28264         },
28265         
28266         destroy: function() {
28267                 this.purgeListeners();
28268                 this.getEl.removeAllListeners();
28269                 this.getEl().remove();
28270                 if (this.dragZone) {
28271                         if (this.dragZone.destroy) {
28272                                 this.dragZone.destroy();
28273                         }
28274                 }
28275                 if (this.dropZone) {
28276                         if (this.dropZone.destroy) {
28277                                 this.dropZone.destroy();
28278                         }
28279                 }
28280         },
28281
28282 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
28283         getName: function() {
28284                 return this.name;
28285         },
28286
28287 /**     Loads the View from a JSON string representing the Records to put into the Store. */
28288         setValue: function(v) {
28289                 if (!this.store) {
28290                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
28291                 }
28292                 var data = {};
28293                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
28294                 this.store.proxy = new Roo.data.MemoryProxy(data);
28295                 this.store.load();
28296         },
28297
28298 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
28299         getValue: function() {
28300                 var result = '(';
28301                 this.store.each(function(rec) {
28302                         result += rec.id + ',';
28303                 });
28304                 return result.substr(0, result.length - 1) + ')';
28305         },
28306         
28307         getIds: function() {
28308                 var i = 0, result = new Array(this.store.getCount());
28309                 this.store.each(function(rec) {
28310                         result[i++] = rec.id;
28311                 });
28312                 return result;
28313         },
28314         
28315         isDirty: function() {
28316                 return this.isDirtyFlag;
28317         },
28318
28319 /**
28320  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
28321  *      whole Element becomes the target, and this causes the drop gesture to append.
28322  */
28323     getTargetFromEvent : function(e) {
28324                 var target = e.getTarget();
28325                 while ((target !== null) && (target.parentNode != this.el.dom)) {
28326                 target = target.parentNode;
28327                 }
28328                 if (!target) {
28329                         target = this.el.dom.lastChild || this.el.dom;
28330                 }
28331                 return target;
28332     },
28333
28334 /**
28335  *      Create the drag data which consists of an object which has the property "ddel" as
28336  *      the drag proxy element. 
28337  */
28338     getDragData : function(e) {
28339         var target = this.findItemFromChild(e.getTarget());
28340                 if(target) {
28341                         this.handleSelection(e);
28342                         var selNodes = this.getSelectedNodes();
28343             var dragData = {
28344                 source: this,
28345                 copy: this.copy || (this.allowCopy && e.ctrlKey),
28346                 nodes: selNodes,
28347                 records: []
28348                         };
28349                         var selectedIndices = this.getSelectedIndexes();
28350                         for (var i = 0; i < selectedIndices.length; i++) {
28351                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
28352                         }
28353                         if (selNodes.length == 1) {
28354                                 dragData.ddel = target.cloneNode(true); // the div element
28355                         } else {
28356                                 var div = document.createElement('div'); // create the multi element drag "ghost"
28357                                 div.className = 'multi-proxy';
28358                                 for (var i = 0, len = selNodes.length; i < len; i++) {
28359                                         div.appendChild(selNodes[i].cloneNode(true));
28360                                 }
28361                                 dragData.ddel = div;
28362                         }
28363             //console.log(dragData)
28364             //console.log(dragData.ddel.innerHTML)
28365                         return dragData;
28366                 }
28367         //console.log('nodragData')
28368                 return false;
28369     },
28370     
28371 /**     Specify to which ddGroup items in this DDView may be dragged. */
28372     setDraggable: function(ddGroup) {
28373         if (ddGroup instanceof Array) {
28374                 Roo.each(ddGroup, this.setDraggable, this);
28375                 return;
28376         }
28377         if (this.dragZone) {
28378                 this.dragZone.addToGroup(ddGroup);
28379         } else {
28380                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
28381                                 containerScroll: true,
28382                                 ddGroup: ddGroup 
28383
28384                         });
28385 //                      Draggability implies selection. DragZone's mousedown selects the element.
28386                         if (!this.multiSelect) { this.singleSelect = true; }
28387
28388 //                      Wire the DragZone's handlers up to methods in *this*
28389                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
28390                 }
28391     },
28392
28393 /**     Specify from which ddGroup this DDView accepts drops. */
28394     setDroppable: function(ddGroup) {
28395         if (ddGroup instanceof Array) {
28396                 Roo.each(ddGroup, this.setDroppable, this);
28397                 return;
28398         }
28399         if (this.dropZone) {
28400                 this.dropZone.addToGroup(ddGroup);
28401         } else {
28402                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
28403                                 containerScroll: true,
28404                                 ddGroup: ddGroup
28405                         });
28406
28407 //                      Wire the DropZone's handlers up to methods in *this*
28408                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
28409                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
28410                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
28411                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
28412                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
28413                 }
28414     },
28415
28416 /**     Decide whether to drop above or below a View node. */
28417     getDropPoint : function(e, n, dd){
28418         if (n == this.el.dom) { return "above"; }
28419                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
28420                 var c = t + (b - t) / 2;
28421                 var y = Roo.lib.Event.getPageY(e);
28422                 if(y <= c) {
28423                         return "above";
28424                 }else{
28425                         return "below";
28426                 }
28427     },
28428
28429     onNodeEnter : function(n, dd, e, data){
28430                 return false;
28431     },
28432     
28433     onNodeOver : function(n, dd, e, data){
28434                 var pt = this.getDropPoint(e, n, dd);
28435                 // set the insert point style on the target node
28436                 var dragElClass = this.dropNotAllowed;
28437                 if (pt) {
28438                         var targetElClass;
28439                         if (pt == "above"){
28440                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
28441                                 targetElClass = "x-view-drag-insert-above";
28442                         } else {
28443                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
28444                                 targetElClass = "x-view-drag-insert-below";
28445                         }
28446                         if (this.lastInsertClass != targetElClass){
28447                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
28448                                 this.lastInsertClass = targetElClass;
28449                         }
28450                 }
28451                 return dragElClass;
28452         },
28453
28454     onNodeOut : function(n, dd, e, data){
28455                 this.removeDropIndicators(n);
28456     },
28457
28458     onNodeDrop : function(n, dd, e, data){
28459         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
28460                 return false;
28461         }
28462         var pt = this.getDropPoint(e, n, dd);
28463                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
28464                 if (pt == "below") { insertAt++; }
28465                 for (var i = 0; i < data.records.length; i++) {
28466                         var r = data.records[i];
28467                         var dup = this.store.getById(r.id);
28468                         if (dup && (dd != this.dragZone)) {
28469                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
28470                         } else {
28471                                 if (data.copy) {
28472                                         this.store.insert(insertAt++, r.copy());
28473                                 } else {
28474                                         data.source.isDirtyFlag = true;
28475                                         r.store.remove(r);
28476                                         this.store.insert(insertAt++, r);
28477                                 }
28478                                 this.isDirtyFlag = true;
28479                         }
28480                 }
28481                 this.dragZone.cachedTarget = null;
28482                 return true;
28483     },
28484
28485     removeDropIndicators : function(n){
28486                 if(n){
28487                         Roo.fly(n).removeClass([
28488                                 "x-view-drag-insert-above",
28489                                 "x-view-drag-insert-below"]);
28490                         this.lastInsertClass = "_noclass";
28491                 }
28492     },
28493
28494 /**
28495  *      Utility method. Add a delete option to the DDView's context menu.
28496  *      @param {String} imageUrl The URL of the "delete" icon image.
28497  */
28498         setDeletable: function(imageUrl) {
28499                 if (!this.singleSelect && !this.multiSelect) {
28500                         this.singleSelect = true;
28501                 }
28502                 var c = this.getContextMenu();
28503                 this.contextMenu.on("itemclick", function(item) {
28504                         switch (item.id) {
28505                                 case "delete":
28506                                         this.remove(this.getSelectedIndexes());
28507                                         break;
28508                         }
28509                 }, this);
28510                 this.contextMenu.add({
28511                         icon: imageUrl,
28512                         id: "delete",
28513                         text: 'Delete'
28514                 });
28515         },
28516         
28517 /**     Return the context menu for this DDView. */
28518         getContextMenu: function() {
28519                 if (!this.contextMenu) {
28520 //                      Create the View's context menu
28521                         this.contextMenu = new Roo.menu.Menu({
28522                                 id: this.id + "-contextmenu"
28523                         });
28524                         this.el.on("contextmenu", this.showContextMenu, this);
28525                 }
28526                 return this.contextMenu;
28527         },
28528         
28529         disableContextMenu: function() {
28530                 if (this.contextMenu) {
28531                         this.el.un("contextmenu", this.showContextMenu, this);
28532                 }
28533         },
28534
28535         showContextMenu: function(e, item) {
28536         item = this.findItemFromChild(e.getTarget());
28537                 if (item) {
28538                         e.stopEvent();
28539                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
28540                         this.contextMenu.showAt(e.getXY());
28541             }
28542     },
28543
28544 /**
28545  *      Remove {@link Roo.data.Record}s at the specified indices.
28546  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
28547  */
28548     remove: function(selectedIndices) {
28549                 selectedIndices = [].concat(selectedIndices);
28550                 for (var i = 0; i < selectedIndices.length; i++) {
28551                         var rec = this.store.getAt(selectedIndices[i]);
28552                         this.store.remove(rec);
28553                 }
28554     },
28555
28556 /**
28557  *      Double click fires the event, but also, if this is draggable, and there is only one other
28558  *      related DropZone, it transfers the selected node.
28559  */
28560     onDblClick : function(e){
28561         var item = this.findItemFromChild(e.getTarget());
28562         if(item){
28563             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
28564                 return false;
28565             }
28566             if (this.dragGroup) {
28567                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
28568                     while (targets.indexOf(this.dropZone) > -1) {
28569                             targets.remove(this.dropZone);
28570                                 }
28571                     if (targets.length == 1) {
28572                                         this.dragZone.cachedTarget = null;
28573                         var el = Roo.get(targets[0].getEl());
28574                         var box = el.getBox(true);
28575                         targets[0].onNodeDrop(el.dom, {
28576                                 target: el.dom,
28577                                 xy: [box.x, box.y + box.height - 1]
28578                         }, null, this.getDragData(e));
28579                     }
28580                 }
28581         }
28582     },
28583     
28584     handleSelection: function(e) {
28585                 this.dragZone.cachedTarget = null;
28586         var item = this.findItemFromChild(e.getTarget());
28587         if (!item) {
28588                 this.clearSelections(true);
28589                 return;
28590         }
28591                 if (item && (this.multiSelect || this.singleSelect)){
28592                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
28593                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
28594                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
28595                                 this.unselect(item);
28596                         } else {
28597                                 this.select(item, this.multiSelect && e.ctrlKey);
28598                                 this.lastSelection = item;
28599                         }
28600                 }
28601     },
28602
28603     onItemClick : function(item, index, e){
28604                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
28605                         return false;
28606                 }
28607                 return true;
28608     },
28609
28610     unselect : function(nodeInfo, suppressEvent){
28611                 var node = this.getNode(nodeInfo);
28612                 if(node && this.isSelected(node)){
28613                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28614                                 Roo.fly(node).removeClass(this.selectedClass);
28615                                 this.selections.remove(node);
28616                                 if(!suppressEvent){
28617                                         this.fireEvent("selectionchange", this, this.selections);
28618                                 }
28619                         }
28620                 }
28621     }
28622 });
28623 /*
28624  * Based on:
28625  * Ext JS Library 1.1.1
28626  * Copyright(c) 2006-2007, Ext JS, LLC.
28627  *
28628  * Originally Released Under LGPL - original licence link has changed is not relivant.
28629  *
28630  * Fork - LGPL
28631  * <script type="text/javascript">
28632  */
28633  
28634 /**
28635  * @class Roo.LayoutManager
28636  * @extends Roo.util.Observable
28637  * Base class for layout managers.
28638  */
28639 Roo.LayoutManager = function(container, config){
28640     Roo.LayoutManager.superclass.constructor.call(this);
28641     this.el = Roo.get(container);
28642     // ie scrollbar fix
28643     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
28644         document.body.scroll = "no";
28645     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
28646         this.el.position('relative');
28647     }
28648     this.id = this.el.id;
28649     this.el.addClass("x-layout-container");
28650     /** false to disable window resize monitoring @type Boolean */
28651     this.monitorWindowResize = true;
28652     this.regions = {};
28653     this.addEvents({
28654         /**
28655          * @event layout
28656          * Fires when a layout is performed. 
28657          * @param {Roo.LayoutManager} this
28658          */
28659         "layout" : true,
28660         /**
28661          * @event regionresized
28662          * Fires when the user resizes a region. 
28663          * @param {Roo.LayoutRegion} region The resized region
28664          * @param {Number} newSize The new size (width for east/west, height for north/south)
28665          */
28666         "regionresized" : true,
28667         /**
28668          * @event regioncollapsed
28669          * Fires when a region is collapsed. 
28670          * @param {Roo.LayoutRegion} region The collapsed region
28671          */
28672         "regioncollapsed" : true,
28673         /**
28674          * @event regionexpanded
28675          * Fires when a region is expanded.  
28676          * @param {Roo.LayoutRegion} region The expanded region
28677          */
28678         "regionexpanded" : true
28679     });
28680     this.updating = false;
28681     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
28682 };
28683
28684 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
28685     /**
28686      * Returns true if this layout is currently being updated
28687      * @return {Boolean}
28688      */
28689     isUpdating : function(){
28690         return this.updating; 
28691     },
28692     
28693     /**
28694      * Suspend the LayoutManager from doing auto-layouts while
28695      * making multiple add or remove calls
28696      */
28697     beginUpdate : function(){
28698         this.updating = true;    
28699     },
28700     
28701     /**
28702      * Restore auto-layouts and optionally disable the manager from performing a layout
28703      * @param {Boolean} noLayout true to disable a layout update 
28704      */
28705     endUpdate : function(noLayout){
28706         this.updating = false;
28707         if(!noLayout){
28708             this.layout();
28709         }    
28710     },
28711     
28712     layout: function(){
28713         
28714     },
28715     
28716     onRegionResized : function(region, newSize){
28717         this.fireEvent("regionresized", region, newSize);
28718         this.layout();
28719     },
28720     
28721     onRegionCollapsed : function(region){
28722         this.fireEvent("regioncollapsed", region);
28723     },
28724     
28725     onRegionExpanded : function(region){
28726         this.fireEvent("regionexpanded", region);
28727     },
28728         
28729     /**
28730      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
28731      * performs box-model adjustments.
28732      * @return {Object} The size as an object {width: (the width), height: (the height)}
28733      */
28734     getViewSize : function(){
28735         var size;
28736         if(this.el.dom != document.body){
28737             size = this.el.getSize();
28738         }else{
28739             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
28740         }
28741         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
28742         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
28743         return size;
28744     },
28745     
28746     /**
28747      * Returns the Element this layout is bound to.
28748      * @return {Roo.Element}
28749      */
28750     getEl : function(){
28751         return this.el;
28752     },
28753     
28754     /**
28755      * Returns the specified region.
28756      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
28757      * @return {Roo.LayoutRegion}
28758      */
28759     getRegion : function(target){
28760         return this.regions[target.toLowerCase()];
28761     },
28762     
28763     onWindowResize : function(){
28764         if(this.monitorWindowResize){
28765             this.layout();
28766         }
28767     }
28768 });/*
28769  * Based on:
28770  * Ext JS Library 1.1.1
28771  * Copyright(c) 2006-2007, Ext JS, LLC.
28772  *
28773  * Originally Released Under LGPL - original licence link has changed is not relivant.
28774  *
28775  * Fork - LGPL
28776  * <script type="text/javascript">
28777  */
28778 /**
28779  * @class Roo.BorderLayout
28780  * @extends Roo.LayoutManager
28781  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
28782  * please see: <br><br>
28783  * <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>
28784  * <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>
28785  * Example:
28786  <pre><code>
28787  var layout = new Roo.BorderLayout(document.body, {
28788     north: {
28789         initialSize: 25,
28790         titlebar: false
28791     },
28792     west: {
28793         split:true,
28794         initialSize: 200,
28795         minSize: 175,
28796         maxSize: 400,
28797         titlebar: true,
28798         collapsible: true
28799     },
28800     east: {
28801         split:true,
28802         initialSize: 202,
28803         minSize: 175,
28804         maxSize: 400,
28805         titlebar: true,
28806         collapsible: true
28807     },
28808     south: {
28809         split:true,
28810         initialSize: 100,
28811         minSize: 100,
28812         maxSize: 200,
28813         titlebar: true,
28814         collapsible: true
28815     },
28816     center: {
28817         titlebar: true,
28818         autoScroll:true,
28819         resizeTabs: true,
28820         minTabWidth: 50,
28821         preferredTabWidth: 150
28822     }
28823 });
28824
28825 // shorthand
28826 var CP = Roo.ContentPanel;
28827
28828 layout.beginUpdate();
28829 layout.add("north", new CP("north", "North"));
28830 layout.add("south", new CP("south", {title: "South", closable: true}));
28831 layout.add("west", new CP("west", {title: "West"}));
28832 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
28833 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
28834 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
28835 layout.getRegion("center").showPanel("center1");
28836 layout.endUpdate();
28837 </code></pre>
28838
28839 <b>The container the layout is rendered into can be either the body element or any other element.
28840 If it is not the body element, the container needs to either be an absolute positioned element,
28841 or you will need to add "position:relative" to the css of the container.  You will also need to specify
28842 the container size if it is not the body element.</b>
28843
28844 * @constructor
28845 * Create a new BorderLayout
28846 * @param {String/HTMLElement/Element} container The container this layout is bound to
28847 * @param {Object} config Configuration options
28848  */
28849 Roo.BorderLayout = function(container, config){
28850     config = config || {};
28851     Roo.BorderLayout.superclass.constructor.call(this, container, config);
28852     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
28853     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
28854         var target = this.factory.validRegions[i];
28855         if(config[target]){
28856             this.addRegion(target, config[target]);
28857         }
28858     }
28859 };
28860
28861 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
28862     /**
28863      * Creates and adds a new region if it doesn't already exist.
28864      * @param {String} target The target region key (north, south, east, west or center).
28865      * @param {Object} config The regions config object
28866      * @return {BorderLayoutRegion} The new region
28867      */
28868     addRegion : function(target, config){
28869         if(!this.regions[target]){
28870             var r = this.factory.create(target, this, config);
28871             this.bindRegion(target, r);
28872         }
28873         return this.regions[target];
28874     },
28875
28876     // private (kinda)
28877     bindRegion : function(name, r){
28878         this.regions[name] = r;
28879         r.on("visibilitychange", this.layout, this);
28880         r.on("paneladded", this.layout, this);
28881         r.on("panelremoved", this.layout, this);
28882         r.on("invalidated", this.layout, this);
28883         r.on("resized", this.onRegionResized, this);
28884         r.on("collapsed", this.onRegionCollapsed, this);
28885         r.on("expanded", this.onRegionExpanded, this);
28886     },
28887
28888     /**
28889      * Performs a layout update.
28890      */
28891     layout : function(){
28892         if(this.updating) return;
28893         var size = this.getViewSize();
28894         var w = size.width;
28895         var h = size.height;
28896         var centerW = w;
28897         var centerH = h;
28898         var centerY = 0;
28899         var centerX = 0;
28900         //var x = 0, y = 0;
28901
28902         var rs = this.regions;
28903         var north = rs["north"];
28904         var south = rs["south"]; 
28905         var west = rs["west"];
28906         var east = rs["east"];
28907         var center = rs["center"];
28908         //if(this.hideOnLayout){ // not supported anymore
28909             //c.el.setStyle("display", "none");
28910         //}
28911         if(north && north.isVisible()){
28912             var b = north.getBox();
28913             var m = north.getMargins();
28914             b.width = w - (m.left+m.right);
28915             b.x = m.left;
28916             b.y = m.top;
28917             centerY = b.height + b.y + m.bottom;
28918             centerH -= centerY;
28919             north.updateBox(this.safeBox(b));
28920         }
28921         if(south && south.isVisible()){
28922             var b = south.getBox();
28923             var m = south.getMargins();
28924             b.width = w - (m.left+m.right);
28925             b.x = m.left;
28926             var totalHeight = (b.height + m.top + m.bottom);
28927             b.y = h - totalHeight + m.top;
28928             centerH -= totalHeight;
28929             south.updateBox(this.safeBox(b));
28930         }
28931         if(west && west.isVisible()){
28932             var b = west.getBox();
28933             var m = west.getMargins();
28934             b.height = centerH - (m.top+m.bottom);
28935             b.x = m.left;
28936             b.y = centerY + m.top;
28937             var totalWidth = (b.width + m.left + m.right);
28938             centerX += totalWidth;
28939             centerW -= totalWidth;
28940             west.updateBox(this.safeBox(b));
28941         }
28942         if(east && east.isVisible()){
28943             var b = east.getBox();
28944             var m = east.getMargins();
28945             b.height = centerH - (m.top+m.bottom);
28946             var totalWidth = (b.width + m.left + m.right);
28947             b.x = w - totalWidth + m.left;
28948             b.y = centerY + m.top;
28949             centerW -= totalWidth;
28950             east.updateBox(this.safeBox(b));
28951         }
28952         if(center){
28953             var m = center.getMargins();
28954             var centerBox = {
28955                 x: centerX + m.left,
28956                 y: centerY + m.top,
28957                 width: centerW - (m.left+m.right),
28958                 height: centerH - (m.top+m.bottom)
28959             };
28960             //if(this.hideOnLayout){
28961                 //center.el.setStyle("display", "block");
28962             //}
28963             center.updateBox(this.safeBox(centerBox));
28964         }
28965         this.el.repaint();
28966         this.fireEvent("layout", this);
28967     },
28968
28969     // private
28970     safeBox : function(box){
28971         box.width = Math.max(0, box.width);
28972         box.height = Math.max(0, box.height);
28973         return box;
28974     },
28975
28976     /**
28977      * Adds a ContentPanel (or subclass) to this layout.
28978      * @param {String} target The target region key (north, south, east, west or center).
28979      * @param {Roo.ContentPanel} panel The panel to add
28980      * @return {Roo.ContentPanel} The added panel
28981      */
28982     add : function(target, panel){
28983          
28984         target = target.toLowerCase();
28985         return this.regions[target].add(panel);
28986     },
28987
28988     /**
28989      * Remove a ContentPanel (or subclass) to this layout.
28990      * @param {String} target The target region key (north, south, east, west or center).
28991      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
28992      * @return {Roo.ContentPanel} The removed panel
28993      */
28994     remove : function(target, panel){
28995         target = target.toLowerCase();
28996         return this.regions[target].remove(panel);
28997     },
28998
28999     /**
29000      * Searches all regions for a panel with the specified id
29001      * @param {String} panelId
29002      * @return {Roo.ContentPanel} The panel or null if it wasn't found
29003      */
29004     findPanel : function(panelId){
29005         var rs = this.regions;
29006         for(var target in rs){
29007             if(typeof rs[target] != "function"){
29008                 var p = rs[target].getPanel(panelId);
29009                 if(p){
29010                     return p;
29011                 }
29012             }
29013         }
29014         return null;
29015     },
29016
29017     /**
29018      * Searches all regions for a panel with the specified id and activates (shows) it.
29019      * @param {String/ContentPanel} panelId The panels id or the panel itself
29020      * @return {Roo.ContentPanel} The shown panel or null
29021      */
29022     showPanel : function(panelId) {
29023       var rs = this.regions;
29024       for(var target in rs){
29025          var r = rs[target];
29026          if(typeof r != "function"){
29027             if(r.hasPanel(panelId)){
29028                return r.showPanel(panelId);
29029             }
29030          }
29031       }
29032       return null;
29033    },
29034
29035    /**
29036      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
29037      * @param {Roo.state.Provider} provider (optional) An alternate state provider
29038      */
29039     restoreState : function(provider){
29040         if(!provider){
29041             provider = Roo.state.Manager;
29042         }
29043         var sm = new Roo.LayoutStateManager();
29044         sm.init(this, provider);
29045     },
29046
29047     /**
29048      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
29049      * object should contain properties for each region to add ContentPanels to, and each property's value should be
29050      * a valid ContentPanel config object.  Example:
29051      * <pre><code>
29052 // Create the main layout
29053 var layout = new Roo.BorderLayout('main-ct', {
29054     west: {
29055         split:true,
29056         minSize: 175,
29057         titlebar: true
29058     },
29059     center: {
29060         title:'Components'
29061     }
29062 }, 'main-ct');
29063
29064 // Create and add multiple ContentPanels at once via configs
29065 layout.batchAdd({
29066    west: {
29067        id: 'source-files',
29068        autoCreate:true,
29069        title:'Ext Source Files',
29070        autoScroll:true,
29071        fitToFrame:true
29072    },
29073    center : {
29074        el: cview,
29075        autoScroll:true,
29076        fitToFrame:true,
29077        toolbar: tb,
29078        resizeEl:'cbody'
29079    }
29080 });
29081 </code></pre>
29082      * @param {Object} regions An object containing ContentPanel configs by region name
29083      */
29084     batchAdd : function(regions){
29085         this.beginUpdate();
29086         for(var rname in regions){
29087             var lr = this.regions[rname];
29088             if(lr){
29089                 this.addTypedPanels(lr, regions[rname]);
29090             }
29091         }
29092         this.endUpdate();
29093     },
29094
29095     // private
29096     addTypedPanels : function(lr, ps){
29097         if(typeof ps == 'string'){
29098             lr.add(new Roo.ContentPanel(ps));
29099         }
29100         else if(ps instanceof Array){
29101             for(var i =0, len = ps.length; i < len; i++){
29102                 this.addTypedPanels(lr, ps[i]);
29103             }
29104         }
29105         else if(!ps.events){ // raw config?
29106             var el = ps.el;
29107             delete ps.el; // prevent conflict
29108             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
29109         }
29110         else {  // panel object assumed!
29111             lr.add(ps);
29112         }
29113     },
29114     /**
29115      * Adds a xtype elements to the layout.
29116      * <pre><code>
29117
29118 layout.addxtype({
29119        xtype : 'ContentPanel',
29120        region: 'west',
29121        items: [ .... ]
29122    }
29123 );
29124
29125 layout.addxtype({
29126         xtype : 'NestedLayoutPanel',
29127         region: 'west',
29128         layout: {
29129            center: { },
29130            west: { }   
29131         },
29132         items : [ ... list of content panels or nested layout panels.. ]
29133    }
29134 );
29135 </code></pre>
29136      * @param {Object} cfg Xtype definition of item to add.
29137      */
29138     addxtype : function(cfg)
29139     {
29140         // basically accepts a pannel...
29141         // can accept a layout region..!?!?
29142        // console.log('BorderLayout add ' + cfg.xtype)
29143         
29144         if (!cfg.xtype.match(/Panel$/)) {
29145             return false;
29146         }
29147         var ret = false;
29148         var region = cfg.region;
29149         delete cfg.region;
29150         
29151           
29152         var xitems = [];
29153         if (cfg.items) {
29154             xitems = cfg.items;
29155             delete cfg.items;
29156         }
29157         
29158         
29159         switch(cfg.xtype) 
29160         {
29161             case 'ContentPanel':  // ContentPanel (el, cfg)
29162             case 'ScrollPanel':  // ContentPanel (el, cfg)
29163                 if(cfg.autoCreate) {
29164                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29165                 } else {
29166                     var el = this.el.createChild();
29167                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
29168                 }
29169                 
29170                 this.add(region, ret);
29171                 break;
29172             
29173             
29174             case 'TreePanel': // our new panel!
29175                 cfg.el = this.el.createChild();
29176                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29177                 this.add(region, ret);
29178                 break;
29179             
29180             case 'NestedLayoutPanel': 
29181                 // create a new Layout (which is  a Border Layout...
29182                 var el = this.el.createChild();
29183                 var clayout = cfg.layout;
29184                 delete cfg.layout;
29185                 clayout.items   = clayout.items  || [];
29186                 // replace this exitems with the clayout ones..
29187                 xitems = clayout.items;
29188                  
29189                 
29190                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
29191                     cfg.background = false;
29192                 }
29193                 var layout = new Roo.BorderLayout(el, clayout);
29194                 
29195                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
29196                 //console.log('adding nested layout panel '  + cfg.toSource());
29197                 this.add(region, ret);
29198                 
29199                 break;
29200                 
29201             case 'GridPanel': 
29202             
29203                 // needs grid and region
29204                 
29205                 //var el = this.getRegion(region).el.createChild();
29206                 var el = this.el.createChild();
29207                 // create the grid first...
29208                 
29209                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
29210                 delete cfg.grid;
29211                 if (region == 'center' && this.active ) {
29212                     cfg.background = false;
29213                 }
29214                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
29215                 
29216                 this.add(region, ret);
29217                 if (cfg.background) {
29218                     ret.on('activate', function(gp) {
29219                         if (!gp.grid.rendered) {
29220                             gp.grid.render();
29221                         }
29222                     });
29223                 } else {
29224                     grid.render();
29225                 }
29226                 break;
29227            
29228                
29229                 
29230                 
29231             default: 
29232                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
29233                 return;
29234              // GridPanel (grid, cfg)
29235             
29236         }
29237         this.beginUpdate();
29238         // add children..
29239         Roo.each(xitems, function(i)  {
29240             ret.addxtype(i);
29241         });
29242         this.endUpdate();
29243         return ret;
29244         
29245     }
29246 });
29247
29248 /**
29249  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
29250  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
29251  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
29252  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
29253  * <pre><code>
29254 // shorthand
29255 var CP = Roo.ContentPanel;
29256
29257 var layout = Roo.BorderLayout.create({
29258     north: {
29259         initialSize: 25,
29260         titlebar: false,
29261         panels: [new CP("north", "North")]
29262     },
29263     west: {
29264         split:true,
29265         initialSize: 200,
29266         minSize: 175,
29267         maxSize: 400,
29268         titlebar: true,
29269         collapsible: true,
29270         panels: [new CP("west", {title: "West"})]
29271     },
29272     east: {
29273         split:true,
29274         initialSize: 202,
29275         minSize: 175,
29276         maxSize: 400,
29277         titlebar: true,
29278         collapsible: true,
29279         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
29280     },
29281     south: {
29282         split:true,
29283         initialSize: 100,
29284         minSize: 100,
29285         maxSize: 200,
29286         titlebar: true,
29287         collapsible: true,
29288         panels: [new CP("south", {title: "South", closable: true})]
29289     },
29290     center: {
29291         titlebar: true,
29292         autoScroll:true,
29293         resizeTabs: true,
29294         minTabWidth: 50,
29295         preferredTabWidth: 150,
29296         panels: [
29297             new CP("center1", {title: "Close Me", closable: true}),
29298             new CP("center2", {title: "Center Panel", closable: false})
29299         ]
29300     }
29301 }, document.body);
29302
29303 layout.getRegion("center").showPanel("center1");
29304 </code></pre>
29305  * @param config
29306  * @param targetEl
29307  */
29308 Roo.BorderLayout.create = function(config, targetEl){
29309     var layout = new Roo.BorderLayout(targetEl || document.body, config);
29310     layout.beginUpdate();
29311     var regions = Roo.BorderLayout.RegionFactory.validRegions;
29312     for(var j = 0, jlen = regions.length; j < jlen; j++){
29313         var lr = regions[j];
29314         if(layout.regions[lr] && config[lr].panels){
29315             var r = layout.regions[lr];
29316             var ps = config[lr].panels;
29317             layout.addTypedPanels(r, ps);
29318         }
29319     }
29320     layout.endUpdate();
29321     return layout;
29322 };
29323
29324 // private
29325 Roo.BorderLayout.RegionFactory = {
29326     // private
29327     validRegions : ["north","south","east","west","center"],
29328
29329     // private
29330     create : function(target, mgr, config){
29331         target = target.toLowerCase();
29332         if(config.lightweight || config.basic){
29333             return new Roo.BasicLayoutRegion(mgr, config, target);
29334         }
29335         switch(target){
29336             case "north":
29337                 return new Roo.NorthLayoutRegion(mgr, config);
29338             case "south":
29339                 return new Roo.SouthLayoutRegion(mgr, config);
29340             case "east":
29341                 return new Roo.EastLayoutRegion(mgr, config);
29342             case "west":
29343                 return new Roo.WestLayoutRegion(mgr, config);
29344             case "center":
29345                 return new Roo.CenterLayoutRegion(mgr, config);
29346         }
29347         throw 'Layout region "'+target+'" not supported.';
29348     }
29349 };/*
29350  * Based on:
29351  * Ext JS Library 1.1.1
29352  * Copyright(c) 2006-2007, Ext JS, LLC.
29353  *
29354  * Originally Released Under LGPL - original licence link has changed is not relivant.
29355  *
29356  * Fork - LGPL
29357  * <script type="text/javascript">
29358  */
29359  
29360 /**
29361  * @class Roo.BasicLayoutRegion
29362  * @extends Roo.util.Observable
29363  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
29364  * and does not have a titlebar, tabs or any other features. All it does is size and position 
29365  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
29366  */
29367 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
29368     this.mgr = mgr;
29369     this.position  = pos;
29370     this.events = {
29371         /**
29372          * @scope Roo.BasicLayoutRegion
29373          */
29374         
29375         /**
29376          * @event beforeremove
29377          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
29378          * @param {Roo.LayoutRegion} this
29379          * @param {Roo.ContentPanel} panel The panel
29380          * @param {Object} e The cancel event object
29381          */
29382         "beforeremove" : true,
29383         /**
29384          * @event invalidated
29385          * Fires when the layout for this region is changed.
29386          * @param {Roo.LayoutRegion} this
29387          */
29388         "invalidated" : true,
29389         /**
29390          * @event visibilitychange
29391          * Fires when this region is shown or hidden 
29392          * @param {Roo.LayoutRegion} this
29393          * @param {Boolean} visibility true or false
29394          */
29395         "visibilitychange" : true,
29396         /**
29397          * @event paneladded
29398          * Fires when a panel is added. 
29399          * @param {Roo.LayoutRegion} this
29400          * @param {Roo.ContentPanel} panel The panel
29401          */
29402         "paneladded" : true,
29403         /**
29404          * @event panelremoved
29405          * Fires when a panel is removed. 
29406          * @param {Roo.LayoutRegion} this
29407          * @param {Roo.ContentPanel} panel The panel
29408          */
29409         "panelremoved" : true,
29410         /**
29411          * @event collapsed
29412          * Fires when this region is collapsed.
29413          * @param {Roo.LayoutRegion} this
29414          */
29415         "collapsed" : true,
29416         /**
29417          * @event expanded
29418          * Fires when this region is expanded.
29419          * @param {Roo.LayoutRegion} this
29420          */
29421         "expanded" : true,
29422         /**
29423          * @event slideshow
29424          * Fires when this region is slid into view.
29425          * @param {Roo.LayoutRegion} this
29426          */
29427         "slideshow" : true,
29428         /**
29429          * @event slidehide
29430          * Fires when this region slides out of view. 
29431          * @param {Roo.LayoutRegion} this
29432          */
29433         "slidehide" : true,
29434         /**
29435          * @event panelactivated
29436          * Fires when a panel is activated. 
29437          * @param {Roo.LayoutRegion} this
29438          * @param {Roo.ContentPanel} panel The activated panel
29439          */
29440         "panelactivated" : true,
29441         /**
29442          * @event resized
29443          * Fires when the user resizes this region. 
29444          * @param {Roo.LayoutRegion} this
29445          * @param {Number} newSize The new size (width for east/west, height for north/south)
29446          */
29447         "resized" : true
29448     };
29449     /** A collection of panels in this region. @type Roo.util.MixedCollection */
29450     this.panels = new Roo.util.MixedCollection();
29451     this.panels.getKey = this.getPanelId.createDelegate(this);
29452     this.box = null;
29453     this.activePanel = null;
29454     // ensure listeners are added...
29455     
29456     if (config.listeners || config.events) {
29457         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
29458             listeners : config.listeners || {},
29459             events : config.events || {}
29460         });
29461     }
29462     
29463     if(skipConfig !== true){
29464         this.applyConfig(config);
29465     }
29466 };
29467
29468 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
29469     getPanelId : function(p){
29470         return p.getId();
29471     },
29472     
29473     applyConfig : function(config){
29474         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
29475         this.config = config;
29476         
29477     },
29478     
29479     /**
29480      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
29481      * the width, for horizontal (north, south) the height.
29482      * @param {Number} newSize The new width or height
29483      */
29484     resizeTo : function(newSize){
29485         var el = this.el ? this.el :
29486                  (this.activePanel ? this.activePanel.getEl() : null);
29487         if(el){
29488             switch(this.position){
29489                 case "east":
29490                 case "west":
29491                     el.setWidth(newSize);
29492                     this.fireEvent("resized", this, newSize);
29493                 break;
29494                 case "north":
29495                 case "south":
29496                     el.setHeight(newSize);
29497                     this.fireEvent("resized", this, newSize);
29498                 break;                
29499             }
29500         }
29501     },
29502     
29503     getBox : function(){
29504         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
29505     },
29506     
29507     getMargins : function(){
29508         return this.margins;
29509     },
29510     
29511     updateBox : function(box){
29512         this.box = box;
29513         var el = this.activePanel.getEl();
29514         el.dom.style.left = box.x + "px";
29515         el.dom.style.top = box.y + "px";
29516         this.activePanel.setSize(box.width, box.height);
29517     },
29518     
29519     /**
29520      * Returns the container element for this region.
29521      * @return {Roo.Element}
29522      */
29523     getEl : function(){
29524         return this.activePanel;
29525     },
29526     
29527     /**
29528      * Returns true if this region is currently visible.
29529      * @return {Boolean}
29530      */
29531     isVisible : function(){
29532         return this.activePanel ? true : false;
29533     },
29534     
29535     setActivePanel : function(panel){
29536         panel = this.getPanel(panel);
29537         if(this.activePanel && this.activePanel != panel){
29538             this.activePanel.setActiveState(false);
29539             this.activePanel.getEl().setLeftTop(-10000,-10000);
29540         }
29541         this.activePanel = panel;
29542         panel.setActiveState(true);
29543         if(this.box){
29544             panel.setSize(this.box.width, this.box.height);
29545         }
29546         this.fireEvent("panelactivated", this, panel);
29547         this.fireEvent("invalidated");
29548     },
29549     
29550     /**
29551      * Show the specified panel.
29552      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
29553      * @return {Roo.ContentPanel} The shown panel or null
29554      */
29555     showPanel : function(panel){
29556         if(panel = this.getPanel(panel)){
29557             this.setActivePanel(panel);
29558         }
29559         return panel;
29560     },
29561     
29562     /**
29563      * Get the active panel for this region.
29564      * @return {Roo.ContentPanel} The active panel or null
29565      */
29566     getActivePanel : function(){
29567         return this.activePanel;
29568     },
29569     
29570     /**
29571      * Add the passed ContentPanel(s)
29572      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
29573      * @return {Roo.ContentPanel} The panel added (if only one was added)
29574      */
29575     add : function(panel){
29576         if(arguments.length > 1){
29577             for(var i = 0, len = arguments.length; i < len; i++) {
29578                 this.add(arguments[i]);
29579             }
29580             return null;
29581         }
29582         if(this.hasPanel(panel)){
29583             this.showPanel(panel);
29584             return panel;
29585         }
29586         var el = panel.getEl();
29587         if(el.dom.parentNode != this.mgr.el.dom){
29588             this.mgr.el.dom.appendChild(el.dom);
29589         }
29590         if(panel.setRegion){
29591             panel.setRegion(this);
29592         }
29593         this.panels.add(panel);
29594         el.setStyle("position", "absolute");
29595         if(!panel.background){
29596             this.setActivePanel(panel);
29597             if(this.config.initialSize && this.panels.getCount()==1){
29598                 this.resizeTo(this.config.initialSize);
29599             }
29600         }
29601         this.fireEvent("paneladded", this, panel);
29602         return panel;
29603     },
29604     
29605     /**
29606      * Returns true if the panel is in this region.
29607      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29608      * @return {Boolean}
29609      */
29610     hasPanel : function(panel){
29611         if(typeof panel == "object"){ // must be panel obj
29612             panel = panel.getId();
29613         }
29614         return this.getPanel(panel) ? true : false;
29615     },
29616     
29617     /**
29618      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
29619      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29620      * @param {Boolean} preservePanel Overrides the config preservePanel option
29621      * @return {Roo.ContentPanel} The panel that was removed
29622      */
29623     remove : function(panel, preservePanel){
29624         panel = this.getPanel(panel);
29625         if(!panel){
29626             return null;
29627         }
29628         var e = {};
29629         this.fireEvent("beforeremove", this, panel, e);
29630         if(e.cancel === true){
29631             return null;
29632         }
29633         var panelId = panel.getId();
29634         this.panels.removeKey(panelId);
29635         return panel;
29636     },
29637     
29638     /**
29639      * Returns the panel specified or null if it's not in this region.
29640      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29641      * @return {Roo.ContentPanel}
29642      */
29643     getPanel : function(id){
29644         if(typeof id == "object"){ // must be panel obj
29645             return id;
29646         }
29647         return this.panels.get(id);
29648     },
29649     
29650     /**
29651      * Returns this regions position (north/south/east/west/center).
29652      * @return {String} 
29653      */
29654     getPosition: function(){
29655         return this.position;    
29656     }
29657 });/*
29658  * Based on:
29659  * Ext JS Library 1.1.1
29660  * Copyright(c) 2006-2007, Ext JS, LLC.
29661  *
29662  * Originally Released Under LGPL - original licence link has changed is not relivant.
29663  *
29664  * Fork - LGPL
29665  * <script type="text/javascript">
29666  */
29667  
29668 /**
29669  * @class Roo.LayoutRegion
29670  * @extends Roo.BasicLayoutRegion
29671  * This class represents a region in a layout manager.
29672  * @cfg {Boolean} collapsible False to disable collapsing (defaults to true)
29673  * @cfg {Boolean} collapsed True to set the initial display to collapsed (defaults to false)
29674  * @cfg {Boolean} floatable False to disable floating (defaults to true)
29675  * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
29676  * @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})
29677  * @cfg {String} tabPosition "top" or "bottom" (defaults to "bottom")
29678  * @cfg {String} collapsedTitle Optional string message to display in the collapsed block of a north or south region
29679  * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
29680  * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
29681  * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
29682  * @cfg {String} title The title for the region (overrides panel titles)
29683  * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
29684  * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
29685  * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
29686  * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
29687  * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
29688  * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
29689  * the space available, similar to FireFox 1.5 tabs (defaults to false)
29690  * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
29691  * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
29692  * @cfg {Boolean} showPin True to show a pin button
29693 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
29694 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
29695 * @cfg {Boolean} disableTabTips True to disable tab tooltips
29696 * @cfg {Number} width  For East/West panels
29697 * @cfg {Number} height For North/South panels
29698 * @cfg {Boolean} split To show the splitter
29699  */
29700 Roo.LayoutRegion = function(mgr, config, pos){
29701     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
29702     var dh = Roo.DomHelper;
29703     /** This region's container element 
29704     * @type Roo.Element */
29705     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
29706     /** This region's title element 
29707     * @type Roo.Element */
29708
29709     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
29710         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
29711         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
29712     ]}, true);
29713     this.titleEl.enableDisplayMode();
29714     /** This region's title text element 
29715     * @type HTMLElement */
29716     this.titleTextEl = this.titleEl.dom.firstChild;
29717     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
29718     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
29719     this.closeBtn.enableDisplayMode();
29720     this.closeBtn.on("click", this.closeClicked, this);
29721     this.closeBtn.hide();
29722
29723     this.createBody(config);
29724     this.visible = true;
29725     this.collapsed = false;
29726
29727     if(config.hideWhenEmpty){
29728         this.hide();
29729         this.on("paneladded", this.validateVisibility, this);
29730         this.on("panelremoved", this.validateVisibility, this);
29731     }
29732     this.applyConfig(config);
29733 };
29734
29735 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
29736
29737     createBody : function(){
29738         /** This region's body element 
29739         * @type Roo.Element */
29740         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
29741     },
29742
29743     applyConfig : function(c){
29744         if(c.collapsible && this.position != "center" && !this.collapsedEl){
29745             var dh = Roo.DomHelper;
29746             if(c.titlebar !== false){
29747                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
29748                 this.collapseBtn.on("click", this.collapse, this);
29749                 this.collapseBtn.enableDisplayMode();
29750
29751                 if(c.showPin === true || this.showPin){
29752                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
29753                     this.stickBtn.enableDisplayMode();
29754                     this.stickBtn.on("click", this.expand, this);
29755                     this.stickBtn.hide();
29756                 }
29757             }
29758             /** This region's collapsed element
29759             * @type Roo.Element */
29760             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
29761                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
29762             ]}, true);
29763             if(c.floatable !== false){
29764                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
29765                this.collapsedEl.on("click", this.collapseClick, this);
29766             }
29767
29768             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
29769                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
29770                    id: "message", unselectable: "on", style:{"float":"left"}});
29771                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
29772              }
29773             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
29774             this.expandBtn.on("click", this.expand, this);
29775         }
29776         if(this.collapseBtn){
29777             this.collapseBtn.setVisible(c.collapsible == true);
29778         }
29779         this.cmargins = c.cmargins || this.cmargins ||
29780                          (this.position == "west" || this.position == "east" ?
29781                              {top: 0, left: 2, right:2, bottom: 0} :
29782                              {top: 2, left: 0, right:0, bottom: 2});
29783         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
29784         this.bottomTabs = c.tabPosition != "top";
29785         this.autoScroll = c.autoScroll || false;
29786         if(this.autoScroll){
29787             this.bodyEl.setStyle("overflow", "auto");
29788         }else{
29789             this.bodyEl.setStyle("overflow", "hidden");
29790         }
29791         //if(c.titlebar !== false){
29792             if((!c.titlebar && !c.title) || c.titlebar === false){
29793                 this.titleEl.hide();
29794             }else{
29795                 this.titleEl.show();
29796                 if(c.title){
29797                     this.titleTextEl.innerHTML = c.title;
29798                 }
29799             }
29800         //}
29801         this.duration = c.duration || .30;
29802         this.slideDuration = c.slideDuration || .45;
29803         this.config = c;
29804         if(c.collapsed){
29805             this.collapse(true);
29806         }
29807         if(c.hidden){
29808             this.hide();
29809         }
29810     },
29811     /**
29812      * Returns true if this region is currently visible.
29813      * @return {Boolean}
29814      */
29815     isVisible : function(){
29816         return this.visible;
29817     },
29818
29819     /**
29820      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
29821      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
29822      */
29823     setCollapsedTitle : function(title){
29824         title = title || "&#160;";
29825         if(this.collapsedTitleTextEl){
29826             this.collapsedTitleTextEl.innerHTML = title;
29827         }
29828     },
29829
29830     getBox : function(){
29831         var b;
29832         if(!this.collapsed){
29833             b = this.el.getBox(false, true);
29834         }else{
29835             b = this.collapsedEl.getBox(false, true);
29836         }
29837         return b;
29838     },
29839
29840     getMargins : function(){
29841         return this.collapsed ? this.cmargins : this.margins;
29842     },
29843
29844     highlight : function(){
29845         this.el.addClass("x-layout-panel-dragover");
29846     },
29847
29848     unhighlight : function(){
29849         this.el.removeClass("x-layout-panel-dragover");
29850     },
29851
29852     updateBox : function(box){
29853         this.box = box;
29854         if(!this.collapsed){
29855             this.el.dom.style.left = box.x + "px";
29856             this.el.dom.style.top = box.y + "px";
29857             this.updateBody(box.width, box.height);
29858         }else{
29859             this.collapsedEl.dom.style.left = box.x + "px";
29860             this.collapsedEl.dom.style.top = box.y + "px";
29861             this.collapsedEl.setSize(box.width, box.height);
29862         }
29863         if(this.tabs){
29864             this.tabs.autoSizeTabs();
29865         }
29866     },
29867
29868     updateBody : function(w, h){
29869         if(w !== null){
29870             this.el.setWidth(w);
29871             w -= this.el.getBorderWidth("rl");
29872             if(this.config.adjustments){
29873                 w += this.config.adjustments[0];
29874             }
29875         }
29876         if(h !== null){
29877             this.el.setHeight(h);
29878             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
29879             h -= this.el.getBorderWidth("tb");
29880             if(this.config.adjustments){
29881                 h += this.config.adjustments[1];
29882             }
29883             this.bodyEl.setHeight(h);
29884             if(this.tabs){
29885                 h = this.tabs.syncHeight(h);
29886             }
29887         }
29888         if(this.panelSize){
29889             w = w !== null ? w : this.panelSize.width;
29890             h = h !== null ? h : this.panelSize.height;
29891         }
29892         if(this.activePanel){
29893             var el = this.activePanel.getEl();
29894             w = w !== null ? w : el.getWidth();
29895             h = h !== null ? h : el.getHeight();
29896             this.panelSize = {width: w, height: h};
29897             this.activePanel.setSize(w, h);
29898         }
29899         if(Roo.isIE && this.tabs){
29900             this.tabs.el.repaint();
29901         }
29902     },
29903
29904     /**
29905      * Returns the container element for this region.
29906      * @return {Roo.Element}
29907      */
29908     getEl : function(){
29909         return this.el;
29910     },
29911
29912     /**
29913      * Hides this region.
29914      */
29915     hide : function(){
29916         if(!this.collapsed){
29917             this.el.dom.style.left = "-2000px";
29918             this.el.hide();
29919         }else{
29920             this.collapsedEl.dom.style.left = "-2000px";
29921             this.collapsedEl.hide();
29922         }
29923         this.visible = false;
29924         this.fireEvent("visibilitychange", this, false);
29925     },
29926
29927     /**
29928      * Shows this region if it was previously hidden.
29929      */
29930     show : function(){
29931         if(!this.collapsed){
29932             this.el.show();
29933         }else{
29934             this.collapsedEl.show();
29935         }
29936         this.visible = true;
29937         this.fireEvent("visibilitychange", this, true);
29938     },
29939
29940     closeClicked : function(){
29941         if(this.activePanel){
29942             this.remove(this.activePanel);
29943         }
29944     },
29945
29946     collapseClick : function(e){
29947         if(this.isSlid){
29948            e.stopPropagation();
29949            this.slideIn();
29950         }else{
29951            e.stopPropagation();
29952            this.slideOut();
29953         }
29954     },
29955
29956     /**
29957      * Collapses this region.
29958      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
29959      */
29960     collapse : function(skipAnim){
29961         if(this.collapsed) return;
29962         this.collapsed = true;
29963         if(this.split){
29964             this.split.el.hide();
29965         }
29966         if(this.config.animate && skipAnim !== true){
29967             this.fireEvent("invalidated", this);
29968             this.animateCollapse();
29969         }else{
29970             this.el.setLocation(-20000,-20000);
29971             this.el.hide();
29972             this.collapsedEl.show();
29973             this.fireEvent("collapsed", this);
29974             this.fireEvent("invalidated", this);
29975         }
29976     },
29977
29978     animateCollapse : function(){
29979         // overridden
29980     },
29981
29982     /**
29983      * Expands this region if it was previously collapsed.
29984      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
29985      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
29986      */
29987     expand : function(e, skipAnim){
29988         if(e) e.stopPropagation();
29989         if(!this.collapsed || this.el.hasActiveFx()) return;
29990         if(this.isSlid){
29991             this.afterSlideIn();
29992             skipAnim = true;
29993         }
29994         this.collapsed = false;
29995         if(this.config.animate && skipAnim !== true){
29996             this.animateExpand();
29997         }else{
29998             this.el.show();
29999             if(this.split){
30000                 this.split.el.show();
30001             }
30002             this.collapsedEl.setLocation(-2000,-2000);
30003             this.collapsedEl.hide();
30004             this.fireEvent("invalidated", this);
30005             this.fireEvent("expanded", this);
30006         }
30007     },
30008
30009     animateExpand : function(){
30010         // overridden
30011     },
30012
30013     initTabs : function(){
30014         this.bodyEl.setStyle("overflow", "hidden");
30015         var ts = new Roo.TabPanel(this.bodyEl.dom, {
30016             tabPosition: this.bottomTabs ? 'bottom' : 'top',
30017             disableTooltips: this.config.disableTabTips
30018         });
30019         if(this.config.hideTabs){
30020             ts.stripWrap.setDisplayed(false);
30021         }
30022         this.tabs = ts;
30023         ts.resizeTabs = this.config.resizeTabs === true;
30024         ts.minTabWidth = this.config.minTabWidth || 40;
30025         ts.maxTabWidth = this.config.maxTabWidth || 250;
30026         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
30027         ts.monitorResize = false;
30028         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30029         ts.bodyEl.addClass('x-layout-tabs-body');
30030         this.panels.each(this.initPanelAsTab, this);
30031     },
30032
30033     initPanelAsTab : function(panel){
30034         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
30035                     this.config.closeOnTab && panel.isClosable());
30036         if(panel.tabTip !== undefined){
30037             ti.setTooltip(panel.tabTip);
30038         }
30039         ti.on("activate", function(){
30040               this.setActivePanel(panel);
30041         }, this);
30042         if(this.config.closeOnTab){
30043             ti.on("beforeclose", function(t, e){
30044                 e.cancel = true;
30045                 this.remove(panel);
30046             }, this);
30047         }
30048         return ti;
30049     },
30050
30051     updatePanelTitle : function(panel, title){
30052         if(this.activePanel == panel){
30053             this.updateTitle(title);
30054         }
30055         if(this.tabs){
30056             var ti = this.tabs.getTab(panel.getEl().id);
30057             ti.setText(title);
30058             if(panel.tabTip !== undefined){
30059                 ti.setTooltip(panel.tabTip);
30060             }
30061         }
30062     },
30063
30064     updateTitle : function(title){
30065         if(this.titleTextEl && !this.config.title){
30066             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
30067         }
30068     },
30069
30070     setActivePanel : function(panel){
30071         panel = this.getPanel(panel);
30072         if(this.activePanel && this.activePanel != panel){
30073             this.activePanel.setActiveState(false);
30074         }
30075         this.activePanel = panel;
30076         panel.setActiveState(true);
30077         if(this.panelSize){
30078             panel.setSize(this.panelSize.width, this.panelSize.height);
30079         }
30080         if(this.closeBtn){
30081             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
30082         }
30083         this.updateTitle(panel.getTitle());
30084         if(this.tabs){
30085             this.fireEvent("invalidated", this);
30086         }
30087         this.fireEvent("panelactivated", this, panel);
30088     },
30089
30090     /**
30091      * Shows the specified panel.
30092      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
30093      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
30094      */
30095     showPanel : function(panel){
30096         if(panel = this.getPanel(panel)){
30097             if(this.tabs){
30098                 var tab = this.tabs.getTab(panel.getEl().id);
30099                 if(tab.isHidden()){
30100                     this.tabs.unhideTab(tab.id);
30101                 }
30102                 tab.activate();
30103             }else{
30104                 this.setActivePanel(panel);
30105             }
30106         }
30107         return panel;
30108     },
30109
30110     /**
30111      * Get the active panel for this region.
30112      * @return {Roo.ContentPanel} The active panel or null
30113      */
30114     getActivePanel : function(){
30115         return this.activePanel;
30116     },
30117
30118     validateVisibility : function(){
30119         if(this.panels.getCount() < 1){
30120             this.updateTitle("&#160;");
30121             this.closeBtn.hide();
30122             this.hide();
30123         }else{
30124             if(!this.isVisible()){
30125                 this.show();
30126             }
30127         }
30128     },
30129
30130     /**
30131      * Adds the passed ContentPanel(s) to this region.
30132      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
30133      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
30134      */
30135     add : function(panel){
30136         if(arguments.length > 1){
30137             for(var i = 0, len = arguments.length; i < len; i++) {
30138                 this.add(arguments[i]);
30139             }
30140             return null;
30141         }
30142         if(this.hasPanel(panel)){
30143             this.showPanel(panel);
30144             return panel;
30145         }
30146         panel.setRegion(this);
30147         this.panels.add(panel);
30148         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
30149             this.bodyEl.dom.appendChild(panel.getEl().dom);
30150             if(panel.background !== true){
30151                 this.setActivePanel(panel);
30152             }
30153             this.fireEvent("paneladded", this, panel);
30154             return panel;
30155         }
30156         if(!this.tabs){
30157             this.initTabs();
30158         }else{
30159             this.initPanelAsTab(panel);
30160         }
30161         if(panel.background !== true){
30162             this.tabs.activate(panel.getEl().id);
30163         }
30164         this.fireEvent("paneladded", this, panel);
30165         return panel;
30166     },
30167
30168     /**
30169      * Hides the tab for the specified panel.
30170      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30171      */
30172     hidePanel : function(panel){
30173         if(this.tabs && (panel = this.getPanel(panel))){
30174             this.tabs.hideTab(panel.getEl().id);
30175         }
30176     },
30177
30178     /**
30179      * Unhides the tab for a previously hidden panel.
30180      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30181      */
30182     unhidePanel : function(panel){
30183         if(this.tabs && (panel = this.getPanel(panel))){
30184             this.tabs.unhideTab(panel.getEl().id);
30185         }
30186     },
30187
30188     clearPanels : function(){
30189         while(this.panels.getCount() > 0){
30190              this.remove(this.panels.first());
30191         }
30192     },
30193
30194     /**
30195      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
30196      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30197      * @param {Boolean} preservePanel Overrides the config preservePanel option
30198      * @return {Roo.ContentPanel} The panel that was removed
30199      */
30200     remove : function(panel, preservePanel){
30201         panel = this.getPanel(panel);
30202         if(!panel){
30203             return null;
30204         }
30205         var e = {};
30206         this.fireEvent("beforeremove", this, panel, e);
30207         if(e.cancel === true){
30208             return null;
30209         }
30210         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
30211         var panelId = panel.getId();
30212         this.panels.removeKey(panelId);
30213         if(preservePanel){
30214             document.body.appendChild(panel.getEl().dom);
30215         }
30216         if(this.tabs){
30217             this.tabs.removeTab(panel.getEl().id);
30218         }else if (!preservePanel){
30219             this.bodyEl.dom.removeChild(panel.getEl().dom);
30220         }
30221         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
30222             var p = this.panels.first();
30223             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
30224             tempEl.appendChild(p.getEl().dom);
30225             this.bodyEl.update("");
30226             this.bodyEl.dom.appendChild(p.getEl().dom);
30227             tempEl = null;
30228             this.updateTitle(p.getTitle());
30229             this.tabs = null;
30230             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30231             this.setActivePanel(p);
30232         }
30233         panel.setRegion(null);
30234         if(this.activePanel == panel){
30235             this.activePanel = null;
30236         }
30237         if(this.config.autoDestroy !== false && preservePanel !== true){
30238             try{panel.destroy();}catch(e){}
30239         }
30240         this.fireEvent("panelremoved", this, panel);
30241         return panel;
30242     },
30243
30244     /**
30245      * Returns the TabPanel component used by this region
30246      * @return {Roo.TabPanel}
30247      */
30248     getTabs : function(){
30249         return this.tabs;
30250     },
30251
30252     createTool : function(parentEl, className){
30253         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
30254             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
30255         btn.addClassOnOver("x-layout-tools-button-over");
30256         return btn;
30257     }
30258 });/*
30259  * Based on:
30260  * Ext JS Library 1.1.1
30261  * Copyright(c) 2006-2007, Ext JS, LLC.
30262  *
30263  * Originally Released Under LGPL - original licence link has changed is not relivant.
30264  *
30265  * Fork - LGPL
30266  * <script type="text/javascript">
30267  */
30268  
30269
30270
30271 /**
30272  * @class Roo.SplitLayoutRegion
30273  * @extends Roo.LayoutRegion
30274  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
30275  */
30276 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
30277     this.cursor = cursor;
30278     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
30279 };
30280
30281 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
30282     splitTip : "Drag to resize.",
30283     collapsibleSplitTip : "Drag to resize. Double click to hide.",
30284     useSplitTips : false,
30285
30286     applyConfig : function(config){
30287         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
30288         if(config.split){
30289             if(!this.split){
30290                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
30291                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
30292                 /** The SplitBar for this region 
30293                 * @type Roo.SplitBar */
30294                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
30295                 this.split.on("moved", this.onSplitMove, this);
30296                 this.split.useShim = config.useShim === true;
30297                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
30298                 if(this.useSplitTips){
30299                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
30300                 }
30301                 if(config.collapsible){
30302                     this.split.el.on("dblclick", this.collapse,  this);
30303                 }
30304             }
30305             if(typeof config.minSize != "undefined"){
30306                 this.split.minSize = config.minSize;
30307             }
30308             if(typeof config.maxSize != "undefined"){
30309                 this.split.maxSize = config.maxSize;
30310             }
30311             if(config.hideWhenEmpty || config.hidden || config.collapsed){
30312                 this.hideSplitter();
30313             }
30314         }
30315     },
30316
30317     getHMaxSize : function(){
30318          var cmax = this.config.maxSize || 10000;
30319          var center = this.mgr.getRegion("center");
30320          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
30321     },
30322
30323     getVMaxSize : function(){
30324          var cmax = this.config.maxSize || 10000;
30325          var center = this.mgr.getRegion("center");
30326          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
30327     },
30328
30329     onSplitMove : function(split, newSize){
30330         this.fireEvent("resized", this, newSize);
30331     },
30332     
30333     /** 
30334      * Returns the {@link Roo.SplitBar} for this region.
30335      * @return {Roo.SplitBar}
30336      */
30337     getSplitBar : function(){
30338         return this.split;
30339     },
30340     
30341     hide : function(){
30342         this.hideSplitter();
30343         Roo.SplitLayoutRegion.superclass.hide.call(this);
30344     },
30345
30346     hideSplitter : function(){
30347         if(this.split){
30348             this.split.el.setLocation(-2000,-2000);
30349             this.split.el.hide();
30350         }
30351     },
30352
30353     show : function(){
30354         if(this.split){
30355             this.split.el.show();
30356         }
30357         Roo.SplitLayoutRegion.superclass.show.call(this);
30358     },
30359     
30360     beforeSlide: function(){
30361         if(Roo.isGecko){// firefox overflow auto bug workaround
30362             this.bodyEl.clip();
30363             if(this.tabs) this.tabs.bodyEl.clip();
30364             if(this.activePanel){
30365                 this.activePanel.getEl().clip();
30366                 
30367                 if(this.activePanel.beforeSlide){
30368                     this.activePanel.beforeSlide();
30369                 }
30370             }
30371         }
30372     },
30373     
30374     afterSlide : function(){
30375         if(Roo.isGecko){// firefox overflow auto bug workaround
30376             this.bodyEl.unclip();
30377             if(this.tabs) this.tabs.bodyEl.unclip();
30378             if(this.activePanel){
30379                 this.activePanel.getEl().unclip();
30380                 if(this.activePanel.afterSlide){
30381                     this.activePanel.afterSlide();
30382                 }
30383             }
30384         }
30385     },
30386
30387     initAutoHide : function(){
30388         if(this.autoHide !== false){
30389             if(!this.autoHideHd){
30390                 var st = new Roo.util.DelayedTask(this.slideIn, this);
30391                 this.autoHideHd = {
30392                     "mouseout": function(e){
30393                         if(!e.within(this.el, true)){
30394                             st.delay(500);
30395                         }
30396                     },
30397                     "mouseover" : function(e){
30398                         st.cancel();
30399                     },
30400                     scope : this
30401                 };
30402             }
30403             this.el.on(this.autoHideHd);
30404         }
30405     },
30406
30407     clearAutoHide : function(){
30408         if(this.autoHide !== false){
30409             this.el.un("mouseout", this.autoHideHd.mouseout);
30410             this.el.un("mouseover", this.autoHideHd.mouseover);
30411         }
30412     },
30413
30414     clearMonitor : function(){
30415         Roo.get(document).un("click", this.slideInIf, this);
30416     },
30417
30418     // these names are backwards but not changed for compat
30419     slideOut : function(){
30420         if(this.isSlid || this.el.hasActiveFx()){
30421             return;
30422         }
30423         this.isSlid = true;
30424         if(this.collapseBtn){
30425             this.collapseBtn.hide();
30426         }
30427         this.closeBtnState = this.closeBtn.getStyle('display');
30428         this.closeBtn.hide();
30429         if(this.stickBtn){
30430             this.stickBtn.show();
30431         }
30432         this.el.show();
30433         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
30434         this.beforeSlide();
30435         this.el.setStyle("z-index", 10001);
30436         this.el.slideIn(this.getSlideAnchor(), {
30437             callback: function(){
30438                 this.afterSlide();
30439                 this.initAutoHide();
30440                 Roo.get(document).on("click", this.slideInIf, this);
30441                 this.fireEvent("slideshow", this);
30442             },
30443             scope: this,
30444             block: true
30445         });
30446     },
30447
30448     afterSlideIn : function(){
30449         this.clearAutoHide();
30450         this.isSlid = false;
30451         this.clearMonitor();
30452         this.el.setStyle("z-index", "");
30453         if(this.collapseBtn){
30454             this.collapseBtn.show();
30455         }
30456         this.closeBtn.setStyle('display', this.closeBtnState);
30457         if(this.stickBtn){
30458             this.stickBtn.hide();
30459         }
30460         this.fireEvent("slidehide", this);
30461     },
30462
30463     slideIn : function(cb){
30464         if(!this.isSlid || this.el.hasActiveFx()){
30465             Roo.callback(cb);
30466             return;
30467         }
30468         this.isSlid = false;
30469         this.beforeSlide();
30470         this.el.slideOut(this.getSlideAnchor(), {
30471             callback: function(){
30472                 this.el.setLeftTop(-10000, -10000);
30473                 this.afterSlide();
30474                 this.afterSlideIn();
30475                 Roo.callback(cb);
30476             },
30477             scope: this,
30478             block: true
30479         });
30480     },
30481     
30482     slideInIf : function(e){
30483         if(!e.within(this.el)){
30484             this.slideIn();
30485         }
30486     },
30487
30488     animateCollapse : function(){
30489         this.beforeSlide();
30490         this.el.setStyle("z-index", 20000);
30491         var anchor = this.getSlideAnchor();
30492         this.el.slideOut(anchor, {
30493             callback : function(){
30494                 this.el.setStyle("z-index", "");
30495                 this.collapsedEl.slideIn(anchor, {duration:.3});
30496                 this.afterSlide();
30497                 this.el.setLocation(-10000,-10000);
30498                 this.el.hide();
30499                 this.fireEvent("collapsed", this);
30500             },
30501             scope: this,
30502             block: true
30503         });
30504     },
30505
30506     animateExpand : function(){
30507         this.beforeSlide();
30508         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
30509         this.el.setStyle("z-index", 20000);
30510         this.collapsedEl.hide({
30511             duration:.1
30512         });
30513         this.el.slideIn(this.getSlideAnchor(), {
30514             callback : function(){
30515                 this.el.setStyle("z-index", "");
30516                 this.afterSlide();
30517                 if(this.split){
30518                     this.split.el.show();
30519                 }
30520                 this.fireEvent("invalidated", this);
30521                 this.fireEvent("expanded", this);
30522             },
30523             scope: this,
30524             block: true
30525         });
30526     },
30527
30528     anchors : {
30529         "west" : "left",
30530         "east" : "right",
30531         "north" : "top",
30532         "south" : "bottom"
30533     },
30534
30535     sanchors : {
30536         "west" : "l",
30537         "east" : "r",
30538         "north" : "t",
30539         "south" : "b"
30540     },
30541
30542     canchors : {
30543         "west" : "tl-tr",
30544         "east" : "tr-tl",
30545         "north" : "tl-bl",
30546         "south" : "bl-tl"
30547     },
30548
30549     getAnchor : function(){
30550         return this.anchors[this.position];
30551     },
30552
30553     getCollapseAnchor : function(){
30554         return this.canchors[this.position];
30555     },
30556
30557     getSlideAnchor : function(){
30558         return this.sanchors[this.position];
30559     },
30560
30561     getAlignAdj : function(){
30562         var cm = this.cmargins;
30563         switch(this.position){
30564             case "west":
30565                 return [0, 0];
30566             break;
30567             case "east":
30568                 return [0, 0];
30569             break;
30570             case "north":
30571                 return [0, 0];
30572             break;
30573             case "south":
30574                 return [0, 0];
30575             break;
30576         }
30577     },
30578
30579     getExpandAdj : function(){
30580         var c = this.collapsedEl, cm = this.cmargins;
30581         switch(this.position){
30582             case "west":
30583                 return [-(cm.right+c.getWidth()+cm.left), 0];
30584             break;
30585             case "east":
30586                 return [cm.right+c.getWidth()+cm.left, 0];
30587             break;
30588             case "north":
30589                 return [0, -(cm.top+cm.bottom+c.getHeight())];
30590             break;
30591             case "south":
30592                 return [0, cm.top+cm.bottom+c.getHeight()];
30593             break;
30594         }
30595     }
30596 });/*
30597  * Based on:
30598  * Ext JS Library 1.1.1
30599  * Copyright(c) 2006-2007, Ext JS, LLC.
30600  *
30601  * Originally Released Under LGPL - original licence link has changed is not relivant.
30602  *
30603  * Fork - LGPL
30604  * <script type="text/javascript">
30605  */
30606 /*
30607  * These classes are private internal classes
30608  */
30609 Roo.CenterLayoutRegion = function(mgr, config){
30610     Roo.LayoutRegion.call(this, mgr, config, "center");
30611     this.visible = true;
30612     this.minWidth = config.minWidth || 20;
30613     this.minHeight = config.minHeight || 20;
30614 };
30615
30616 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
30617     hide : function(){
30618         // center panel can't be hidden
30619     },
30620     
30621     show : function(){
30622         // center panel can't be hidden
30623     },
30624     
30625     getMinWidth: function(){
30626         return this.minWidth;
30627     },
30628     
30629     getMinHeight: function(){
30630         return this.minHeight;
30631     }
30632 });
30633
30634
30635 Roo.NorthLayoutRegion = function(mgr, config){
30636     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
30637     if(this.split){
30638         this.split.placement = Roo.SplitBar.TOP;
30639         this.split.orientation = Roo.SplitBar.VERTICAL;
30640         this.split.el.addClass("x-layout-split-v");
30641     }
30642     var size = config.initialSize || config.height;
30643     if(typeof size != "undefined"){
30644         this.el.setHeight(size);
30645     }
30646 };
30647 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
30648     orientation: Roo.SplitBar.VERTICAL,
30649     getBox : function(){
30650         if(this.collapsed){
30651             return this.collapsedEl.getBox();
30652         }
30653         var box = this.el.getBox();
30654         if(this.split){
30655             box.height += this.split.el.getHeight();
30656         }
30657         return box;
30658     },
30659     
30660     updateBox : function(box){
30661         if(this.split && !this.collapsed){
30662             box.height -= this.split.el.getHeight();
30663             this.split.el.setLeft(box.x);
30664             this.split.el.setTop(box.y+box.height);
30665             this.split.el.setWidth(box.width);
30666         }
30667         if(this.collapsed){
30668             this.updateBody(box.width, null);
30669         }
30670         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30671     }
30672 });
30673
30674 Roo.SouthLayoutRegion = function(mgr, config){
30675     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
30676     if(this.split){
30677         this.split.placement = Roo.SplitBar.BOTTOM;
30678         this.split.orientation = Roo.SplitBar.VERTICAL;
30679         this.split.el.addClass("x-layout-split-v");
30680     }
30681     var size = config.initialSize || config.height;
30682     if(typeof size != "undefined"){
30683         this.el.setHeight(size);
30684     }
30685 };
30686 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
30687     orientation: Roo.SplitBar.VERTICAL,
30688     getBox : function(){
30689         if(this.collapsed){
30690             return this.collapsedEl.getBox();
30691         }
30692         var box = this.el.getBox();
30693         if(this.split){
30694             var sh = this.split.el.getHeight();
30695             box.height += sh;
30696             box.y -= sh;
30697         }
30698         return box;
30699     },
30700     
30701     updateBox : function(box){
30702         if(this.split && !this.collapsed){
30703             var sh = this.split.el.getHeight();
30704             box.height -= sh;
30705             box.y += sh;
30706             this.split.el.setLeft(box.x);
30707             this.split.el.setTop(box.y-sh);
30708             this.split.el.setWidth(box.width);
30709         }
30710         if(this.collapsed){
30711             this.updateBody(box.width, null);
30712         }
30713         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30714     }
30715 });
30716
30717 Roo.EastLayoutRegion = function(mgr, config){
30718     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
30719     if(this.split){
30720         this.split.placement = Roo.SplitBar.RIGHT;
30721         this.split.orientation = Roo.SplitBar.HORIZONTAL;
30722         this.split.el.addClass("x-layout-split-h");
30723     }
30724     var size = config.initialSize || config.width;
30725     if(typeof size != "undefined"){
30726         this.el.setWidth(size);
30727     }
30728 };
30729 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
30730     orientation: Roo.SplitBar.HORIZONTAL,
30731     getBox : function(){
30732         if(this.collapsed){
30733             return this.collapsedEl.getBox();
30734         }
30735         var box = this.el.getBox();
30736         if(this.split){
30737             var sw = this.split.el.getWidth();
30738             box.width += sw;
30739             box.x -= sw;
30740         }
30741         return box;
30742     },
30743
30744     updateBox : function(box){
30745         if(this.split && !this.collapsed){
30746             var sw = this.split.el.getWidth();
30747             box.width -= sw;
30748             this.split.el.setLeft(box.x);
30749             this.split.el.setTop(box.y);
30750             this.split.el.setHeight(box.height);
30751             box.x += sw;
30752         }
30753         if(this.collapsed){
30754             this.updateBody(null, box.height);
30755         }
30756         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30757     }
30758 });
30759
30760 Roo.WestLayoutRegion = function(mgr, config){
30761     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
30762     if(this.split){
30763         this.split.placement = Roo.SplitBar.LEFT;
30764         this.split.orientation = Roo.SplitBar.HORIZONTAL;
30765         this.split.el.addClass("x-layout-split-h");
30766     }
30767     var size = config.initialSize || config.width;
30768     if(typeof size != "undefined"){
30769         this.el.setWidth(size);
30770     }
30771 };
30772 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
30773     orientation: Roo.SplitBar.HORIZONTAL,
30774     getBox : function(){
30775         if(this.collapsed){
30776             return this.collapsedEl.getBox();
30777         }
30778         var box = this.el.getBox();
30779         if(this.split){
30780             box.width += this.split.el.getWidth();
30781         }
30782         return box;
30783     },
30784     
30785     updateBox : function(box){
30786         if(this.split && !this.collapsed){
30787             var sw = this.split.el.getWidth();
30788             box.width -= sw;
30789             this.split.el.setLeft(box.x+box.width);
30790             this.split.el.setTop(box.y);
30791             this.split.el.setHeight(box.height);
30792         }
30793         if(this.collapsed){
30794             this.updateBody(null, box.height);
30795         }
30796         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30797     }
30798 });
30799 /*
30800  * Based on:
30801  * Ext JS Library 1.1.1
30802  * Copyright(c) 2006-2007, Ext JS, LLC.
30803  *
30804  * Originally Released Under LGPL - original licence link has changed is not relivant.
30805  *
30806  * Fork - LGPL
30807  * <script type="text/javascript">
30808  */
30809  
30810  
30811 /*
30812  * Private internal class for reading and applying state
30813  */
30814 Roo.LayoutStateManager = function(layout){
30815      // default empty state
30816      this.state = {
30817         north: {},
30818         south: {},
30819         east: {},
30820         west: {}       
30821     };
30822 };
30823
30824 Roo.LayoutStateManager.prototype = {
30825     init : function(layout, provider){
30826         this.provider = provider;
30827         var state = provider.get(layout.id+"-layout-state");
30828         if(state){
30829             var wasUpdating = layout.isUpdating();
30830             if(!wasUpdating){
30831                 layout.beginUpdate();
30832             }
30833             for(var key in state){
30834                 if(typeof state[key] != "function"){
30835                     var rstate = state[key];
30836                     var r = layout.getRegion(key);
30837                     if(r && rstate){
30838                         if(rstate.size){
30839                             r.resizeTo(rstate.size);
30840                         }
30841                         if(rstate.collapsed == true){
30842                             r.collapse(true);
30843                         }else{
30844                             r.expand(null, true);
30845                         }
30846                     }
30847                 }
30848             }
30849             if(!wasUpdating){
30850                 layout.endUpdate();
30851             }
30852             this.state = state; 
30853         }
30854         this.layout = layout;
30855         layout.on("regionresized", this.onRegionResized, this);
30856         layout.on("regioncollapsed", this.onRegionCollapsed, this);
30857         layout.on("regionexpanded", this.onRegionExpanded, this);
30858     },
30859     
30860     storeState : function(){
30861         this.provider.set(this.layout.id+"-layout-state", this.state);
30862     },
30863     
30864     onRegionResized : function(region, newSize){
30865         this.state[region.getPosition()].size = newSize;
30866         this.storeState();
30867     },
30868     
30869     onRegionCollapsed : function(region){
30870         this.state[region.getPosition()].collapsed = true;
30871         this.storeState();
30872     },
30873     
30874     onRegionExpanded : function(region){
30875         this.state[region.getPosition()].collapsed = false;
30876         this.storeState();
30877     }
30878 };/*
30879  * Based on:
30880  * Ext JS Library 1.1.1
30881  * Copyright(c) 2006-2007, Ext JS, LLC.
30882  *
30883  * Originally Released Under LGPL - original licence link has changed is not relivant.
30884  *
30885  * Fork - LGPL
30886  * <script type="text/javascript">
30887  */
30888 /**
30889  * @class Roo.ContentPanel
30890  * @extends Roo.util.Observable
30891  * A basic ContentPanel element.
30892  * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes  (defaults to false)
30893  * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
30894  * @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
30895  * @cfg {Boolean} closable True if the panel can be closed/removed
30896  * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
30897  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
30898  * @cfg {Toolbar} toolbar A toolbar for this panel
30899  * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
30900  * @cfg {String} title The title for this panel
30901  * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
30902  * @cfg {String} url Calls {@link #setUrl} with this value
30903  * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
30904  * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
30905  * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
30906  * @constructor
30907  * Create a new ContentPanel.
30908  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
30909  * @param {String/Object} config A string to set only the title or a config object
30910  * @param {String} content (optional) Set the HTML content for this panel
30911  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
30912  */
30913 Roo.ContentPanel = function(el, config, content){
30914     
30915      
30916     /*
30917     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
30918         config = el;
30919         el = Roo.id();
30920     }
30921     if (config && config.parentLayout) { 
30922         el = config.parentLayout.el.createChild(); 
30923     }
30924     */
30925     if(el.autoCreate){ // xtype is available if this is called from factory
30926         config = el;
30927         el = Roo.id();
30928     }
30929     this.el = Roo.get(el);
30930     if(!this.el && config && config.autoCreate){
30931         if(typeof config.autoCreate == "object"){
30932             if(!config.autoCreate.id){
30933                 config.autoCreate.id = config.id||el;
30934             }
30935             this.el = Roo.DomHelper.append(document.body,
30936                         config.autoCreate, true);
30937         }else{
30938             this.el = Roo.DomHelper.append(document.body,
30939                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
30940         }
30941     }
30942     this.closable = false;
30943     this.loaded = false;
30944     this.active = false;
30945     if(typeof config == "string"){
30946         this.title = config;
30947     }else{
30948         Roo.apply(this, config);
30949     }
30950     
30951     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
30952         this.wrapEl = this.el.wrap();    
30953         this.toolbar = new Roo.Toolbar(this.el.insertSibling(false, 'before'), [] , this.toolbar);
30954         
30955     }
30956     
30957     
30958     
30959     if(this.resizeEl){
30960         this.resizeEl = Roo.get(this.resizeEl, true);
30961     }else{
30962         this.resizeEl = this.el;
30963     }
30964     this.addEvents({
30965         /**
30966          * @event activate
30967          * Fires when this panel is activated. 
30968          * @param {Roo.ContentPanel} this
30969          */
30970         "activate" : true,
30971         /**
30972          * @event deactivate
30973          * Fires when this panel is activated. 
30974          * @param {Roo.ContentPanel} this
30975          */
30976         "deactivate" : true,
30977
30978         /**
30979          * @event resize
30980          * Fires when this panel is resized if fitToFrame is true.
30981          * @param {Roo.ContentPanel} this
30982          * @param {Number} width The width after any component adjustments
30983          * @param {Number} height The height after any component adjustments
30984          */
30985         "resize" : true
30986     });
30987     if(this.autoScroll){
30988         this.resizeEl.setStyle("overflow", "auto");
30989     } else {
30990         // fix randome scrolling
30991         this.el.on('scroll', function() {
30992             this.scrollTo('top',0); 
30993         });
30994     }
30995     content = content || this.content;
30996     if(content){
30997         this.setContent(content);
30998     }
30999     if(config && config.url){
31000         this.setUrl(this.url, this.params, this.loadOnce);
31001     }
31002     
31003     
31004     
31005     Roo.ContentPanel.superclass.constructor.call(this);
31006 };
31007
31008 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
31009     tabTip:'',
31010     setRegion : function(region){
31011         this.region = region;
31012         if(region){
31013            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
31014         }else{
31015            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
31016         } 
31017     },
31018     
31019     /**
31020      * Returns the toolbar for this Panel if one was configured. 
31021      * @return {Roo.Toolbar} 
31022      */
31023     getToolbar : function(){
31024         return this.toolbar;
31025     },
31026     
31027     setActiveState : function(active){
31028         this.active = active;
31029         if(!active){
31030             this.fireEvent("deactivate", this);
31031         }else{
31032             this.fireEvent("activate", this);
31033         }
31034     },
31035     /**
31036      * Updates this panel's element
31037      * @param {String} content The new content
31038      * @param {Boolean} loadScripts (optional) true to look for and process scripts
31039     */
31040     setContent : function(content, loadScripts){
31041         this.el.update(content, loadScripts);
31042     },
31043
31044     ignoreResize : function(w, h){
31045         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
31046             return true;
31047         }else{
31048             this.lastSize = {width: w, height: h};
31049             return false;
31050         }
31051     },
31052     /**
31053      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
31054      * @return {Roo.UpdateManager} The UpdateManager
31055      */
31056     getUpdateManager : function(){
31057         return this.el.getUpdateManager();
31058     },
31059      /**
31060      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
31061      * @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:
31062 <pre><code>
31063 panel.load({
31064     url: "your-url.php",
31065     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
31066     callback: yourFunction,
31067     scope: yourObject, //(optional scope)
31068     discardUrl: false,
31069     nocache: false,
31070     text: "Loading...",
31071     timeout: 30,
31072     scripts: false
31073 });
31074 </code></pre>
31075      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
31076      * 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.
31077      * @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}
31078      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
31079      * @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.
31080      * @return {Roo.ContentPanel} this
31081      */
31082     load : function(){
31083         var um = this.el.getUpdateManager();
31084         um.update.apply(um, arguments);
31085         return this;
31086     },
31087
31088
31089     /**
31090      * 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.
31091      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
31092      * @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)
31093      * @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)
31094      * @return {Roo.UpdateManager} The UpdateManager
31095      */
31096     setUrl : function(url, params, loadOnce){
31097         if(this.refreshDelegate){
31098             this.removeListener("activate", this.refreshDelegate);
31099         }
31100         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
31101         this.on("activate", this.refreshDelegate);
31102         return this.el.getUpdateManager();
31103     },
31104     
31105     _handleRefresh : function(url, params, loadOnce){
31106         if(!loadOnce || !this.loaded){
31107             var updater = this.el.getUpdateManager();
31108             updater.update(url, params, this._setLoaded.createDelegate(this));
31109         }
31110     },
31111     
31112     _setLoaded : function(){
31113         this.loaded = true;
31114     }, 
31115     
31116     /**
31117      * Returns this panel's id
31118      * @return {String} 
31119      */
31120     getId : function(){
31121         return this.el.id;
31122     },
31123     
31124     /** 
31125      * Returns this panel's element - used by regiosn to add.
31126      * @return {Roo.Element} 
31127      */
31128     getEl : function(){
31129         return this.wrapEl || this.el;
31130     },
31131     
31132     adjustForComponents : function(width, height){
31133         if(this.resizeEl != this.el){
31134             width -= this.el.getFrameWidth('lr');
31135             height -= this.el.getFrameWidth('tb');
31136         }
31137         if(this.toolbar){
31138             var te = this.toolbar.getEl();
31139             height -= te.getHeight();
31140             te.setWidth(width);
31141         }
31142         if(this.adjustments){
31143             width += this.adjustments[0];
31144             height += this.adjustments[1];
31145         }
31146         return {"width": width, "height": height};
31147     },
31148     
31149     setSize : function(width, height){
31150         if(this.fitToFrame && !this.ignoreResize(width, height)){
31151             if(this.fitContainer && this.resizeEl != this.el){
31152                 this.el.setSize(width, height);
31153             }
31154             var size = this.adjustForComponents(width, height);
31155             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
31156             this.fireEvent('resize', this, size.width, size.height);
31157         }
31158     },
31159     
31160     /**
31161      * Returns this panel's title
31162      * @return {String} 
31163      */
31164     getTitle : function(){
31165         return this.title;
31166     },
31167     
31168     /**
31169      * Set this panel's title
31170      * @param {String} title
31171      */
31172     setTitle : function(title){
31173         this.title = title;
31174         if(this.region){
31175             this.region.updatePanelTitle(this, title);
31176         }
31177     },
31178     
31179     /**
31180      * Returns true is this panel was configured to be closable
31181      * @return {Boolean} 
31182      */
31183     isClosable : function(){
31184         return this.closable;
31185     },
31186     
31187     beforeSlide : function(){
31188         this.el.clip();
31189         this.resizeEl.clip();
31190     },
31191     
31192     afterSlide : function(){
31193         this.el.unclip();
31194         this.resizeEl.unclip();
31195     },
31196     
31197     /**
31198      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
31199      *   Will fail silently if the {@link #setUrl} method has not been called.
31200      *   This does not activate the panel, just updates its content.
31201      */
31202     refresh : function(){
31203         if(this.refreshDelegate){
31204            this.loaded = false;
31205            this.refreshDelegate();
31206         }
31207     },
31208     
31209     /**
31210      * Destroys this panel
31211      */
31212     destroy : function(){
31213         this.el.removeAllListeners();
31214         var tempEl = document.createElement("span");
31215         tempEl.appendChild(this.el.dom);
31216         tempEl.innerHTML = "";
31217         this.el.remove();
31218         this.el = null;
31219     },
31220     
31221       /**
31222      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
31223      * <pre><code>
31224
31225 layout.addxtype({
31226        xtype : 'Form',
31227        items: [ .... ]
31228    }
31229 );
31230
31231 </code></pre>
31232      * @param {Object} cfg Xtype definition of item to add.
31233      */
31234     
31235     addxtype : function(cfg) {
31236         // add form..
31237         if (cfg.xtype.match(/^Form$/)) {
31238             var el = this.el.createChild();
31239
31240             this.form = new  Roo.form.Form(cfg);
31241             
31242             
31243             if ( this.form.allItems.length) this.form.render(el.dom);
31244             return this.form;
31245         }
31246         if (['View', 'JsonView'].indexOf(cfg.xtype) > -1) {
31247             // views..
31248             cfg.el = this.el.appendChild(document.createElement("div"));
31249             // factory?
31250             var ret = new Roo[cfg.xtype](cfg);
31251             ret.render(false, ''); // render blank..
31252             return ret;
31253             
31254         }
31255         return false;
31256         
31257     }
31258 });
31259
31260 /**
31261  * @class Roo.GridPanel
31262  * @extends Roo.ContentPanel
31263  * @constructor
31264  * Create a new GridPanel.
31265  * @param {Roo.grid.Grid} grid The grid for this panel
31266  * @param {String/Object} config A string to set only the panel's title, or a config object
31267  */
31268 Roo.GridPanel = function(grid, config){
31269     
31270   
31271     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
31272         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
31273         
31274     this.wrapper.dom.appendChild(grid.getGridEl().dom);
31275     
31276     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
31277     
31278     if(this.toolbar){
31279         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
31280     }
31281     // xtype created footer. - not sure if will work as we normally have to render first..
31282     if (this.footer && !this.footer.el && this.footer.xtype) {
31283         
31284         this.footer.container = this.grid.getView().getFooterPanel(true);
31285         this.footer.dataSource = this.grid.dataSource;
31286         this.footer = Roo.factory(this.footer, Roo);
31287         
31288     }
31289     
31290     grid.monitorWindowResize = false; // turn off autosizing
31291     grid.autoHeight = false;
31292     grid.autoWidth = false;
31293     this.grid = grid;
31294     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
31295 };
31296
31297 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
31298     getId : function(){
31299         return this.grid.id;
31300     },
31301     
31302     /**
31303      * Returns the grid for this panel
31304      * @return {Roo.grid.Grid} 
31305      */
31306     getGrid : function(){
31307         return this.grid;    
31308     },
31309     
31310     setSize : function(width, height){
31311         if(!this.ignoreResize(width, height)){
31312             var grid = this.grid;
31313             var size = this.adjustForComponents(width, height);
31314             grid.getGridEl().setSize(size.width, size.height);
31315             grid.autoSize();
31316         }
31317     },
31318     
31319     beforeSlide : function(){
31320         this.grid.getView().scroller.clip();
31321     },
31322     
31323     afterSlide : function(){
31324         this.grid.getView().scroller.unclip();
31325     },
31326     
31327     destroy : function(){
31328         this.grid.destroy();
31329         delete this.grid;
31330         Roo.GridPanel.superclass.destroy.call(this); 
31331     }
31332 });
31333
31334
31335 /**
31336  * @class Roo.NestedLayoutPanel
31337  * @extends Roo.ContentPanel
31338  * @constructor
31339  * Create a new NestedLayoutPanel.
31340  * 
31341  * 
31342  * @param {Roo.BorderLayout} layout The layout for this panel
31343  * @param {String/Object} config A string to set only the title or a config object
31344  */
31345 Roo.NestedLayoutPanel = function(layout, config)
31346 {
31347     // construct with only one argument..
31348     /* FIXME - implement nicer consturctors
31349     if (layout.layout) {
31350         config = layout;
31351         layout = config.layout;
31352         delete config.layout;
31353     }
31354     if (layout.xtype && !layout.getEl) {
31355         // then layout needs constructing..
31356         layout = Roo.factory(layout, Roo);
31357     }
31358     */
31359     
31360     
31361     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
31362     
31363     layout.monitorWindowResize = false; // turn off autosizing
31364     this.layout = layout;
31365     this.layout.getEl().addClass("x-layout-nested-layout");
31366     
31367     
31368     
31369     
31370 };
31371
31372 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
31373
31374     setSize : function(width, height){
31375         if(!this.ignoreResize(width, height)){
31376             var size = this.adjustForComponents(width, height);
31377             var el = this.layout.getEl();
31378             el.setSize(size.width, size.height);
31379             var touch = el.dom.offsetWidth;
31380             this.layout.layout();
31381             // ie requires a double layout on the first pass
31382             if(Roo.isIE && !this.initialized){
31383                 this.initialized = true;
31384                 this.layout.layout();
31385             }
31386         }
31387     },
31388     
31389     // activate all subpanels if not currently active..
31390     
31391     setActiveState : function(active){
31392         this.active = active;
31393         if(!active){
31394             this.fireEvent("deactivate", this);
31395             return;
31396         }
31397         
31398         this.fireEvent("activate", this);
31399         // not sure if this should happen before or after..
31400         if (!this.layout) {
31401             return; // should not happen..
31402         }
31403         var reg = false;
31404         for (var r in this.layout.regions) {
31405             reg = this.layout.getRegion(r);
31406             if (reg.getActivePanel()) {
31407                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
31408                 reg.setActivePanel(reg.getActivePanel());
31409                 continue;
31410             }
31411             if (!reg.panels.length) {
31412                 continue;
31413             }
31414             reg.showPanel(reg.getPanel(0));
31415         }
31416         
31417         
31418         
31419         
31420     },
31421     
31422     /**
31423      * Returns the nested BorderLayout for this panel
31424      * @return {Roo.BorderLayout} 
31425      */
31426     getLayout : function(){
31427         return this.layout;
31428     },
31429     
31430      /**
31431      * Adds a xtype elements to the layout of the nested panel
31432      * <pre><code>
31433
31434 panel.addxtype({
31435        xtype : 'ContentPanel',
31436        region: 'west',
31437        items: [ .... ]
31438    }
31439 );
31440
31441 panel.addxtype({
31442         xtype : 'NestedLayoutPanel',
31443         region: 'west',
31444         layout: {
31445            center: { },
31446            west: { }   
31447         },
31448         items : [ ... list of content panels or nested layout panels.. ]
31449    }
31450 );
31451 </code></pre>
31452      * @param {Object} cfg Xtype definition of item to add.
31453      */
31454     addxtype : function(cfg) {
31455         return this.layout.addxtype(cfg);
31456     
31457     }
31458 });
31459
31460 Roo.ScrollPanel = function(el, config, content){
31461     config = config || {};
31462     config.fitToFrame = true;
31463     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
31464     
31465     this.el.dom.style.overflow = "hidden";
31466     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
31467     this.el.removeClass("x-layout-inactive-content");
31468     this.el.on("mousewheel", this.onWheel, this);
31469
31470     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
31471     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
31472     up.unselectable(); down.unselectable();
31473     up.on("click", this.scrollUp, this);
31474     down.on("click", this.scrollDown, this);
31475     up.addClassOnOver("x-scroller-btn-over");
31476     down.addClassOnOver("x-scroller-btn-over");
31477     up.addClassOnClick("x-scroller-btn-click");
31478     down.addClassOnClick("x-scroller-btn-click");
31479     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
31480
31481     this.resizeEl = this.el;
31482     this.el = wrap; this.up = up; this.down = down;
31483 };
31484
31485 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
31486     increment : 100,
31487     wheelIncrement : 5,
31488     scrollUp : function(){
31489         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
31490     },
31491
31492     scrollDown : function(){
31493         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
31494     },
31495
31496     afterScroll : function(){
31497         var el = this.resizeEl;
31498         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
31499         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31500         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31501     },
31502
31503     setSize : function(){
31504         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
31505         this.afterScroll();
31506     },
31507
31508     onWheel : function(e){
31509         var d = e.getWheelDelta();
31510         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
31511         this.afterScroll();
31512         e.stopEvent();
31513     },
31514
31515     setContent : function(content, loadScripts){
31516         this.resizeEl.update(content, loadScripts);
31517     }
31518
31519 });
31520
31521
31522
31523
31524
31525
31526
31527
31528
31529 /**
31530  * @class Roo.TreePanel
31531  * @extends Roo.ContentPanel
31532  * @constructor
31533  * Create a new TreePanel. - defaults to fit/scoll contents.
31534  * @param {String/Object} config A string to set only the panel's title, or a config object
31535  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
31536  */
31537 Roo.TreePanel = function(config){
31538     var el = config.el;
31539     var tree = config.tree;
31540     delete config.tree; 
31541     delete config.el; // hopefull!
31542     
31543     // wrapper for IE7 strict & safari scroll issue
31544     
31545     var treeEl = el.createChild();
31546     config.resizeEl = treeEl;
31547     
31548     
31549     
31550     Roo.TreePanel.superclass.constructor.call(this, el, config);
31551  
31552  
31553     this.tree = new Roo.tree.TreePanel(treeEl , tree);
31554     //console.log(tree);
31555     this.on('activate', function()
31556     {
31557         if (this.tree.rendered) {
31558             return;
31559         }
31560         //console.log('render tree');
31561         this.tree.render();
31562     });
31563     
31564     this.on('resize',  function (cp, w, h) {
31565             this.tree.innerCt.setWidth(w);
31566             this.tree.innerCt.setHeight(h);
31567             this.tree.innerCt.setStyle('overflow-y', 'auto');
31568     });
31569
31570         
31571     
31572 };
31573
31574 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
31575     fitToFrame : true,
31576     autoScroll : true
31577 });
31578
31579
31580
31581
31582
31583
31584
31585
31586
31587
31588
31589 /*
31590  * Based on:
31591  * Ext JS Library 1.1.1
31592  * Copyright(c) 2006-2007, Ext JS, LLC.
31593  *
31594  * Originally Released Under LGPL - original licence link has changed is not relivant.
31595  *
31596  * Fork - LGPL
31597  * <script type="text/javascript">
31598  */
31599  
31600
31601 /**
31602  * @class Roo.ReaderLayout
31603  * @extends Roo.BorderLayout
31604  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
31605  * center region containing two nested regions (a top one for a list view and one for item preview below),
31606  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
31607  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
31608  * expedites the setup of the overall layout and regions for this common application style.
31609  * Example:
31610  <pre><code>
31611 var reader = new Roo.ReaderLayout();
31612 var CP = Roo.ContentPanel;  // shortcut for adding
31613
31614 reader.beginUpdate();
31615 reader.add("north", new CP("north", "North"));
31616 reader.add("west", new CP("west", {title: "West"}));
31617 reader.add("east", new CP("east", {title: "East"}));
31618
31619 reader.regions.listView.add(new CP("listView", "List"));
31620 reader.regions.preview.add(new CP("preview", "Preview"));
31621 reader.endUpdate();
31622 </code></pre>
31623 * @constructor
31624 * Create a new ReaderLayout
31625 * @param {Object} config Configuration options
31626 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
31627 * document.body if omitted)
31628 */
31629 Roo.ReaderLayout = function(config, renderTo){
31630     var c = config || {size:{}};
31631     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
31632         north: c.north !== false ? Roo.apply({
31633             split:false,
31634             initialSize: 32,
31635             titlebar: false
31636         }, c.north) : false,
31637         west: c.west !== false ? Roo.apply({
31638             split:true,
31639             initialSize: 200,
31640             minSize: 175,
31641             maxSize: 400,
31642             titlebar: true,
31643             collapsible: true,
31644             animate: true,
31645             margins:{left:5,right:0,bottom:5,top:5},
31646             cmargins:{left:5,right:5,bottom:5,top:5}
31647         }, c.west) : false,
31648         east: c.east !== 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:0,right:5,bottom:5,top:5},
31657             cmargins:{left:5,right:5,bottom:5,top:5}
31658         }, c.east) : false,
31659         center: Roo.apply({
31660             tabPosition: 'top',
31661             autoScroll:false,
31662             closeOnTab: true,
31663             titlebar:false,
31664             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
31665         }, c.center)
31666     });
31667
31668     this.el.addClass('x-reader');
31669
31670     this.beginUpdate();
31671
31672     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
31673         south: c.preview !== false ? Roo.apply({
31674             split:true,
31675             initialSize: 200,
31676             minSize: 100,
31677             autoScroll:true,
31678             collapsible:true,
31679             titlebar: true,
31680             cmargins:{top:5,left:0, right:0, bottom:0}
31681         }, c.preview) : false,
31682         center: Roo.apply({
31683             autoScroll:false,
31684             titlebar:false,
31685             minHeight:200
31686         }, c.listView)
31687     });
31688     this.add('center', new Roo.NestedLayoutPanel(inner,
31689             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
31690
31691     this.endUpdate();
31692
31693     this.regions.preview = inner.getRegion('south');
31694     this.regions.listView = inner.getRegion('center');
31695 };
31696
31697 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
31698  * Based on:
31699  * Ext JS Library 1.1.1
31700  * Copyright(c) 2006-2007, Ext JS, LLC.
31701  *
31702  * Originally Released Under LGPL - original licence link has changed is not relivant.
31703  *
31704  * Fork - LGPL
31705  * <script type="text/javascript">
31706  */
31707  
31708 /**
31709  * @class Roo.grid.Grid
31710  * @extends Roo.util.Observable
31711  * This class represents the primary interface of a component based grid control.
31712  * <br><br>Usage:<pre><code>
31713  var grid = new Roo.grid.Grid("my-container-id", {
31714      ds: myDataStore,
31715      cm: myColModel,
31716      selModel: mySelectionModel,
31717      autoSizeColumns: true,
31718      monitorWindowResize: false,
31719      trackMouseOver: true
31720  });
31721  // set any options
31722  grid.render();
31723  * </code></pre>
31724  * <b>Common Problems:</b><br/>
31725  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
31726  * element will correct this<br/>
31727  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
31728  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
31729  * are unpredictable.<br/>
31730  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
31731  * grid to calculate dimensions/offsets.<br/>
31732   * @constructor
31733  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
31734  * The container MUST have some type of size defined for the grid to fill. The container will be
31735  * automatically set to position relative if it isn't already.
31736  * @param {Object} config A config object that sets properties on this grid.
31737  */
31738 Roo.grid.Grid = function(container, config){
31739         // initialize the container
31740         this.container = Roo.get(container);
31741         this.container.update("");
31742         this.container.setStyle("overflow", "hidden");
31743     this.container.addClass('x-grid-container');
31744
31745     this.id = this.container.id;
31746
31747     Roo.apply(this, config);
31748     // check and correct shorthanded configs
31749     if(this.ds){
31750         this.dataSource = this.ds;
31751         delete this.ds;
31752     }
31753     if(this.cm){
31754         this.colModel = this.cm;
31755         delete this.cm;
31756     }
31757     if(this.sm){
31758         this.selModel = this.sm;
31759         delete this.sm;
31760     }
31761
31762     if (this.selModel) {
31763         this.selModel = Roo.factory(this.selModel, Roo.grid);
31764         this.sm = this.selModel;
31765         this.sm.xmodule = this.xmodule || false;
31766     }
31767     if (typeof(this.colModel.config) == 'undefined') {
31768         this.colModel = new Roo.grid.ColumnModel(this.colModel);
31769         this.cm = this.colModel;
31770         this.cm.xmodule = this.xmodule || false;
31771     }
31772     if (this.dataSource) {
31773         this.dataSource= Roo.factory(this.dataSource, Roo.data);
31774         this.ds = this.dataSource;
31775         this.ds.xmodule = this.xmodule || false;
31776         
31777     }
31778     
31779     
31780     
31781     if(this.width){
31782         this.container.setWidth(this.width);
31783     }
31784
31785     if(this.height){
31786         this.container.setHeight(this.height);
31787     }
31788     /** @private */
31789         this.addEvents({
31790             // raw events
31791             /**
31792              * @event click
31793              * The raw click event for the entire grid.
31794              * @param {Roo.EventObject} e
31795              */
31796             "click" : true,
31797             /**
31798              * @event dblclick
31799              * The raw dblclick event for the entire grid.
31800              * @param {Roo.EventObject} e
31801              */
31802             "dblclick" : true,
31803             /**
31804              * @event contextmenu
31805              * The raw contextmenu event for the entire grid.
31806              * @param {Roo.EventObject} e
31807              */
31808             "contextmenu" : true,
31809             /**
31810              * @event mousedown
31811              * The raw mousedown event for the entire grid.
31812              * @param {Roo.EventObject} e
31813              */
31814             "mousedown" : true,
31815             /**
31816              * @event mouseup
31817              * The raw mouseup event for the entire grid.
31818              * @param {Roo.EventObject} e
31819              */
31820             "mouseup" : true,
31821             /**
31822              * @event mouseover
31823              * The raw mouseover event for the entire grid.
31824              * @param {Roo.EventObject} e
31825              */
31826             "mouseover" : true,
31827             /**
31828              * @event mouseout
31829              * The raw mouseout event for the entire grid.
31830              * @param {Roo.EventObject} e
31831              */
31832             "mouseout" : true,
31833             /**
31834              * @event keypress
31835              * The raw keypress event for the entire grid.
31836              * @param {Roo.EventObject} e
31837              */
31838             "keypress" : true,
31839             /**
31840              * @event keydown
31841              * The raw keydown event for the entire grid.
31842              * @param {Roo.EventObject} e
31843              */
31844             "keydown" : true,
31845
31846             // custom events
31847
31848             /**
31849              * @event cellclick
31850              * Fires when a cell is clicked
31851              * @param {Grid} this
31852              * @param {Number} rowIndex
31853              * @param {Number} columnIndex
31854              * @param {Roo.EventObject} e
31855              */
31856             "cellclick" : true,
31857             /**
31858              * @event celldblclick
31859              * Fires when a cell is double clicked
31860              * @param {Grid} this
31861              * @param {Number} rowIndex
31862              * @param {Number} columnIndex
31863              * @param {Roo.EventObject} e
31864              */
31865             "celldblclick" : true,
31866             /**
31867              * @event rowclick
31868              * Fires when a row is clicked
31869              * @param {Grid} this
31870              * @param {Number} rowIndex
31871              * @param {Roo.EventObject} e
31872              */
31873             "rowclick" : true,
31874             /**
31875              * @event rowdblclick
31876              * Fires when a row is double clicked
31877              * @param {Grid} this
31878              * @param {Number} rowIndex
31879              * @param {Roo.EventObject} e
31880              */
31881             "rowdblclick" : true,
31882             /**
31883              * @event headerclick
31884              * Fires when a header is clicked
31885              * @param {Grid} this
31886              * @param {Number} columnIndex
31887              * @param {Roo.EventObject} e
31888              */
31889             "headerclick" : true,
31890             /**
31891              * @event headerdblclick
31892              * Fires when a header cell is double clicked
31893              * @param {Grid} this
31894              * @param {Number} columnIndex
31895              * @param {Roo.EventObject} e
31896              */
31897             "headerdblclick" : true,
31898             /**
31899              * @event rowcontextmenu
31900              * Fires when a row is right clicked
31901              * @param {Grid} this
31902              * @param {Number} rowIndex
31903              * @param {Roo.EventObject} e
31904              */
31905             "rowcontextmenu" : true,
31906             /**
31907          * @event cellcontextmenu
31908          * Fires when a cell is right clicked
31909          * @param {Grid} this
31910          * @param {Number} rowIndex
31911          * @param {Number} cellIndex
31912          * @param {Roo.EventObject} e
31913          */
31914          "cellcontextmenu" : true,
31915             /**
31916              * @event headercontextmenu
31917              * Fires when a header is right clicked
31918              * @param {Grid} this
31919              * @param {Number} columnIndex
31920              * @param {Roo.EventObject} e
31921              */
31922             "headercontextmenu" : true,
31923             /**
31924              * @event bodyscroll
31925              * Fires when the body element is scrolled
31926              * @param {Number} scrollLeft
31927              * @param {Number} scrollTop
31928              */
31929             "bodyscroll" : true,
31930             /**
31931              * @event columnresize
31932              * Fires when the user resizes a column
31933              * @param {Number} columnIndex
31934              * @param {Number} newSize
31935              */
31936             "columnresize" : true,
31937             /**
31938              * @event columnmove
31939              * Fires when the user moves a column
31940              * @param {Number} oldIndex
31941              * @param {Number} newIndex
31942              */
31943             "columnmove" : true,
31944             /**
31945              * @event startdrag
31946              * Fires when row(s) start being dragged
31947              * @param {Grid} this
31948              * @param {Roo.GridDD} dd The drag drop object
31949              * @param {event} e The raw browser event
31950              */
31951             "startdrag" : true,
31952             /**
31953              * @event enddrag
31954              * Fires when a drag operation is complete
31955              * @param {Grid} this
31956              * @param {Roo.GridDD} dd The drag drop object
31957              * @param {event} e The raw browser event
31958              */
31959             "enddrag" : true,
31960             /**
31961              * @event dragdrop
31962              * Fires when dragged row(s) are dropped on a valid DD target
31963              * @param {Grid} this
31964              * @param {Roo.GridDD} dd The drag drop object
31965              * @param {String} targetId The target drag drop object
31966              * @param {event} e The raw browser event
31967              */
31968             "dragdrop" : true,
31969             /**
31970              * @event dragover
31971              * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
31972              * @param {Grid} this
31973              * @param {Roo.GridDD} dd The drag drop object
31974              * @param {String} targetId The target drag drop object
31975              * @param {event} e The raw browser event
31976              */
31977             "dragover" : true,
31978             /**
31979              * @event dragenter
31980              *  Fires when the dragged row(s) first cross another DD target while being dragged
31981              * @param {Grid} this
31982              * @param {Roo.GridDD} dd The drag drop object
31983              * @param {String} targetId The target drag drop object
31984              * @param {event} e The raw browser event
31985              */
31986             "dragenter" : true,
31987             /**
31988              * @event dragout
31989              * Fires when the dragged row(s) leave another DD target while being dragged
31990              * @param {Grid} this
31991              * @param {Roo.GridDD} dd The drag drop object
31992              * @param {String} targetId The target drag drop object
31993              * @param {event} e The raw browser event
31994              */
31995             "dragout" : true,
31996         /**
31997          * @event render
31998          * Fires when the grid is rendered
31999          * @param {Grid} grid
32000          */
32001         render : true
32002     });
32003
32004     Roo.grid.Grid.superclass.constructor.call(this);
32005 };
32006 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
32007     
32008     /**
32009      * @cfg {String} ddGroup - drag drop group.
32010          */
32011     
32012     /**
32013      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
32014          */
32015         minColumnWidth : 25,
32016
32017     /**
32018          * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
32019          * <b>on initial render.</b> It is more efficient to explicitly size the columns
32020          * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
32021          */
32022         autoSizeColumns : false,
32023
32024         /**
32025          * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
32026          */
32027         autoSizeHeaders : true,
32028
32029         /**
32030          * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
32031          */
32032         monitorWindowResize : true,
32033
32034         /**
32035          * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
32036          * rows measured to get a columns size. Default is 0 (all rows).
32037          */
32038         maxRowsToMeasure : 0,
32039
32040         /**
32041          * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
32042          */
32043         trackMouseOver : true,
32044
32045     /**
32046          * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
32047          */
32048     
32049         /**
32050          * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
32051          */
32052         enableDragDrop : false,
32053
32054         /**
32055          * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
32056          */
32057         enableColumnMove : true,
32058
32059         /**
32060          * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
32061          */
32062         enableColumnHide : true,
32063
32064         /**
32065          * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
32066          */
32067         enableRowHeightSync : false,
32068
32069         /**
32070          * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
32071          */
32072         stripeRows : true,
32073
32074         /**
32075          * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
32076          */
32077         autoHeight : false,
32078
32079     /**
32080      * @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.
32081      */
32082     autoExpandColumn : false,
32083
32084     /**
32085     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
32086     * Default is 50.
32087     */
32088     autoExpandMin : 50,
32089
32090     /**
32091     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
32092     */
32093     autoExpandMax : 1000,
32094
32095     /**
32096          * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
32097          */
32098         view : null,
32099
32100         /**
32101      * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
32102          */
32103         loadMask : false,
32104
32105     // private
32106     rendered : false,
32107
32108     /**
32109     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
32110     * of a fixed width. Default is false.
32111     */
32112     /**
32113     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
32114     */
32115     /**
32116      * Called once after all setup has been completed and the grid is ready to be rendered.
32117      * @return {Roo.grid.Grid} this
32118      */
32119     render : function(){
32120         var c = this.container;
32121         // try to detect autoHeight/width mode
32122         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
32123             this.autoHeight = true;
32124         }
32125         var view = this.getView();
32126         view.init(this);
32127
32128         c.on("click", this.onClick, this);
32129         c.on("dblclick", this.onDblClick, this);
32130         c.on("contextmenu", this.onContextMenu, this);
32131         c.on("keydown", this.onKeyDown, this);
32132
32133         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
32134
32135         this.getSelectionModel().init(this);
32136
32137         view.render();
32138
32139         if(this.loadMask){
32140             this.loadMask = new Roo.LoadMask(this.container,
32141                     Roo.apply({store:this.dataSource}, this.loadMask));
32142         }
32143         
32144         
32145         if (this.toolbar && this.toolbar.xtype) {
32146             this.toolbar.container = this.getView().getHeaderPanel(true);
32147             this.toolbar = new Ext.Toolbar(this.toolbar);
32148         }
32149         if (this.footer && this.footer.xtype) {
32150             this.footer.dataSource = this.getDataSource();
32151             this.footer.container = this.getView().getFooterPanel(true);
32152             this.footer = Roo.factory(this.footer, Roo);
32153         }
32154         if (this.dragTarget && this.dragTarget) {
32155             delete this.dragTarget.xtype;
32156             this.dragTarget =  new Ext.dd.DropTarget(this.getView().mainBody, this.dragTarget);
32157         }
32158         
32159         
32160         this.rendered = true;
32161         this.fireEvent('render', this);
32162         return this;
32163     },
32164
32165         /**
32166          * Reconfigures the grid to use a different Store and Column Model.
32167          * The View will be bound to the new objects and refreshed.
32168          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
32169          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
32170          */
32171     reconfigure : function(dataSource, colModel){
32172         if(this.loadMask){
32173             this.loadMask.destroy();
32174             this.loadMask = new Roo.LoadMask(this.container,
32175                     Roo.apply({store:dataSource}, this.loadMask));
32176         }
32177         this.view.bind(dataSource, colModel);
32178         this.dataSource = dataSource;
32179         this.colModel = colModel;
32180         this.view.refresh(true);
32181     },
32182
32183     // private
32184     onKeyDown : function(e){
32185         this.fireEvent("keydown", e);
32186     },
32187
32188     /**
32189      * Destroy this grid.
32190      * @param {Boolean} removeEl True to remove the element
32191      */
32192     destroy : function(removeEl, keepListeners){
32193         if(this.loadMask){
32194             this.loadMask.destroy();
32195         }
32196         var c = this.container;
32197         c.removeAllListeners();
32198         this.view.destroy();
32199         this.colModel.purgeListeners();
32200         if(!keepListeners){
32201             this.purgeListeners();
32202         }
32203         c.update("");
32204         if(removeEl === true){
32205             c.remove();
32206         }
32207     },
32208
32209     // private
32210     processEvent : function(name, e){
32211         this.fireEvent(name, e);
32212         var t = e.getTarget();
32213         var v = this.view;
32214         var header = v.findHeaderIndex(t);
32215         if(header !== false){
32216             this.fireEvent("header" + name, this, header, e);
32217         }else{
32218             var row = v.findRowIndex(t);
32219             var cell = v.findCellIndex(t);
32220             if(row !== false){
32221                 this.fireEvent("row" + name, this, row, e);
32222                 if(cell !== false){
32223                     this.fireEvent("cell" + name, this, row, cell, e);
32224                 }
32225             }
32226         }
32227     },
32228
32229     // private
32230     onClick : function(e){
32231         this.processEvent("click", e);
32232     },
32233
32234     // private
32235     onContextMenu : function(e, t){
32236         this.processEvent("contextmenu", e);
32237     },
32238
32239     // private
32240     onDblClick : function(e){
32241         this.processEvent("dblclick", e);
32242     },
32243
32244     // private
32245     walkCells : function(row, col, step, fn, scope){
32246         var cm = this.colModel, clen = cm.getColumnCount();
32247         var ds = this.dataSource, rlen = ds.getCount(), first = true;
32248         if(step < 0){
32249             if(col < 0){
32250                 row--;
32251                 first = false;
32252             }
32253             while(row >= 0){
32254                 if(!first){
32255                     col = clen-1;
32256                 }
32257                 first = false;
32258                 while(col >= 0){
32259                     if(fn.call(scope || this, row, col, cm) === true){
32260                         return [row, col];
32261                     }
32262                     col--;
32263                 }
32264                 row--;
32265             }
32266         } else {
32267             if(col >= clen){
32268                 row++;
32269                 first = false;
32270             }
32271             while(row < rlen){
32272                 if(!first){
32273                     col = 0;
32274                 }
32275                 first = false;
32276                 while(col < clen){
32277                     if(fn.call(scope || this, row, col, cm) === true){
32278                         return [row, col];
32279                     }
32280                     col++;
32281                 }
32282                 row++;
32283             }
32284         }
32285         return null;
32286     },
32287
32288     // private
32289     getSelections : function(){
32290         return this.selModel.getSelections();
32291     },
32292
32293     /**
32294      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
32295      * but if manual update is required this method will initiate it.
32296      */
32297     autoSize : function(){
32298         if(this.rendered){
32299             this.view.layout();
32300             if(this.view.adjustForScroll){
32301                 this.view.adjustForScroll();
32302             }
32303         }
32304     },
32305
32306     /**
32307      * Returns the grid's underlying element.
32308      * @return {Element} The element
32309      */
32310     getGridEl : function(){
32311         return this.container;
32312     },
32313
32314     // private for compatibility, overridden by editor grid
32315     stopEditing : function(){},
32316
32317     /**
32318      * Returns the grid's SelectionModel.
32319      * @return {SelectionModel}
32320      */
32321     getSelectionModel : function(){
32322         if(!this.selModel){
32323             this.selModel = new Roo.grid.RowSelectionModel();
32324         }
32325         return this.selModel;
32326     },
32327
32328     /**
32329      * Returns the grid's DataSource.
32330      * @return {DataSource}
32331      */
32332     getDataSource : function(){
32333         return this.dataSource;
32334     },
32335
32336     /**
32337      * Returns the grid's ColumnModel.
32338      * @return {ColumnModel}
32339      */
32340     getColumnModel : function(){
32341         return this.colModel;
32342     },
32343
32344     /**
32345      * Returns the grid's GridView object.
32346      * @return {GridView}
32347      */
32348     getView : function(){
32349         if(!this.view){
32350             this.view = new Roo.grid.GridView(this.viewConfig);
32351         }
32352         return this.view;
32353     },
32354     /**
32355      * Called to get grid's drag proxy text, by default returns this.ddText.
32356      * @return {String}
32357      */
32358     getDragDropText : function(){
32359         var count = this.selModel.getCount();
32360         return String.format(this.ddText, count, count == 1 ? '' : 's');
32361     }
32362 });
32363 /**
32364  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
32365  * %0 is replaced with the number of selected rows.
32366  * @type String
32367  */
32368 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
32369  * Based on:
32370  * Ext JS Library 1.1.1
32371  * Copyright(c) 2006-2007, Ext JS, LLC.
32372  *
32373  * Originally Released Under LGPL - original licence link has changed is not relivant.
32374  *
32375  * Fork - LGPL
32376  * <script type="text/javascript">
32377  */
32378  
32379 Roo.grid.AbstractGridView = function(){
32380         this.grid = null;
32381         
32382         this.events = {
32383             "beforerowremoved" : true,
32384             "beforerowsinserted" : true,
32385             "beforerefresh" : true,
32386             "rowremoved" : true,
32387             "rowsinserted" : true,
32388             "rowupdated" : true,
32389             "refresh" : true
32390         };
32391     Roo.grid.AbstractGridView.superclass.constructor.call(this);
32392 };
32393
32394 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
32395     rowClass : "x-grid-row",
32396     cellClass : "x-grid-cell",
32397     tdClass : "x-grid-td",
32398     hdClass : "x-grid-hd",
32399     splitClass : "x-grid-hd-split",
32400     
32401         init: function(grid){
32402         this.grid = grid;
32403                 var cid = this.grid.getGridEl().id;
32404         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
32405         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
32406         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
32407         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
32408         },
32409         
32410         getColumnRenderers : function(){
32411         var renderers = [];
32412         var cm = this.grid.colModel;
32413         var colCount = cm.getColumnCount();
32414         for(var i = 0; i < colCount; i++){
32415             renderers[i] = cm.getRenderer(i);
32416         }
32417         return renderers;
32418     },
32419     
32420     getColumnIds : function(){
32421         var ids = [];
32422         var cm = this.grid.colModel;
32423         var colCount = cm.getColumnCount();
32424         for(var i = 0; i < colCount; i++){
32425             ids[i] = cm.getColumnId(i);
32426         }
32427         return ids;
32428     },
32429     
32430     getDataIndexes : function(){
32431         if(!this.indexMap){
32432             this.indexMap = this.buildIndexMap();
32433         }
32434         return this.indexMap.colToData;
32435     },
32436     
32437     getColumnIndexByDataIndex : function(dataIndex){
32438         if(!this.indexMap){
32439             this.indexMap = this.buildIndexMap();
32440         }
32441         return this.indexMap.dataToCol[dataIndex];
32442     },
32443     
32444     /**
32445      * Set a css style for a column dynamically. 
32446      * @param {Number} colIndex The index of the column
32447      * @param {String} name The css property name
32448      * @param {String} value The css value
32449      */
32450     setCSSStyle : function(colIndex, name, value){
32451         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
32452         Roo.util.CSS.updateRule(selector, name, value);
32453     },
32454     
32455     generateRules : function(cm){
32456         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
32457         Roo.util.CSS.removeStyleSheet(rulesId);
32458         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
32459             var cid = cm.getColumnId(i);
32460             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
32461                          this.tdSelector, cid, " {\n}\n",
32462                          this.hdSelector, cid, " {\n}\n",
32463                          this.splitSelector, cid, " {\n}\n");
32464         }
32465         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
32466     }
32467 });/*
32468  * Based on:
32469  * Ext JS Library 1.1.1
32470  * Copyright(c) 2006-2007, Ext JS, LLC.
32471  *
32472  * Originally Released Under LGPL - original licence link has changed is not relivant.
32473  *
32474  * Fork - LGPL
32475  * <script type="text/javascript">
32476  */
32477
32478 // private
32479 // This is a support class used internally by the Grid components
32480 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
32481     this.grid = grid;
32482     this.view = grid.getView();
32483     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
32484     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
32485     if(hd2){
32486         this.setHandleElId(Roo.id(hd));
32487         this.setOuterHandleElId(Roo.id(hd2));
32488     }
32489     this.scroll = false;
32490 };
32491 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
32492     maxDragWidth: 120,
32493     getDragData : function(e){
32494         var t = Roo.lib.Event.getTarget(e);
32495         var h = this.view.findHeaderCell(t);
32496         if(h){
32497             return {ddel: h.firstChild, header:h};
32498         }
32499         return false;
32500     },
32501
32502     onInitDrag : function(e){
32503         this.view.headersDisabled = true;
32504         var clone = this.dragData.ddel.cloneNode(true);
32505         clone.id = Roo.id();
32506         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
32507         this.proxy.update(clone);
32508         return true;
32509     },
32510
32511     afterValidDrop : function(){
32512         var v = this.view;
32513         setTimeout(function(){
32514             v.headersDisabled = false;
32515         }, 50);
32516     },
32517
32518     afterInvalidDrop : function(){
32519         var v = this.view;
32520         setTimeout(function(){
32521             v.headersDisabled = false;
32522         }, 50);
32523     }
32524 });
32525 /*
32526  * Based on:
32527  * Ext JS Library 1.1.1
32528  * Copyright(c) 2006-2007, Ext JS, LLC.
32529  *
32530  * Originally Released Under LGPL - original licence link has changed is not relivant.
32531  *
32532  * Fork - LGPL
32533  * <script type="text/javascript">
32534  */
32535 // private
32536 // This is a support class used internally by the Grid components
32537 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
32538     this.grid = grid;
32539     this.view = grid.getView();
32540     // split the proxies so they don't interfere with mouse events
32541     this.proxyTop = Roo.DomHelper.append(document.body, {
32542         cls:"col-move-top", html:"&#160;"
32543     }, true);
32544     this.proxyBottom = Roo.DomHelper.append(document.body, {
32545         cls:"col-move-bottom", html:"&#160;"
32546     }, true);
32547     this.proxyTop.hide = this.proxyBottom.hide = function(){
32548         this.setLeftTop(-100,-100);
32549         this.setStyle("visibility", "hidden");
32550     };
32551     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
32552     // temporarily disabled
32553     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
32554     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
32555 };
32556 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
32557     proxyOffsets : [-4, -9],
32558     fly: Roo.Element.fly,
32559
32560     getTargetFromEvent : function(e){
32561         var t = Roo.lib.Event.getTarget(e);
32562         var cindex = this.view.findCellIndex(t);
32563         if(cindex !== false){
32564             return this.view.getHeaderCell(cindex);
32565         }
32566     },
32567
32568     nextVisible : function(h){
32569         var v = this.view, cm = this.grid.colModel;
32570         h = h.nextSibling;
32571         while(h){
32572             if(!cm.isHidden(v.getCellIndex(h))){
32573                 return h;
32574             }
32575             h = h.nextSibling;
32576         }
32577         return null;
32578     },
32579
32580     prevVisible : function(h){
32581         var v = this.view, cm = this.grid.colModel;
32582         h = h.prevSibling;
32583         while(h){
32584             if(!cm.isHidden(v.getCellIndex(h))){
32585                 return h;
32586             }
32587             h = h.prevSibling;
32588         }
32589         return null;
32590     },
32591
32592     positionIndicator : function(h, n, e){
32593         var x = Roo.lib.Event.getPageX(e);
32594         var r = Roo.lib.Dom.getRegion(n.firstChild);
32595         var px, pt, py = r.top + this.proxyOffsets[1];
32596         if((r.right - x) <= (r.right-r.left)/2){
32597             px = r.right+this.view.borderWidth;
32598             pt = "after";
32599         }else{
32600             px = r.left;
32601             pt = "before";
32602         }
32603         var oldIndex = this.view.getCellIndex(h);
32604         var newIndex = this.view.getCellIndex(n);
32605
32606         if(this.grid.colModel.isFixed(newIndex)){
32607             return false;
32608         }
32609
32610         var locked = this.grid.colModel.isLocked(newIndex);
32611
32612         if(pt == "after"){
32613             newIndex++;
32614         }
32615         if(oldIndex < newIndex){
32616             newIndex--;
32617         }
32618         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
32619             return false;
32620         }
32621         px +=  this.proxyOffsets[0];
32622         this.proxyTop.setLeftTop(px, py);
32623         this.proxyTop.show();
32624         if(!this.bottomOffset){
32625             this.bottomOffset = this.view.mainHd.getHeight();
32626         }
32627         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
32628         this.proxyBottom.show();
32629         return pt;
32630     },
32631
32632     onNodeEnter : function(n, dd, e, data){
32633         if(data.header != n){
32634             this.positionIndicator(data.header, n, e);
32635         }
32636     },
32637
32638     onNodeOver : function(n, dd, e, data){
32639         var result = false;
32640         if(data.header != n){
32641             result = this.positionIndicator(data.header, n, e);
32642         }
32643         if(!result){
32644             this.proxyTop.hide();
32645             this.proxyBottom.hide();
32646         }
32647         return result ? this.dropAllowed : this.dropNotAllowed;
32648     },
32649
32650     onNodeOut : function(n, dd, e, data){
32651         this.proxyTop.hide();
32652         this.proxyBottom.hide();
32653     },
32654
32655     onNodeDrop : function(n, dd, e, data){
32656         var h = data.header;
32657         if(h != n){
32658             var cm = this.grid.colModel;
32659             var x = Roo.lib.Event.getPageX(e);
32660             var r = Roo.lib.Dom.getRegion(n.firstChild);
32661             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
32662             var oldIndex = this.view.getCellIndex(h);
32663             var newIndex = this.view.getCellIndex(n);
32664             var locked = cm.isLocked(newIndex);
32665             if(pt == "after"){
32666                 newIndex++;
32667             }
32668             if(oldIndex < newIndex){
32669                 newIndex--;
32670             }
32671             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
32672                 return false;
32673             }
32674             cm.setLocked(oldIndex, locked, true);
32675             cm.moveColumn(oldIndex, newIndex);
32676             this.grid.fireEvent("columnmove", oldIndex, newIndex);
32677             return true;
32678         }
32679         return false;
32680     }
32681 });
32682 /*
32683  * Based on:
32684  * Ext JS Library 1.1.1
32685  * Copyright(c) 2006-2007, Ext JS, LLC.
32686  *
32687  * Originally Released Under LGPL - original licence link has changed is not relivant.
32688  *
32689  * Fork - LGPL
32690  * <script type="text/javascript">
32691  */
32692   
32693 /**
32694  * @class Roo.grid.GridView
32695  * @extends Roo.util.Observable
32696  *
32697  * @constructor
32698  * @param {Object} config
32699  */
32700 Roo.grid.GridView = function(config){
32701     Roo.grid.GridView.superclass.constructor.call(this);
32702     this.el = null;
32703
32704     Roo.apply(this, config);
32705 };
32706
32707 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
32708
32709     /**
32710      * Override this function to apply custom css classes to rows during rendering
32711      * @param {Record} record The record
32712      * @param {Number} index
32713      * @method getRowClass
32714      */
32715     rowClass : "x-grid-row",
32716
32717     cellClass : "x-grid-col",
32718
32719     tdClass : "x-grid-td",
32720
32721     hdClass : "x-grid-hd",
32722
32723     splitClass : "x-grid-split",
32724
32725     sortClasses : ["sort-asc", "sort-desc"],
32726
32727     enableMoveAnim : false,
32728
32729     hlColor: "C3DAF9",
32730
32731     dh : Roo.DomHelper,
32732
32733     fly : Roo.Element.fly,
32734
32735     css : Roo.util.CSS,
32736
32737     borderWidth: 1,
32738
32739     splitOffset: 3,
32740
32741     scrollIncrement : 22,
32742
32743     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
32744
32745     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
32746
32747     bind : function(ds, cm){
32748         if(this.ds){
32749             this.ds.un("load", this.onLoad, this);
32750             this.ds.un("datachanged", this.onDataChange, this);
32751             this.ds.un("add", this.onAdd, this);
32752             this.ds.un("remove", this.onRemove, this);
32753             this.ds.un("update", this.onUpdate, this);
32754             this.ds.un("clear", this.onClear, this);
32755         }
32756         if(ds){
32757             ds.on("load", this.onLoad, this);
32758             ds.on("datachanged", this.onDataChange, this);
32759             ds.on("add", this.onAdd, this);
32760             ds.on("remove", this.onRemove, this);
32761             ds.on("update", this.onUpdate, this);
32762             ds.on("clear", this.onClear, this);
32763         }
32764         this.ds = ds;
32765
32766         if(this.cm){
32767             this.cm.un("widthchange", this.onColWidthChange, this);
32768             this.cm.un("headerchange", this.onHeaderChange, this);
32769             this.cm.un("hiddenchange", this.onHiddenChange, this);
32770             this.cm.un("columnmoved", this.onColumnMove, this);
32771             this.cm.un("columnlockchange", this.onColumnLock, this);
32772         }
32773         if(cm){
32774             this.generateRules(cm);
32775             cm.on("widthchange", this.onColWidthChange, this);
32776             cm.on("headerchange", this.onHeaderChange, this);
32777             cm.on("hiddenchange", this.onHiddenChange, this);
32778             cm.on("columnmoved", this.onColumnMove, this);
32779             cm.on("columnlockchange", this.onColumnLock, this);
32780         }
32781         this.cm = cm;
32782     },
32783
32784     init: function(grid){
32785                 Roo.grid.GridView.superclass.init.call(this, grid);
32786
32787                 this.bind(grid.dataSource, grid.colModel);
32788
32789             grid.on("headerclick", this.handleHeaderClick, this);
32790
32791         if(grid.trackMouseOver){
32792             grid.on("mouseover", this.onRowOver, this);
32793                 grid.on("mouseout", this.onRowOut, this);
32794             }
32795             grid.cancelTextSelection = function(){};
32796                 this.gridId = grid.id;
32797
32798                 var tpls = this.templates || {};
32799
32800                 if(!tpls.master){
32801                     tpls.master = new Roo.Template(
32802                        '<div class="x-grid" hidefocus="true">',
32803                           '<div class="x-grid-topbar"></div>',
32804                           '<div class="x-grid-scroller"><div></div></div>',
32805                           '<div class="x-grid-locked">',
32806                               '<div class="x-grid-header">{lockedHeader}</div>',
32807                               '<div class="x-grid-body">{lockedBody}</div>',
32808                           "</div>",
32809                           '<div class="x-grid-viewport">',
32810                               '<div class="x-grid-header">{header}</div>',
32811                               '<div class="x-grid-body">{body}</div>',
32812                           "</div>",
32813                           '<div class="x-grid-bottombar"></div>',
32814                           '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
32815                           '<div class="x-grid-resize-proxy">&#160;</div>',
32816                        "</div>"
32817                     );
32818                     tpls.master.disableformats = true;
32819                 }
32820
32821                 if(!tpls.header){
32822                     tpls.header = new Roo.Template(
32823                        '<table border="0" cellspacing="0" cellpadding="0">',
32824                        '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
32825                        "</table>{splits}"
32826                     );
32827                     tpls.header.disableformats = true;
32828                 }
32829                 tpls.header.compile();
32830
32831                 if(!tpls.hcell){
32832                     tpls.hcell = new Roo.Template(
32833                         '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
32834                         '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
32835                         "</div></td>"
32836                      );
32837                      tpls.hcell.disableFormats = true;
32838                 }
32839                 tpls.hcell.compile();
32840
32841                 if(!tpls.hsplit){
32842                     tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
32843                     tpls.hsplit.disableFormats = true;
32844                 }
32845                 tpls.hsplit.compile();
32846
32847                 if(!tpls.body){
32848                     tpls.body = new Roo.Template(
32849                        '<table border="0" cellspacing="0" cellpadding="0">',
32850                        "<tbody>{rows}</tbody>",
32851                        "</table>"
32852                     );
32853                     tpls.body.disableFormats = true;
32854                 }
32855                 tpls.body.compile();
32856
32857                 if(!tpls.row){
32858                     tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
32859                     tpls.row.disableFormats = true;
32860                 }
32861                 tpls.row.compile();
32862
32863                 if(!tpls.cell){
32864                     tpls.cell = new Roo.Template(
32865                         '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
32866                         '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
32867                         "</td>"
32868                     );
32869             tpls.cell.disableFormats = true;
32870         }
32871                 tpls.cell.compile();
32872
32873                 this.templates = tpls;
32874         },
32875
32876         // remap these for backwards compat
32877     onColWidthChange : function(){
32878         this.updateColumns.apply(this, arguments);
32879     },
32880     onHeaderChange : function(){
32881         this.updateHeaders.apply(this, arguments);
32882     }, 
32883     onHiddenChange : function(){
32884         this.handleHiddenChange.apply(this, arguments);
32885     },
32886     onColumnMove : function(){
32887         this.handleColumnMove.apply(this, arguments);
32888     },
32889     onColumnLock : function(){
32890         this.handleLockChange.apply(this, arguments);
32891     },
32892
32893     onDataChange : function(){
32894         this.refresh();
32895         this.updateHeaderSortState();
32896     },
32897
32898         onClear : function(){
32899         this.refresh();
32900     },
32901
32902         onUpdate : function(ds, record){
32903         this.refreshRow(record);
32904     },
32905
32906     refreshRow : function(record){
32907         var ds = this.ds, index;
32908         if(typeof record == 'number'){
32909             index = record;
32910             record = ds.getAt(index);
32911         }else{
32912             index = ds.indexOf(record);
32913         }
32914         this.insertRows(ds, index, index, true);
32915         this.onRemove(ds, record, index+1, true);
32916         this.syncRowHeights(index, index);
32917         this.layout();
32918         this.fireEvent("rowupdated", this, index, record);
32919     },
32920
32921     onAdd : function(ds, records, index){
32922         this.insertRows(ds, index, index + (records.length-1));
32923     },
32924
32925     onRemove : function(ds, record, index, isUpdate){
32926         if(isUpdate !== true){
32927             this.fireEvent("beforerowremoved", this, index, record);
32928         }
32929         var bt = this.getBodyTable(), lt = this.getLockedTable();
32930         if(bt.rows[index]){
32931             bt.firstChild.removeChild(bt.rows[index]);
32932         }
32933         if(lt.rows[index]){
32934             lt.firstChild.removeChild(lt.rows[index]);
32935         }
32936         if(isUpdate !== true){
32937             this.stripeRows(index);
32938             this.syncRowHeights(index, index);
32939             this.layout();
32940             this.fireEvent("rowremoved", this, index, record);
32941         }
32942     },
32943
32944     onLoad : function(){
32945         this.scrollToTop();
32946     },
32947
32948     /**
32949      * Scrolls the grid to the top
32950      */
32951     scrollToTop : function(){
32952         if(this.scroller){
32953             this.scroller.dom.scrollTop = 0;
32954             this.syncScroll();
32955         }
32956     },
32957
32958     /**
32959      * Gets a panel in the header of the grid that can be used for toolbars etc.
32960      * After modifying the contents of this panel a call to grid.autoSize() may be
32961      * required to register any changes in size.
32962      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
32963      * @return Roo.Element
32964      */
32965     getHeaderPanel : function(doShow){
32966         if(doShow){
32967             this.headerPanel.show();
32968         }
32969         return this.headerPanel;
32970         },
32971
32972         /**
32973      * Gets a panel in the footer 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 footer is hidden. Pass true to show the panel
32977      * @return Roo.Element
32978      */
32979     getFooterPanel : function(doShow){
32980         if(doShow){
32981             this.footerPanel.show();
32982         }
32983         return this.footerPanel;
32984         },
32985
32986         initElements : function(){
32987             var E = Roo.Element;
32988             var el = this.grid.getGridEl().dom.firstChild;
32989             var cs = el.childNodes;
32990
32991             this.el = new E(el);
32992             this.headerPanel = new E(el.firstChild);
32993             this.headerPanel.enableDisplayMode("block");
32994
32995         this.scroller = new E(cs[1]);
32996             this.scrollSizer = new E(this.scroller.dom.firstChild);
32997
32998             this.lockedWrap = new E(cs[2]);
32999             this.lockedHd = new E(this.lockedWrap.dom.firstChild);
33000             this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
33001
33002             this.mainWrap = new E(cs[3]);
33003             this.mainHd = new E(this.mainWrap.dom.firstChild);
33004             this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
33005
33006             this.footerPanel = new E(cs[4]);
33007             this.footerPanel.enableDisplayMode("block");
33008
33009         this.focusEl = new E(cs[5]);
33010         this.focusEl.swallowEvent("click", true);
33011         this.resizeProxy = new E(cs[6]);
33012
33013             this.headerSelector = String.format(
33014                '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
33015                this.lockedHd.id, this.mainHd.id
33016             );
33017
33018             this.splitterSelector = String.format(
33019                '#{0} div.x-grid-split, #{1} div.x-grid-split',
33020                this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
33021             );
33022     },
33023     idToCssName : function(s)
33024     {
33025         return s.replace(/[^a-z0-9]+/ig, '-');
33026     },
33027
33028         getHeaderCell : function(index){
33029             return Roo.DomQuery.select(this.headerSelector)[index];
33030         },
33031
33032         getHeaderCellMeasure : function(index){
33033             return this.getHeaderCell(index).firstChild;
33034         },
33035
33036         getHeaderCellText : function(index){
33037             return this.getHeaderCell(index).firstChild.firstChild;
33038         },
33039
33040         getLockedTable : function(){
33041             return this.lockedBody.dom.firstChild;
33042         },
33043
33044         getBodyTable : function(){
33045             return this.mainBody.dom.firstChild;
33046         },
33047
33048         getLockedRow : function(index){
33049             return this.getLockedTable().rows[index];
33050         },
33051
33052         getRow : function(index){
33053             return this.getBodyTable().rows[index];
33054         },
33055
33056         getRowComposite : function(index){
33057             if(!this.rowEl){
33058                 this.rowEl = new Roo.CompositeElementLite();
33059             }
33060         var els = [], lrow, mrow;
33061         if(lrow = this.getLockedRow(index)){
33062             els.push(lrow);
33063         }
33064         if(mrow = this.getRow(index)){
33065             els.push(mrow);
33066         }
33067         this.rowEl.elements = els;
33068             return this.rowEl;
33069         },
33070
33071         getCell : function(rowIndex, colIndex){
33072             var locked = this.cm.getLockedCount();
33073             var source;
33074             if(colIndex < locked){
33075                 source = this.lockedBody.dom.firstChild;
33076             }else{
33077                 source = this.mainBody.dom.firstChild;
33078                 colIndex -= locked;
33079             }
33080         return source.rows[rowIndex].childNodes[colIndex];
33081         },
33082
33083         getCellText : function(rowIndex, colIndex){
33084             return this.getCell(rowIndex, colIndex).firstChild.firstChild;
33085         },
33086
33087         getCellBox : function(cell){
33088             var b = this.fly(cell).getBox();
33089         if(Roo.isOpera){ // opera fails to report the Y
33090             b.y = cell.offsetTop + this.mainBody.getY();
33091         }
33092         return b;
33093     },
33094
33095     getCellIndex : function(cell){
33096         var id = String(cell.className).match(this.cellRE);
33097         if(id){
33098             return parseInt(id[1], 10);
33099         }
33100         return 0;
33101     },
33102
33103     findHeaderIndex : function(n){
33104         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33105         return r ? this.getCellIndex(r) : false;
33106     },
33107
33108     findHeaderCell : function(n){
33109         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33110         return r ? r : false;
33111     },
33112
33113     findRowIndex : function(n){
33114         if(!n){
33115             return false;
33116         }
33117         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
33118         return r ? r.rowIndex : false;
33119     },
33120
33121     findCellIndex : function(node){
33122         var stop = this.el.dom;
33123         while(node && node != stop){
33124             if(this.findRE.test(node.className)){
33125                 return this.getCellIndex(node);
33126             }
33127             node = node.parentNode;
33128         }
33129         return false;
33130     },
33131
33132     getColumnId : function(index){
33133             return this.cm.getColumnId(index);
33134         },
33135
33136         getSplitters : function(){
33137             if(this.splitterSelector){
33138                return Roo.DomQuery.select(this.splitterSelector);
33139             }else{
33140                 return null;
33141             }
33142         },
33143
33144         getSplitter : function(index){
33145             return this.getSplitters()[index];
33146         },
33147
33148     onRowOver : function(e, t){
33149         var row;
33150         if((row = this.findRowIndex(t)) !== false){
33151             this.getRowComposite(row).addClass("x-grid-row-over");
33152         }
33153     },
33154
33155     onRowOut : function(e, t){
33156         var row;
33157         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
33158             this.getRowComposite(row).removeClass("x-grid-row-over");
33159         }
33160     },
33161
33162     renderHeaders : function(){
33163             var cm = this.cm;
33164         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
33165         var cb = [], lb = [], sb = [], lsb = [], p = {};
33166         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33167             p.cellId = "x-grid-hd-0-" + i;
33168             p.splitId = "x-grid-csplit-0-" + i;
33169             p.id = cm.getColumnId(i);
33170             p.title = cm.getColumnTooltip(i) || "";
33171             p.value = cm.getColumnHeader(i) || "";
33172             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
33173             if(!cm.isLocked(i)){
33174                 cb[cb.length] = ct.apply(p);
33175                 sb[sb.length] = st.apply(p);
33176             }else{
33177                 lb[lb.length] = ct.apply(p);
33178                 lsb[lsb.length] = st.apply(p);
33179             }
33180         }
33181         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
33182                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
33183         },
33184
33185         updateHeaders : function(){
33186         var html = this.renderHeaders();
33187         this.lockedHd.update(html[0]);
33188         this.mainHd.update(html[1]);
33189     },
33190
33191     /**
33192      * Focuses the specified row.
33193      * @param {Number} row The row index
33194      */
33195     focusRow : function(row){
33196         var x = this.scroller.dom.scrollLeft;
33197         this.focusCell(row, 0, false);
33198         this.scroller.dom.scrollLeft = x;
33199     },
33200
33201     /**
33202      * Focuses the specified cell.
33203      * @param {Number} row The row index
33204      * @param {Number} col The column index
33205      * @param {Boolean} hscroll false to disable horizontal scrolling
33206      */
33207     focusCell : function(row, col, hscroll){
33208         var el = this.ensureVisible(row, col, hscroll);
33209         this.focusEl.alignTo(el, "tl-tl");
33210         if(Roo.isGecko){
33211             this.focusEl.focus();
33212         }else{
33213             this.focusEl.focus.defer(1, this.focusEl);
33214         }
33215     },
33216
33217     /**
33218      * Scrolls the specified cell into view
33219      * @param {Number} row The row index
33220      * @param {Number} col The column index
33221      * @param {Boolean} hscroll false to disable horizontal scrolling
33222      */
33223     ensureVisible : function(row, col, hscroll){
33224         if(typeof row != "number"){
33225             row = row.rowIndex;
33226         }
33227         if(row < 0 && row >= this.ds.getCount()){
33228             return;
33229         }
33230         col = (col !== undefined ? col : 0);
33231         var cm = this.grid.colModel;
33232         while(cm.isHidden(col)){
33233             col++;
33234         }
33235
33236         var el = this.getCell(row, col);
33237         if(!el){
33238             return;
33239         }
33240         var c = this.scroller.dom;
33241
33242         var ctop = parseInt(el.offsetTop, 10);
33243         var cleft = parseInt(el.offsetLeft, 10);
33244         var cbot = ctop + el.offsetHeight;
33245         var cright = cleft + el.offsetWidth;
33246
33247         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
33248         var stop = parseInt(c.scrollTop, 10);
33249         var sleft = parseInt(c.scrollLeft, 10);
33250         var sbot = stop + ch;
33251         var sright = sleft + c.clientWidth;
33252
33253         if(ctop < stop){
33254                 c.scrollTop = ctop;
33255         }else if(cbot > sbot){
33256             c.scrollTop = cbot-ch;
33257         }
33258
33259         if(hscroll !== false){
33260             if(cleft < sleft){
33261                 c.scrollLeft = cleft;
33262             }else if(cright > sright){
33263                 c.scrollLeft = cright-c.clientWidth;
33264             }
33265         }
33266         return el;
33267     },
33268
33269     updateColumns : function(){
33270         this.grid.stopEditing();
33271         var cm = this.grid.colModel, colIds = this.getColumnIds();
33272         //var totalWidth = cm.getTotalWidth();
33273         var pos = 0;
33274         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33275             //if(cm.isHidden(i)) continue;
33276             var w = cm.getColumnWidth(i);
33277             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33278             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33279         }
33280         this.updateSplitters();
33281     },
33282
33283     generateRules : function(cm){
33284         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
33285         Roo.util.CSS.removeStyleSheet(rulesId);
33286         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33287             var cid = cm.getColumnId(i);
33288             var align = '';
33289             if(cm.config[i].align){
33290                 align = 'text-align:'+cm.config[i].align+';';
33291             }
33292             var hidden = '';
33293             if(cm.isHidden(i)){
33294                 hidden = 'display:none;';
33295             }
33296             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
33297             ruleBuf.push(
33298                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
33299                     this.hdSelector, cid, " {\n", align, width, "}\n",
33300                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
33301                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
33302         }
33303         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
33304     },
33305
33306     updateSplitters : function(){
33307         var cm = this.cm, s = this.getSplitters();
33308         if(s){ // splitters not created yet
33309             var pos = 0, locked = true;
33310             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33311                 if(cm.isHidden(i)) continue;
33312                 var w = cm.getColumnWidth(i);
33313                 if(!cm.isLocked(i) && locked){
33314                     pos = 0;
33315                     locked = false;
33316                 }
33317                 pos += w;
33318                 s[i].style.left = (pos-this.splitOffset) + "px";
33319             }
33320         }
33321     },
33322
33323     handleHiddenChange : function(colModel, colIndex, hidden){
33324         if(hidden){
33325             this.hideColumn(colIndex);
33326         }else{
33327             this.unhideColumn(colIndex);
33328         }
33329     },
33330
33331     hideColumn : function(colIndex){
33332         var cid = this.getColumnId(colIndex);
33333         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
33334         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
33335         if(Roo.isSafari){
33336             this.updateHeaders();
33337         }
33338         this.updateSplitters();
33339         this.layout();
33340     },
33341
33342     unhideColumn : function(colIndex){
33343         var cid = this.getColumnId(colIndex);
33344         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
33345         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
33346
33347         if(Roo.isSafari){
33348             this.updateHeaders();
33349         }
33350         this.updateSplitters();
33351         this.layout();
33352     },
33353
33354     insertRows : function(dm, firstRow, lastRow, isUpdate){
33355         if(firstRow == 0 && lastRow == dm.getCount()-1){
33356             this.refresh();
33357         }else{
33358             if(!isUpdate){
33359                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
33360             }
33361             var s = this.getScrollState();
33362             var markup = this.renderRows(firstRow, lastRow);
33363             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
33364             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
33365             this.restoreScroll(s);
33366             if(!isUpdate){
33367                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
33368                 this.syncRowHeights(firstRow, lastRow);
33369                 this.stripeRows(firstRow);
33370                 this.layout();
33371             }
33372         }
33373     },
33374
33375     bufferRows : function(markup, target, index){
33376         var before = null, trows = target.rows, tbody = target.tBodies[0];
33377         if(index < trows.length){
33378             before = trows[index];
33379         }
33380         var b = document.createElement("div");
33381         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
33382         var rows = b.firstChild.rows;
33383         for(var i = 0, len = rows.length; i < len; i++){
33384             if(before){
33385                 tbody.insertBefore(rows[0], before);
33386             }else{
33387                 tbody.appendChild(rows[0]);
33388             }
33389         }
33390         b.innerHTML = "";
33391         b = null;
33392     },
33393
33394     deleteRows : function(dm, firstRow, lastRow){
33395         if(dm.getRowCount()<1){
33396             this.fireEvent("beforerefresh", this);
33397             this.mainBody.update("");
33398             this.lockedBody.update("");
33399             this.fireEvent("refresh", this);
33400         }else{
33401             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
33402             var bt = this.getBodyTable();
33403             var tbody = bt.firstChild;
33404             var rows = bt.rows;
33405             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
33406                 tbody.removeChild(rows[firstRow]);
33407             }
33408             this.stripeRows(firstRow);
33409             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
33410         }
33411     },
33412
33413     updateRows : function(dataSource, firstRow, lastRow){
33414         var s = this.getScrollState();
33415         this.refresh();
33416         this.restoreScroll(s);
33417     },
33418
33419     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
33420         if(!noRefresh){
33421            this.refresh();
33422         }
33423         this.updateHeaderSortState();
33424     },
33425
33426     getScrollState : function(){
33427         var sb = this.scroller.dom;
33428         return {left: sb.scrollLeft, top: sb.scrollTop};
33429     },
33430
33431     stripeRows : function(startRow){
33432         if(!this.grid.stripeRows || this.ds.getCount() < 1){
33433             return;
33434         }
33435         startRow = startRow || 0;
33436         var rows = this.getBodyTable().rows;
33437         var lrows = this.getLockedTable().rows;
33438         var cls = ' x-grid-row-alt ';
33439         for(var i = startRow, len = rows.length; i < len; i++){
33440             var row = rows[i], lrow = lrows[i];
33441             var isAlt = ((i+1) % 2 == 0);
33442             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
33443             if(isAlt == hasAlt){
33444                 continue;
33445             }
33446             if(isAlt){
33447                 row.className += " x-grid-row-alt";
33448             }else{
33449                 row.className = row.className.replace("x-grid-row-alt", "");
33450             }
33451             if(lrow){
33452                 lrow.className = row.className;
33453             }
33454         }
33455     },
33456
33457     restoreScroll : function(state){
33458         var sb = this.scroller.dom;
33459         sb.scrollLeft = state.left;
33460         sb.scrollTop = state.top;
33461         this.syncScroll();
33462     },
33463
33464     syncScroll : function(){
33465         var sb = this.scroller.dom;
33466         var sh = this.mainHd.dom;
33467         var bs = this.mainBody.dom;
33468         var lv = this.lockedBody.dom;
33469         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
33470         lv.scrollTop = bs.scrollTop = sb.scrollTop;
33471     },
33472
33473     handleScroll : function(e){
33474         this.syncScroll();
33475         var sb = this.scroller.dom;
33476         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
33477         e.stopEvent();
33478     },
33479
33480     handleWheel : function(e){
33481         var d = e.getWheelDelta();
33482         this.scroller.dom.scrollTop -= d*22;
33483         // set this here to prevent jumpy scrolling on large tables
33484         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
33485         e.stopEvent();
33486     },
33487
33488     renderRows : function(startRow, endRow){
33489         // pull in all the crap needed to render rows
33490         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
33491         var colCount = cm.getColumnCount();
33492
33493         if(ds.getCount() < 1){
33494             return ["", ""];
33495         }
33496
33497         // build a map for all the columns
33498         var cs = [];
33499         for(var i = 0; i < colCount; i++){
33500             var name = cm.getDataIndex(i);
33501             cs[i] = {
33502                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
33503                 renderer : cm.getRenderer(i),
33504                 id : cm.getColumnId(i),
33505                 locked : cm.isLocked(i)
33506             };
33507         }
33508
33509         startRow = startRow || 0;
33510         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
33511
33512         // records to render
33513         var rs = ds.getRange(startRow, endRow);
33514
33515         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
33516     },
33517
33518     // As much as I hate to duplicate code, this was branched because FireFox really hates
33519     // [].join("") on strings. The performance difference was substantial enough to
33520     // branch this function
33521     doRender : Roo.isGecko ?
33522             function(cs, rs, ds, startRow, colCount, stripe){
33523                 var ts = this.templates, ct = ts.cell, rt = ts.row;
33524                 // buffers
33525                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
33526                 for(var j = 0, len = rs.length; j < len; j++){
33527                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
33528                     for(var i = 0; i < colCount; i++){
33529                         c = cs[i];
33530                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
33531                         p.id = c.id;
33532                         p.css = p.attr = "";
33533                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
33534                         if(p.value == undefined || p.value === "") p.value = "&#160;";
33535                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
33536                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
33537                         }
33538                         var markup = ct.apply(p);
33539                         if(!c.locked){
33540                             cb+= markup;
33541                         }else{
33542                             lcb+= markup;
33543                         }
33544                     }
33545                     var alt = [];
33546                     if(stripe && ((rowIndex+1) % 2 == 0)){
33547                         alt[0] = "x-grid-row-alt";
33548                     }
33549                     if(r.dirty){
33550                         alt[1] = " x-grid-dirty-row";
33551                     }
33552                     rp.cells = lcb;
33553                     if(this.getRowClass){
33554                         alt[2] = this.getRowClass(r, rowIndex);
33555                     }
33556                     rp.alt = alt.join(" ");
33557                     lbuf+= rt.apply(rp);
33558                     rp.cells = cb;
33559                     buf+=  rt.apply(rp);
33560                 }
33561                 return [lbuf, buf];
33562             } :
33563             function(cs, rs, ds, startRow, colCount, stripe){
33564                 var ts = this.templates, ct = ts.cell, rt = ts.row;
33565                 // buffers
33566                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
33567                 for(var j = 0, len = rs.length; j < len; j++){
33568                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
33569                     for(var i = 0; i < colCount; i++){
33570                         c = cs[i];
33571                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
33572                         p.id = c.id;
33573                         p.css = p.attr = "";
33574                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
33575                         if(p.value == undefined || p.value === "") p.value = "&#160;";
33576                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
33577                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
33578                         }
33579                         var markup = ct.apply(p);
33580                         if(!c.locked){
33581                             cb[cb.length] = markup;
33582                         }else{
33583                             lcb[lcb.length] = markup;
33584                         }
33585                     }
33586                     var alt = [];
33587                     if(stripe && ((rowIndex+1) % 2 == 0)){
33588                         alt[0] = "x-grid-row-alt";
33589                     }
33590                     if(r.dirty){
33591                         alt[1] = " x-grid-dirty-row";
33592                     }
33593                     rp.cells = lcb;
33594                     if(this.getRowClass){
33595                         alt[2] = this.getRowClass(r, rowIndex);
33596                     }
33597                     rp.alt = alt.join(" ");
33598                     rp.cells = lcb.join("");
33599                     lbuf[lbuf.length] = rt.apply(rp);
33600                     rp.cells = cb.join("");
33601                     buf[buf.length] =  rt.apply(rp);
33602                 }
33603                 return [lbuf.join(""), buf.join("")];
33604             },
33605
33606     renderBody : function(){
33607         var markup = this.renderRows();
33608         var bt = this.templates.body;
33609         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
33610     },
33611
33612     /**
33613      * Refreshes the grid
33614      * @param {Boolean} headersToo
33615      */
33616     refresh : function(headersToo){
33617         this.fireEvent("beforerefresh", this);
33618         this.grid.stopEditing();
33619         var result = this.renderBody();
33620         this.lockedBody.update(result[0]);
33621         this.mainBody.update(result[1]);
33622         if(headersToo === true){
33623             this.updateHeaders();
33624             this.updateColumns();
33625             this.updateSplitters();
33626             this.updateHeaderSortState();
33627         }
33628         this.syncRowHeights();
33629         this.layout();
33630         this.fireEvent("refresh", this);
33631     },
33632
33633     handleColumnMove : function(cm, oldIndex, newIndex){
33634         this.indexMap = null;
33635         var s = this.getScrollState();
33636         this.refresh(true);
33637         this.restoreScroll(s);
33638         this.afterMove(newIndex);
33639     },
33640
33641     afterMove : function(colIndex){
33642         if(this.enableMoveAnim && Roo.enableFx){
33643             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
33644         }
33645     },
33646
33647     updateCell : function(dm, rowIndex, dataIndex){
33648         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
33649         if(typeof colIndex == "undefined"){ // not present in grid
33650             return;
33651         }
33652         var cm = this.grid.colModel;
33653         var cell = this.getCell(rowIndex, colIndex);
33654         var cellText = this.getCellText(rowIndex, colIndex);
33655
33656         var p = {
33657             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
33658             id : cm.getColumnId(colIndex),
33659             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
33660         };
33661         var renderer = cm.getRenderer(colIndex);
33662         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
33663         if(typeof val == "undefined" || val === "") val = "&#160;";
33664         cellText.innerHTML = val;
33665         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
33666         this.syncRowHeights(rowIndex, rowIndex);
33667     },
33668
33669     calcColumnWidth : function(colIndex, maxRowsToMeasure){
33670         var maxWidth = 0;
33671         if(this.grid.autoSizeHeaders){
33672             var h = this.getHeaderCellMeasure(colIndex);
33673             maxWidth = Math.max(maxWidth, h.scrollWidth);
33674         }
33675         var tb, index;
33676         if(this.cm.isLocked(colIndex)){
33677             tb = this.getLockedTable();
33678             index = colIndex;
33679         }else{
33680             tb = this.getBodyTable();
33681             index = colIndex - this.cm.getLockedCount();
33682         }
33683         if(tb && tb.rows){
33684             var rows = tb.rows;
33685             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
33686             for(var i = 0; i < stopIndex; i++){
33687                 var cell = rows[i].childNodes[index].firstChild;
33688                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
33689             }
33690         }
33691         return maxWidth + /*margin for error in IE*/ 5;
33692     },
33693     /**
33694      * Autofit a column to its content.
33695      * @param {Number} colIndex
33696      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
33697      */
33698      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
33699          if(this.cm.isHidden(colIndex)){
33700              return; // can't calc a hidden column
33701          }
33702         if(forceMinSize){
33703             var cid = this.cm.getColumnId(colIndex);
33704             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
33705            if(this.grid.autoSizeHeaders){
33706                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
33707            }
33708         }
33709         var newWidth = this.calcColumnWidth(colIndex);
33710         this.cm.setColumnWidth(colIndex,
33711             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
33712         if(!suppressEvent){
33713             this.grid.fireEvent("columnresize", colIndex, newWidth);
33714         }
33715     },
33716
33717     /**
33718      * Autofits all columns to their content and then expands to fit any extra space in the grid
33719      */
33720      autoSizeColumns : function(){
33721         var cm = this.grid.colModel;
33722         var colCount = cm.getColumnCount();
33723         for(var i = 0; i < colCount; i++){
33724             this.autoSizeColumn(i, true, true);
33725         }
33726         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
33727             this.fitColumns();
33728         }else{
33729             this.updateColumns();
33730             this.layout();
33731         }
33732     },
33733
33734     /**
33735      * Autofits all columns to the grid's width proportionate with their current size
33736      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
33737      */
33738     fitColumns : function(reserveScrollSpace){
33739         var cm = this.grid.colModel;
33740         var colCount = cm.getColumnCount();
33741         var cols = [];
33742         var width = 0;
33743         var i, w;
33744         for (i = 0; i < colCount; i++){
33745             if(!cm.isHidden(i) && !cm.isFixed(i)){
33746                 w = cm.getColumnWidth(i);
33747                 cols.push(i);
33748                 cols.push(w);
33749                 width += w;
33750             }
33751         }
33752         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
33753         if(reserveScrollSpace){
33754             avail -= 17;
33755         }
33756         var frac = (avail - cm.getTotalWidth())/width;
33757         while (cols.length){
33758             w = cols.pop();
33759             i = cols.pop();
33760             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
33761         }
33762         this.updateColumns();
33763         this.layout();
33764     },
33765
33766     onRowSelect : function(rowIndex){
33767         var row = this.getRowComposite(rowIndex);
33768         row.addClass("x-grid-row-selected");
33769     },
33770
33771     onRowDeselect : function(rowIndex){
33772         var row = this.getRowComposite(rowIndex);
33773         row.removeClass("x-grid-row-selected");
33774     },
33775
33776     onCellSelect : function(row, col){
33777         var cell = this.getCell(row, col);
33778         if(cell){
33779             Roo.fly(cell).addClass("x-grid-cell-selected");
33780         }
33781     },
33782
33783     onCellDeselect : function(row, col){
33784         var cell = this.getCell(row, col);
33785         if(cell){
33786             Roo.fly(cell).removeClass("x-grid-cell-selected");
33787         }
33788     },
33789
33790     updateHeaderSortState : function(){
33791         var state = this.ds.getSortState();
33792         if(!state){
33793             return;
33794         }
33795         this.sortState = state;
33796         var sortColumn = this.cm.findColumnIndex(state.field);
33797         if(sortColumn != -1){
33798             var sortDir = state.direction;
33799             var sc = this.sortClasses;
33800             var hds = this.el.select(this.headerSelector).removeClass(sc);
33801             hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
33802         }
33803     },
33804
33805     handleHeaderClick : function(g, index){
33806         if(this.headersDisabled){
33807             return;
33808         }
33809         var dm = g.dataSource, cm = g.colModel;
33810             if(!cm.isSortable(index)){
33811             return;
33812         }
33813             g.stopEditing();
33814         dm.sort(cm.getDataIndex(index));
33815     },
33816
33817
33818     destroy : function(){
33819         if(this.colMenu){
33820             this.colMenu.removeAll();
33821             Roo.menu.MenuMgr.unregister(this.colMenu);
33822             this.colMenu.getEl().remove();
33823             delete this.colMenu;
33824         }
33825         if(this.hmenu){
33826             this.hmenu.removeAll();
33827             Roo.menu.MenuMgr.unregister(this.hmenu);
33828             this.hmenu.getEl().remove();
33829             delete this.hmenu;
33830         }
33831         if(this.grid.enableColumnMove){
33832             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
33833             if(dds){
33834                 for(var dd in dds){
33835                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
33836                         var elid = dds[dd].dragElId;
33837                         dds[dd].unreg();
33838                         Roo.get(elid).remove();
33839                     } else if(dds[dd].config.isTarget){
33840                         dds[dd].proxyTop.remove();
33841                         dds[dd].proxyBottom.remove();
33842                         dds[dd].unreg();
33843                     }
33844                     if(Roo.dd.DDM.locationCache[dd]){
33845                         delete Roo.dd.DDM.locationCache[dd];
33846                     }
33847                 }
33848                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
33849             }
33850         }
33851         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
33852         this.bind(null, null);
33853         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
33854     },
33855
33856     handleLockChange : function(){
33857         this.refresh(true);
33858     },
33859
33860     onDenyColumnLock : function(){
33861
33862     },
33863
33864     onDenyColumnHide : function(){
33865
33866     },
33867
33868     handleHdMenuClick : function(item){
33869         var index = this.hdCtxIndex;
33870         var cm = this.cm, ds = this.ds;
33871         switch(item.id){
33872             case "asc":
33873                 ds.sort(cm.getDataIndex(index), "ASC");
33874                 break;
33875             case "desc":
33876                 ds.sort(cm.getDataIndex(index), "DESC");
33877                 break;
33878             case "lock":
33879                 var lc = cm.getLockedCount();
33880                 if(cm.getColumnCount(true) <= lc+1){
33881                     this.onDenyColumnLock();
33882                     return;
33883                 }
33884                 if(lc != index){
33885                     cm.setLocked(index, true, true);
33886                     cm.moveColumn(index, lc);
33887                     this.grid.fireEvent("columnmove", index, lc);
33888                 }else{
33889                     cm.setLocked(index, true);
33890                 }
33891             break;
33892             case "unlock":
33893                 var lc = cm.getLockedCount();
33894                 if((lc-1) != index){
33895                     cm.setLocked(index, false, true);
33896                     cm.moveColumn(index, lc-1);
33897                     this.grid.fireEvent("columnmove", index, lc-1);
33898                 }else{
33899                     cm.setLocked(index, false);
33900                 }
33901             break;
33902             default:
33903                 index = cm.getIndexById(item.id.substr(4));
33904                 if(index != -1){
33905                     if(item.checked && cm.getColumnCount(true) <= 1){
33906                         this.onDenyColumnHide();
33907                         return false;
33908                     }
33909                     cm.setHidden(index, item.checked);
33910                 }
33911         }
33912         return true;
33913     },
33914
33915     beforeColMenuShow : function(){
33916         var cm = this.cm,  colCount = cm.getColumnCount();
33917         this.colMenu.removeAll();
33918         for(var i = 0; i < colCount; i++){
33919             this.colMenu.add(new Roo.menu.CheckItem({
33920                 id: "col-"+cm.getColumnId(i),
33921                 text: cm.getColumnHeader(i),
33922                 checked: !cm.isHidden(i),
33923                 hideOnClick:false
33924             }));
33925         }
33926     },
33927
33928     handleHdCtx : function(g, index, e){
33929         e.stopEvent();
33930         var hd = this.getHeaderCell(index);
33931         this.hdCtxIndex = index;
33932         var ms = this.hmenu.items, cm = this.cm;
33933         ms.get("asc").setDisabled(!cm.isSortable(index));
33934         ms.get("desc").setDisabled(!cm.isSortable(index));
33935         if(this.grid.enableColLock !== false){
33936             ms.get("lock").setDisabled(cm.isLocked(index));
33937             ms.get("unlock").setDisabled(!cm.isLocked(index));
33938         }
33939         this.hmenu.show(hd, "tl-bl");
33940     },
33941
33942     handleHdOver : function(e){
33943         var hd = this.findHeaderCell(e.getTarget());
33944         if(hd && !this.headersDisabled){
33945             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
33946                this.fly(hd).addClass("x-grid-hd-over");
33947             }
33948         }
33949     },
33950
33951     handleHdOut : function(e){
33952         var hd = this.findHeaderCell(e.getTarget());
33953         if(hd){
33954             this.fly(hd).removeClass("x-grid-hd-over");
33955         }
33956     },
33957
33958     handleSplitDblClick : function(e, t){
33959         var i = this.getCellIndex(t);
33960         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
33961             this.autoSizeColumn(i, true);
33962             this.layout();
33963         }
33964     },
33965
33966     render : function(){
33967
33968         var cm = this.cm;
33969         var colCount = cm.getColumnCount();
33970
33971         if(this.grid.monitorWindowResize === true){
33972             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
33973         }
33974         var header = this.renderHeaders();
33975         var body = this.templates.body.apply({rows:""});
33976         var html = this.templates.master.apply({
33977             lockedBody: body,
33978             body: body,
33979             lockedHeader: header[0],
33980             header: header[1]
33981         });
33982
33983         //this.updateColumns();
33984
33985         this.grid.getGridEl().dom.innerHTML = html;
33986
33987         this.initElements();
33988         
33989         // a kludge to fix the random scolling effect in webkit
33990         this.el.on("scroll", function() {
33991             this.el.dom.scrollTop=0; // hopefully not recursive..
33992         },this);
33993
33994         this.scroller.on("scroll", this.handleScroll, this);
33995         this.lockedBody.on("mousewheel", this.handleWheel, this);
33996         this.mainBody.on("mousewheel", this.handleWheel, this);
33997
33998         this.mainHd.on("mouseover", this.handleHdOver, this);
33999         this.mainHd.on("mouseout", this.handleHdOut, this);
34000         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
34001                 {delegate: "."+this.splitClass});
34002
34003         this.lockedHd.on("mouseover", this.handleHdOver, this);
34004         this.lockedHd.on("mouseout", this.handleHdOut, this);
34005         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
34006                 {delegate: "."+this.splitClass});
34007
34008         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
34009             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34010         }
34011
34012         this.updateSplitters();
34013
34014         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
34015             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34016             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34017         }
34018
34019         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
34020             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
34021             this.hmenu.add(
34022                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
34023                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
34024             );
34025             if(this.grid.enableColLock !== false){
34026                 this.hmenu.add('-',
34027                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
34028                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
34029                 );
34030             }
34031             if(this.grid.enableColumnHide !== false){
34032
34033                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
34034                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
34035                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
34036
34037                 this.hmenu.add('-',
34038                     {id:"columns", text: this.columnsText, menu: this.colMenu}
34039                 );
34040             }
34041             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
34042
34043             this.grid.on("headercontextmenu", this.handleHdCtx, this);
34044         }
34045
34046         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
34047             this.dd = new Roo.grid.GridDragZone(this.grid, {
34048                 ddGroup : this.grid.ddGroup || 'GridDD'
34049             });
34050         }
34051
34052         /*
34053         for(var i = 0; i < colCount; i++){
34054             if(cm.isHidden(i)){
34055                 this.hideColumn(i);
34056             }
34057             if(cm.config[i].align){
34058                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
34059                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
34060             }
34061         }*/
34062         
34063         this.updateHeaderSortState();
34064
34065         this.beforeInitialResize();
34066         this.layout(true);
34067
34068         // two part rendering gives faster view to the user
34069         this.renderPhase2.defer(1, this);
34070     },
34071
34072     renderPhase2 : function(){
34073         // render the rows now
34074         this.refresh();
34075         if(this.grid.autoSizeColumns){
34076             this.autoSizeColumns();
34077         }
34078     },
34079
34080     beforeInitialResize : function(){
34081
34082     },
34083
34084     onColumnSplitterMoved : function(i, w){
34085         this.userResized = true;
34086         var cm = this.grid.colModel;
34087         cm.setColumnWidth(i, w, true);
34088         var cid = cm.getColumnId(i);
34089         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34090         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34091         this.updateSplitters();
34092         this.layout();
34093         this.grid.fireEvent("columnresize", i, w);
34094     },
34095
34096     syncRowHeights : function(startIndex, endIndex){
34097         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
34098             startIndex = startIndex || 0;
34099             var mrows = this.getBodyTable().rows;
34100             var lrows = this.getLockedTable().rows;
34101             var len = mrows.length-1;
34102             endIndex = Math.min(endIndex || len, len);
34103             for(var i = startIndex; i <= endIndex; i++){
34104                 var m = mrows[i], l = lrows[i];
34105                 var h = Math.max(m.offsetHeight, l.offsetHeight);
34106                 m.style.height = l.style.height = h + "px";
34107             }
34108         }
34109     },
34110
34111     layout : function(initialRender, is2ndPass){
34112         var g = this.grid;
34113         var auto = g.autoHeight;
34114         var scrollOffset = 16;
34115         var c = g.getGridEl(), cm = this.cm,
34116                 expandCol = g.autoExpandColumn,
34117                 gv = this;
34118         //c.beginMeasure();
34119
34120         if(!c.dom.offsetWidth){ // display:none?
34121             if(initialRender){
34122                 this.lockedWrap.show();
34123                 this.mainWrap.show();
34124             }
34125             return;
34126         }
34127
34128         var hasLock = this.cm.isLocked(0);
34129
34130         var tbh = this.headerPanel.getHeight();
34131         var bbh = this.footerPanel.getHeight();
34132
34133         if(auto){
34134             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
34135             var newHeight = ch + c.getBorderWidth("tb");
34136             if(g.maxHeight){
34137                 newHeight = Math.min(g.maxHeight, newHeight);
34138             }
34139             c.setHeight(newHeight);
34140         }
34141
34142         if(g.autoWidth){
34143             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
34144         }
34145
34146         var s = this.scroller;
34147
34148         var csize = c.getSize(true);
34149
34150         this.el.setSize(csize.width, csize.height);
34151
34152         this.headerPanel.setWidth(csize.width);
34153         this.footerPanel.setWidth(csize.width);
34154
34155         var hdHeight = this.mainHd.getHeight();
34156         var vw = csize.width;
34157         var vh = csize.height - (tbh + bbh);
34158
34159         s.setSize(vw, vh);
34160
34161         var bt = this.getBodyTable();
34162         var ltWidth = hasLock ?
34163                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
34164
34165         var scrollHeight = bt.offsetHeight;
34166         var scrollWidth = ltWidth + bt.offsetWidth;
34167         var vscroll = false, hscroll = false;
34168
34169         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
34170
34171         var lw = this.lockedWrap, mw = this.mainWrap;
34172         var lb = this.lockedBody, mb = this.mainBody;
34173
34174         setTimeout(function(){
34175             var t = s.dom.offsetTop;
34176             var w = s.dom.clientWidth,
34177                 h = s.dom.clientHeight;
34178
34179             lw.setTop(t);
34180             lw.setSize(ltWidth, h);
34181
34182             mw.setLeftTop(ltWidth, t);
34183             mw.setSize(w-ltWidth, h);
34184
34185             lb.setHeight(h-hdHeight);
34186             mb.setHeight(h-hdHeight);
34187
34188             if(is2ndPass !== true && !gv.userResized && expandCol){
34189                 // high speed resize without full column calculation
34190                 
34191                 var ci = cm.getIndexById(expandCol);
34192                 if (ci < 0) {
34193                     ci = cm.findColumnIndex(expandCol);
34194                 }
34195                 ci = Math.max(0, ci); // make sure it's got at least the first col.
34196                 var expandId = cm.getColumnId(ci);
34197                 var  tw = cm.getTotalWidth(false);
34198                 var currentWidth = cm.getColumnWidth(ci);
34199                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
34200                 if(currentWidth != cw){
34201                     cm.setColumnWidth(ci, cw, true);
34202                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34203                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34204                     gv.updateSplitters();
34205                     gv.layout(false, true);
34206                 }
34207             }
34208
34209             if(initialRender){
34210                 lw.show();
34211                 mw.show();
34212             }
34213             //c.endMeasure();
34214         }, 10);
34215     },
34216
34217     onWindowResize : function(){
34218         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
34219             return;
34220         }
34221         this.layout();
34222     },
34223
34224     appendFooter : function(parentEl){
34225         return null;
34226     },
34227
34228     sortAscText : "Sort Ascending",
34229     sortDescText : "Sort Descending",
34230     lockText : "Lock Column",
34231     unlockText : "Unlock Column",
34232     columnsText : "Columns"
34233 });
34234
34235
34236 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
34237     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
34238     this.proxy.el.addClass('x-grid3-col-dd');
34239 };
34240
34241 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
34242     handleMouseDown : function(e){
34243
34244     },
34245
34246     callHandleMouseDown : function(e){
34247         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
34248     }
34249 });
34250 /*
34251  * Based on:
34252  * Ext JS Library 1.1.1
34253  * Copyright(c) 2006-2007, Ext JS, LLC.
34254  *
34255  * Originally Released Under LGPL - original licence link has changed is not relivant.
34256  *
34257  * Fork - LGPL
34258  * <script type="text/javascript">
34259  */
34260  
34261 // private
34262 // This is a support class used internally by the Grid components
34263 Roo.grid.SplitDragZone = function(grid, hd, hd2){
34264     this.grid = grid;
34265     this.view = grid.getView();
34266     this.proxy = this.view.resizeProxy;
34267     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
34268         "gridSplitters" + this.grid.getGridEl().id, {
34269         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
34270     });
34271     this.setHandleElId(Roo.id(hd));
34272     this.setOuterHandleElId(Roo.id(hd2));
34273     this.scroll = false;
34274 };
34275 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
34276     fly: Roo.Element.fly,
34277
34278     b4StartDrag : function(x, y){
34279         this.view.headersDisabled = true;
34280         this.proxy.setHeight(this.view.mainWrap.getHeight());
34281         var w = this.cm.getColumnWidth(this.cellIndex);
34282         var minw = Math.max(w-this.grid.minColumnWidth, 0);
34283         this.resetConstraints();
34284         this.setXConstraint(minw, 1000);
34285         this.setYConstraint(0, 0);
34286         this.minX = x - minw;
34287         this.maxX = x + 1000;
34288         this.startPos = x;
34289         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
34290     },
34291
34292
34293     handleMouseDown : function(e){
34294         ev = Roo.EventObject.setEvent(e);
34295         var t = this.fly(ev.getTarget());
34296         if(t.hasClass("x-grid-split")){
34297             this.cellIndex = this.view.getCellIndex(t.dom);
34298             this.split = t.dom;
34299             this.cm = this.grid.colModel;
34300             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
34301                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
34302             }
34303         }
34304     },
34305
34306     endDrag : function(e){
34307         this.view.headersDisabled = false;
34308         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
34309         var diff = endX - this.startPos;
34310         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
34311     },
34312
34313     autoOffset : function(){
34314         this.setDelta(0,0);
34315     }
34316 });/*
34317  * Based on:
34318  * Ext JS Library 1.1.1
34319  * Copyright(c) 2006-2007, Ext JS, LLC.
34320  *
34321  * Originally Released Under LGPL - original licence link has changed is not relivant.
34322  *
34323  * Fork - LGPL
34324  * <script type="text/javascript">
34325  */
34326  
34327 // private
34328 // This is a support class used internally by the Grid components
34329 Roo.grid.GridDragZone = function(grid, config){
34330     this.view = grid.getView();
34331     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
34332     if(this.view.lockedBody){
34333         this.setHandleElId(Roo.id(this.view.mainBody.dom));
34334         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
34335     }
34336     this.scroll = false;
34337     this.grid = grid;
34338     this.ddel = document.createElement('div');
34339     this.ddel.className = 'x-grid-dd-wrap';
34340 };
34341
34342 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
34343     ddGroup : "GridDD",
34344
34345     getDragData : function(e){
34346         var t = Roo.lib.Event.getTarget(e);
34347         var rowIndex = this.view.findRowIndex(t);
34348         if(rowIndex !== false){
34349             var sm = this.grid.selModel;
34350             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
34351               //  sm.mouseDown(e, t);
34352             //}
34353             if (e.hasModifier()){
34354                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
34355             }
34356             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
34357         }
34358         return false;
34359     },
34360
34361     onInitDrag : function(e){
34362         var data = this.dragData;
34363         this.ddel.innerHTML = this.grid.getDragDropText();
34364         this.proxy.update(this.ddel);
34365         // fire start drag?
34366     },
34367
34368     afterRepair : function(){
34369         this.dragging = false;
34370     },
34371
34372     getRepairXY : function(e, data){
34373         return false;
34374     },
34375
34376     onEndDrag : function(data, e){
34377         // fire end drag?
34378     },
34379
34380     onValidDrop : function(dd, e, id){
34381         // fire drag drop?
34382         this.hideProxy();
34383     },
34384
34385     beforeInvalidDrop : function(e, id){
34386
34387     }
34388 });/*
34389  * Based on:
34390  * Ext JS Library 1.1.1
34391  * Copyright(c) 2006-2007, Ext JS, LLC.
34392  *
34393  * Originally Released Under LGPL - original licence link has changed is not relivant.
34394  *
34395  * Fork - LGPL
34396  * <script type="text/javascript">
34397  */
34398  
34399
34400 /**
34401  * @class Roo.grid.ColumnModel
34402  * @extends Roo.util.Observable
34403  * This is the default implementation of a ColumnModel used by the Grid. It defines
34404  * the columns in the grid.
34405  * <br>Usage:<br>
34406  <pre><code>
34407  var colModel = new Roo.grid.ColumnModel([
34408         {header: "Ticker", width: 60, sortable: true, locked: true},
34409         {header: "Company Name", width: 150, sortable: true},
34410         {header: "Market Cap.", width: 100, sortable: true},
34411         {header: "$ Sales", width: 100, sortable: true, renderer: money},
34412         {header: "Employees", width: 100, sortable: true, resizable: false}
34413  ]);
34414  </code></pre>
34415  * <p>
34416  
34417  * The config options listed for this class are options which may appear in each
34418  * individual column definition.
34419  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
34420  * @constructor
34421  * @param {Object} config An Array of column config objects. See this class's
34422  * config objects for details.
34423 */
34424 Roo.grid.ColumnModel = function(config){
34425         /**
34426      * The config passed into the constructor
34427      */
34428     this.config = config;
34429     this.lookup = {};
34430
34431     // if no id, create one
34432     // if the column does not have a dataIndex mapping,
34433     // map it to the order it is in the config
34434     for(var i = 0, len = config.length; i < len; i++){
34435         var c = config[i];
34436         if(typeof c.dataIndex == "undefined"){
34437             c.dataIndex = i;
34438         }
34439         if(typeof c.renderer == "string"){
34440             c.renderer = Roo.util.Format[c.renderer];
34441         }
34442         if(typeof c.id == "undefined"){
34443             c.id = Roo.id();
34444         }
34445         if(c.editor && c.editor.xtype){
34446             c.editor  = Roo.factory(c.editor, Roo.grid);
34447         }
34448         if(c.editor && c.editor.isFormField){
34449             c.editor = new Roo.grid.GridEditor(c.editor);
34450         }
34451         this.lookup[c.id] = c;
34452     }
34453
34454     /**
34455      * The width of columns which have no width specified (defaults to 100)
34456      * @type Number
34457      */
34458     this.defaultWidth = 100;
34459
34460     /**
34461      * Default sortable of columns which have no sortable specified (defaults to false)
34462      * @type Boolean
34463      */
34464     this.defaultSortable = false;
34465
34466     this.addEvents({
34467         /**
34468              * @event widthchange
34469              * Fires when the width of a column changes.
34470              * @param {ColumnModel} this
34471              * @param {Number} columnIndex The column index
34472              * @param {Number} newWidth The new width
34473              */
34474             "widthchange": true,
34475         /**
34476              * @event headerchange
34477              * Fires when the text of a header changes.
34478              * @param {ColumnModel} this
34479              * @param {Number} columnIndex The column index
34480              * @param {Number} newText The new header text
34481              */
34482             "headerchange": true,
34483         /**
34484              * @event hiddenchange
34485              * Fires when a column is hidden or "unhidden".
34486              * @param {ColumnModel} this
34487              * @param {Number} columnIndex The column index
34488              * @param {Boolean} hidden true if hidden, false otherwise
34489              */
34490             "hiddenchange": true,
34491             /**
34492          * @event columnmoved
34493          * Fires when a column is moved.
34494          * @param {ColumnModel} this
34495          * @param {Number} oldIndex
34496          * @param {Number} newIndex
34497          */
34498         "columnmoved" : true,
34499         /**
34500          * @event columlockchange
34501          * Fires when a column's locked state is changed
34502          * @param {ColumnModel} this
34503          * @param {Number} colIndex
34504          * @param {Boolean} locked true if locked
34505          */
34506         "columnlockchange" : true
34507     });
34508     Roo.grid.ColumnModel.superclass.constructor.call(this);
34509 };
34510 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
34511     /**
34512      * @cfg {String} header The header text to display in the Grid view.
34513      */
34514     /**
34515      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
34516      * {@link Roo.data.Record} definition from which to draw the column's value. If not
34517      * specified, the column's index is used as an index into the Record's data Array.
34518      */
34519     /**
34520      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
34521      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
34522      */
34523     /**
34524      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
34525      * Defaults to the value of the {@link #defaultSortable} property.
34526      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
34527      */
34528     /**
34529      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
34530      */
34531     /**
34532      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
34533      */
34534     /**
34535      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
34536      */
34537     /**
34538      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
34539      */
34540     /**
34541      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
34542      * given the cell's data value. See {@link #setRenderer}. If not specified, the
34543      * default renderer uses the raw data value.
34544      */
34545        /**
34546      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
34547      */
34548     /**
34549      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
34550      */
34551
34552     /**
34553      * Returns the id of the column at the specified index.
34554      * @param {Number} index The column index
34555      * @return {String} the id
34556      */
34557     getColumnId : function(index){
34558         return this.config[index].id;
34559     },
34560
34561     /**
34562      * Returns the column for a specified id.
34563      * @param {String} id The column id
34564      * @return {Object} the column
34565      */
34566     getColumnById : function(id){
34567         return this.lookup[id];
34568     },
34569
34570     
34571     /**
34572      * Returns the column for a specified dataIndex.
34573      * @param {String} dataIndex The column dataIndex
34574      * @return {Object|Boolean} the column or false if not found
34575      */
34576     getColumnByDataIndex: function(dataIndex){
34577         var index = this.findColumnIndex(dataIndex);
34578         return index > -1 ? this.config[index] : false;
34579     },
34580     
34581     /**
34582      * Returns the index for a specified column id.
34583      * @param {String} id The column id
34584      * @return {Number} the index, or -1 if not found
34585      */
34586     getIndexById : function(id){
34587         for(var i = 0, len = this.config.length; i < len; i++){
34588             if(this.config[i].id == id){
34589                 return i;
34590             }
34591         }
34592         return -1;
34593     },
34594     
34595     /**
34596      * Returns the index for a specified column dataIndex.
34597      * @param {String} dataIndex The column dataIndex
34598      * @return {Number} the index, or -1 if not found
34599      */
34600     
34601     findColumnIndex : function(dataIndex){
34602         for(var i = 0, len = this.config.length; i < len; i++){
34603             if(this.config[i].dataIndex == dataIndex){
34604                 return i;
34605             }
34606         }
34607         return -1;
34608     },
34609     
34610     
34611     moveColumn : function(oldIndex, newIndex){
34612         var c = this.config[oldIndex];
34613         this.config.splice(oldIndex, 1);
34614         this.config.splice(newIndex, 0, c);
34615         this.dataMap = null;
34616         this.fireEvent("columnmoved", this, oldIndex, newIndex);
34617     },
34618
34619     isLocked : function(colIndex){
34620         return this.config[colIndex].locked === true;
34621     },
34622
34623     setLocked : function(colIndex, value, suppressEvent){
34624         if(this.isLocked(colIndex) == value){
34625             return;
34626         }
34627         this.config[colIndex].locked = value;
34628         if(!suppressEvent){
34629             this.fireEvent("columnlockchange", this, colIndex, value);
34630         }
34631     },
34632
34633     getTotalLockedWidth : function(){
34634         var totalWidth = 0;
34635         for(var i = 0; i < this.config.length; i++){
34636             if(this.isLocked(i) && !this.isHidden(i)){
34637                 this.totalWidth += this.getColumnWidth(i);
34638             }
34639         }
34640         return totalWidth;
34641     },
34642
34643     getLockedCount : function(){
34644         for(var i = 0, len = this.config.length; i < len; i++){
34645             if(!this.isLocked(i)){
34646                 return i;
34647             }
34648         }
34649     },
34650
34651     /**
34652      * Returns the number of columns.
34653      * @return {Number}
34654      */
34655     getColumnCount : function(visibleOnly){
34656         if(visibleOnly === true){
34657             var c = 0;
34658             for(var i = 0, len = this.config.length; i < len; i++){
34659                 if(!this.isHidden(i)){
34660                     c++;
34661                 }
34662             }
34663             return c;
34664         }
34665         return this.config.length;
34666     },
34667
34668     /**
34669      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
34670      * @param {Function} fn
34671      * @param {Object} scope (optional)
34672      * @return {Array} result
34673      */
34674     getColumnsBy : function(fn, scope){
34675         var r = [];
34676         for(var i = 0, len = this.config.length; i < len; i++){
34677             var c = this.config[i];
34678             if(fn.call(scope||this, c, i) === true){
34679                 r[r.length] = c;
34680             }
34681         }
34682         return r;
34683     },
34684
34685     /**
34686      * Returns true if the specified column is sortable.
34687      * @param {Number} col The column index
34688      * @return {Boolean}
34689      */
34690     isSortable : function(col){
34691         if(typeof this.config[col].sortable == "undefined"){
34692             return this.defaultSortable;
34693         }
34694         return this.config[col].sortable;
34695     },
34696
34697     /**
34698      * Returns the rendering (formatting) function defined for the column.
34699      * @param {Number} col The column index.
34700      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
34701      */
34702     getRenderer : function(col){
34703         if(!this.config[col].renderer){
34704             return Roo.grid.ColumnModel.defaultRenderer;
34705         }
34706         return this.config[col].renderer;
34707     },
34708
34709     /**
34710      * Sets the rendering (formatting) function for a column.
34711      * @param {Number} col The column index
34712      * @param {Function} fn The function to use to process the cell's raw data
34713      * to return HTML markup for the grid view. The render function is called with
34714      * the following parameters:<ul>
34715      * <li>Data value.</li>
34716      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
34717      * <li>css A CSS style string to apply to the table cell.</li>
34718      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
34719      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
34720      * <li>Row index</li>
34721      * <li>Column index</li>
34722      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
34723      */
34724     setRenderer : function(col, fn){
34725         this.config[col].renderer = fn;
34726     },
34727
34728     /**
34729      * Returns the width for the specified column.
34730      * @param {Number} col The column index
34731      * @return {Number}
34732      */
34733     getColumnWidth : function(col){
34734         return this.config[col].width || this.defaultWidth;
34735     },
34736
34737     /**
34738      * Sets the width for a column.
34739      * @param {Number} col The column index
34740      * @param {Number} width The new width
34741      */
34742     setColumnWidth : function(col, width, suppressEvent){
34743         this.config[col].width = width;
34744         this.totalWidth = null;
34745         if(!suppressEvent){
34746              this.fireEvent("widthchange", this, col, width);
34747         }
34748     },
34749
34750     /**
34751      * Returns the total width of all columns.
34752      * @param {Boolean} includeHidden True to include hidden column widths
34753      * @return {Number}
34754      */
34755     getTotalWidth : function(includeHidden){
34756         if(!this.totalWidth){
34757             this.totalWidth = 0;
34758             for(var i = 0, len = this.config.length; i < len; i++){
34759                 if(includeHidden || !this.isHidden(i)){
34760                     this.totalWidth += this.getColumnWidth(i);
34761                 }
34762             }
34763         }
34764         return this.totalWidth;
34765     },
34766
34767     /**
34768      * Returns the header for the specified column.
34769      * @param {Number} col The column index
34770      * @return {String}
34771      */
34772     getColumnHeader : function(col){
34773         return this.config[col].header;
34774     },
34775
34776     /**
34777      * Sets the header for a column.
34778      * @param {Number} col The column index
34779      * @param {String} header The new header
34780      */
34781     setColumnHeader : function(col, header){
34782         this.config[col].header = header;
34783         this.fireEvent("headerchange", this, col, header);
34784     },
34785
34786     /**
34787      * Returns the tooltip for the specified column.
34788      * @param {Number} col The column index
34789      * @return {String}
34790      */
34791     getColumnTooltip : function(col){
34792             return this.config[col].tooltip;
34793     },
34794     /**
34795      * Sets the tooltip for a column.
34796      * @param {Number} col The column index
34797      * @param {String} tooltip The new tooltip
34798      */
34799     setColumnTooltip : function(col, tooltip){
34800             this.config[col].tooltip = tooltip;
34801     },
34802
34803     /**
34804      * Returns the dataIndex for the specified column.
34805      * @param {Number} col The column index
34806      * @return {Number}
34807      */
34808     getDataIndex : function(col){
34809         return this.config[col].dataIndex;
34810     },
34811
34812     /**
34813      * Sets the dataIndex for a column.
34814      * @param {Number} col The column index
34815      * @param {Number} dataIndex The new dataIndex
34816      */
34817     setDataIndex : function(col, dataIndex){
34818         this.config[col].dataIndex = dataIndex;
34819     },
34820
34821     
34822     
34823     /**
34824      * Returns true if the cell is editable.
34825      * @param {Number} colIndex The column index
34826      * @param {Number} rowIndex The row index
34827      * @return {Boolean}
34828      */
34829     isCellEditable : function(colIndex, rowIndex){
34830         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
34831     },
34832
34833     /**
34834      * Returns the editor defined for the cell/column.
34835      * return false or null to disable editing.
34836      * @param {Number} colIndex The column index
34837      * @param {Number} rowIndex The row index
34838      * @return {Object}
34839      */
34840     getCellEditor : function(colIndex, rowIndex){
34841         return this.config[colIndex].editor;
34842     },
34843
34844     /**
34845      * Sets if a column is editable.
34846      * @param {Number} col The column index
34847      * @param {Boolean} editable True if the column is editable
34848      */
34849     setEditable : function(col, editable){
34850         this.config[col].editable = editable;
34851     },
34852
34853
34854     /**
34855      * Returns true if the column is hidden.
34856      * @param {Number} colIndex The column index
34857      * @return {Boolean}
34858      */
34859     isHidden : function(colIndex){
34860         return this.config[colIndex].hidden;
34861     },
34862
34863
34864     /**
34865      * Returns true if the column width cannot be changed
34866      */
34867     isFixed : function(colIndex){
34868         return this.config[colIndex].fixed;
34869     },
34870
34871     /**
34872      * Returns true if the column can be resized
34873      * @return {Boolean}
34874      */
34875     isResizable : function(colIndex){
34876         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
34877     },
34878     /**
34879      * Sets if a column is hidden.
34880      * @param {Number} colIndex The column index
34881      * @param {Boolean} hidden True if the column is hidden
34882      */
34883     setHidden : function(colIndex, hidden){
34884         this.config[colIndex].hidden = hidden;
34885         this.totalWidth = null;
34886         this.fireEvent("hiddenchange", this, colIndex, hidden);
34887     },
34888
34889     /**
34890      * Sets the editor for a column.
34891      * @param {Number} col The column index
34892      * @param {Object} editor The editor object
34893      */
34894     setEditor : function(col, editor){
34895         this.config[col].editor = editor;
34896     }
34897 });
34898
34899 Roo.grid.ColumnModel.defaultRenderer = function(value){
34900         if(typeof value == "string" && value.length < 1){
34901             return "&#160;";
34902         }
34903         return value;
34904 };
34905
34906 // Alias for backwards compatibility
34907 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
34908 /*
34909  * Based on:
34910  * Ext JS Library 1.1.1
34911  * Copyright(c) 2006-2007, Ext JS, LLC.
34912  *
34913  * Originally Released Under LGPL - original licence link has changed is not relivant.
34914  *
34915  * Fork - LGPL
34916  * <script type="text/javascript">
34917  */
34918
34919 /**
34920  * @class Roo.grid.AbstractSelectionModel
34921  * @extends Roo.util.Observable
34922  * Abstract base class for grid SelectionModels.  It provides the interface that should be
34923  * implemented by descendant classes.  This class should not be directly instantiated.
34924  * @constructor
34925  */
34926 Roo.grid.AbstractSelectionModel = function(){
34927     this.locked = false;
34928     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
34929 };
34930
34931 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
34932     /** @ignore Called by the grid automatically. Do not call directly. */
34933     init : function(grid){
34934         this.grid = grid;
34935         this.initEvents();
34936     },
34937
34938     /**
34939      * Locks the selections.
34940      */
34941     lock : function(){
34942         this.locked = true;
34943     },
34944
34945     /**
34946      * Unlocks the selections.
34947      */
34948     unlock : function(){
34949         this.locked = false;
34950     },
34951
34952     /**
34953      * Returns true if the selections are locked.
34954      * @return {Boolean}
34955      */
34956     isLocked : function(){
34957         return this.locked;
34958     }
34959 });/*
34960  * Based on:
34961  * Ext JS Library 1.1.1
34962  * Copyright(c) 2006-2007, Ext JS, LLC.
34963  *
34964  * Originally Released Under LGPL - original licence link has changed is not relivant.
34965  *
34966  * Fork - LGPL
34967  * <script type="text/javascript">
34968  */
34969 /**
34970  * @extends Roo.grid.AbstractSelectionModel
34971  * @class Roo.grid.RowSelectionModel
34972  * The default SelectionModel used by {@link Roo.grid.Grid}.
34973  * It supports multiple selections and keyboard selection/navigation. 
34974  * @constructor
34975  * @param {Object} config
34976  */
34977 Roo.grid.RowSelectionModel = function(config){
34978     Roo.apply(this, config);
34979     this.selections = new Roo.util.MixedCollection(false, function(o){
34980         return o.id;
34981     });
34982
34983     this.last = false;
34984     this.lastActive = false;
34985
34986     this.addEvents({
34987         /**
34988              * @event selectionchange
34989              * Fires when the selection changes
34990              * @param {SelectionModel} this
34991              */
34992             "selectionchange" : true,
34993         /**
34994              * @event afterselectionchange
34995              * Fires after the selection changes (eg. by key press or clicking)
34996              * @param {SelectionModel} this
34997              */
34998             "afterselectionchange" : true,
34999         /**
35000              * @event beforerowselect
35001              * Fires when a row is selected being selected, return false to cancel.
35002              * @param {SelectionModel} this
35003              * @param {Number} rowIndex The selected index
35004              * @param {Boolean} keepExisting False if other selections will be cleared
35005              */
35006             "beforerowselect" : true,
35007         /**
35008              * @event rowselect
35009              * Fires when a row is selected.
35010              * @param {SelectionModel} this
35011              * @param {Number} rowIndex The selected index
35012              * @param {Roo.data.Record} r The record
35013              */
35014             "rowselect" : true,
35015         /**
35016              * @event rowdeselect
35017              * Fires when a row is deselected.
35018              * @param {SelectionModel} this
35019              * @param {Number} rowIndex The selected index
35020              */
35021         "rowdeselect" : true
35022     });
35023     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
35024     this.locked = false;
35025 };
35026
35027 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
35028     /**
35029      * @cfg {Boolean} singleSelect
35030      * True to allow selection of only one row at a time (defaults to false)
35031      */
35032     singleSelect : false,
35033
35034     // private
35035     initEvents : function(){
35036
35037         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
35038             this.grid.on("mousedown", this.handleMouseDown, this);
35039         }else{ // allow click to work like normal
35040             this.grid.on("rowclick", this.handleDragableRowClick, this);
35041         }
35042
35043         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
35044             "up" : function(e){
35045                 if(!e.shiftKey){
35046                     this.selectPrevious(e.shiftKey);
35047                 }else if(this.last !== false && this.lastActive !== false){
35048                     var last = this.last;
35049                     this.selectRange(this.last,  this.lastActive-1);
35050                     this.grid.getView().focusRow(this.lastActive);
35051                     if(last !== false){
35052                         this.last = last;
35053                     }
35054                 }else{
35055                     this.selectFirstRow();
35056                 }
35057                 this.fireEvent("afterselectionchange", this);
35058             },
35059             "down" : function(e){
35060                 if(!e.shiftKey){
35061                     this.selectNext(e.shiftKey);
35062                 }else if(this.last !== false && this.lastActive !== false){
35063                     var last = this.last;
35064                     this.selectRange(this.last,  this.lastActive+1);
35065                     this.grid.getView().focusRow(this.lastActive);
35066                     if(last !== false){
35067                         this.last = last;
35068                     }
35069                 }else{
35070                     this.selectFirstRow();
35071                 }
35072                 this.fireEvent("afterselectionchange", this);
35073             },
35074             scope: this
35075         });
35076
35077         var view = this.grid.view;
35078         view.on("refresh", this.onRefresh, this);
35079         view.on("rowupdated", this.onRowUpdated, this);
35080         view.on("rowremoved", this.onRemove, this);
35081     },
35082
35083     // private
35084     onRefresh : function(){
35085         var ds = this.grid.dataSource, i, v = this.grid.view;
35086         var s = this.selections;
35087         s.each(function(r){
35088             if((i = ds.indexOfId(r.id)) != -1){
35089                 v.onRowSelect(i);
35090             }else{
35091                 s.remove(r);
35092             }
35093         });
35094     },
35095
35096     // private
35097     onRemove : function(v, index, r){
35098         this.selections.remove(r);
35099     },
35100
35101     // private
35102     onRowUpdated : function(v, index, r){
35103         if(this.isSelected(r)){
35104             v.onRowSelect(index);
35105         }
35106     },
35107
35108     /**
35109      * Select records.
35110      * @param {Array} records The records to select
35111      * @param {Boolean} keepExisting (optional) True to keep existing selections
35112      */
35113     selectRecords : function(records, keepExisting){
35114         if(!keepExisting){
35115             this.clearSelections();
35116         }
35117         var ds = this.grid.dataSource;
35118         for(var i = 0, len = records.length; i < len; i++){
35119             this.selectRow(ds.indexOf(records[i]), true);
35120         }
35121     },
35122
35123     /**
35124      * Gets the number of selected rows.
35125      * @return {Number}
35126      */
35127     getCount : function(){
35128         return this.selections.length;
35129     },
35130
35131     /**
35132      * Selects the first row in the grid.
35133      */
35134     selectFirstRow : function(){
35135         this.selectRow(0);
35136     },
35137
35138     /**
35139      * Select the last row.
35140      * @param {Boolean} keepExisting (optional) True to keep existing selections
35141      */
35142     selectLastRow : function(keepExisting){
35143         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
35144     },
35145
35146     /**
35147      * Selects the row immediately following the last selected row.
35148      * @param {Boolean} keepExisting (optional) True to keep existing selections
35149      */
35150     selectNext : function(keepExisting){
35151         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
35152             this.selectRow(this.last+1, keepExisting);
35153             this.grid.getView().focusRow(this.last);
35154         }
35155     },
35156
35157     /**
35158      * Selects the row that precedes the last selected row.
35159      * @param {Boolean} keepExisting (optional) True to keep existing selections
35160      */
35161     selectPrevious : function(keepExisting){
35162         if(this.last){
35163             this.selectRow(this.last-1, keepExisting);
35164             this.grid.getView().focusRow(this.last);
35165         }
35166     },
35167
35168     /**
35169      * Returns the selected records
35170      * @return {Array} Array of selected records
35171      */
35172     getSelections : function(){
35173         return [].concat(this.selections.items);
35174     },
35175
35176     /**
35177      * Returns the first selected record.
35178      * @return {Record}
35179      */
35180     getSelected : function(){
35181         return this.selections.itemAt(0);
35182     },
35183
35184
35185     /**
35186      * Clears all selections.
35187      */
35188     clearSelections : function(fast){
35189         if(this.locked) return;
35190         if(fast !== true){
35191             var ds = this.grid.dataSource;
35192             var s = this.selections;
35193             s.each(function(r){
35194                 this.deselectRow(ds.indexOfId(r.id));
35195             }, this);
35196             s.clear();
35197         }else{
35198             this.selections.clear();
35199         }
35200         this.last = false;
35201     },
35202
35203
35204     /**
35205      * Selects all rows.
35206      */
35207     selectAll : function(){
35208         if(this.locked) return;
35209         this.selections.clear();
35210         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
35211             this.selectRow(i, true);
35212         }
35213     },
35214
35215     /**
35216      * Returns True if there is a selection.
35217      * @return {Boolean}
35218      */
35219     hasSelection : function(){
35220         return this.selections.length > 0;
35221     },
35222
35223     /**
35224      * Returns True if the specified row is selected.
35225      * @param {Number/Record} record The record or index of the record to check
35226      * @return {Boolean}
35227      */
35228     isSelected : function(index){
35229         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
35230         return (r && this.selections.key(r.id) ? true : false);
35231     },
35232
35233     /**
35234      * Returns True if the specified record id is selected.
35235      * @param {String} id The id of record to check
35236      * @return {Boolean}
35237      */
35238     isIdSelected : function(id){
35239         return (this.selections.key(id) ? true : false);
35240     },
35241
35242     // private
35243     handleMouseDown : function(e, t){
35244         var view = this.grid.getView(), rowIndex;
35245         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
35246             return;
35247         };
35248         if(e.shiftKey && this.last !== false){
35249             var last = this.last;
35250             this.selectRange(last, rowIndex, e.ctrlKey);
35251             this.last = last; // reset the last
35252             view.focusRow(rowIndex);
35253         }else{
35254             var isSelected = this.isSelected(rowIndex);
35255             if(e.button !== 0 && isSelected){
35256                 view.focusRow(rowIndex);
35257             }else if(e.ctrlKey && isSelected){
35258                 this.deselectRow(rowIndex);
35259             }else if(!isSelected){
35260                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
35261                 view.focusRow(rowIndex);
35262             }
35263         }
35264         this.fireEvent("afterselectionchange", this);
35265     },
35266     // private
35267     handleDragableRowClick :  function(grid, rowIndex, e) 
35268     {
35269         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
35270             this.selectRow(rowIndex, false);
35271             grid.view.focusRow(rowIndex);
35272              this.fireEvent("afterselectionchange", this);
35273         }
35274     },
35275     
35276     /**
35277      * Selects multiple rows.
35278      * @param {Array} rows Array of the indexes of the row to select
35279      * @param {Boolean} keepExisting (optional) True to keep existing selections
35280      */
35281     selectRows : function(rows, keepExisting){
35282         if(!keepExisting){
35283             this.clearSelections();
35284         }
35285         for(var i = 0, len = rows.length; i < len; i++){
35286             this.selectRow(rows[i], true);
35287         }
35288     },
35289
35290     /**
35291      * Selects a range of rows. All rows in between startRow and endRow are also selected.
35292      * @param {Number} startRow The index of the first row in the range
35293      * @param {Number} endRow The index of the last row in the range
35294      * @param {Boolean} keepExisting (optional) True to retain existing selections
35295      */
35296     selectRange : function(startRow, endRow, keepExisting){
35297         if(this.locked) return;
35298         if(!keepExisting){
35299             this.clearSelections();
35300         }
35301         if(startRow <= endRow){
35302             for(var i = startRow; i <= endRow; i++){
35303                 this.selectRow(i, true);
35304             }
35305         }else{
35306             for(var i = startRow; i >= endRow; i--){
35307                 this.selectRow(i, true);
35308             }
35309         }
35310     },
35311
35312     /**
35313      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
35314      * @param {Number} startRow The index of the first row in the range
35315      * @param {Number} endRow The index of the last row in the range
35316      */
35317     deselectRange : function(startRow, endRow, preventViewNotify){
35318         if(this.locked) return;
35319         for(var i = startRow; i <= endRow; i++){
35320             this.deselectRow(i, preventViewNotify);
35321         }
35322     },
35323
35324     /**
35325      * Selects a row.
35326      * @param {Number} row The index of the row to select
35327      * @param {Boolean} keepExisting (optional) True to keep existing selections
35328      */
35329     selectRow : function(index, keepExisting, preventViewNotify){
35330         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
35331         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
35332             if(!keepExisting || this.singleSelect){
35333                 this.clearSelections();
35334             }
35335             var r = this.grid.dataSource.getAt(index);
35336             this.selections.add(r);
35337             this.last = this.lastActive = index;
35338             if(!preventViewNotify){
35339                 this.grid.getView().onRowSelect(index);
35340             }
35341             this.fireEvent("rowselect", this, index, r);
35342             this.fireEvent("selectionchange", this);
35343         }
35344     },
35345
35346     /**
35347      * Deselects a row.
35348      * @param {Number} row The index of the row to deselect
35349      */
35350     deselectRow : function(index, preventViewNotify){
35351         if(this.locked) return;
35352         if(this.last == index){
35353             this.last = false;
35354         }
35355         if(this.lastActive == index){
35356             this.lastActive = false;
35357         }
35358         var r = this.grid.dataSource.getAt(index);
35359         this.selections.remove(r);
35360         if(!preventViewNotify){
35361             this.grid.getView().onRowDeselect(index);
35362         }
35363         this.fireEvent("rowdeselect", this, index);
35364         this.fireEvent("selectionchange", this);
35365     },
35366
35367     // private
35368     restoreLast : function(){
35369         if(this._last){
35370             this.last = this._last;
35371         }
35372     },
35373
35374     // private
35375     acceptsNav : function(row, col, cm){
35376         return !cm.isHidden(col) && cm.isCellEditable(col, row);
35377     },
35378
35379     // private
35380     onEditorKey : function(field, e){
35381         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
35382         if(k == e.TAB){
35383             e.stopEvent();
35384             ed.completeEdit();
35385             if(e.shiftKey){
35386                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
35387             }else{
35388                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
35389             }
35390         }else if(k == e.ENTER && !e.ctrlKey){
35391             e.stopEvent();
35392             ed.completeEdit();
35393             if(e.shiftKey){
35394                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
35395             }else{
35396                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
35397             }
35398         }else if(k == e.ESC){
35399             ed.cancelEdit();
35400         }
35401         if(newCell){
35402             g.startEditing(newCell[0], newCell[1]);
35403         }
35404     }
35405 });/*
35406  * Based on:
35407  * Ext JS Library 1.1.1
35408  * Copyright(c) 2006-2007, Ext JS, LLC.
35409  *
35410  * Originally Released Under LGPL - original licence link has changed is not relivant.
35411  *
35412  * Fork - LGPL
35413  * <script type="text/javascript">
35414  */
35415 /**
35416  * @class Roo.grid.CellSelectionModel
35417  * @extends Roo.grid.AbstractSelectionModel
35418  * This class provides the basic implementation for cell selection in a grid.
35419  * @constructor
35420  * @param {Object} config The object containing the configuration of this model.
35421  */
35422 Roo.grid.CellSelectionModel = function(config){
35423     Roo.apply(this, config);
35424
35425     this.selection = null;
35426
35427     this.addEvents({
35428         /**
35429              * @event beforerowselect
35430              * Fires before a cell is selected.
35431              * @param {SelectionModel} this
35432              * @param {Number} rowIndex The selected row index
35433              * @param {Number} colIndex The selected cell index
35434              */
35435             "beforecellselect" : true,
35436         /**
35437              * @event cellselect
35438              * Fires when a cell is selected.
35439              * @param {SelectionModel} this
35440              * @param {Number} rowIndex The selected row index
35441              * @param {Number} colIndex The selected cell index
35442              */
35443             "cellselect" : true,
35444         /**
35445              * @event selectionchange
35446              * Fires when the active selection changes.
35447              * @param {SelectionModel} this
35448              * @param {Object} selection null for no selection or an object (o) with two properties
35449                 <ul>
35450                 <li>o.record: the record object for the row the selection is in</li>
35451                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
35452                 </ul>
35453              */
35454             "selectionchange" : true
35455     });
35456     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
35457 };
35458
35459 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
35460
35461     /** @ignore */
35462     initEvents : function(){
35463         this.grid.on("mousedown", this.handleMouseDown, this);
35464         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
35465         var view = this.grid.view;
35466         view.on("refresh", this.onViewChange, this);
35467         view.on("rowupdated", this.onRowUpdated, this);
35468         view.on("beforerowremoved", this.clearSelections, this);
35469         view.on("beforerowsinserted", this.clearSelections, this);
35470         if(this.grid.isEditor){
35471             this.grid.on("beforeedit", this.beforeEdit,  this);
35472         }
35473     },
35474
35475         //private
35476     beforeEdit : function(e){
35477         this.select(e.row, e.column, false, true, e.record);
35478     },
35479
35480         //private
35481     onRowUpdated : function(v, index, r){
35482         if(this.selection && this.selection.record == r){
35483             v.onCellSelect(index, this.selection.cell[1]);
35484         }
35485     },
35486
35487         //private
35488     onViewChange : function(){
35489         this.clearSelections(true);
35490     },
35491
35492         /**
35493          * Returns the currently selected cell,.
35494          * @return {Array} The selected cell (row, column) or null if none selected.
35495          */
35496     getSelectedCell : function(){
35497         return this.selection ? this.selection.cell : null;
35498     },
35499
35500     /**
35501      * Clears all selections.
35502      * @param {Boolean} true to prevent the gridview from being notified about the change.
35503      */
35504     clearSelections : function(preventNotify){
35505         var s = this.selection;
35506         if(s){
35507             if(preventNotify !== true){
35508                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
35509             }
35510             this.selection = null;
35511             this.fireEvent("selectionchange", this, null);
35512         }
35513     },
35514
35515     /**
35516      * Returns true if there is a selection.
35517      * @return {Boolean}
35518      */
35519     hasSelection : function(){
35520         return this.selection ? true : false;
35521     },
35522
35523     /** @ignore */
35524     handleMouseDown : function(e, t){
35525         var v = this.grid.getView();
35526         if(this.isLocked()){
35527             return;
35528         };
35529         var row = v.findRowIndex(t);
35530         var cell = v.findCellIndex(t);
35531         if(row !== false && cell !== false){
35532             this.select(row, cell);
35533         }
35534     },
35535
35536     /**
35537      * Selects a cell.
35538      * @param {Number} rowIndex
35539      * @param {Number} collIndex
35540      */
35541     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
35542         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
35543             this.clearSelections();
35544             r = r || this.grid.dataSource.getAt(rowIndex);
35545             this.selection = {
35546                 record : r,
35547                 cell : [rowIndex, colIndex]
35548             };
35549             if(!preventViewNotify){
35550                 var v = this.grid.getView();
35551                 v.onCellSelect(rowIndex, colIndex);
35552                 if(preventFocus !== true){
35553                     v.focusCell(rowIndex, colIndex);
35554                 }
35555             }
35556             this.fireEvent("cellselect", this, rowIndex, colIndex);
35557             this.fireEvent("selectionchange", this, this.selection);
35558         }
35559     },
35560
35561         //private
35562     isSelectable : function(rowIndex, colIndex, cm){
35563         return !cm.isHidden(colIndex);
35564     },
35565
35566     /** @ignore */
35567     handleKeyDown : function(e){
35568         Roo.log('Cell Sel Model handleKeyDown');
35569         if(!e.isNavKeyPress()){
35570             return;
35571         }
35572         var g = this.grid, s = this.selection;
35573         if(!s){
35574             e.stopEvent();
35575             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
35576             if(cell){
35577                 this.select(cell[0], cell[1]);
35578             }
35579             return;
35580         }
35581         var sm = this;
35582         var walk = function(row, col, step){
35583             return g.walkCells(row, col, step, sm.isSelectable,  sm);
35584         };
35585         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
35586         var newCell;
35587
35588         switch(k){
35589             case e.TAB:
35590                 // handled by onEditorKey
35591                 if (g.isEditor && g.editing) {
35592                     return;
35593                 }
35594                 if(e.shiftKey){
35595                      newCell = walk(r, c-1, -1);
35596                 }else{
35597                      newCell = walk(r, c+1, 1);
35598                 }
35599              break;
35600              case e.DOWN:
35601                  newCell = walk(r+1, c, 1);
35602              break;
35603              case e.UP:
35604                  newCell = walk(r-1, c, -1);
35605              break;
35606              case e.RIGHT:
35607                  newCell = walk(r, c+1, 1);
35608              break;
35609              case e.LEFT:
35610                  newCell = walk(r, c-1, -1);
35611              break;
35612              case e.ENTER:
35613                  if(g.isEditor && !g.editing){
35614                     g.startEditing(r, c);
35615                     e.stopEvent();
35616                     return;
35617                 }
35618              break;
35619         };
35620         if(newCell){
35621             this.select(newCell[0], newCell[1]);
35622             e.stopEvent();
35623         }
35624     },
35625
35626     acceptsNav : function(row, col, cm){
35627         return !cm.isHidden(col) && cm.isCellEditable(col, row);
35628     },
35629
35630     onEditorKey : function(field, e){
35631         
35632         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
35633         ///Roo.log('onEditorKey' + k);
35634         
35635         if(k == e.TAB){
35636             if(e.shiftKey){
35637                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
35638             }else{
35639                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
35640             }
35641             e.stopEvent();
35642         }else if(k == e.ENTER && !e.ctrlKey){
35643             ed.completeEdit();
35644             e.stopEvent();
35645             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
35646         }else if(k == e.ESC){
35647             ed.cancelEdit();
35648         }
35649         
35650         
35651         if(newCell){
35652             //Roo.log('next cell after edit');
35653             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
35654         }
35655     }
35656 });/*
35657  * Based on:
35658  * Ext JS Library 1.1.1
35659  * Copyright(c) 2006-2007, Ext JS, LLC.
35660  *
35661  * Originally Released Under LGPL - original licence link has changed is not relivant.
35662  *
35663  * Fork - LGPL
35664  * <script type="text/javascript">
35665  */
35666  
35667 /**
35668  * @class Roo.grid.EditorGrid
35669  * @extends Roo.grid.Grid
35670  * Class for creating and editable grid.
35671  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
35672  * The container MUST have some type of size defined for the grid to fill. The container will be 
35673  * automatically set to position relative if it isn't already.
35674  * @param {Object} dataSource The data model to bind to
35675  * @param {Object} colModel The column model with info about this grid's columns
35676  */
35677 Roo.grid.EditorGrid = function(container, config){
35678     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
35679     this.getGridEl().addClass("xedit-grid");
35680
35681     if(!this.selModel){
35682         this.selModel = new Roo.grid.CellSelectionModel();
35683     }
35684
35685     this.activeEditor = null;
35686
35687         this.addEvents({
35688             /**
35689              * @event beforeedit
35690              * Fires before cell editing is triggered. The edit event object has the following properties <br />
35691              * <ul style="padding:5px;padding-left:16px;">
35692              * <li>grid - This grid</li>
35693              * <li>record - The record being edited</li>
35694              * <li>field - The field name being edited</li>
35695              * <li>value - The value for the field being edited.</li>
35696              * <li>row - The grid row index</li>
35697              * <li>column - The grid column index</li>
35698              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
35699              * </ul>
35700              * @param {Object} e An edit event (see above for description)
35701              */
35702             "beforeedit" : true,
35703             /**
35704              * @event afteredit
35705              * Fires after a cell is edited. <br />
35706              * <ul style="padding:5px;padding-left:16px;">
35707              * <li>grid - This grid</li>
35708              * <li>record - The record being edited</li>
35709              * <li>field - The field name being edited</li>
35710              * <li>value - The value being set</li>
35711              * <li>originalValue - The original value for the field, before the edit.</li>
35712              * <li>row - The grid row index</li>
35713              * <li>column - The grid column index</li>
35714              * </ul>
35715              * @param {Object} e An edit event (see above for description)
35716              */
35717             "afteredit" : true,
35718             /**
35719              * @event validateedit
35720              * Fires after a cell is edited, but before the value is set in the record. 
35721          * You can use this to modify the value being set in the field, Return false
35722              * to cancel the change. The edit event object has the following properties <br />
35723              * <ul style="padding:5px;padding-left:16px;">
35724          * <li>editor - This editor</li>
35725              * <li>grid - This grid</li>
35726              * <li>record - The record being edited</li>
35727              * <li>field - The field name being edited</li>
35728              * <li>value - The value being set</li>
35729              * <li>originalValue - The original value for the field, before the edit.</li>
35730              * <li>row - The grid row index</li>
35731              * <li>column - The grid column index</li>
35732              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
35733              * </ul>
35734              * @param {Object} e An edit event (see above for description)
35735              */
35736             "validateedit" : true
35737         });
35738     this.on("bodyscroll", this.stopEditing,  this);
35739     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
35740 };
35741
35742 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
35743     /**
35744      * @cfg {Number} clicksToEdit
35745      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
35746      */
35747     clicksToEdit: 2,
35748
35749     // private
35750     isEditor : true,
35751     // private
35752     trackMouseOver: false, // causes very odd FF errors
35753
35754     onCellDblClick : function(g, row, col){
35755         this.startEditing(row, col);
35756     },
35757
35758     onEditComplete : function(ed, value, startValue){
35759         this.editing = false;
35760         this.activeEditor = null;
35761         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
35762         var r = ed.record;
35763         var field = this.colModel.getDataIndex(ed.col);
35764         var e = {
35765             grid: this,
35766             record: r,
35767             field: field,
35768             originalValue: startValue,
35769             value: value,
35770             row: ed.row,
35771             column: ed.col,
35772             cancel:false,
35773             editor: ed
35774         };
35775         if(String(value) !== String(startValue)){
35776             
35777             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
35778                 r.set(field, e.value);
35779                 // if we are dealing with a combo box..
35780                 // then we also set the 'name' colum to be the displayField
35781                 if (ed.field.displayField && ed.field.name) {
35782                     r.set(ed.field.name, ed.field.el.dom.value);
35783                 }
35784                 
35785                 delete e.cancel; //?? why!!!
35786                 this.fireEvent("afteredit", e);
35787             }
35788         } else {
35789             this.fireEvent("afteredit", e); // always fire it!
35790         }
35791         this.view.focusCell(ed.row, ed.col);
35792     },
35793
35794     /**
35795      * Starts editing the specified for the specified row/column
35796      * @param {Number} rowIndex
35797      * @param {Number} colIndex
35798      */
35799     startEditing : function(row, col){
35800         this.stopEditing();
35801         if(this.colModel.isCellEditable(col, row)){
35802             this.view.ensureVisible(row, col, true);
35803             var r = this.dataSource.getAt(row);
35804             var field = this.colModel.getDataIndex(col);
35805             var e = {
35806                 grid: this,
35807                 record: r,
35808                 field: field,
35809                 value: r.data[field],
35810                 row: row,
35811                 column: col,
35812                 cancel:false
35813             };
35814             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
35815                 this.editing = true;
35816                 var ed = this.colModel.getCellEditor(col, row);
35817                 
35818                 if (!ed) {
35819                     return;
35820                 }
35821                 if(!ed.rendered){
35822                     ed.render(ed.parentEl || document.body);
35823                 }
35824                 ed.field.reset();
35825                 (function(){ // complex but required for focus issues in safari, ie and opera
35826                     ed.row = row;
35827                     ed.col = col;
35828                     ed.record = r;
35829                     ed.on("complete", this.onEditComplete, this, {single: true});
35830                     ed.on("specialkey", this.selModel.onEditorKey, this.selModel);
35831                     this.activeEditor = ed;
35832                     var v = r.data[field];
35833                     ed.startEdit(this.view.getCell(row, col), v);
35834                     // combo's with 'displayField and name set
35835                     if (ed.field.displayField && ed.field.name) {
35836                         ed.field.el.dom.value = r.data[ed.field.name];
35837                     }
35838                     
35839                     
35840                 }).defer(50, this);
35841             }
35842         }
35843     },
35844         
35845     /**
35846      * Stops any active editing
35847      */
35848     stopEditing : function(){
35849         if(this.activeEditor){
35850             this.activeEditor.completeEdit();
35851         }
35852         this.activeEditor = null;
35853     }
35854 });/*
35855  * Based on:
35856  * Ext JS Library 1.1.1
35857  * Copyright(c) 2006-2007, Ext JS, LLC.
35858  *
35859  * Originally Released Under LGPL - original licence link has changed is not relivant.
35860  *
35861  * Fork - LGPL
35862  * <script type="text/javascript">
35863  */
35864
35865 // private - not really -- you end up using it !
35866 // This is a support class used internally by the Grid components
35867
35868 /**
35869  * @class Roo.grid.GridEditor
35870  * @extends Roo.Editor
35871  * Class for creating and editable grid elements.
35872  * @param {Object} config any settings (must include field)
35873  */
35874 Roo.grid.GridEditor = function(field, config){
35875     if (!config && field.field) {
35876         config = field;
35877         field = Roo.factory(config.field, Roo.form);
35878     }
35879     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
35880     field.monitorTab = false;
35881 };
35882
35883 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
35884     
35885     /**
35886      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
35887      */
35888     
35889     alignment: "tl-tl",
35890     autoSize: "width",
35891     hideEl : false,
35892     cls: "x-small-editor x-grid-editor",
35893     shim:false,
35894     shadow:"frame"
35895 });/*
35896  * Based on:
35897  * Ext JS Library 1.1.1
35898  * Copyright(c) 2006-2007, Ext JS, LLC.
35899  *
35900  * Originally Released Under LGPL - original licence link has changed is not relivant.
35901  *
35902  * Fork - LGPL
35903  * <script type="text/javascript">
35904  */
35905   
35906
35907   
35908 Roo.grid.PropertyRecord = Roo.data.Record.create([
35909     {name:'name',type:'string'},  'value'
35910 ]);
35911
35912
35913 Roo.grid.PropertyStore = function(grid, source){
35914     this.grid = grid;
35915     this.store = new Roo.data.Store({
35916         recordType : Roo.grid.PropertyRecord
35917     });
35918     this.store.on('update', this.onUpdate,  this);
35919     if(source){
35920         this.setSource(source);
35921     }
35922     Roo.grid.PropertyStore.superclass.constructor.call(this);
35923 };
35924
35925
35926
35927 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
35928     setSource : function(o){
35929         this.source = o;
35930         this.store.removeAll();
35931         var data = [];
35932         for(var k in o){
35933             if(this.isEditableValue(o[k])){
35934                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
35935             }
35936         }
35937         this.store.loadRecords({records: data}, {}, true);
35938     },
35939
35940     onUpdate : function(ds, record, type){
35941         if(type == Roo.data.Record.EDIT){
35942             var v = record.data['value'];
35943             var oldValue = record.modified['value'];
35944             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
35945                 this.source[record.id] = v;
35946                 record.commit();
35947                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
35948             }else{
35949                 record.reject();
35950             }
35951         }
35952     },
35953
35954     getProperty : function(row){
35955        return this.store.getAt(row);
35956     },
35957
35958     isEditableValue: function(val){
35959         if(val && val instanceof Date){
35960             return true;
35961         }else if(typeof val == 'object' || typeof val == 'function'){
35962             return false;
35963         }
35964         return true;
35965     },
35966
35967     setValue : function(prop, value){
35968         this.source[prop] = value;
35969         this.store.getById(prop).set('value', value);
35970     },
35971
35972     getSource : function(){
35973         return this.source;
35974     }
35975 });
35976
35977 Roo.grid.PropertyColumnModel = function(grid, store){
35978     this.grid = grid;
35979     var g = Roo.grid;
35980     g.PropertyColumnModel.superclass.constructor.call(this, [
35981         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
35982         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
35983     ]);
35984     this.store = store;
35985     this.bselect = Roo.DomHelper.append(document.body, {
35986         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
35987             {tag: 'option', value: 'true', html: 'true'},
35988             {tag: 'option', value: 'false', html: 'false'}
35989         ]
35990     });
35991     Roo.id(this.bselect);
35992     var f = Roo.form;
35993     this.editors = {
35994         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
35995         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
35996         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
35997         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
35998         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
35999     };
36000     this.renderCellDelegate = this.renderCell.createDelegate(this);
36001     this.renderPropDelegate = this.renderProp.createDelegate(this);
36002 };
36003
36004 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
36005     
36006     
36007     nameText : 'Name',
36008     valueText : 'Value',
36009     
36010     dateFormat : 'm/j/Y',
36011     
36012     
36013     renderDate : function(dateVal){
36014         return dateVal.dateFormat(this.dateFormat);
36015     },
36016
36017     renderBool : function(bVal){
36018         return bVal ? 'true' : 'false';
36019     },
36020
36021     isCellEditable : function(colIndex, rowIndex){
36022         return colIndex == 1;
36023     },
36024
36025     getRenderer : function(col){
36026         return col == 1 ?
36027             this.renderCellDelegate : this.renderPropDelegate;
36028     },
36029
36030     renderProp : function(v){
36031         return this.getPropertyName(v);
36032     },
36033
36034     renderCell : function(val){
36035         var rv = val;
36036         if(val instanceof Date){
36037             rv = this.renderDate(val);
36038         }else if(typeof val == 'boolean'){
36039             rv = this.renderBool(val);
36040         }
36041         return Roo.util.Format.htmlEncode(rv);
36042     },
36043
36044     getPropertyName : function(name){
36045         var pn = this.grid.propertyNames;
36046         return pn && pn[name] ? pn[name] : name;
36047     },
36048
36049     getCellEditor : function(colIndex, rowIndex){
36050         var p = this.store.getProperty(rowIndex);
36051         var n = p.data['name'], val = p.data['value'];
36052         
36053         if(typeof(this.grid.customEditors[n]) == 'string'){
36054             return this.editors[this.grid.customEditors[n]];
36055         }
36056         if(typeof(this.grid.customEditors[n]) != 'undefined'){
36057             return this.grid.customEditors[n];
36058         }
36059         if(val instanceof Date){
36060             return this.editors['date'];
36061         }else if(typeof val == 'number'){
36062             return this.editors['number'];
36063         }else if(typeof val == 'boolean'){
36064             return this.editors['boolean'];
36065         }else{
36066             return this.editors['string'];
36067         }
36068     }
36069 });
36070
36071 /**
36072  * @class Roo.grid.PropertyGrid
36073  * @extends Roo.grid.EditorGrid
36074  * This class represents the  interface of a component based property grid control.
36075  * <br><br>Usage:<pre><code>
36076  var grid = new Roo.grid.PropertyGrid("my-container-id", {
36077       
36078  });
36079  // set any options
36080  grid.render();
36081  * </code></pre>
36082   
36083  * @constructor
36084  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
36085  * The container MUST have some type of size defined for the grid to fill. The container will be
36086  * automatically set to position relative if it isn't already.
36087  * @param {Object} config A config object that sets properties on this grid.
36088  */
36089 Roo.grid.PropertyGrid = function(container, config){
36090     config = config || {};
36091     var store = new Roo.grid.PropertyStore(this);
36092     this.store = store;
36093     var cm = new Roo.grid.PropertyColumnModel(this, store);
36094     store.store.sort('name', 'ASC');
36095     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
36096         ds: store.store,
36097         cm: cm,
36098         enableColLock:false,
36099         enableColumnMove:false,
36100         stripeRows:false,
36101         trackMouseOver: false,
36102         clicksToEdit:1
36103     }, config));
36104     this.getGridEl().addClass('x-props-grid');
36105     this.lastEditRow = null;
36106     this.on('columnresize', this.onColumnResize, this);
36107     this.addEvents({
36108          /**
36109              * @event beforepropertychange
36110              * Fires before a property changes (return false to stop?)
36111              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
36112              * @param {String} id Record Id
36113              * @param {String} newval New Value
36114          * @param {String} oldval Old Value
36115              */
36116         "beforepropertychange": true,
36117         /**
36118              * @event propertychange
36119              * Fires after a property changes
36120              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
36121              * @param {String} id Record Id
36122              * @param {String} newval New Value
36123          * @param {String} oldval Old Value
36124              */
36125         "propertychange": true
36126     });
36127     this.customEditors = this.customEditors || {};
36128 };
36129 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
36130     
36131      /**
36132      * @cfg {Object} customEditors map of colnames=> custom editors.
36133      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
36134      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
36135      * false disables editing of the field.
36136          */
36137     
36138       /**
36139      * @cfg {Object} propertyNames map of property Names to their displayed value
36140          */
36141     
36142     render : function(){
36143         Roo.grid.PropertyGrid.superclass.render.call(this);
36144         this.autoSize.defer(100, this);
36145     },
36146
36147     autoSize : function(){
36148         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
36149         if(this.view){
36150             this.view.fitColumns();
36151         }
36152     },
36153
36154     onColumnResize : function(){
36155         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
36156         this.autoSize();
36157     },
36158     /**
36159      * Sets the data for the Grid
36160      * accepts a Key => Value object of all the elements avaiable.
36161      * @param {Object} data  to appear in grid.
36162      */
36163     setSource : function(source){
36164         this.store.setSource(source);
36165         //this.autoSize();
36166     },
36167     /**
36168      * Gets all the data from the grid.
36169      * @return {Object} data  data stored in grid
36170      */
36171     getSource : function(){
36172         return this.store.getSource();
36173     }
36174 });/*
36175  * Based on:
36176  * Ext JS Library 1.1.1
36177  * Copyright(c) 2006-2007, Ext JS, LLC.
36178  *
36179  * Originally Released Under LGPL - original licence link has changed is not relivant.
36180  *
36181  * Fork - LGPL
36182  * <script type="text/javascript">
36183  */
36184  
36185 /**
36186  * @class Roo.LoadMask
36187  * A simple utility class for generically masking elements while loading data.  If the element being masked has
36188  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
36189  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
36190  * element's UpdateManager load indicator and will be destroyed after the initial load.
36191  * @constructor
36192  * Create a new LoadMask
36193  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
36194  * @param {Object} config The config object
36195  */
36196 Roo.LoadMask = function(el, config){
36197     this.el = Roo.get(el);
36198     Roo.apply(this, config);
36199     if(this.store){
36200         this.store.on('beforeload', this.onBeforeLoad, this);
36201         this.store.on('load', this.onLoad, this);
36202         this.store.on('loadexception', this.onLoad, this);
36203         this.removeMask = false;
36204     }else{
36205         var um = this.el.getUpdateManager();
36206         um.showLoadIndicator = false; // disable the default indicator
36207         um.on('beforeupdate', this.onBeforeLoad, this);
36208         um.on('update', this.onLoad, this);
36209         um.on('failure', this.onLoad, this);
36210         this.removeMask = true;
36211     }
36212 };
36213
36214 Roo.LoadMask.prototype = {
36215     /**
36216      * @cfg {Boolean} removeMask
36217      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
36218      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
36219      */
36220     /**
36221      * @cfg {String} msg
36222      * The text to display in a centered loading message box (defaults to 'Loading...')
36223      */
36224     msg : 'Loading...',
36225     /**
36226      * @cfg {String} msgCls
36227      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
36228      */
36229     msgCls : 'x-mask-loading',
36230
36231     /**
36232      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
36233      * @type Boolean
36234      */
36235     disabled: false,
36236
36237     /**
36238      * Disables the mask to prevent it from being displayed
36239      */
36240     disable : function(){
36241        this.disabled = true;
36242     },
36243
36244     /**
36245      * Enables the mask so that it can be displayed
36246      */
36247     enable : function(){
36248         this.disabled = false;
36249     },
36250
36251     // private
36252     onLoad : function(){
36253         this.el.unmask(this.removeMask);
36254     },
36255
36256     // private
36257     onBeforeLoad : function(){
36258         if(!this.disabled){
36259             this.el.mask(this.msg, this.msgCls);
36260         }
36261     },
36262
36263     // private
36264     destroy : function(){
36265         if(this.store){
36266             this.store.un('beforeload', this.onBeforeLoad, this);
36267             this.store.un('load', this.onLoad, this);
36268             this.store.un('loadexception', this.onLoad, this);
36269         }else{
36270             var um = this.el.getUpdateManager();
36271             um.un('beforeupdate', this.onBeforeLoad, this);
36272             um.un('update', this.onLoad, this);
36273             um.un('failure', this.onLoad, this);
36274         }
36275     }
36276 };/*
36277  * Based on:
36278  * Ext JS Library 1.1.1
36279  * Copyright(c) 2006-2007, Ext JS, LLC.
36280  *
36281  * Originally Released Under LGPL - original licence link has changed is not relivant.
36282  *
36283  * Fork - LGPL
36284  * <script type="text/javascript">
36285  */
36286 Roo.XTemplate = function(){
36287     Roo.XTemplate.superclass.constructor.apply(this, arguments);
36288     var s = this.html;
36289
36290     s = ['<tpl>', s, '</tpl>'].join('');
36291
36292     var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/;
36293
36294     var nameRe = /^<tpl\b[^>]*?for="(.*?)"/;
36295     var ifRe = /^<tpl\b[^>]*?if="(.*?)"/;
36296     var execRe = /^<tpl\b[^>]*?exec="(.*?)"/;
36297     var m, id = 0;
36298     var tpls = [];
36299
36300     while(m = s.match(re)){
36301        var m2 = m[0].match(nameRe);
36302        var m3 = m[0].match(ifRe);
36303        var m4 = m[0].match(execRe);
36304        var exp = null, fn = null, exec = null;
36305        var name = m2 && m2[1] ? m2[1] : '';
36306        if(m3){
36307            exp = m3 && m3[1] ? m3[1] : null;
36308            if(exp){
36309                fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
36310            }
36311        }
36312        if(m4){
36313            exp = m4 && m4[1] ? m4[1] : null;
36314            if(exp){
36315                exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
36316            }
36317        }
36318        if(name){
36319            switch(name){
36320                case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
36321                case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
36322                default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
36323            }
36324        }
36325        tpls.push({
36326             id: id,
36327             target: name,
36328             exec: exec,
36329             test: fn,
36330             body: m[1]||''
36331         });
36332        s = s.replace(m[0], '{xtpl'+ id + '}');
36333        ++id;
36334     }
36335     for(var i = tpls.length-1; i >= 0; --i){
36336         this.compileTpl(tpls[i]);
36337     }
36338     this.master = tpls[tpls.length-1];
36339     this.tpls = tpls;
36340 };
36341 Roo.extend(Roo.XTemplate, Roo.Template, {
36342
36343     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
36344
36345     applySubTemplate : function(id, values, parent){
36346         var t = this.tpls[id];
36347         if(t.test && !t.test.call(this, values, parent)){
36348             return '';
36349         }
36350         if(t.exec && t.exec.call(this, values, parent)){
36351             return '';
36352         }
36353         var vs = t.target ? t.target.call(this, values, parent) : values;
36354         parent = t.target ? values : parent;
36355         if(t.target && vs instanceof Array){
36356             var buf = [];
36357             for(var i = 0, len = vs.length; i < len; i++){
36358                 buf[buf.length] = t.compiled.call(this, vs[i], parent);
36359             }
36360             return buf.join('');
36361         }
36362         return t.compiled.call(this, vs, parent);
36363     },
36364
36365     compileTpl : function(tpl){
36366         var fm = Roo.util.Format;
36367         var useF = this.disableFormats !== true;
36368         var sep = Roo.isGecko ? "+" : ",";
36369         var fn = function(m, name, format, args){
36370             if(name.substr(0, 4) == 'xtpl'){
36371                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
36372             }
36373             var v;
36374             if(name.indexOf('.') != -1){
36375                 v = name;
36376             }else{
36377                 v = "values['" + name + "']";
36378             }
36379             if(format && useF){
36380                 args = args ? ',' + args : "";
36381                 if(format.substr(0, 5) != "this."){
36382                     format = "fm." + format + '(';
36383                 }else{
36384                     format = 'this.call("'+ format.substr(5) + '", ';
36385                     args = ", values";
36386                 }
36387             }else{
36388                 args= ''; format = "("+v+" === undefined ? '' : ";
36389             }
36390             return "'"+ sep + format + v + args + ")"+sep+"'";
36391         };
36392         var body;
36393         // branched to use + in gecko and [].join() in others
36394         if(Roo.isGecko){
36395             body = "tpl.compiled = function(values, parent){ return '" +
36396                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
36397                     "';};";
36398         }else{
36399             body = ["tpl.compiled = function(values, parent){ return ['"];
36400             body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
36401             body.push("'].join('');};");
36402             body = body.join('');
36403         }
36404         /** eval:var:zzzzzzz */
36405         eval(body);
36406         return this;
36407     },
36408
36409     applyTemplate : function(values){
36410         return this.master.compiled.call(this, values, {});
36411         var s = this.subs;
36412     },
36413
36414     apply : function(){
36415         return this.applyTemplate.apply(this, arguments);
36416     },
36417
36418     compile : function(){return this;}
36419 });
36420
36421 Roo.XTemplate.from = function(el){
36422     el = Roo.getDom(el);
36423     return new Roo.XTemplate(el.value || el.innerHTML);
36424 };/*
36425  * Original code for Roojs - LGPL
36426  * <script type="text/javascript">
36427  */
36428  
36429 /**
36430  * @class Roo.XComponent
36431  * A delayed Element creator...
36432  * 
36433  * Mypart.xyx = new Roo.XComponent({
36434
36435     parent : 'Mypart.xyz', // empty == document.element.!!
36436     order : '001',
36437     name : 'xxxx'
36438     region : 'xxxx'
36439     disabled : function() {} 
36440      
36441     tree : function() { // return an tree of xtype declared components
36442         var MODULE = this;
36443         return 
36444         {
36445             xtype : 'NestedLayoutPanel',
36446             // technicall
36447         }
36448      ]
36449  *})
36450  * @extends Roo.util.Observable
36451  * @constructor
36452  * @param cfg {Object} configuration of component
36453  * 
36454  */
36455 Roo.XComponent = function(cfg) {
36456     Roo.apply(this, cfg);
36457     this.addEvents({ 
36458         /**
36459              * @event built
36460              * Fires when this the componnt is built
36461              * @param {Roo.XComponent} c the component
36462              */
36463         'built' : true,
36464         /**
36465              * @event buildcomplete
36466              * Fires on the top level element when all elements have been built
36467              * @param {Roo.XComponent} c the top level component.
36468          */
36469         'buildcomplete' : true
36470         
36471     });
36472     
36473     Roo.XComponent.register(this);
36474     this.modules = false;
36475     this.el = false; // where the layout goes..
36476     
36477     
36478 }
36479 Roo.extend(Roo.XComponent, Roo.util.Observable, {
36480     /**
36481      * @property el
36482      * The created element (with Roo.factory())
36483      * @type {Roo.Layout}
36484      */
36485     el  : false,
36486     
36487     /**
36488      * @property el
36489      * for BC  - use el in new code
36490      * @type {Roo.Layout}
36491      */
36492     panel : false,
36493     
36494     /**
36495      * @property layout
36496      * for BC  - use el in new code
36497      * @type {Roo.Layout}
36498      */
36499     layout : false,
36500     
36501      /**
36502      * @cfg {Function|boolean} disabled
36503      * If this module is disabled by some rule, return true from the funtion
36504      */
36505     disabled : false,
36506     
36507     /**
36508      * @cfg {String} parent 
36509      * Name of parent element which it get xtype added to..
36510      */
36511     parent: false,
36512     
36513     /**
36514      * @cfg {String} order
36515      * Used to set the order in which elements are created (usefull for multiple tabs)
36516      */
36517     
36518     order : false,
36519     /**
36520      * @cfg {String} name
36521      * String to display while loading.
36522      */
36523     name : false,
36524     /**
36525      * @cfg {Array} items
36526      * A single item array - the first element is the root of the tree..
36527      * It's done this way to stay compatible with the Xtype system...
36528      */
36529     items : false
36530      
36531      
36532     
36533 });
36534
36535 Roo.apply(Roo.XComponent, {
36536     
36537     /**
36538      * @property  buildCompleted
36539      * True when the builder has completed building the interface.
36540      * @type Boolean
36541      */
36542     buildCompleted : false,
36543      
36544     /**
36545      * @property  topModule
36546      * the upper most module - uses document.element as it's constructor.
36547      * @type Object
36548      */
36549      
36550     topModule  : false,
36551       
36552     /**
36553      * @property  modules
36554      * array of modules to be created by registration system.
36555      * @type Roo.XComponent
36556      */
36557     
36558     modules : [],
36559       
36560     
36561     /**
36562      * Register components to be built later.
36563      *
36564      * This solves the following issues
36565      * - Building is not done on page load, but after an authentication process has occured.
36566      * - Interface elements are registered on page load
36567      * - Parent Interface elements may not be loaded before child, so this handles that..
36568      * 
36569      *
36570      * example:
36571      * 
36572      * MyApp.register({
36573           order : '000001',
36574           module : 'Pman.Tab.projectMgr',
36575           region : 'center',
36576           parent : 'Pman.layout',
36577           disabled : false,  // or use a function..
36578         })
36579      
36580      * * @param {Object} details about module
36581      */
36582     register : function(obj) {
36583         this.modules.push(obj);
36584          
36585     },
36586     /**
36587      * convert a string to an object..
36588      * 
36589      */
36590     
36591     toObject : function(str)
36592     {
36593         if (!str || typeof(str) == 'object') {
36594             return str;
36595         }
36596         var ar = str.split('.');
36597         var rt, o;
36598         rt = ar.shift();
36599             /** eval:var:o */
36600         eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
36601         if (o === false) {
36602             throw "Module not found : " + str;
36603         }
36604         Roo.each(ar, function(e) {
36605             if (typeof(o[e]) == 'undefined') {
36606                 throw "Module not found : " + str;
36607             }
36608             o = o[e];
36609         });
36610         return o;
36611         
36612     },
36613     
36614     
36615     /**
36616      * move modules into their correct place in the tree..
36617      * 
36618      */
36619     preBuild : function ()
36620     {
36621         
36622         Roo.each(this.modules , function (obj)
36623         {
36624             obj.parent = this.toObject(obj.parent);
36625             
36626             if (!obj.parent) {
36627                 this.topModule = obj;
36628                 return;
36629             }
36630             
36631             if (!obj.parent.modules) {
36632                 obj.parent.modules = new Roo.util.MixedCollection(false, 
36633                     function(o) { return o.order + '' }
36634                 );
36635             }
36636             
36637             obj.parent.modules.add(obj);
36638         }, this);
36639     },
36640     
36641      /**
36642      * make a list of modules to build.
36643      * @return {Array} list of modules. 
36644      */ 
36645     
36646     buildOrder : function()
36647     {
36648         var _this = this;
36649         var cmp = function(a,b) {   
36650             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
36651         };
36652         
36653         if (!this.topModule || !this.topModule.modules) {
36654             throw "No top level modules to build";
36655         }
36656        
36657         // make a flat list in order of modules to build.
36658         var mods = [ this.topModule ];
36659         
36660         
36661         // add modules to their parents..
36662         var addMod = function(m) {
36663            // Roo.debug && Roo.log(m.modKey);
36664             
36665             mods.push(m);
36666             if (m.modules) {
36667                 m.modules.keySort('ASC',  cmp );
36668                 m.modules.each(addMod);
36669             }
36670             // not sure if this is used any more..
36671             if (m.finalize) {
36672                 m.finalize.name = m.name + " (clean up) ";
36673                 mods.push(m.finalize);
36674             }
36675             
36676         }
36677         this.topModule.modules.keySort('ASC',  cmp );
36678         this.topModule.modules.each(addMod);
36679         return mods;
36680     },
36681     
36682      /**
36683      * Build the registered modules.
36684      * @param {Object} parent element.
36685      * @param {Function} optional method to call after module has been added.
36686      * 
36687      */ 
36688    
36689     build : function() 
36690     {
36691         
36692         this.preBuild();
36693         var mods = this.buildOrder();
36694       
36695         //this.allmods = mods;
36696         //Roo.debug && Roo.log(mods);
36697         //return;
36698         if (!mods.length) { // should not happen
36699             throw "NO modules!!!";
36700         }
36701         
36702         
36703         
36704         // flash it up as modal - so we store the mask!?
36705         Roo.MessageBox.show({ title: 'loading' });
36706         Roo.MessageBox.show({
36707            title: "Please wait...",
36708            msg: "Building Interface...",
36709            width:450,
36710            progress:true,
36711            closable:false,
36712            modal: false
36713           
36714         });
36715         var total = mods.length;
36716         
36717         var _this = this;
36718         var progressRun = function() {
36719             if (!mods.length) {
36720                 Roo.debug && Roo.log('hide?');
36721                 Roo.MessageBox.hide();
36722                 _this.topModule.fireEvent('buildcomplete', _this.topModule);
36723                 return;    
36724             }
36725             
36726             var m = mods.shift();
36727             Roo.debug && Roo.log(m);
36728             if (typeof(m) == 'function') { // not sure if this is supported any more..
36729                 m.call(this);
36730                 return progressRun.defer(10, _this);
36731             } 
36732             
36733             Roo.MessageBox.updateProgress(
36734                 (total  - mods.length)/total,  "Building Interface " + (total  - mods.length) + 
36735                     " of " + total + 
36736                     (m.name ? (' - ' + m.name) : '')
36737                     );
36738             
36739          
36740             
36741             var disabled = (typeof(m.disabled) == 'function') ?
36742                 m.disabled.call(m.module.disabled) : m.disabled;    
36743             
36744             
36745             if (disabled) {
36746                 return progressRun(); // we do not update the display!
36747             }
36748             
36749             if (!m.parent) {
36750                 // it's a top level one..
36751                 var layoutbase = new Ext.BorderLayout(document.body, {
36752                
36753                     center: {
36754                          titlebar: false,
36755                          autoScroll:false,
36756                          closeOnTab: true,
36757                          tabPosition: 'top',
36758                          //resizeTabs: true,
36759                          alwaysShowTabs: true,
36760                          minTabWidth: 140
36761                     }
36762                 });
36763                 var tree = m.tree();
36764                 tree.region = 'center';
36765                 m.el = layoutbase.addxtype(tree);
36766                 m.panel = m.el;
36767                 m.layout = m.panel.layout;    
36768                 return progressRun.defer(10, _this);
36769             }
36770             
36771             var tree = m.tree();
36772             tree.region = tree.region || m.region;
36773             m.el = m.parent.el.addxtype(tree);
36774             m.fireEvent('built', m);
36775             m.panel = m.el;
36776             m.layout = m.panel.layout;    
36777             progressRun.defer(10, _this); 
36778             
36779         }
36780         progressRun.defer(1, _this);
36781      
36782         
36783         
36784     }
36785      
36786    
36787     
36788     
36789 });
36790  //<script type="text/javascript">
36791
36792
36793 /**
36794  * @class Roo.Login
36795  * @extends Roo.LayoutDialog
36796  * A generic Login Dialog..... - only one needed in theory!?!?
36797  *
36798  * Fires XComponent builder on success...
36799  * 
36800  * Sends 
36801  *    username,password, lang = for login actions.
36802  *    check = 1 for periodic checking that sesion is valid.
36803  *    passwordRequest = email request password
36804  *    logout = 1 = to logout
36805  * 
36806  * Affects: (this id="????" elements)
36807  *   loading  (removed) (used to indicate application is loading)
36808  *   loading-mask (hides) (used to hide application when it's building loading)
36809  *   
36810  * 
36811  * Usage: 
36812  *    
36813  * 
36814  * Myapp.login = Roo.Login({
36815      url: xxxx,
36816    
36817      realm : 'Myapp', 
36818      
36819      
36820      method : 'POST',
36821      
36822      
36823      * 
36824  })
36825  * 
36826  * 
36827  * 
36828  **/
36829  
36830 Roo.Login = function(cfg)
36831 {
36832     this.addEvents({
36833         'refreshed' : true
36834     });
36835     
36836     Roo.apply(this,cfg);
36837     
36838     Roo.onReady(function() {
36839         this.onLoad();
36840     }, this);
36841     // call parent..
36842     
36843    
36844     Roo.Login.superclass.constructor.call(this, this);
36845     //this.addxtype(this.items[0]);
36846     
36847     
36848 }
36849
36850
36851 Roo.extend(Roo.Login, Roo.LayoutDialog, {
36852     
36853     /**
36854      * @cfg {String} method
36855      * Method used to query for login details.
36856      */
36857     
36858     method : 'POST',
36859     /**
36860      * @cfg {String} url
36861      * URL to query login data. - eg. baseURL + '/Login.php'
36862      */
36863     url : '',
36864     
36865     /**
36866      * @property user
36867      * The user data - if user.id < 0 then login will be bypassed. (used for inital setup situation.
36868      * @type {Object} 
36869      */
36870     user : false,
36871     /**
36872      * @property checkFails
36873      * Number of times we have attempted to get authentication check, and failed.
36874      * @type {Number} 
36875      */
36876     checkFails : 0,
36877       /**
36878      * @property intervalID
36879      * The window interval that does the constant login checking.
36880      * @type {Number} 
36881      */
36882     intervalID : 0,
36883     
36884     
36885     onLoad : function() // called on page load...
36886     {
36887         // load 
36888          
36889         if (Roo.get('loading')) { // clear any loading indicator..
36890             Roo.get('loading').remove();
36891         }
36892         
36893         //this.switchLang('en'); // set the language to english..
36894        
36895         this.check({
36896             success:  function(response, opts)  {  // check successfull...
36897             
36898                 var res = this.processResponse(response);
36899                 this.checkFails =0;
36900                 if (!res.success) { // error!
36901                     this.checkFails = 5;
36902                     //console.log('call failure');
36903                     return this.failure(response,opts);
36904                 }
36905                 
36906                 if (!res.data.id) { // id=0 == login failure.
36907                     return this.show();
36908                 }
36909                 
36910                               
36911                         //console.log(success);
36912                 this.fillAuth(res.data);   
36913                 this.checkFails =0;
36914                 Roo.XComponent.build();
36915             },
36916             failure : this.show
36917         });
36918         
36919     }, 
36920     
36921     
36922     check: function(cfg) // called every so often to refresh cookie etc..
36923     {
36924         if (cfg.again) { // could be undefined..
36925             this.checkFails++;
36926         } else {
36927             this.checkFails = 0;
36928         }
36929         var _this = this;
36930         if (this.sending) {
36931             if ( this.checkFails > 4) {
36932                 Roo.MessageBox.alert("Error",  
36933                     "Error getting authentication status. - try reloading, or wait a while", function() {
36934                         _this.sending = false;
36935                     }); 
36936                 return;
36937             }
36938             cfg.again = true;
36939             _this.check.defer(10000, _this, [ cfg ]); // check in 10 secs.
36940             return;
36941         }
36942         this.sending = true;
36943         
36944         Roo.Ajax.request({  
36945             url: this.url,
36946             params: {
36947                 getAuthUser: true
36948             },  
36949             method: this.method,
36950             success:  cfg.success || this.success,
36951             failure : cfg.failure || this.failure,
36952             scope : this,
36953             callCfg : cfg
36954               
36955         });  
36956     }, 
36957     
36958     
36959     logout: function()
36960     {
36961         window.onbeforeunload = function() { }; // false does not work for IE..
36962         this.user = false;
36963         var _this = this;
36964         
36965         Roo.Ajax.request({  
36966             url: this.url,
36967             params: {
36968                 logout: 1
36969             },  
36970             method: 'GET',
36971             failure : function() {
36972                 Roo.MessageBox.alert("Error", "Error logging out. - continuing anyway.", function() {
36973                     document.location = document.location.toString() + '?ts=' + Math.random();
36974                 });
36975                 
36976             },
36977             success : function() {
36978                 _this.user = false;
36979                 this.checkFails =0;
36980                 // fixme..
36981                 document.location = document.location.toString() + '?ts=' + Math.random();
36982             }
36983               
36984               
36985         }); 
36986     },
36987     
36988     processResponse : function (response)
36989     {
36990         var res = '';
36991         try {
36992             res = Roo.decode(response.responseText);
36993             // oops...
36994             if (typeof(res) != 'object') {
36995                 res = { success : false, errorMsg : res, errors : true };
36996             }
36997             if (typeof(res.success) == 'undefined') {
36998                 res.success = false;
36999             }
37000             
37001         } catch(e) {
37002             res = { success : false,  errorMsg : response.responseText, errors : true };
37003         }
37004         return res;
37005     },
37006     
37007     success : function(response, opts)  // check successfull...
37008     {  
37009         this.sending = false;
37010         var res = this.processResponse(response);
37011         if (!res.success) {
37012             return this.failure(response, opts);
37013         }
37014         if (!res.data || !res.data.id) {
37015             return this.failure(response,opts);
37016         }
37017         //console.log(res);
37018         this.fillAuth(res.data);
37019         
37020         this.checkFails =0;
37021         
37022     },
37023     
37024     
37025     failure : function (response, opts) // called if login 'check' fails.. (causes re-check)
37026     {
37027         this.authUser = -1;
37028         this.sending = false;
37029         var res = this.processResponse(response);
37030         //console.log(res);
37031         if ( this.checkFails > 2) {
37032         
37033             Roo.MessageBox.alert("Error", res.errorMsg ? res.errorMsg : 
37034                 "Error getting authentication status. - try reloading"); 
37035             return;
37036         }
37037         opts.callCfg.again = true;
37038         this.check.defer(1000, this, [ opts.callCfg ]);
37039         return;  
37040     },
37041     
37042     
37043     
37044     fillAuth: function(au) {
37045         this.startAuthCheck();
37046         this.authUserId = au.id;
37047         this.authUser = au;
37048         this.lastChecked = new Date();
37049         this.fireEvent('refreshed', au);
37050         //Pman.Tab.FaxQueue.newMaxId(au.faxMax);
37051         //Pman.Tab.FaxTab.setTitle(au.faxNumPending);
37052         au.lang = au.lang || 'en';
37053         //this.switchLang(Roo.state.Manager.get('Pman.Login.lang', 'en'));
37054         Roo.state.Manager.set( this.realm + 'lang' , au.lang);
37055         this.switchLang(au.lang );
37056         
37057      
37058         // open system... - -on setyp..
37059         if (this.authUserId  < 0) {
37060             Roo.MessageBox.alert("Warning", 
37061                 "This is an open system - please set up a admin user with a password.");  
37062         }
37063          
37064         //Pman.onload(); // which should do nothing if it's a re-auth result...
37065         
37066              
37067     },
37068     
37069     startAuthCheck : function() // starter for timeout checking..
37070     {
37071         if (this.intervalID) { // timer already in place...
37072             return false;
37073         }
37074         var _this = this;
37075         this.intervalID =  window.setInterval(function() {
37076               _this.check(false);
37077             }, 120000); // every 120 secs = 2mins..
37078         
37079         
37080     },
37081          
37082     
37083     switchLang : function (lang) 
37084     {
37085         _T = typeof(_T) == 'undefined' ? false : _T;
37086           if (!_T || !lang.length) {
37087             return;
37088         }
37089         
37090         if (!_T && lang != 'en') {
37091             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
37092             return;
37093         }
37094         
37095         if (typeof(_T.en) == 'undefined') {
37096             _T.en = {};
37097             Roo.apply(_T.en, _T);
37098         }
37099         
37100         if (typeof(_T[lang]) == 'undefined') {
37101             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
37102             return;
37103         }
37104         
37105         
37106         Roo.apply(_T, _T[lang]);
37107         // just need to set the text values for everything...
37108         var _this = this;
37109         /* this will not work ...
37110         if (this.form) { 
37111             
37112                
37113             function formLabel(name, val) {
37114                 _this.form.findField(name).fieldEl.child('label').dom.innerHTML  = val;
37115             }
37116             
37117             formLabel('password', "Password"+':');
37118             formLabel('username', "Email Address"+':');
37119             formLabel('lang', "Language"+':');
37120             this.dialog.setTitle("Login");
37121             this.dialog.buttons[0].setText("Forgot Password");
37122             this.dialog.buttons[1].setText("Login");
37123         }
37124         */
37125         
37126         
37127     },
37128     
37129     
37130     title: "Login",
37131     modal: true,
37132     width:  350,
37133     //height: 230,
37134     height: 180,
37135     shadow: true,
37136     minWidth:200,
37137     minHeight:180,
37138     //proxyDrag: true,
37139     closable: false,
37140     draggable: false,
37141     collapsible: false,
37142     resizable: false,
37143     center: {  // needed??
37144         autoScroll:false,
37145         titlebar: false,
37146        // tabPosition: 'top',
37147         hideTabs: true,
37148         closeOnTab: true,
37149         alwaysShowTabs: false
37150     } ,
37151     listeners : {
37152         
37153         show  : function(dlg)
37154         {
37155             //console.log(this);
37156             this.form = this.layout.getRegion('center').activePanel.form;
37157             this.form.dialog = dlg;
37158             this.buttons[0].form = this.form;
37159             this.buttons[0].dialog = dlg;
37160             this.buttons[1].form = this.form;
37161             this.buttons[1].dialog = dlg;
37162            
37163            //this.resizeToLogo.defer(1000,this);
37164             // this is all related to resizing for logos..
37165             //var sz = Roo.get(Pman.Login.form.el.query('img')[0]).getSize();
37166            //// if (!sz) {
37167              //   this.resizeToLogo.defer(1000,this);
37168              //   return;
37169            // }
37170             //var w = Ext.lib.Dom.getViewWidth() - 100;
37171             //var h = Ext.lib.Dom.getViewHeight() - 100;
37172             //this.resizeTo(Math.max(350, Math.min(sz.width + 30, w)),Math.min(sz.height+200, h));
37173             //this.center();
37174             if (this.disabled) {
37175                 this.hide();
37176                 return;
37177             }
37178             
37179             if (this.user.id < 0) { // used for inital setup situations.
37180                 return;
37181             }
37182             
37183             if (this.intervalID) {
37184                 // remove the timer
37185                 window.clearInterval(this.intervalID);
37186                 this.intervalID = false;
37187             }
37188             
37189             
37190             if (Roo.get('loading')) {
37191                 Roo.get('loading').remove();
37192             }
37193             if (Roo.get('loading-mask')) {
37194                 Roo.get('loading-mask').hide();
37195             }
37196             
37197             //incomming._node = tnode;
37198             this.form.reset();
37199             //this.dialog.modal = !modal;
37200             //this.dialog.show();
37201             this.el.unmask(); 
37202             
37203             
37204             this.form.setValues({
37205                 'username' : Roo.state.Manager.get(this.realm + '.username', ''),
37206                 'lang' : Roo.state.Manager.get(this.realm + '.lang', 'en')
37207             });
37208             
37209             this.switchLang(Roo.state.Manager.get(this.realm + '.lang', 'en'));
37210             if (this.form.findField('username').getValue().length > 0 ){
37211                 this.form.findField('password').focus();
37212             } else {
37213                this.form.findField('username').focus();
37214             }
37215     
37216         }
37217     },
37218     items : [
37219          {
37220        
37221             xtype : 'ContentPanel',
37222             xns : Roo,
37223             region: 'center',
37224             fitToFrame : true,
37225             
37226             items : [
37227     
37228                 {
37229                
37230                     xtype : 'Form',
37231                     xns : Roo.form,
37232                     labelWidth: 100,
37233                     style : 'margin: 10px;',
37234                     
37235                     listeners : {
37236                         actionfailed : function(f, act) {
37237                             // form can return { errors: .... }
37238                                 
37239                             //act.result.errors // invalid form element list...
37240                             //act.result.errorMsg// invalid form element list...
37241                             
37242                             this.dialog.el.unmask();
37243                             Roo.MessageBox.alert("Error", act.result.errorMsg ? act.result.errorMsg : 
37244                                         "Login failed - communication error - try again.");
37245                                       
37246                         },
37247                         actioncomplete: function(re, act) {
37248                              
37249                             Roo.state.Manager.set(
37250                                 this.dialog.realm + '.username',  
37251                                     this.findField('username').getValue()
37252                             );
37253                             Roo.state.Manager.set(
37254                                 this.dialog.realm + '.lang',  
37255                                 this.findField('lang').getValue() 
37256                             );
37257                             
37258                             this.dialog.fillAuth(act.result.data);
37259                               
37260                             this.dialog.hide();
37261                             
37262                             if (Roo.get('loading-mask')) {
37263                                 Roo.get('loading-mask').show();
37264                             }
37265                             Roo.XComponent.build();
37266                             
37267                              
37268                             
37269                         }
37270                     },
37271                     items : [
37272                         {
37273                             xtype : 'TextField',
37274                             xns : Roo.form,
37275                             fieldLabel: "Email Address",
37276                             name: 'username',
37277                             width:200,
37278                             autoCreate : {tag: "input", type: "text", size: "20"}
37279                         },
37280                         {
37281                             xtype : 'TextField',
37282                             xns : Roo.form,
37283                             fieldLabel: "Password",
37284                             inputType: 'password',
37285                             name: 'password',
37286                             width:200,
37287                             autoCreate : {tag: "input", type: "text", size: "20"},
37288                             listeners : {
37289                                 specialkey : function(e,ev) {
37290                                     if (ev.keyCode == 13) {
37291                                         this.form.dialog.el.mask("Logging in");
37292                                         this.form.doAction('submit', {
37293                                             url: this.form.dialog.url,
37294                                             method: this.form.dialog.method
37295                                         });
37296                                     }
37297                                 }
37298                             }  
37299                         },
37300                         {
37301                             xtype : 'ComboBox',
37302                             xns : Roo.form,
37303                             fieldLabel: "Language",
37304                             name : 'langdisp',
37305                             store: {
37306                                 xtype : 'SimpleStore',
37307                                 fields: ['lang', 'ldisp'],
37308                                 data : [
37309                                     [ 'en', 'English' ],
37310                                     [ 'zh_HK' , '\u7E41\u4E2D' ],
37311                                     [ 'zh_CN', '\u7C21\u4E2D' ]
37312                                 ]
37313                             },
37314                             
37315                             valueField : 'lang',
37316                             hiddenName:  'lang',
37317                             width: 200,
37318                             displayField:'ldisp',
37319                             typeAhead: false,
37320                             editable: false,
37321                             mode: 'local',
37322                             triggerAction: 'all',
37323                             emptyText:'Select a Language...',
37324                             selectOnFocus:true,
37325                             listeners : {
37326                                 select :  function(cb, rec, ix) {
37327                                     this.form.switchLang(rec.data.lang);
37328                                 }
37329                             }
37330                         
37331                         }
37332                     ]
37333                 }
37334                   
37335                 
37336             ]
37337         }
37338     ],
37339     buttons : [
37340         {
37341             xtype : 'Button',
37342             xns : 'Roo',
37343             text : "Forgot Password",
37344             listeners : {
37345                 click : function() {
37346                     //console.log(this);
37347                     var n = this.form.findField('username').getValue();
37348                     if (!n.length) {
37349                         Roo.MessageBox.alert("Error", "Fill in your email address");
37350                         return;
37351                     }
37352                     Roo.Ajax.request({
37353                         url: this.dialog.url,
37354                         params: {
37355                             passwordRequest: n
37356                         },
37357                         method: this.dialog.method,
37358                         success:  function(response, opts)  {  // check successfull...
37359                         
37360                             var res = this.dialog.processResponse(response);
37361                             if (!res.success) { // error!
37362                                Roo.MessageBox.alert("Error" ,
37363                                     res.errorMsg ? res.errorMsg  : "Problem Requesting Password Reset");
37364                                return;
37365                             }
37366                             Roo.MessageBox.alert("Notice" ,
37367                                 "Please check you email for the Password Reset message");
37368                         },
37369                         failure : function() {
37370                             Roo.MessageBox.alert("Error" , "Problem Requesting Password Reset");
37371                         }
37372                         
37373                     });
37374                 }
37375             }
37376         },
37377         {
37378             xtype : 'Button',
37379             xns : 'Roo',
37380             text : "Login",
37381             listeners : {
37382                 
37383                 click : function () {
37384                         
37385                     this.dialog.el.mask("Logging in");
37386                     this.form.doAction('submit', {
37387                             url: this.dialog.url,
37388                             method: this.dialog.method
37389                     });
37390                 }
37391             }
37392         }
37393     ]
37394   
37395   
37396 })
37397  
37398
37399
37400